mirror of
https://github.com/Lorenzooone/cc3dsfs.git
synced 2025-06-18 16:45:39 -04:00
503 lines
24 KiB
C++
503 lines
24 KiB
C++
#include "devicecapture.hpp"
|
|
#include "usb_is_device_setup_general.hpp"
|
|
#include "usb_is_device_libusb.hpp"
|
|
#include "usb_is_device_is_driver.hpp"
|
|
#include "usb_is_device_communications.hpp"
|
|
#include "usb_is_device_acquisition.hpp"
|
|
#include "usb_is_device_acquisition_general.hpp"
|
|
#include "usb_is_nitro_acquisition_capture.hpp"
|
|
#include "usb_is_nitro_acquisition_emulator.hpp"
|
|
#include "usb_is_twl_acquisition_capture.hpp"
|
|
#include "usb_generic.hpp"
|
|
|
|
#include <libusb.h>
|
|
#include <chrono>
|
|
#include <cstring>
|
|
|
|
// 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
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
#define SERIAL_NUMBER_SIZE (IS_DEVICE_REAL_SERIAL_NUMBER_SIZE + 1)
|
|
#define MAX_TIME_WAIT 1.0
|
|
#define FRAME_BUFFER_SIZE 32
|
|
|
|
static void is_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status);
|
|
|
|
static bool initial_cleanup(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers) {
|
|
switch(usb_device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return initial_cleanup_emulator(usb_device_desc, handlers) != LIBUSB_SUCCESS;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return initial_cleanup_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return initial_cleanup_twl_capture(usb_device_desc, handlers) != LIBUSB_SUCCESS;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string get_serial(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers, int& curr_serial_extra_id) {
|
|
uint8_t data[SERIAL_NUMBER_SIZE];
|
|
std::string serial_str = std::to_string(curr_serial_extra_id);
|
|
bool conn_success = true;
|
|
if(initial_cleanup(usb_device_desc, handlers))
|
|
conn_success = false;
|
|
if (conn_success && (GetDeviceSerial(handlers, data, usb_device_desc) != LIBUSB_SUCCESS))
|
|
conn_success = false;
|
|
if (conn_success) {
|
|
data[IS_DEVICE_REAL_SERIAL_NUMBER_SIZE] = '\0';
|
|
serial_str = std::string((const char*)data);
|
|
}
|
|
else
|
|
curr_serial_extra_id += 1;
|
|
return serial_str;
|
|
}
|
|
|
|
void is_device_insert_device(std::vector<CaptureDevice>& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device, std::string path) {
|
|
devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, path, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type);
|
|
}
|
|
|
|
void is_device_insert_device(std::vector<CaptureDevice>& devices_list, is_device_device_handlers* handlers, const is_device_usb_device* usb_device_desc, int& curr_serial_extra_id_is_device) {
|
|
devices_list.emplace_back(get_serial(usb_device_desc, handlers, curr_serial_extra_id_is_device), usb_device_desc->name, usb_device_desc->long_name, CAPTURE_CONN_IS_NITRO, (void*)usb_device_desc, false, false, usb_device_desc->audio_enabled, WIDTH_DS, HEIGHT_DS + HEIGHT_DS, usb_device_desc->max_audio_samples_size, 0, 0, 0, 0, HEIGHT_DS, usb_device_desc->video_data_type);
|
|
}
|
|
|
|
static is_device_device_handlers* usb_find_by_serial_number(const is_device_usb_device* usb_device_desc, CaptureDevice* device) {
|
|
is_device_device_handlers* final_handlers = NULL;
|
|
int curr_serial_extra_id = 0;
|
|
final_handlers = is_device_libusb_serial_reconnection(usb_device_desc, device, curr_serial_extra_id);
|
|
|
|
if (final_handlers == NULL)
|
|
final_handlers = is_driver_serial_reconnection(device);
|
|
return final_handlers;
|
|
}
|
|
|
|
void list_devices_is_device(std::vector<CaptureDevice> &devices_list, std::vector<no_access_recap_data> &no_access_list) {
|
|
const size_t num_is_device_desc = GetNumISDeviceDesc();
|
|
int* curr_serial_extra_id_is_device = new int[num_is_device_desc];
|
|
bool* no_access_elems = new bool[num_is_device_desc];
|
|
bool* not_supported_elems = new bool[num_is_device_desc];
|
|
for (int i = 0; i < num_is_device_desc; i++) {
|
|
no_access_elems[i] = false;
|
|
not_supported_elems[i] = false;
|
|
curr_serial_extra_id_is_device[i] = 0;
|
|
}
|
|
is_device_libusb_list_devices(devices_list, no_access_elems, not_supported_elems, curr_serial_extra_id_is_device, num_is_device_desc);
|
|
|
|
bool any_not_supported = false;
|
|
for(int i = 0; i < num_is_device_desc; i++)
|
|
any_not_supported |= not_supported_elems[i];
|
|
for(int i = 0; i < num_is_device_desc; i++)
|
|
if(no_access_elems[i]) {
|
|
const is_device_usb_device* usb_device = GetISDeviceDesc(i);
|
|
no_access_list.emplace_back(usb_device->vid, usb_device->pid);
|
|
}
|
|
if(any_not_supported)
|
|
is_driver_list_devices(devices_list, not_supported_elems, curr_serial_extra_id_is_device, num_is_device_desc);
|
|
|
|
delete[] curr_serial_extra_id_is_device;
|
|
delete[] no_access_elems;
|
|
delete[] not_supported_elems;
|
|
}
|
|
|
|
static void is_device_connection_end(is_device_device_handlers* handlers, const is_device_usb_device *device_desc, bool interface_claimed = true) {
|
|
if (handlers == NULL)
|
|
return;
|
|
if (handlers->usb_handle)
|
|
is_device_libusb_end_connection(handlers, device_desc, interface_claimed);
|
|
else
|
|
is_driver_end_connection(handlers);
|
|
delete handlers;
|
|
}
|
|
|
|
bool is_device_connect_usb(bool print_failed, CaptureData* capture_data, CaptureDevice* device) {
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor;
|
|
is_device_device_handlers* handlers = usb_find_by_serial_number(usb_device_info, device);
|
|
if(handlers == NULL) {
|
|
capture_error_print(true, capture_data, "Device not found");
|
|
return false;
|
|
}
|
|
capture_data->handle = (void*)handlers;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64_t usb_is_device_get_video_in_size(CaptureScreensType capture_type, is_device_type device_type) {
|
|
if(device_type == IS_TWL_CAPTURE_DEVICE)
|
|
return sizeof(ISTWLCaptureVideoReceived);
|
|
if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM))
|
|
return sizeof(ISNitroEmulatorVideoInputData) / 2;
|
|
return sizeof(ISNitroEmulatorVideoInputData);
|
|
}
|
|
|
|
|
|
uint64_t usb_is_device_get_video_in_size(CaptureStatus* status) {
|
|
return usb_is_device_get_video_in_size(status->capture_type, ((const is_device_usb_device*)(status->device.descriptor))->device_type);
|
|
}
|
|
|
|
|
|
uint64_t usb_is_device_get_video_in_size(CaptureData* capture_data) {
|
|
return usb_is_device_get_video_in_size(&capture_data->status);
|
|
}
|
|
|
|
int set_acquisition_mode(is_device_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_device_usb_device* usb_device_desc) {
|
|
is_device_forward_config_values_screens capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_BOTH;
|
|
switch(capture_type) {
|
|
case CAPTURE_SCREENS_TOP:
|
|
capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_TOP;
|
|
break;
|
|
case CAPTURE_SCREENS_BOTTOM:
|
|
capture_mode_flag = IS_DEVICE_FORWARD_CONFIG_MODE_BOTTOM;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
is_device_forward_config_values_rate capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_FULL;
|
|
switch(capture_speed) {
|
|
case CAPTURE_SPEEDS_HALF:
|
|
capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_HALF;
|
|
break;
|
|
case CAPTURE_SPEEDS_THIRD:
|
|
capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_THIRD;
|
|
break;
|
|
case CAPTURE_SPEEDS_QUARTER:
|
|
capture_rate_flag = IS_DEVICE_FORWARD_CONFIG_RATE_QUARTER;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return UpdateFrameForwardConfig(handlers, IS_DEVICE_FORWARD_CONFIG_COLOR_RGB24, capture_mode_flag, capture_rate_flag, usb_device_desc);
|
|
}
|
|
|
|
int EndAcquisition(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
|
|
switch(((const is_device_usb_device*)(capture_data->status.device.descriptor))->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return EndAcquisitionEmulator(capture_data, is_device_capture_recv_data, do_drain_frames, start_frames, capture_type);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return EndAcquisitionCapture(capture_data, is_device_capture_recv_data);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return EndAcquisitionTWLCapture(capture_data, is_device_capture_recv_data);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int EndAcquisition(const is_device_usb_device* usb_device_desc, is_device_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
|
|
switch(usb_device_desc->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
return EndAcquisitionEmulator(usb_device_desc, handlers, do_drain_frames, start_frames, capture_type);
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
return EndAcquisitionCapture(usb_device_desc, handlers);
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
return EndAcquisitionTWLCapture(usb_device_desc, handlers);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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, size_t read_size) {
|
|
// Output to the other threads...
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor;
|
|
const auto curr_time = std::chrono::high_resolution_clock::now();
|
|
const std::chrono::duration<double> diff = curr_time - (*clock_start);
|
|
*clock_start = curr_time;
|
|
if(usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE) {
|
|
CaptureDataSingleBuffer* target = capture_data->data_buffers.GetWriterBuffer();
|
|
// Copy data to buffer, with special memcpy which accounts for ftd2 header data and skips synch bytes
|
|
size_t video_size = usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type);
|
|
memcpy(&target->capture_buf.is_twl_capture_received.video_capture_in, &capture_buf->is_twl_capture_received.video_capture_in, video_size);
|
|
if(read_size > video_size) {
|
|
size_t audio_size = ((read_size - video_size) / sizeof(ISTWLCaptureSoundData)) * sizeof(ISTWLCaptureAudioReceived);
|
|
memcpy(&target->capture_buf.is_twl_capture_received.audio_capture_in, &capture_buf->is_twl_capture_received.audio_capture_in, audio_size);
|
|
}
|
|
target->read = read_size;
|
|
target->time_in_buf = diff.count();
|
|
capture_data->data_buffers.ReleaseWriterBuffer();
|
|
}
|
|
else
|
|
capture_data->data_buffers.WriteToBuffer(capture_buf, read_size, 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();
|
|
}
|
|
|
|
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) {
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor;
|
|
output_to_thread(capture_data, capture_buf, curr_capture_type, clock_start, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type));
|
|
}
|
|
|
|
int is_device_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) {
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor;
|
|
int ret = ReadFrame((is_device_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), usb_device_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
output_to_thread(capture_data, capture_buf, curr_capture_type, &clock_start);
|
|
return ret;
|
|
}
|
|
|
|
void is_device_read_frame_request(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, CaptureScreensType curr_capture_type, uint32_t index) {
|
|
if(is_device_capture_recv_data == NULL)
|
|
return;
|
|
if((*is_device_capture_recv_data->status) < 0)
|
|
return;
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)capture_data->status.device.descriptor;
|
|
is_device_capture_recv_data->index = index;
|
|
is_device_capture_recv_data->curr_capture_type = curr_capture_type;
|
|
is_device_capture_recv_data->cb_data.function = is_device_read_frame_cb;
|
|
ReadFrameAsync((is_device_device_handlers*)capture_data->handle, (uint8_t*)&is_device_capture_recv_data->buffer, usb_is_device_get_video_in_size(curr_capture_type, usb_device_info->device_type), usb_device_info, &is_device_capture_recv_data->cb_data);
|
|
}
|
|
|
|
static void end_is_device_read_frame_cb(ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
is_device_capture_recv_data->in_use = false;
|
|
is_device_capture_recv_data->is_buffer_free_shared_mutex->specific_unlock(is_device_capture_recv_data->cb_data.internal_index);
|
|
}
|
|
|
|
static void is_device_read_frame_cb(void* user_data, int transfer_length, int transfer_status) {
|
|
ISDeviceCaptureReceivedData* is_device_capture_recv_data = (ISDeviceCaptureReceivedData*)user_data;
|
|
if((*is_device_capture_recv_data->status) < 0)
|
|
return end_is_device_read_frame_cb(is_device_capture_recv_data);
|
|
if(transfer_status != LIBUSB_TRANSFER_COMPLETED) {
|
|
*is_device_capture_recv_data->status = LIBUSB_ERROR_OTHER;
|
|
return end_is_device_read_frame_cb(is_device_capture_recv_data);
|
|
}
|
|
|
|
if(((int32_t)(is_device_capture_recv_data->index - (*is_device_capture_recv_data->last_index))) <= 0) {
|
|
//*is_device_capture_recv_data->status = LIBUSB_ERROR_INTERRUPTED;
|
|
return end_is_device_read_frame_cb(is_device_capture_recv_data);
|
|
}
|
|
*is_device_capture_recv_data->last_index = is_device_capture_recv_data->index;
|
|
|
|
output_to_thread(is_device_capture_recv_data->capture_data, &is_device_capture_recv_data->buffer, is_device_capture_recv_data->curr_capture_type, is_device_capture_recv_data->clock_start);
|
|
end_is_device_read_frame_cb(is_device_capture_recv_data);
|
|
}
|
|
|
|
int is_device_get_num_free_buffers(ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
int num_free = 0;
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
|
|
if(!is_device_capture_recv_data[i].in_use)
|
|
num_free += 1;
|
|
return num_free;
|
|
}
|
|
|
|
static void close_all_reads_error(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool &async_read_closed, bool &reset_usb_device, bool do_reset) {
|
|
if(get_is_device_status(is_device_capture_recv_data) >= 0)
|
|
return;
|
|
if(!async_read_closed) {
|
|
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
|
|
CloseAsyncRead((is_device_device_handlers*)capture_data->handle, &is_device_capture_recv_data[i].cb_data);
|
|
async_read_closed = true;
|
|
}
|
|
if(do_reset && (!reset_usb_device)) {
|
|
int ret = ResetUSBDevice((is_device_device_handlers*)capture_data->handle);
|
|
if((ret == LIBUSB_ERROR_NO_DEVICE) || (ret == LIBUSB_ERROR_NOT_FOUND)) {
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
|
|
is_device_capture_recv_data[i].cb_data.transfer_data_access.lock();
|
|
is_device_capture_recv_data[i].cb_data.transfer_data = NULL;
|
|
is_device_capture_recv_data[i].cb_data.transfer_data_access.unlock();
|
|
is_device_capture_recv_data[i].in_use = false;
|
|
}
|
|
}
|
|
reset_usb_device = 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(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data, bool &async_read_closed, bool &reset_usb_device, const std::chrono::time_point<std::chrono::high_resolution_clock> &start_time) {
|
|
if(has_too_much_time_passed(start_time)) {
|
|
error_is_device_status(is_device_capture_recv_data, -1);
|
|
close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, true);
|
|
}
|
|
}
|
|
|
|
void wait_all_is_device_transfers_done(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
if(is_device_capture_recv_data == NULL)
|
|
return;
|
|
bool async_read_closed = false;
|
|
bool reset_usb_device = false;
|
|
close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, false);
|
|
const auto start_time = std::chrono::high_resolution_clock::now();
|
|
for (int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
|
|
void* transfer_data;
|
|
do {
|
|
is_device_capture_recv_data[i].cb_data.transfer_data_access.lock();
|
|
transfer_data = is_device_capture_recv_data[i].cb_data.transfer_data;
|
|
is_device_capture_recv_data[i].cb_data.transfer_data_access.unlock();
|
|
if(transfer_data) {
|
|
error_too_much_time_passed(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, start_time);
|
|
is_device_capture_recv_data[i].cb_data.is_transfer_done_mutex->specific_timed_lock(i);
|
|
}
|
|
} while(transfer_data);
|
|
}
|
|
}
|
|
|
|
void wait_all_is_device_buffers_free(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
if(is_device_capture_recv_data == NULL)
|
|
return;
|
|
bool async_read_closed = false;
|
|
bool reset_usb_device = false;
|
|
close_all_reads_error(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, false);
|
|
const auto start_time = std::chrono::high_resolution_clock::now();
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
|
|
while(is_device_capture_recv_data[i].in_use) {
|
|
error_too_much_time_passed(capture_data, is_device_capture_recv_data, async_read_closed, reset_usb_device, start_time);
|
|
is_device_capture_recv_data[i].is_buffer_free_shared_mutex->specific_timed_lock(i);
|
|
}
|
|
}
|
|
|
|
void wait_one_is_device_buffer_free(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
bool done = false;
|
|
const auto start_time = std::chrono::high_resolution_clock::now();
|
|
while(!done) {
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
|
|
if(!is_device_capture_recv_data[i].in_use)
|
|
done = true;
|
|
}
|
|
if(!done) {
|
|
if(has_too_much_time_passed(start_time))
|
|
return;
|
|
if(get_is_device_status(is_device_capture_recv_data) < 0)
|
|
return;
|
|
int dummy = 0;
|
|
is_device_capture_recv_data[0].is_buffer_free_shared_mutex->general_timed_lock(&dummy);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool is_device_are_buffers_all_free(ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
return is_device_get_num_free_buffers(is_device_capture_recv_data) == NUM_CAPTURE_RECEIVED_DATA_BUFFERS;
|
|
}
|
|
|
|
ISDeviceCaptureReceivedData* is_device_get_free_buffer(CaptureData* capture_data, ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
wait_one_is_device_buffer_free(capture_data, is_device_capture_recv_data);
|
|
if(get_is_device_status(is_device_capture_recv_data) < 0)
|
|
return NULL;
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++)
|
|
if(!is_device_capture_recv_data[i].in_use) {
|
|
is_device_capture_recv_data[i].is_buffer_free_shared_mutex->specific_try_lock(i);
|
|
is_device_capture_recv_data[i].in_use = true;
|
|
return &is_device_capture_recv_data[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int get_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
return *is_device_capture_recv_data[0].status;
|
|
}
|
|
|
|
void error_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data, int error_val) {
|
|
if((error_val == 0) || (get_is_device_status(is_device_capture_recv_data) == 0))
|
|
*is_device_capture_recv_data[0].status = error_val;
|
|
}
|
|
|
|
void reset_is_device_status(ISDeviceCaptureReceivedData* is_device_capture_recv_data) {
|
|
error_is_device_status(is_device_capture_recv_data, 0);
|
|
}
|
|
|
|
void is_device_acquisition_main_loop(CaptureData* capture_data) {
|
|
if(!usb_is_initialized())
|
|
return;
|
|
bool is_done_thread;
|
|
ConsumerMutex has_data_been_processed;
|
|
std::thread async_processing_thread;
|
|
|
|
uint32_t last_index = -1;
|
|
int status = 0;
|
|
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();
|
|
ISDeviceCaptureReceivedData* is_device_capture_recv_data = new ISDeviceCaptureReceivedData[NUM_CAPTURE_RECEIVED_DATA_BUFFERS];
|
|
for(int i = 0; i < NUM_CAPTURE_RECEIVED_DATA_BUFFERS; i++) {
|
|
is_device_capture_recv_data[i].in_use = false;
|
|
is_device_capture_recv_data[i].index = i;
|
|
is_device_capture_recv_data[i].capture_data = capture_data;
|
|
is_device_capture_recv_data[i].last_index = &last_index;
|
|
is_device_capture_recv_data[i].clock_start = &clock_start;
|
|
is_device_capture_recv_data[i].is_buffer_free_shared_mutex = &is_buffer_free_shared_mutex;
|
|
is_device_capture_recv_data[i].status = &status;
|
|
is_device_capture_recv_data[i].cb_data.actual_user_data = &is_device_capture_recv_data[i];
|
|
is_device_capture_recv_data[i].cb_data.transfer_data = NULL;
|
|
is_device_capture_recv_data[i].cb_data.is_transfer_done_mutex = &is_transfer_done_shared_mutex;
|
|
is_device_capture_recv_data[i].cb_data.internal_index = i;
|
|
is_device_capture_recv_data[i].cb_data.is_transfer_data_ready_mutex = &is_transfer_data_ready_shared_mutex;
|
|
is_device_capture_recv_data[i].cb_data.is_data_ready = false;
|
|
}
|
|
SetupISDeviceAsyncThread((is_device_device_handlers*)capture_data->handle, is_device_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed);
|
|
capture_data->status.reset_hardware = false;
|
|
switch(((const is_device_usb_device*)(capture_data->status.device.descriptor))->device_type) {
|
|
case IS_NITRO_EMULATOR_DEVICE:
|
|
is_nitro_acquisition_emulator_main_loop(capture_data, is_device_capture_recv_data);
|
|
break;
|
|
case IS_NITRO_CAPTURE_DEVICE:
|
|
is_nitro_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data);
|
|
break;
|
|
case IS_TWL_CAPTURE_DEVICE:
|
|
is_twl_acquisition_capture_main_loop(capture_data, is_device_capture_recv_data);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
wait_all_is_device_buffers_free(capture_data, is_device_capture_recv_data);
|
|
EndISDeviceAsyncThread((is_device_device_handlers*)capture_data->handle, is_device_capture_recv_data, &async_processing_thread, &is_done_thread, &has_data_been_processed);
|
|
delete []is_device_capture_recv_data;
|
|
}
|
|
|
|
void usb_is_device_acquisition_cleanup(CaptureData* capture_data) {
|
|
if(!usb_is_initialized())
|
|
return;
|
|
is_device_connection_end((is_device_device_handlers*)capture_data->handle, (const is_device_usb_device*)capture_data->status.device.descriptor);
|
|
capture_data->handle = NULL;
|
|
}
|
|
|
|
bool is_device_is_capture(CaptureDevice* device) {
|
|
if(device->cc_type != CAPTURE_CONN_IS_NITRO)
|
|
return false;
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor;
|
|
return (usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE) || (usb_device_info->device_type == IS_NITRO_CAPTURE_DEVICE);
|
|
}
|
|
|
|
bool is_device_is_nitro(CaptureDevice* device) {
|
|
if(device->cc_type != CAPTURE_CONN_IS_NITRO)
|
|
return false;
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor;
|
|
return (usb_device_info->device_type == IS_NITRO_EMULATOR_DEVICE) || (usb_device_info->device_type == IS_NITRO_CAPTURE_DEVICE);
|
|
}
|
|
|
|
bool is_device_is_twl(CaptureDevice* device) {
|
|
if(device->cc_type != CAPTURE_CONN_IS_NITRO)
|
|
return false;
|
|
const is_device_usb_device* usb_device_info = (const is_device_usb_device*)device->descriptor;
|
|
return (usb_device_info->device_type == IS_TWL_CAPTURE_DEVICE);
|
|
}
|
|
|
|
void usb_is_device_init() {
|
|
return usb_init();
|
|
}
|
|
|
|
void usb_is_device_close() {
|
|
usb_close();
|
|
}
|
|
|