Implement IS Nitro Async USB accesses

This commit is contained in:
Lorenzooone 2024-11-08 03:21:42 +01:00
parent 3b18026c74
commit dccc50036c
15 changed files with 882 additions and 117 deletions

View File

@ -5,7 +5,8 @@
#include "capture_structs.hpp"
int initial_cleanup_capture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers);
int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc);
void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf);
int EndAcquisitionCapture(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
int EndAcquisitionCapture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers);
void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
#endif

View File

@ -5,7 +5,8 @@
#include "capture_structs.hpp"
int initial_cleanup_emulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers);
int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc);
void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf);
int EndAcquisitionEmulator(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
int EndAcquisitionEmulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
#endif

View File

@ -2,12 +2,39 @@
#define __USB_IS_NITRO_ACQUISITION_GENERAL_HPP
#include "usb_is_nitro_communications.hpp"
#include "utils.hpp"
#include "capture_structs.hpp"
#include "display_structs.hpp"
#define NUM_CAPTURE_RECEIVED_DATA_BUFFERS 4
struct ISNitroCaptureReceivedData {
volatile bool in_use;
uint32_t index;
CaptureReceived buffer;
SharedConsumerMutex* is_buffer_free_shared_mutex;
int* status;
uint32_t* last_index;
CaptureData* capture_data;
std::chrono::time_point<std::chrono::high_resolution_clock>* clock_start;
CaptureScreensType curr_capture_type;
isn_async_callback_data cb_data;
};
uint64_t _is_nitro_get_video_in_size(CaptureScreensType capture_type);
int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_nitro_usb_device* usb_device_desc);
int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc);
int EndAcquisition(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
int EndAcquisition(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
int is_nitro_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_start);
void is_nitro_read_frame_request(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, CaptureScreensType curr_capture_type, uint32_t index);
ISNitroCaptureReceivedData* is_nitro_get_free_buffer(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
bool is_nitro_are_buffers_all_free(ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
int is_nitro_get_num_free_buffers(ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
void wait_all_is_nitro_transfers_done(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
void wait_all_is_nitro_buffers_free(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
void wait_one_is_nitro_buffer_free(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
int get_is_nitro_status(ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
void reset_is_nitro_status(ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
#endif

View File

@ -25,6 +25,29 @@ enum is_nitro_forward_config_values_rate {
IS_NITRO_FORWARD_CONFIG_RATE_QUARTER = 3,
};
typedef void (*isn_async_callback_function)(void* user_data, int transfer_length, int transfer_status);
struct isn_async_callback_data {
isn_async_callback_function function;
void* actual_user_data;
void* transfer_data;
void* handle;
void* base_transfer_data;
std::mutex transfer_data_access;
SharedConsumerMutex* is_transfer_done_mutex;
SharedConsumerMutex* is_transfer_data_ready_mutex;
size_t requested_length;
uint8_t* buffer;
size_t actual_length;
int status_value;
bool is_data_ready;
int internal_index;
isn_async_callback_data** cb_queue;
int* queue_elems;
bool* one_transfer_active;
isn_async_callback_data* cb_active_transfer;
};
struct is_nitro_usb_device {
std::string name;
int vid;
@ -64,5 +87,13 @@ int ResetCPUStart(is_nitro_device_handlers* handlers, const is_nitro_usb_device*
int ResetCPUEnd(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc);
int ResetFullHardware(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc);
int ReadFrame(is_nitro_device_handlers* handlers, uint8_t* buf, int length, const is_nitro_usb_device* device_desc);
void ReadFrameAsync(is_nitro_device_handlers* handlers, uint8_t* buf, int length, const is_nitro_usb_device* device_desc, isn_async_callback_data* cb_data);
void CloseAsyncRead(is_nitro_device_handlers* handlers, isn_async_callback_data* cb_data);
void SetupISNitroAsyncThread(is_nitro_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready);
void EndISNitroAsyncThread(is_nitro_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready);
void SleepBetweenTransfers(is_nitro_device_handlers* handlers, float ms);
void SleepUntilOneFree(is_nitro_device_handlers* handlers, SharedConsumerMutex* mutex);
void SleepUntilFree(is_nitro_device_handlers* handlers, SharedConsumerMutex* mutex, int index);
#endif

View File

@ -3,12 +3,22 @@
#include <vector>
#include "capture_structs.hpp"
#include "utils.hpp"
#include "usb_is_nitro_communications.hpp"
#include "usb_is_nitro_acquisition_general.hpp"
void is_driver_list_devices(std::vector<CaptureDevice>& devices_list, bool* not_supported_elems, int* curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc);
is_nitro_device_handlers* is_driver_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device);
is_nitro_device_handlers* is_driver_serial_reconnection(CaptureDevice* device);
void is_driver_end_connection(is_nitro_device_handlers* handlers);
int is_driver_bulk_out(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred);
int is_driver_bulk_in(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred);
int is_driver_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred);
int is_driver_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred);
int is_drive_async_in_start(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, isn_async_callback_data* cb_data);
void is_nitro_is_driver_cancel_callback(isn_async_callback_data* cb_data);
#endif
void is_nitro_is_driver_start_thread(std::thread* thread_ptr, bool* usb_thread_run, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, is_nitro_device_handlers* handlers, ConsumerMutex* AsyncMutexPtr);
void is_nitro_is_driver_close_thread(std::thread* thread_ptr, bool* usb_thread_run, ISNitroCaptureReceivedData* is_nitro_capture_recv_data);
void is_nitro_is_driver_sleep_between_transfers(float ms);
void is_nitro_is_driver_sleep_until_one_free(SharedConsumerMutex* mutex);
void is_nitro_is_driver_sleep_until_free(SharedConsumerMutex* mutex, int index);
#endif

View File

@ -5,10 +5,18 @@
#include "capture_structs.hpp"
#include "usb_is_nitro_communications.hpp"
void is_nitro_libusb_start_thread(std::thread* thread_ptr, bool* usb_thread_run);
void is_nitro_libusb_close_thread(std::thread* thread_ptr, bool* usb_thread_run);
void is_nitro_libusb_sleep_between_transfers(float ms);
void is_nitro_libusb_sleep_until_one_free(SharedConsumerMutex* mutex);
void is_nitro_libusb_sleep_until_free(SharedConsumerMutex* mutex, int index);
void is_nitro_libusb_list_devices(std::vector<CaptureDevice>& devices_list, bool* no_access_elems, bool* not_supported_elems, int* curr_serial_extra_id_is_nitro, const size_t num_is_nitro_desc);
is_nitro_device_handlers* is_nitro_libusb_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device, int& curr_serial_extra_id);
void is_nitro_libusb_end_connection(is_nitro_device_handlers* handlers, const is_nitro_usb_device* device_desc, bool interface_claimed);
int is_nitro_libusb_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred);
int is_nitro_libusb_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred);
void is_nitro_libusb_async_in_start(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, isn_async_callback_data* cb_data);
void is_nitro_libusb_cancell_callback(isn_async_callback_data* cb_data);
#endif
#endif

View File

@ -51,6 +51,7 @@ public:
bool try_lock();
void unlock();
void update_time_multiplier(float time_multiplier);
double get_time_s();
private:
std::mutex access_mutex;
@ -60,4 +61,27 @@ private:
float time_multiplier = 1.0;
};
class SharedConsumerMutex {
public:
SharedConsumerMutex(int num_elements);
~SharedConsumerMutex();
void general_lock(int* index);
bool general_timed_lock(int* index);
bool general_try_lock(int* index);
void specific_lock(int index);
bool specific_timed_lock(int index);
bool specific_try_lock(int index);
void specific_unlock(int index);
void update_time_multiplier(float time_multiplier);
double get_time_s();
private:
std::mutex access_mutex;
std::condition_variable_any condition;
const float base_time_fps = 30;
int* counts;
int num_elements;
float time_multiplier = 1.0;
};
#endif

View File

@ -36,6 +36,8 @@
#define FRAME_BUFFER_SIZE 32
static void is_nitro_read_frame_cb(void* user_data, int transfer_length, int transfer_status);
static bool initial_cleanup(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) {
if(!usb_device_desc->is_capture)
return initial_cleanup_emulator(usb_device_desc, handlers) != LIBUSB_SUCCESS;
@ -73,7 +75,7 @@ static is_nitro_device_handlers* usb_find_by_serial_number(const is_nitro_usb_de
final_handlers = is_nitro_libusb_serial_reconnection(usb_device_desc, device, curr_serial_extra_id);
if (final_handlers == NULL)
final_handlers = is_driver_serial_reconnection(usb_device_desc, device);
final_handlers = is_driver_serial_reconnection(device);
return final_handlers;
}
@ -166,39 +168,195 @@ int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType
return UpdateFrameForwardConfig(handlers, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, capture_rate_flag, usb_device_desc);
}
int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) {
if(usb_device_desc->is_capture)
return EndAcquisitionCapture(handlers, usb_device_desc);
return EndAcquisitionEmulator(handlers, do_drain_frames, start_frames, capture_type, usb_device_desc);
int EndAcquisition(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
if(((const is_nitro_usb_device*)(capture_data->status.device.descriptor))->is_capture)
return EndAcquisitionCapture(capture_data, is_nitro_capture_recv_data);
return EndAcquisitionEmulator(capture_data, is_nitro_capture_recv_data, do_drain_frames, start_frames, capture_type);
}
int is_nitro_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_start) {
int ret = ReadFrame((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor);
if (ret < 0)
return ret;
int EndAcquisition(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
if(usb_device_desc->is_capture)
return EndAcquisitionCapture(usb_device_desc, handlers);
return EndAcquisitionEmulator(usb_device_desc, handlers, do_drain_frames, start_frames, capture_type);
}
static void output_to_thread(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point<std::chrono::high_resolution_clock>* clock_start) {
// Output to the other threads...
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
clock_start = curr_time;
const std::chrono::duration<double> diff = curr_time - (*clock_start);
*clock_start = curr_time;
capture_data->data_buffers.WriteToBuffer(capture_buf, _is_nitro_get_video_in_size(curr_capture_type), diff.count(), &capture_data->status.device, curr_capture_type);
if (capture_data->status.cooldown_curr_in)
capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
capture_data->status.video_wait.unlock();
capture_data->status.audio_wait.unlock();
}
int is_nitro_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_start) {
int ret = ReadFrame((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor);
if (ret < 0)
return ret;
output_to_thread(capture_data, capture_buf, curr_capture_type, &clock_start);
return ret;
}
void is_nitro_read_frame_request(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, CaptureScreensType curr_capture_type, uint32_t index) {
if(is_nitro_capture_recv_data == NULL)
return;
is_nitro_capture_recv_data->index = index;
is_nitro_capture_recv_data->curr_capture_type = curr_capture_type;
is_nitro_capture_recv_data->cb_data.function = is_nitro_read_frame_cb;
ReadFrameAsync((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)&is_nitro_capture_recv_data->buffer, _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor, &is_nitro_capture_recv_data->cb_data);
}
static void end_is_nitro_read_frame_cb(ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
is_nitro_capture_recv_data->in_use = false;
is_nitro_capture_recv_data->is_buffer_free_shared_mutex->specific_unlock(is_nitro_capture_recv_data->cb_data.internal_index);
}
static void is_nitro_read_frame_cb(void* user_data, int transfer_length, int transfer_status) {
ISNitroCaptureReceivedData* is_nitro_capture_recv_data = (ISNitroCaptureReceivedData*)user_data;
if((*is_nitro_capture_recv_data->status) < 0)
return end_is_nitro_read_frame_cb(is_nitro_capture_recv_data);
if(transfer_status != LIBUSB_TRANSFER_COMPLETED) {
*is_nitro_capture_recv_data->status = LIBUSB_ERROR_OTHER;
return end_is_nitro_read_frame_cb(is_nitro_capture_recv_data);
}
if(((int32_t)(is_nitro_capture_recv_data->index - (*is_nitro_capture_recv_data->last_index))) <= 0) {
//*is_nitro_capture_recv_data->status = LIBUSB_ERROR_INTERRUPTED;
return end_is_nitro_read_frame_cb(is_nitro_capture_recv_data);
}
*is_nitro_capture_recv_data->last_index = is_nitro_capture_recv_data->index;
output_to_thread(is_nitro_capture_recv_data->capture_data, &is_nitro_capture_recv_data->buffer, is_nitro_capture_recv_data->curr_capture_type, is_nitro_capture_recv_data->clock_start);
end_is_nitro_read_frame_cb(is_nitro_capture_recv_data);
}
int is_nitro_get_num_free_buffers(ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
int num_free = 0;
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
if(!is_nitro_capture_recv_data[i].in_use)
num_free += 1;
return num_free;
}
void wait_all_is_nitro_transfers_done(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
if (is_nitro_capture_recv_data == NULL)
return;
if (*is_nitro_capture_recv_data[0].status < 0) {
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
CloseAsyncRead((is_nitro_device_handlers*)capture_data->handle, &is_nitro_capture_recv_data[i].cb_data);
}
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
void* transfer_data;
do {
is_nitro_capture_recv_data[i].cb_data.transfer_data_access.lock();
transfer_data = is_nitro_capture_recv_data[i].cb_data.transfer_data;
is_nitro_capture_recv_data[i].cb_data.transfer_data_access.unlock();
if(transfer_data)
SleepUntilFree((is_nitro_device_handlers*)capture_data->handle, is_nitro_capture_recv_data[i].cb_data.is_transfer_done_mutex, i);
} while(transfer_data);
}
}
void wait_all_is_nitro_buffers_free(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
if (is_nitro_capture_recv_data == NULL)
return;
if (*is_nitro_capture_recv_data[0].status < 0) {
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
CloseAsyncRead((is_nitro_device_handlers*)capture_data->handle, &is_nitro_capture_recv_data[i].cb_data);
}
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
while (is_nitro_capture_recv_data[i].in_use)
SleepUntilFree((is_nitro_device_handlers*)capture_data->handle, is_nitro_capture_recv_data[i].is_buffer_free_shared_mutex, i);
}
void wait_one_is_nitro_buffer_free(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
bool done = false;
while(!done) {
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
if(!is_nitro_capture_recv_data[i].in_use)
done = true;
}
if(!done) {
if(*is_nitro_capture_recv_data[0].status < 0)
return;
SleepUntilOneFree((is_nitro_device_handlers*)capture_data->handle, is_nitro_capture_recv_data[0].is_buffer_free_shared_mutex);
}
}
}
bool is_nitro_are_buffers_all_free(ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
return is_nitro_get_num_free_buffers(is_nitro_capture_recv_data) == NUM_CAPTURE_RECEIVED_DATA_BUFFERS;
}
ISNitroCaptureReceivedData* is_nitro_get_free_buffer(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
wait_one_is_nitro_buffer_free(capture_data, is_nitro_capture_recv_data);
if(*is_nitro_capture_recv_data[0].status < 0)
return NULL;
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
if(!is_nitro_capture_recv_data[i].in_use) {
is_nitro_capture_recv_data[i].is_buffer_free_shared_mutex->specific_try_lock(i);
is_nitro_capture_recv_data[i].in_use = true;
return &is_nitro_capture_recv_data[i];
}
return NULL;
}
int get_is_nitro_status(ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
return *is_nitro_capture_recv_data[0].status;
}
void reset_is_nitro_status(ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
*is_nitro_capture_recv_data[0].status = 0;
}
void is_nitro_acquisition_main_loop(CaptureData* capture_data) {
if(!usb_is_initialized())
return;
CaptureReceived* capture_buf = new CaptureReceived;
bool is_done_thread;
ConsumerMutex has_data_been_processed;
std::thread async_processing_thread;
uint32_t last_index = -1;
int status = 0;
isn_async_callback_data* cb_queue[NUM_CAPTURE_RECEIVED_DATA_BUFFERS];
int queue_elems = 0;
bool one_transfer_active = false;
SharedConsumerMutex is_buffer_free_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS);
SharedConsumerMutex is_transfer_done_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS);
SharedConsumerMutex is_transfer_data_ready_shared_mutex(NUM_CAPTURE_RECEIVED_DATA_BUFFERS);
std::chrono::time_point<std::chrono::high_resolution_clock> clock_start = std::chrono::high_resolution_clock::now();
ISNitroCaptureReceivedData* is_nitro_capture_recv_data = new ISNitroCaptureReceivedData[NUM_CAPTURE_RECEIVED_DATA_BUFFERS];
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
is_nitro_capture_recv_data[i].in_use = false;
is_nitro_capture_recv_data[i].index = i;
is_nitro_capture_recv_data[i].capture_data = capture_data;
is_nitro_capture_recv_data[i].last_index = &last_index;
is_nitro_capture_recv_data[i].clock_start = &clock_start;
is_nitro_capture_recv_data[i].is_buffer_free_shared_mutex = &is_buffer_free_shared_mutex;
is_nitro_capture_recv_data[i].status = &status;
is_nitro_capture_recv_data[i].cb_data.actual_user_data = &is_nitro_capture_recv_data[i];
is_nitro_capture_recv_data[i].cb_data.transfer_data = NULL;
is_nitro_capture_recv_data[i].cb_data.is_transfer_done_mutex = &is_transfer_done_shared_mutex;
is_nitro_capture_recv_data[i].cb_data.internal_index = i;
is_nitro_capture_recv_data[i].cb_data.is_transfer_data_ready_mutex = &is_transfer_data_ready_shared_mutex;
is_nitro_capture_recv_data[i].cb_data.is_data_ready = false;
is_nitro_capture_recv_data[i].cb_data.cb_queue = cb_queue;
is_nitro_capture_recv_data[i].cb_data.queue_elems = &queue_elems;
is_nitro_capture_recv_data[i].cb_data.one_transfer_active = &one_transfer_active;
}
SetupISNitroAsyncThread((is_nitro_device_handlers*)capture_data->handle, is_nitro_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed);
capture_data->status.reset_hardware = false;
if(((const is_nitro_usb_device*)(capture_data->status.device.descriptor))->is_capture)
is_nitro_acquisition_capture_main_loop(capture_data, capture_buf);
is_nitro_acquisition_capture_main_loop(capture_data, is_nitro_capture_recv_data);
else
is_nitro_acquisition_emulator_main_loop(capture_data, capture_buf);
delete capture_buf;
is_nitro_acquisition_emulator_main_loop(capture_data, is_nitro_capture_recv_data);
wait_all_is_nitro_buffers_free(capture_data, is_nitro_capture_recv_data);
EndISNitroAsyncThread((is_nitro_device_handlers*)capture_data->handle, is_nitro_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed);
delete []is_nitro_capture_recv_data;
}
void usb_is_nitro_acquisition_cleanup(CaptureData* capture_data) {

View File

@ -12,7 +12,9 @@
#define RESET_TIMEOUT 4.0
#define SLEEP_CHECKS_TIME_MS 20
static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, bool* is_acquisition_off, const is_nitro_usb_device* usb_device_desc) {
static int StartAcquisitionCapture(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, bool* is_acquisition_off, uint32_t &index) {
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
int ret = 0;
ret = ReadLidState(handlers, is_acquisition_off, usb_device_desc);
if(ret < 0)
@ -28,15 +30,19 @@ static int StartAcquisitionCapture(is_nitro_device_handlers* handlers, CaptureSc
ret = UpdateFrameForwardEnable(handlers, true, true, usb_device_desc);
if(ret < 0)
return ret;
return StartUsbCaptureDma(handlers, usb_device_desc);
ret = StartUsbCaptureDma(handlers, usb_device_desc);
if(ret < 0)
return ret;
reset_is_nitro_status(is_nitro_capture_recv_data);
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
is_nitro_read_frame_request(capture_data, is_nitro_get_free_buffer(capture_data, is_nitro_capture_recv_data), capture_type, index++);
return ret;
}
static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool* is_acquisition_off) {
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
static int LidReopenCaptureCheck(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool* is_acquisition_off, uint32_t &index) {
curr_capture_type = capture_data->status.capture_type;
curr_capture_speed = capture_data->status.capture_speed;
int ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, is_acquisition_off, usb_device_desc);
int ret = StartAcquisitionCapture(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, is_acquisition_off, index);
if(ret < 0)
return ret;
if(!(*is_acquisition_off))
@ -44,7 +50,7 @@ static int LidReopenCaptureCheck(CaptureData* capture_data, CaptureScreensType &
return ret;
}
static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool *is_acquisition_off, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset) {
static int CaptureResetHardware(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, CaptureScreensType &curr_capture_type, CaptureSpeedsType &curr_capture_speed, bool *is_acquisition_off, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset, uint32_t &index) {
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
bool reset_hardware = capture_data->status.reset_hardware;
@ -60,7 +66,7 @@ static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &c
clock_last_reset = curr_time;
if(!(*is_acquisition_off))
ret = EndAcquisitionCapture(handlers, usb_device_desc);
ret = EndAcquisitionCapture(capture_data, is_nitro_capture_recv_data);
if(ret < 0)
return ret;
ret = ResetCPUStart(handlers, usb_device_desc);
@ -72,7 +78,7 @@ static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &c
if(!(*is_acquisition_off)) {
curr_capture_type = capture_data->status.capture_type;
curr_capture_speed = capture_data->status.capture_speed;
ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, is_acquisition_off, usb_device_desc);
ret = StartAcquisitionCapture(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, is_acquisition_off, index);
}
if(ret < 0)
return ret;
@ -80,38 +86,42 @@ static int CaptureResetHardware(CaptureData* capture_data, CaptureScreensType &c
}
int initial_cleanup_capture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) {
//EndAcquisition(handlers, false, 0, CAPTURE_SCREENS_BOTH, usb_device_desc);
//EndAcquisition(handlers, usb_device_desc, false, 0, CAPTURE_SCREENS_BOTH);
return LIBUSB_SUCCESS;
}
int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc) {
int EndAcquisitionCapture(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
wait_all_is_nitro_buffers_free(capture_data, is_nitro_capture_recv_data);
return EndAcquisitionCapture((const is_nitro_usb_device*)capture_data->status.device.descriptor, (is_nitro_device_handlers*)capture_data->handle);
}
int EndAcquisitionCapture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) {
return StopUsbCaptureDma(handlers, usb_device_desc);
}
void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf) {
void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
bool is_acquisition_off = true;
uint32_t index = 0;
CaptureScreensType curr_capture_type = capture_data->status.capture_type;
CaptureSpeedsType curr_capture_speed = capture_data->status.capture_speed;
int ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, &is_acquisition_off, usb_device_desc);
int ret = StartAcquisitionCapture(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, index);
if(ret < 0) {
capture_error_print(true, capture_data, "Capture Start: Failed");
return;
}
int inner_curr_in = 0;
auto clock_start = std::chrono::high_resolution_clock::now();
auto clock_last_reset = std::chrono::high_resolution_clock::now();
while(capture_data->status.connected && capture_data->status.running) {
ret = CaptureResetHardware(capture_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, clock_last_reset);
ret = CaptureResetHardware(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, clock_last_reset, index);
if(ret < 0) {
capture_error_print(true, capture_data, "Hardware reset: Failed");
return;
}
if(is_acquisition_off) {
default_sleep(SLEEP_CHECKS_TIME_MS);
ret = LidReopenCaptureCheck(capture_data, curr_capture_type, curr_capture_speed, &is_acquisition_off);
SleepBetweenTransfers(handlers, SLEEP_CHECKS_TIME_MS);
ret = LidReopenCaptureCheck(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, index);
if(ret < 0) {
capture_error_print(true, capture_data, "Lid Reopen: Failed");
return;
@ -119,9 +129,10 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureRe
}
if(is_acquisition_off)
continue;
ret = is_nitro_read_frame_and_output(capture_data, capture_buf, curr_capture_type, clock_start);
wait_one_is_nitro_buffer_free(capture_data, is_nitro_capture_recv_data);
ret = get_is_nitro_status(is_nitro_capture_recv_data);
if(ret < 0) {
ret = EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc);
ret = EndAcquisition(capture_data, is_nitro_capture_recv_data, true, 0, curr_capture_type);
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Read error");
return;
@ -129,16 +140,17 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureRe
is_acquisition_off = true;
continue;
}
is_nitro_read_frame_request(capture_data, is_nitro_get_free_buffer(capture_data, is_nitro_capture_recv_data), curr_capture_type, index++);
if((curr_capture_type != capture_data->status.capture_type) || (curr_capture_speed != capture_data->status.capture_speed)) {
ret = EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc);
ret = EndAcquisition(capture_data, is_nitro_capture_recv_data, true, 0, curr_capture_type);
if(ret < 0) {
capture_error_print(true, capture_data, "Capture End: Failed");
return;
}
curr_capture_type = capture_data->status.capture_type;
curr_capture_speed = capture_data->status.capture_speed;
ret = StartAcquisitionCapture(handlers, curr_capture_type, curr_capture_speed, &is_acquisition_off, usb_device_desc);
ret = StartAcquisitionCapture(capture_data, is_nitro_capture_recv_data, curr_capture_type, curr_capture_speed, &is_acquisition_off, index);
if(ret < 0) {
capture_error_print(true, capture_data, "Capture Restart: Failed");
return;
@ -146,5 +158,5 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureRe
}
}
if(!is_acquisition_off)
EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc);
EndAcquisition(capture_data, is_nitro_capture_recv_data, true, 0, curr_capture_type);
}

View File

@ -166,21 +166,30 @@ static bool do_sleep(float single_frame_time, std::chrono::time_point<std::chron
return result;
}
static void frame_wait(float single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> clock_last_reset, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, int curr_frame_counter, int last_frame_counter) {
static void frame_wait(is_nitro_device_handlers* handlers, float single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> clock_last_reset, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, int curr_frame_counter, int last_frame_counter) {
if(curr_frame_counter == FRAME_BUFFER_SIZE)
return;
float sleep_time = 0;
int sleep_counter = 0;
while(do_sleep(single_frame_time, clock_last_reset, capture_type, capture_speed, curr_frame_counter, last_frame_counter, &sleep_time)) {
default_sleep(sleep_time * 1000.0 / SLEEP_TIME_DIVISOR);
SleepBetweenTransfers(handlers, sleep_time * 1000.0 / SLEEP_TIME_DIVISOR);
sleep_counter++;
}
}
static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, CaptureSpeedsType &curr_capture_speed, CaptureSpeedsType wanted_capture_speed, const is_nitro_usb_device* usb_device_desc) {
static int reset_acquisition_frames(CaptureData* capture_data, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, CaptureSpeedsType &curr_capture_speed, CaptureSpeedsType wanted_capture_speed, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
curr_frame_counter += 1;
if(curr_frame_counter < FRAME_BUFFER_SIZE)
return LIBUSB_SUCCESS;
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
wait_all_is_nitro_buffers_free(capture_data, is_nitro_capture_recv_data);
int ret = get_is_nitro_status(is_nitro_capture_recv_data);
if (ret < 0)
return ret;
int multiplier = 1;
switch(curr_capture_speed) {
case CAPTURE_SPEEDS_HALF:
@ -194,7 +203,7 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t
break;
}
int ret = StopUsbCaptureDma(handlers, usb_device_desc);
ret = StopUsbCaptureDma(handlers, usb_device_desc);
if(ret < 0)
return ret;
@ -216,10 +225,10 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t
std::chrono::duration<double> diff;
do {
// Is this not the first loop? Also, do not sleep too much...
if((frame_diff > 0) && ((internalFrameCount & (FRAME_BUFFER_SIZE - 1) < (FRAME_BUFFER_SIZE - 1)))) {
if((frame_diff > 0) && (((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) < (FRAME_BUFFER_SIZE - 1)))) {
float expected_time = single_frame_time * FRAME_BUFFER_SIZE;
if(diff.count() < expected_time)
default_sleep(single_frame_time * 1000.0 / SLEEP_TIME_DIVISOR);
SleepBetweenTransfers(handlers, single_frame_time * 1000.0 / SLEEP_TIME_DIVISOR);
}
// Check how many frames have passed...
ret = GetFrameCounter(handlers, &internalFrameCount, usb_device_desc);
@ -243,10 +252,18 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t
}
// Exit if enough frames have passed, or if there currently is some delay.
// Exiting early makes it possible to catch up to the DMA, if we're behind.
} while(((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) || ((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) >= (FRAME_BUFFER_SIZE - (1 + multiplier))));
} while(((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) || ((internalFrameCount & (FRAME_BUFFER_SIZE - 1)) >= (FRAME_BUFFER_SIZE - ( 1 + multiplier))));
// Determine how much time a single frame takes. We'll use it for sleeps
curr_time = std::chrono::high_resolution_clock::now();
diff = curr_time - clock_last_reset;
if((frame_diff > 0) && (frame_diff < (diff_target - (1 + multiplier)))) {
bool is_lid_closed = false;
ret = ReadLidState(handlers, &is_lid_closed, usb_device_desc);
if(ret < 0)
return ret;
if(is_lid_closed)
frame_diff = 0;
}
if(frame_diff == 0)
single_frame_time = 0;
else
@ -279,10 +296,15 @@ static int reset_acquisition_frames(is_nitro_device_handlers* handlers, uint16_t
}
int initial_cleanup_emulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers) {
return EndAcquisition(handlers, false, 0, CAPTURE_SCREENS_BOTH, usb_device_desc);
return EndAcquisition(usb_device_desc, handlers, false, 0, CAPTURE_SCREENS_BOTH);
}
int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc) {
int EndAcquisitionEmulator(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
wait_all_is_nitro_buffers_free(capture_data, is_nitro_capture_recv_data);
return EndAcquisitionEmulator((const is_nitro_usb_device*)capture_data->status.device.descriptor, (is_nitro_device_handlers*)capture_data->handle, do_drain_frames, start_frames, capture_type);
}
int EndAcquisitionEmulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
int ret = 0;
if (do_drain_frames)
ret = drain_frames(handlers, FRAME_BUFFER_SIZE, start_frames, capture_type, usb_device_desc);
@ -294,9 +316,10 @@ int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_fra
return UpdateFrameForwardEnable(handlers, false, false, usb_device_desc);
}
void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf) {
void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle;
const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor;
uint32_t index = 0;
uint16_t last_frame_counter = 0;
float single_frame_time = 0;
uint16_t curr_frame_counter = 0;
@ -307,29 +330,29 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, CaptureR
capture_error_print(true, capture_data, "Capture Start: Failed");
return;
}
auto clock_start = std::chrono::high_resolution_clock::now();
auto clock_last_reset = std::chrono::high_resolution_clock::now();
while (capture_data->status.connected && capture_data->status.running) {
frame_wait(single_frame_time, clock_last_reset, curr_capture_type, curr_capture_speed, curr_frame_counter, last_frame_counter);
if (single_frame_time > 0) {
ret = is_nitro_read_frame_and_output(capture_data, capture_buf, curr_capture_type, clock_start);
if (ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Read error");
return;
}
ret = get_is_nitro_status(is_nitro_capture_recv_data);
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Read error");
return;
}
if(single_frame_time > 0) {
is_nitro_read_frame_request(capture_data, is_nitro_get_free_buffer(capture_data, is_nitro_capture_recv_data), curr_capture_type, index++);
frame_wait(handlers, single_frame_time, clock_last_reset, curr_capture_type, curr_capture_speed, curr_frame_counter + 1, last_frame_counter);
}
else {
capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
default_sleep(SLEEP_CHECKS_TIME_MS);
clock_last_reset = std::chrono::high_resolution_clock::now();
SleepBetweenTransfers(handlers, SLEEP_CHECKS_TIME_MS);
}
capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE;
ret = reset_acquisition_frames(handlers, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, curr_capture_speed, capture_data->status.capture_speed, usb_device_desc);
if (ret < 0) {
ret = reset_acquisition_frames(capture_data, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, curr_capture_speed, capture_data->status.capture_speed, is_nitro_capture_recv_data);
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Frame counter reset error");
break;
}
}
EndAcquisition(handlers, true, curr_frame_counter, curr_capture_type, usb_device_desc);
EndAcquisition(capture_data, is_nitro_capture_recv_data, true, curr_frame_counter, curr_capture_type);
}

View File

@ -163,14 +163,21 @@ static void fix_endianness_header(is_nitro_nec_packet_header* header) {
static int bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) {
if(handlers->usb_handle)
return is_nitro_libusb_bulk_out(handlers, usb_device_desc, buf, length, transferred);
return is_driver_bulk_out(handlers, buf, length, transferred);
return is_driver_bulk_out(handlers, usb_device_desc, buf, length, transferred);
}
// Read from bulk endpoint. Returns libusb error code
static int bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, int *transferred) {
if(handlers->usb_handle)
return is_nitro_libusb_bulk_in(handlers, usb_device_desc, buf, length, transferred);
return is_driver_bulk_in(handlers, buf, length, transferred);
return is_driver_bulk_in(handlers, usb_device_desc, buf, length, transferred);
}
// Read from bulk endpoint. Returns libusb error code
static void bulk_in_async(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t *buf, int length, isn_async_callback_data* cb_data) {
if(handlers->usb_handle)
return is_nitro_libusb_async_in_start(handlers, usb_device_desc, buf, length, cb_data);
is_drive_async_in_start(handlers, usb_device_desc, buf, length, cb_data);
}
static int SendWritePacket(is_nitro_device_handlers* handlers, uint16_t command, is_nitro_packet_type type, uint32_t address, uint8_t* buf, int length, const is_nitro_usb_device* device_desc, bool expect_result = true) {
@ -385,7 +392,7 @@ int DisableLca2(is_nitro_device_handlers* handlers, const is_nitro_usb_device* d
ret = WriteNecMemU16(handlers, 0x0F84000A, 1, device_desc);
if(ret < 0)
return ret;
//default_sleep(2000);
//SleepBetweenTransfers(handlers, 2000);
return WriteNecMemU16(handlers, 0x0F84000A, 0, device_desc);
}
@ -526,3 +533,42 @@ int ReadFrame(is_nitro_device_handlers* handlers, uint8_t* buf, int length, cons
return ret;
}
void ReadFrameAsync(is_nitro_device_handlers* handlers, uint8_t* buf, int length, const is_nitro_usb_device* device_desc, isn_async_callback_data* cb_data) {
return bulk_in_async(handlers, device_desc, buf, length, cb_data);
}
void CloseAsyncRead(is_nitro_device_handlers* handlers, isn_async_callback_data* cb_data) {
if(handlers->usb_handle)
return is_nitro_libusb_cancell_callback(cb_data);
return is_nitro_is_driver_cancel_callback(cb_data);
}
void SetupISNitroAsyncThread(is_nitro_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready) {
if(handlers->usb_handle)
return is_nitro_libusb_start_thread(thread_ptr, keep_going);
return is_nitro_is_driver_start_thread(thread_ptr, keep_going, (ISNitroCaptureReceivedData*)user_data, handlers, is_data_ready);
}
void EndISNitroAsyncThread(is_nitro_device_handlers* handlers, void* user_data, std::thread* thread_ptr, bool* keep_going, ConsumerMutex* is_data_ready) {
if(handlers->usb_handle)
return is_nitro_libusb_close_thread(thread_ptr, keep_going);
return is_nitro_is_driver_close_thread(thread_ptr, keep_going, (ISNitroCaptureReceivedData*)user_data);
}
void SleepBetweenTransfers(is_nitro_device_handlers* handlers, float ms) {
if (handlers->usb_handle)
return is_nitro_libusb_sleep_between_transfers(ms);
return is_nitro_is_driver_sleep_between_transfers(ms);
}
void SleepUntilOneFree(is_nitro_device_handlers* handlers, SharedConsumerMutex* mutex) {
if (handlers->usb_handle)
return is_nitro_libusb_sleep_until_one_free(mutex);
return is_nitro_is_driver_sleep_until_one_free(mutex);
}
void SleepUntilFree(is_nitro_device_handlers* handlers, SharedConsumerMutex* mutex, int index) {
if (handlers->usb_handle)
return is_nitro_libusb_sleep_until_free(mutex, index);
return is_nitro_is_driver_sleep_until_free(mutex, index);
}

View File

@ -1,6 +1,9 @@
#include "usb_is_nitro_communications.hpp"
#include "usb_is_nitro_setup_general.hpp"
#include "usb_is_nitro_is_driver.hpp"
#include "usb_is_nitro_acquisition_general.hpp"
#include "frontend.hpp"
#include "utils.hpp"
#include <algorithm>
#include <filesystem>
@ -26,8 +29,29 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//#define USE_QUEUED_TRANSFERS
static int SubmitRead(isn_async_callback_data* cb_data);
const GUID is_nitro_driver_guid = { .Data1 = 0xB78D7ADA, .Data2 = 0xDDF4, .Data3 = 0x418F, .Data4 = {0x8C, 0x7C, 0x4A, 0xC8, 0x80, 0x30, 0xF5, 0x42} };
static void is_nitro_is_driver_function(bool* usb_thread_run, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, is_nitro_device_handlers* handlers) {
while(*usb_thread_run) {
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
isn_async_callback_data* cb_data = (isn_async_callback_data*)&is_nitro_capture_recv_data[i].cb_data;
bool read_data_ready = false;
cb_data->transfer_data_access.lock();
read_data_ready = cb_data->is_data_ready;
cb_data->is_data_ready = false;
cb_data->transfer_data_access.unlock();
if(is_nitro_capture_recv_data[i].in_use && read_data_ready)
cb_data->function(&is_nitro_capture_recv_data[i], cb_data->actual_length, cb_data->status_value);
}
int dummy = 0;
is_nitro_capture_recv_data[0].cb_data.is_transfer_data_ready_mutex->general_timed_lock(&dummy);
}
}
static bool read_is_driver_device_info(HANDLE handle, uint8_t* buffer, size_t size, DWORD* num_read) {
return DeviceIoControl(handle, 0x22000C, buffer, size, buffer, size, num_read, NULL);
}
@ -80,16 +104,7 @@ static bool is_driver_get_device_pid_vid(std::string path, uint16_t& out_vid, ui
return true;
}
static void set_handle_timeout(HANDLE handle, int timeout) {
COMMTIMEOUTS timeouts;
if (GetCommTimeouts(handle, &timeouts)) {
timeouts.ReadTotalTimeoutConstant = timeout;
timeouts.ReadTotalTimeoutMultiplier = 0;
SetCommTimeouts(handle, &timeouts);
}
}
static bool is_driver_setup_connection(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, std::string path) {
static bool is_driver_setup_connection(is_nitro_device_handlers* handlers, std::string path) {
handlers->usb_handle = NULL;
handlers->mutex = NULL;
handlers->write_handle = INVALID_HANDLE_VALUE;
@ -103,8 +118,8 @@ static bool is_driver_setup_connection(is_nitro_device_handlers* handlers, const
}
if (handlers->mutex == NULL)
return false;
handlers->write_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe00").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL);
handlers->read_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe01").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, 0, NULL);
handlers->write_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe00").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
handlers->read_handle = CreateFile((path + (char)(std::filesystem::path::preferred_separator)+"Pipe01").c_str(), (GENERIC_READ | GENERIC_WRITE), (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if ((handlers->write_handle == INVALID_HANDLE_VALUE) || (handlers->read_handle == INVALID_HANDLE_VALUE))
return false;
if (!is_driver_device_reset(handlers->write_handle))
@ -113,18 +128,101 @@ static bool is_driver_setup_connection(is_nitro_device_handlers* handlers, const
return false;
if (!is_driver_pipe_reset(handlers->read_handle))
return false;
set_handle_timeout(handlers->read_handle, usb_device_desc->bulk_timeout);
set_handle_timeout(handlers->write_handle, usb_device_desc->bulk_timeout);
return true;
}
static int wait_result_io_operation(HANDLE handle, OVERLAPPED* overlapped_var, int* transferred, const is_nitro_usb_device* usb_device_desc) {
DWORD num_bytes = 0;
int retval = 0;
int error = 0;
do {
retval = GetOverlappedResultEx(handle, overlapped_var, &num_bytes, usb_device_desc->bulk_timeout, true);
error = GetLastError();
} while((retval == 0) && (error == WAIT_IO_COMPLETION));
*transferred = num_bytes;
if (retval == 0) {
if (error == WAIT_TIMEOUT) {
CancelIoEx(handle, overlapped_var);
return LIBUSB_ERROR_TIMEOUT;
}
return LIBUSB_ERROR_OTHER;
}
return LIBUSB_SUCCESS;
}
static void ClearAllQueueElements(isn_async_callback_data* cb_data) {
int queue_elems_curr = *cb_data->queue_elems;
for (int i = 0; i < queue_elems_curr; i++) {
cb_data->cb_queue[i]->transfer_data = NULL;
ISNitroCaptureReceivedData* user_data = (ISNitroCaptureReceivedData*)cb_data->cb_queue[i]->actual_user_data;
user_data->in_use = false;
}
*cb_data->queue_elems = 0;
}
static void BasicCompletionOutputRoutine(isn_async_callback_data* cb_data, int num_bytes, int error) {
cb_data->transfer_data_access.lock();
if ((error == LIBUSB_SUCCESS) && (num_bytes != cb_data->requested_length))
error = LIBUSB_ERROR_INTERRUPTED;
if (error == LIBUSB_SUCCESS) {
if (*cb_data->queue_elems) {
isn_async_callback_data* cb_data_next = (isn_async_callback_data*)cb_data->cb_queue[--(*cb_data->queue_elems)];
int retval = SubmitRead(cb_data_next);
if (retval == 0)
error = LIBUSB_ERROR_BUSY;
}
else
*cb_data->one_transfer_active = false;
}
if (error != LIBUSB_SUCCESS) {
ClearAllQueueElements(cb_data);
*cb_data->one_transfer_active = false;
}
cb_data->transfer_data = NULL;
cb_data->actual_length = num_bytes;
cb_data->status_value = error;
cb_data->is_data_ready = true;
cb_data->is_transfer_data_ready_mutex->specific_unlock(cb_data->internal_index);
cb_data->is_transfer_done_mutex->specific_unlock(cb_data->internal_index);
cb_data->transfer_data_access.unlock();
}
static void OverlappedCompletionNothingRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* overlapped_var) {
return;
}
static void OverlappedCompletionOutputRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* overlapped_var) {
isn_async_callback_data* cb_data = (isn_async_callback_data*)overlapped_var->hEvent;
DWORD num_bytes = 0;
bool retval = false;
int error = LIBUSB_ERROR_OTHER;
if (!dwErrorCode) {
retval = GetOverlappedResult(cb_data->handle, overlapped_var, &num_bytes, false);
error = GetLastError();
}
error = retval ? LIBUSB_SUCCESS : LIBUSB_ERROR_OTHER;
BasicCompletionOutputRoutine(cb_data, num_bytes, error);
}
static int SubmitRead(isn_async_callback_data* cb_data) {
#ifdef USE_QUEUED_TRANSFERS
int retval = ReadFileEx(cb_data->handle, cb_data->buffer, cb_data->requested_length, (OVERLAPPED*)cb_data->transfer_data, OverlappedCompletionOutputRoutine);
#else
int retval = ReadFileEx(cb_data->handle, cb_data->buffer, cb_data->requested_length, (OVERLAPPED*)cb_data->transfer_data, OverlappedCompletionNothingRoutine);
#endif
if (retval)
cb_data->cb_active_transfer = cb_data;
return retval;
}
#endif
is_nitro_device_handlers* is_driver_serial_reconnection(const is_nitro_usb_device* usb_device_desc, CaptureDevice* device) {
is_nitro_device_handlers* is_driver_serial_reconnection(CaptureDevice* device) {
is_nitro_device_handlers* final_handlers = NULL;
#ifdef _WIN32
if (device->path != "") {
is_nitro_device_handlers handlers;
if (is_driver_setup_connection(&handlers, usb_device_desc, device->path)) {
if (is_driver_setup_connection(&handlers, device->path)) {
final_handlers = new is_nitro_device_handlers;
final_handlers->usb_handle = NULL;
final_handlers->mutex = handlers.mutex;
@ -176,7 +274,7 @@ void is_driver_list_devices(std::vector<CaptureDevice> &devices_list, bool* not_
const is_nitro_usb_device* usb_device_desc = GetISNitroDesc(j);
if(not_supported_elems[j] && (usb_device_desc->vid == vid) && (usb_device_desc->pid == pid)) {
is_nitro_device_handlers handlers;
if(is_driver_setup_connection(&handlers, usb_device_desc, path))
if(is_driver_setup_connection(&handlers, path))
is_nitro_insert_device(devices_list, &handlers, usb_device_desc, curr_serial_extra_id_is_nitro[j], path);
is_driver_end_connection(&handlers);
break;
@ -191,23 +289,126 @@ void is_driver_list_devices(std::vector<CaptureDevice> &devices_list, bool* not_
}
// Write to bulk
int is_driver_bulk_out(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred) {
bool result = true;
int is_driver_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred) {
#ifdef _WIN32
OVERLAPPED overlapped_var;
memset(&overlapped_var, 0, sizeof(OVERLAPPED));
DWORD num_bytes = 0;
result = WriteFile(handlers->write_handle, buf, length, &num_bytes, NULL);
*transferred = num_bytes;
int retval = WriteFileEx(handlers->write_handle, buf, length, &overlapped_var, OverlappedCompletionNothingRoutine);
if(retval == 0)
return LIBUSB_ERROR_BUSY;
return wait_result_io_operation(handlers->write_handle, &overlapped_var, transferred, usb_device_desc);
#endif
return result ? LIBUSB_SUCCESS : LIBUSB_ERROR_OTHER;
return LIBUSB_SUCCESS;
}
// Read from bulk
int is_driver_bulk_in(is_nitro_device_handlers* handlers, uint8_t* buf, int length, int* transferred) {
bool result = true;
int is_driver_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred) {
#ifdef _WIN32
DWORD num_bytes = 0;
result = ReadFile(handlers->read_handle, buf, length, &num_bytes, NULL);
*transferred = num_bytes;
OVERLAPPED overlapped_var;
memset(&overlapped_var, 0, sizeof(OVERLAPPED));
int retval = ReadFileEx(handlers->read_handle, buf, length, &overlapped_var, OverlappedCompletionNothingRoutine);
if (retval == 0)
return LIBUSB_ERROR_BUSY;
return wait_result_io_operation(handlers->read_handle, &overlapped_var, transferred, usb_device_desc);
#endif
return LIBUSB_SUCCESS;
}
int is_drive_async_in_start(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, isn_async_callback_data* cb_data) {
#ifdef _WIN32
cb_data->transfer_data_access.lock();
cb_data->is_data_ready = false;
cb_data->transfer_data = cb_data->base_transfer_data;
cb_data->handle = handlers->read_handle;
cb_data->requested_length = length;
cb_data->buffer = buf;
OVERLAPPED* overlap_data = (OVERLAPPED*)cb_data->transfer_data;
memset(overlap_data, 0, sizeof(OVERLAPPED));
#ifdef USE_QUEUED_TRANSFERS
overlap_data->hEvent = cb_data;
#endif
cb_data->is_transfer_done_mutex->specific_try_lock(cb_data->internal_index);
cb_data->is_transfer_data_ready_mutex->specific_try_lock(cb_data->internal_index);
int retval = 1;
if (!(*cb_data->one_transfer_active))
retval = SubmitRead(cb_data);
else
cb_data->cb_queue[(*cb_data->queue_elems)++] = cb_data;
if(retval == 1)
*cb_data->one_transfer_active = true;
cb_data->transfer_data_access.unlock();
if (retval == 0)
return LIBUSB_ERROR_BUSY;
#ifndef USE_QUEUED_TRANSFERS
int num_bytes = 0;
int error = wait_result_io_operation(handlers->read_handle, (OVERLAPPED*)overlap_data, &num_bytes, usb_device_desc);
BasicCompletionOutputRoutine(cb_data, num_bytes, error);
#endif
#endif
return LIBUSB_SUCCESS;
}
void is_nitro_is_driver_start_thread(std::thread* thread_ptr, bool* usb_thread_run, ISNitroCaptureReceivedData* is_nitro_capture_recv_data, is_nitro_device_handlers* handlers, ConsumerMutex* AsyncMutexPtr) {
#ifdef _WIN32
*usb_thread_run = true;
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
is_nitro_capture_recv_data[i].cb_data.base_transfer_data = new OVERLAPPED;
*thread_ptr = std::thread(is_nitro_is_driver_function, usb_thread_run, is_nitro_capture_recv_data, handlers);
#endif
}
void is_nitro_is_driver_close_thread(std::thread* thread_ptr, bool* usb_thread_run, ISNitroCaptureReceivedData* is_nitro_capture_recv_data) {
#ifdef _WIN32
*usb_thread_run = false;
is_nitro_capture_recv_data[0].cb_data.is_transfer_data_ready_mutex->specific_unlock(0);
thread_ptr->join();
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
delete is_nitro_capture_recv_data[i].cb_data.base_transfer_data;
#endif
}
void is_nitro_is_driver_cancel_callback(isn_async_callback_data* cb_data) {
#ifdef _WIN32
cb_data->transfer_data_access.lock();
ClearAllQueueElements(cb_data);
if(*cb_data->one_transfer_active) {
CancelIoEx(cb_data->cb_active_transfer->handle, (OVERLAPPED*)cb_data->cb_active_transfer->transfer_data);
*cb_data->one_transfer_active = false;
}
cb_data->transfer_data_access.unlock();
#endif
}
void is_nitro_is_driver_sleep_between_transfers(float ms) {
#ifdef _WIN32
#ifdef USE_QUEUED_TRANSFERS
SleepEx(ms, true);
#else
default_sleep(ms);
#endif
#endif
}
void is_nitro_is_driver_sleep_until_one_free(SharedConsumerMutex* mutex) {
#ifdef _WIN32
int dummy = 0;
#ifdef USE_QUEUED_TRANSFERS
SleepEx(mutex->get_time_s() * 100, true);
mutex->general_try_lock(&dummy);
#else
mutex->general_timed_lock(&dummy);
#endif
#endif
}
void is_nitro_is_driver_sleep_until_free(SharedConsumerMutex* mutex, int index) {
#ifdef _WIN32
#ifdef USE_QUEUED_TRANSFERS
while (!(mutex->specific_try_lock(index)))
SleepEx(mutex->get_time_s() * 100, true);
#else
mutex->specific_lock(index);
#endif
#endif
return result ? LIBUSB_SUCCESS : LIBUSB_ERROR_OTHER;
}

View File

@ -4,6 +4,7 @@
#include "usb_generic.hpp"
#include <libusb.h>
#include <frontend.hpp>
// Code based off of Gericom's sample code. Distributed under the MIT License. Copyright (c) 2024 Gericom
// Permission is hereby granted, free of charge, to any person obtaining a copy
@ -24,6 +25,30 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
static void is_nitro_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);
}
void is_nitro_libusb_start_thread(std::thread* thread_ptr, bool* usb_thread_run) {
if(!usb_is_initialized())
return;
*usb_thread_run = true;
*thread_ptr = std::thread(is_nitro_usb_thread_function, usb_thread_run);
}
void is_nitro_libusb_close_thread(std::thread* thread_ptr, bool* usb_thread_run) {
if(!usb_is_initialized())
return;
*usb_thread_run = false;
thread_ptr->join();
}
static bool is_nitro_libusb_setup_connection(libusb_device_handle* handle, const is_nitro_usb_device* usb_device_desc) {
if (libusb_set_configuration(handle, usb_device_desc->default_config) != LIBUSB_SUCCESS)
return false;
@ -136,3 +161,46 @@ int is_nitro_libusb_bulk_out(is_nitro_device_handlers* handlers, const is_nitro_
int is_nitro_libusb_bulk_in(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, int* transferred) {
return libusb_bulk_transfer(handlers->usb_handle, usb_device_desc->ep2_in, buf, length, transferred, usb_device_desc->bulk_timeout);
}
void is_nitro_libusb_cancell_callback(isn_async_callback_data* cb_data) {
cb_data->transfer_data_access.lock();
if(cb_data->transfer_data)
libusb_cancel_transfer((libusb_transfer*)cb_data->transfer_data);
cb_data->transfer_data_access.unlock();
}
void is_nitro_libusb_async_callback(libusb_transfer* transfer) {
isn_async_callback_data* cb_data = (isn_async_callback_data*)transfer->user_data;
cb_data->transfer_data_access.lock();
cb_data->transfer_data = NULL;
cb_data->is_transfer_done_mutex->specific_unlock(cb_data->internal_index);
cb_data->transfer_data_access.unlock();
cb_data->function(cb_data->actual_user_data, transfer->actual_length, transfer->status);
}
// Read from bulk
void is_nitro_libusb_async_in_start(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc, uint8_t* buf, int length, isn_async_callback_data* cb_data) {
libusb_transfer *transfer_in = libusb_alloc_transfer(0);
if(!transfer_in)
return;
cb_data->transfer_data_access.lock();
cb_data->transfer_data = transfer_in;
cb_data->is_transfer_done_mutex->specific_try_lock(cb_data->internal_index);
libusb_fill_bulk_transfer(transfer_in, handlers->usb_handle, usb_device_desc->ep2_in, buf, length, is_nitro_libusb_async_callback, cb_data, usb_device_desc->bulk_timeout);
transfer_in->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
libusb_submit_transfer(transfer_in);
cb_data->transfer_data_access.unlock();
}
void is_nitro_libusb_sleep_between_transfers(float ms) {
default_sleep(ms);
}
void is_nitro_libusb_sleep_until_one_free(SharedConsumerMutex* mutex) {
int dummy = 0;
mutex->general_timed_lock(&dummy);
}
void is_nitro_libusb_sleep_until_free(SharedConsumerMutex* mutex, int index) {
mutex->specific_lock(index);
}

View File

@ -1,5 +1,7 @@
#include "usb_generic.hpp"
#include <thread>
static bool usb_thread_run = false;
static bool usb_initialized = false;
static libusb_context* usb_ctx = NULL; // libusb session context
@ -13,13 +15,11 @@ void usb_init() {
return;
}
usb_initialized = true;
return;
}
void usb_close() {
if(usb_initialized) {
if(usb_initialized)
libusb_exit(usb_ctx);
}
usb_ctx = NULL;
usb_initialized = false;
}

View File

@ -214,16 +214,20 @@ ConsumerMutex::ConsumerMutex() {
}
void ConsumerMutex::update_time_multiplier(float time_multiplier) {
if(time_multiplier <= 0)
if (time_multiplier <= 0)
return;
this->time_multiplier = time_multiplier;
}
double ConsumerMutex::get_time_s() {
return 1.0 / ((base_time_fps) * (1.0 / this->time_multiplier));
}
void ConsumerMutex::lock() {
access_mutex.lock();
bool success = false;
while(!success) {
if(count) {
while (!success) {
if (count) {
count--;
success = true;
}
@ -233,41 +237,192 @@ void ConsumerMutex::lock() {
}
access_mutex.unlock();
}
bool ConsumerMutex::timed_lock() {
std::chrono::duration<double>max_timed_wait = std::chrono::duration<double>(1.0 / ((base_time_fps) * (1.0 / this->time_multiplier)));
std::chrono::duration<double>max_timed_wait = std::chrono::duration<double>(this->get_time_s());
access_mutex.lock();
bool success = false;
while(!success) {
if(count) {
while (!success) {
if (count) {
count--;
success = true;
}
else {
auto result = condition.wait_for(access_mutex, max_timed_wait);
if((result == std::cv_status::timeout) && (!count))
if ((result == std::cv_status::timeout) && (!count))
break;
}
}
access_mutex.unlock();
return success;
}
bool ConsumerMutex::try_lock() {
access_mutex.lock();
bool success = false;
if(count) {
if (count) {
count--;
success = true;
}
access_mutex.unlock();
return success;
}
void ConsumerMutex::unlock() {
access_mutex.lock();
// Enforce 1 max
count = 1;
condition.notify_one();
condition.notify_all();
access_mutex.unlock();
}
//============================================================================
SharedConsumerMutex::SharedConsumerMutex(int num_elements) {
this->num_elements = num_elements;
if(this->num_elements <= 0)
this->num_elements = 1;
this->counts = new int[this->num_elements];
for(int i = 0; i < this->num_elements; i++)
this->counts[i] = 0;
}
SharedConsumerMutex::~SharedConsumerMutex() {
delete []this->counts;
}
void SharedConsumerMutex::update_time_multiplier(float time_multiplier) {
if (time_multiplier <= 0)
return;
this->time_multiplier = time_multiplier;
}
double SharedConsumerMutex::get_time_s() {
return 1.0 / ((base_time_fps) * (1.0 / this->time_multiplier));
}
void SharedConsumerMutex::general_lock(int* index) {
access_mutex.lock();
bool success = false;
while (!success) {
for (int i = 0; i < num_elements; i++)
if (counts[i]) {
counts[i]--;
success = true;
*index = i;
break;
}
if (!success)
condition.wait(access_mutex);
}
access_mutex.unlock();
}
bool SharedConsumerMutex::general_timed_lock(int* index) {
std::chrono::time_point<std::chrono::high_resolution_clock> clock_start = std::chrono::high_resolution_clock::now();
std::chrono::time_point<std::chrono::high_resolution_clock> clock_end = clock_start + std::chrono::nanoseconds((int)(this->get_time_s() * 1000 * 1000));
access_mutex.lock();
bool success = false;
auto result = std::cv_status::no_timeout;
while (!success) {
for (int i = 0; i < num_elements; i++)
if (counts[i]) {
counts[i]--;
success = true;
*index = i;
break;
}
if(!success) {
const auto curr_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double>timed_wait = clock_end - curr_time;
if (curr_time >= clock_end)
result = std::cv_status::timeout;
if (result == std::cv_status::timeout)
break;
result = condition.wait_for(access_mutex, timed_wait);
}
}
access_mutex.unlock();
return success;
}
bool SharedConsumerMutex::general_try_lock(int* index) {
access_mutex.lock();
bool success = false;
for (int i = 0; i < num_elements; i++)
if (counts[i]) {
counts[i]--;
success = true;
*index = i;
break;
}
access_mutex.unlock();
return success;
}
void SharedConsumerMutex::specific_unlock(int index) {
if ((index < 0) || (index >= num_elements))
return;
access_mutex.lock();
// Enforce 1 max
counts[index] = 1;
condition.notify_all();
access_mutex.unlock();
}
void SharedConsumerMutex::specific_lock(int index) {
if ((index < 0) || (index >= num_elements))
return;
access_mutex.lock();
bool success = false;
while (!success) {
if(counts[index]) {
counts[index]--;
success = true;
}
else
condition.wait(access_mutex);
}
access_mutex.unlock();
}
bool SharedConsumerMutex::specific_timed_lock(int index) {
if ((index < 0) || (index >= num_elements))
return false;
std::chrono::time_point<std::chrono::high_resolution_clock> clock_start = std::chrono::high_resolution_clock::now();
std::chrono::time_point<std::chrono::high_resolution_clock> clock_end = clock_start + std::chrono::nanoseconds((int)(this->get_time_s() * 1000 * 1000));
access_mutex.lock();
bool success = false;
auto result = std::cv_status::no_timeout;
while (!success) {
if (counts[index]) {
counts[index]--;
success = true;
break;
}
if (!success) {
const auto curr_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double>timed_wait = clock_end - curr_time;
if (curr_time >= clock_end)
result = std::cv_status::timeout;
if (result == std::cv_status::timeout)
break;
result = condition.wait_for(access_mutex, timed_wait);
}
}
access_mutex.unlock();
return success;
}
bool SharedConsumerMutex::specific_try_lock(int index) {
if((index < 0) || (index >= num_elements))
return false;
access_mutex.lock();
bool success = false;
if (counts[index]) {
counts[index]--;
success = true;
}
access_mutex.unlock();
return success;
}