From 74c9b1cb995830d825c85745b68aa9de98f80eca Mon Sep 17 00:00:00 2001 From: Lorenzooone Date: Mon, 16 Mar 2026 03:07:03 +0100 Subject: [PATCH] Improve FTD3 connection --- .../3dscapture_ftd3_compatibility.hpp | 4 +- .../3dscapture_ftd3_libusb_comms.hpp | 4 +- .../3dscapture_ftd3_compatibility.cpp | 22 ++- .../3dscapture_ftd3_libusb_comms.cpp | 30 ++-- .../3dscapture_ftd3_shared.cpp | 140 +++++++++++------- 5 files changed, 125 insertions(+), 75 deletions(-) diff --git a/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.hpp b/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.hpp index c259d60..e73215e 100644 --- a/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.hpp +++ b/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.hpp @@ -17,8 +17,8 @@ void ftd3_close_compat(ftd3_device_device_handlers* handlers); bool ftd3_is_error_compat(ftd3_device_device_handlers* handlers, int error_num); int ftd3_abort_pipe_compat(ftd3_device_device_handlers* handlers, int pipe); int ftd3_set_stream_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, size_t length); -int ftd3_write_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t size, int* num_transferred); -int ftd3_read_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t size, int* num_transferred); +int ftd3_write_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t size, int* num_transferred, int timeout_ms = -1); +int ftd3_read_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t size, int* num_transferred, int timeout_ms = -1); bool ftd3_get_is_bad_compat(ftd3_device_device_handlers* handlers); bool ftd3_get_skip_initial_pipe_abort_compat(ftd3_device_device_handlers* handlers); void ftd3_main_loop_compat(CaptureData* capture_data, int pipe); diff --git a/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.hpp b/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.hpp index ef78ba1..384dfcc 100644 --- a/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.hpp +++ b/include/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.hpp @@ -27,8 +27,8 @@ ftd3_device_device_handlers* ftd3_libusb_serial_reconnection(std::string wanted_ void ftd3_libusb_end_connection(ftd3_device_device_handlers* handlers, bool interface_claimed); int ftd3_libusb_abort_pipe(ftd3_device_device_handlers* handlers, int pipe); -int ftd3_libusb_write_pipe(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t length, int* num_transferred); -int ftd3_libusb_read_pipe(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t length, int* num_transferred); +int ftd3_libusb_write_pipe(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t length, int* num_transferred, int timeout_ms = -1); +int ftd3_libusb_read_pipe(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t length, int* num_transferred, int timeout_ms = -1); int ftd3_libusb_set_stream_pipe(ftd3_device_device_handlers* handlers, int pipe, size_t length); #endif diff --git a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.cpp b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.cpp index 392710b..58f54b1 100644 --- a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.cpp +++ b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_compatibility.cpp @@ -95,15 +95,22 @@ int ftd3_set_stream_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, return 0; } -int ftd3_write_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t size, int* num_transferred) { +int ftd3_write_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t size, int* num_transferred, int timeout_ms) { #ifdef USE_FTD3_LIBUSB if(handlers->usb_handle) - return ftd3_libusb_write_pipe(handlers, pipe, data, size, num_transferred); + return ftd3_libusb_write_pipe(handlers, pipe, data, size, num_transferred, timeout_ms); #endif #ifdef USE_FTD3XX if(handlers->driver_handle) { ULONG transferred_ftd3xx = 0; + ULONG old_timeout_ms = 0; + if(timeout_ms >= 0) { + FT_GetPipeTimeout(handlers->driver_handle, pipe, &old_timeout_ms); + FT_SetPipeTimeout(handlers->driver_handle, pipe, timeout_ms); + } int result = FT_WritePipe(handlers->driver_handle, pipe, (uint8_t*)data, (ULONG)size, &transferred_ftd3xx, 0); + if(timeout_ms >= 0) + FT_SetPipeTimeout(handlers->driver_handle, pipe, old_timeout_ms); if(FT_FAILED(result)) return result; *num_transferred = (int)transferred_ftd3xx; @@ -113,15 +120,22 @@ int ftd3_write_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, cons return 0; } -int ftd3_read_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t size, int* num_transferred) { +int ftd3_read_pipe_compat(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t size, int* num_transferred, int timeout_ms) { #ifdef USE_FTD3_LIBUSB if(handlers->usb_handle) - return ftd3_libusb_read_pipe(handlers, pipe, data, size, num_transferred); + return ftd3_libusb_read_pipe(handlers, pipe, data, size, num_transferred, timeout_ms); #endif #ifdef USE_FTD3XX if(handlers->driver_handle) { ULONG transferred_ftd3xx = 0; + ULONG old_timeout_ms = 0; + if(timeout_ms >= 0) { + FT_GetPipeTimeout(handlers->driver_handle, pipe, &old_timeout_ms); + FT_SetPipeTimeout(handlers->driver_handle, pipe, timeout_ms); + } int result = FT_ReadPipe(handlers->driver_handle, pipe, data, (ULONG)size, &transferred_ftd3xx, 0); + if(timeout_ms >= 0) + FT_SetPipeTimeout(handlers->driver_handle, pipe, old_timeout_ms); if(FT_FAILED(result)) return result; *num_transferred = (int)transferred_ftd3xx; diff --git a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.cpp b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.cpp index d2a77df..8a34a32 100644 --- a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.cpp +++ b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_libusb_comms.cpp @@ -120,9 +120,11 @@ int ftd3_libusb_async_in_start(ftd3_device_device_handlers* handlers, int endpoi return retval; } -static int ftd3_libusb_send_command(libusb_device_handle* handle, uint8_t* data, size_t length) { +static int ftd3_libusb_send_command(libusb_device_handle* handle, uint8_t* data, size_t length, int timeout_ms = -1) { int num_transferred = 0; - int result = libusb_bulk_transfer(handle, FTD3_COMMAND_BULK_PIPE_ID, data, (int)length, &num_transferred, FTD3_COMMAND_TIMEOUT); + if(timeout_ms < 0) + timeout_ms = FTD3_COMMAND_TIMEOUT; + int result = libusb_bulk_transfer(handle, FTD3_COMMAND_BULK_PIPE_ID, data, (int)length, &num_transferred, timeout_ms); if(result < 0) return result; if(num_transferred != ((int)length)) @@ -130,23 +132,23 @@ static int ftd3_libusb_send_command(libusb_device_handle* handle, uint8_t* data, return result; } -static int ftd3_libusb_send_ptr_data_command(libusb_device_handle* handle, uint8_t pipe, uint8_t command) { +static int ftd3_libusb_send_ptr_data_command(libusb_device_handle* handle, uint8_t pipe, uint8_t command, int timeout_ms = -1) { ftd3_command_with_ptr_data command_with_ptr_data; memset((uint8_t*)&command_with_ptr_data, 0, sizeof(ftd3_command_with_ptr_data)); command_with_ptr_data.preamble_data.cmd_id = curr_cmd_id++; command_with_ptr_data.preamble_data.pipe = pipe; command_with_ptr_data.preamble_data.command = command; - return ftd3_libusb_send_command(handle, (uint8_t*)&command_with_ptr_data, sizeof(ftd3_command_with_ptr_data)); + return ftd3_libusb_send_command(handle, (uint8_t*)&command_with_ptr_data, sizeof(ftd3_command_with_ptr_data), timeout_ms); } -static int ftd3_libusb_send_len_data_command(libusb_device_handle* handle, uint8_t pipe, uint8_t command, uint32_t length) { +static int ftd3_libusb_send_len_data_command(libusb_device_handle* handle, uint8_t pipe, uint8_t command, uint32_t length, int timeout_ms = -1) { ftd3_command_with_len_data command_with_len_data; memset((uint8_t*)&command_with_len_data, 0, sizeof(ftd3_command_with_len_data)); command_with_len_data.preamble_data.cmd_id = curr_cmd_id++; command_with_len_data.preamble_data.pipe = pipe; command_with_len_data.preamble_data.command = command; command_with_len_data.len = to_le(length); - return ftd3_libusb_send_command(handle, (uint8_t*)&command_with_len_data, sizeof(ftd3_command_with_len_data)); + return ftd3_libusb_send_command(handle, (uint8_t*)&command_with_len_data, sizeof(ftd3_command_with_len_data), timeout_ms); } static int ftd3_libusb_send_create_abort_command(libusb_device_handle* handle) { @@ -167,18 +169,22 @@ int ftd3_libusb_abort_pipe(ftd3_device_device_handlers* handlers, int pipe) { return ftd3_libusb_send_ptr_data_command((libusb_device_handle*)handlers->usb_handle, pipe, FTD3_COMMAND_DESTROY_ID); } -int ftd3_libusb_write_pipe(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t length, int* num_transferred) { - int result = ftd3_libusb_send_len_data_command((libusb_device_handle*)handlers->usb_handle, pipe, FTD3_COMMAND_RW_OPERATION_PREPARE_ID, (uint32_t)length); +int ftd3_libusb_write_pipe(ftd3_device_device_handlers* handlers, int pipe, const uint8_t* data, size_t length, int* num_transferred, int timeout_ms) { + int result = ftd3_libusb_send_len_data_command((libusb_device_handle*)handlers->usb_handle, pipe, FTD3_COMMAND_RW_OPERATION_PREPARE_ID, (uint32_t)length, timeout_ms); if(result < 0) return result; - return ftd3_libusb_bulk_out(handlers, pipe, FTD3_COMMAND_TIMEOUT, data, (int)length, num_transferred); + if(timeout_ms < 0) + timeout_ms = FTD3_COMMAND_TIMEOUT; + return ftd3_libusb_bulk_out(handlers, pipe, timeout_ms, data, (int)length, num_transferred); } -int ftd3_libusb_read_pipe(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t length, int* num_transferred) { - int result = ftd3_libusb_send_len_data_command((libusb_device_handle*)handlers->usb_handle, pipe, FTD3_COMMAND_RW_OPERATION_PREPARE_ID, (uint32_t)length); +int ftd3_libusb_read_pipe(ftd3_device_device_handlers* handlers, int pipe, uint8_t* data, size_t length, int* num_transferred, int timeout_ms) { + int result = ftd3_libusb_send_len_data_command((libusb_device_handle*)handlers->usb_handle, pipe, FTD3_COMMAND_RW_OPERATION_PREPARE_ID, (uint32_t)length, timeout_ms); if(result < 0) return result; - return ftd3_libusb_bulk_in(handlers, pipe, FTD3_COMMAND_TIMEOUT, data, (int)length, num_transferred); + if(timeout_ms < 0) + timeout_ms = FTD3_COMMAND_TIMEOUT; + return ftd3_libusb_bulk_in(handlers, pipe, timeout_ms, data, (int)length, num_transferred); } int ftd3_libusb_set_stream_pipe(ftd3_device_device_handlers* handlers, int pipe, size_t length) { diff --git a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_shared.cpp b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_shared.cpp index 04c3578..22c80cf 100644 --- a/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_shared.cpp +++ b/source/CaptureDeviceSpecific/3DSCapture_FTD3/3dscapture_ftd3_shared.cpp @@ -10,7 +10,7 @@ #define BULK_OUT 0x02 #define BULK_IN 0x82 -#define FTD3_N3DSXL_CFG_WAIT_MS 120 +#define FTD3_N3DSXL_CFG_WAIT_MS 200 #define CFG_3DS_3D_SCREEN_POS 3 const std::vector valid_3dscapture_descriptions = {"N3DSXL", "N3DSXL.2"}; @@ -97,47 +97,74 @@ static void preemptive_close_connection(CaptureData* capture_data) { capture_data->handle = NULL; } -static bool reset_3ds_state(bool print_failed, CaptureData* capture_data) { - uint8_t buf[4] = {0x80, 0x01, 0xAB, 0x00}; +static bool set_spi_access(bool print_failed, CaptureData* capture_data, bool enable) { + uint8_t buf[4] = {0x40, 0x00, 0x00, 0x00}; int transferred = 0; ftd3_device_device_handlers* handlers = (ftd3_device_device_handlers*)capture_data->handle; + if(enable) + buf[1] |= 0x80; + if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { - capture_error_print(print_failed, capture_data, "Write failed"); + capture_error_print(print_failed, capture_data, "Setting SPI access failed"); preemptive_close_connection(capture_data); return false; } - buf[0] = 0x43; - buf[1] = 0; - buf[2] = 0; + return true; +} + +// Checks the upgradable firmware that is available... Not really needed, but +// it is nice documentation in case of future stuff... +static bool spi_3ds_cc_stuff(bool print_failed, CaptureData* capture_data) { + uint8_t buf[4] = {0x80, 0x01, 0xAB, 0x00}; + uint8_t buf2[8] = {0x90, 0x08, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00}; + uint8_t in_buf[0x10]; + int transferred = 0; + ftd3_device_device_handlers* handlers = (ftd3_device_device_handlers*)capture_data->handle; + + if(!set_spi_access(print_failed, capture_data, true)) + return false; if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { capture_error_print(print_failed, capture_data, "Write failed"); preemptive_close_connection(capture_data); return false; } - default_sleep(FTD3_N3DSXL_CFG_WAIT_MS); + + if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf2, 8, &transferred))) { + capture_error_print(print_failed, capture_data, "Write failed"); + preemptive_close_connection(capture_data); + return false; + } + + if(ftd3_is_error_compat(handlers, ftd3_read_pipe_compat(handlers, BULK_IN, in_buf, 0x10, &transferred))) { + capture_error_print(print_failed, capture_data, "Read failed"); + preemptive_close_connection(capture_data); + return false; + } + + if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { + capture_error_print(print_failed, capture_data, "Write failed"); + preemptive_close_connection(capture_data); + return false; + } + + if(!set_spi_access(print_failed, capture_data, false)) + return false; return true; } static bool read_3ds_config_3d(bool print_failed, CaptureData* capture_data, CaptureDevice* device) { - uint8_t buf[4] = {0x40, 0x80, 0x00, 0x00}; + uint8_t buf[4] = {0x98, 0x05, 0x9F, 0x00}; uint8_t in_buf[0x10]; int transferred = 0; + ftd3_device_device_handlers* handlers = (ftd3_device_device_handlers*)capture_data->handle; - if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { - capture_error_print(print_failed, capture_data, "Write failed"); - preemptive_close_connection(capture_data); + if(!set_spi_access(print_failed, capture_data, true)) return false; - } - - buf[0] = 0x98; - buf[1] = 0x05; - buf[2] = 0x9F; - buf[3] = 0; if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { capture_error_print(print_failed, capture_data, "Write failed"); @@ -150,58 +177,72 @@ static bool read_3ds_config_3d(bool print_failed, CaptureData* capture_data, Cap preemptive_close_connection(capture_data); return false; } - default_sleep(FTD3_N3DSXL_CFG_WAIT_MS); if(transferred >= CFG_3DS_3D_SCREEN_POS) { if(in_buf[CFG_3DS_3D_SCREEN_POS] != 0xc1) device->has_3d = false; } - buf[0] = 0x40; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - - if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { - capture_error_print(print_failed, capture_data, "Write failed"); - preemptive_close_connection(capture_data); + if(!set_spi_access(print_failed, capture_data, false)) return false; - } - default_sleep(FTD3_N3DSXL_CFG_WAIT_MS); return true; } -static bool set_3ds_state(bool print_failed, CaptureData* capture_data, uint8_t cfg_value) { - uint8_t buf[4] = {0x40, 0x80, 0x00, 0x00}; +static bool load_3ds_cc_firmware(bool print_failed, CaptureData* capture_data, uint8_t firmware_id) { + uint8_t buf[4] = {0x42, 0x00, 0x00, 0x00}; int transferred = 0; ftd3_device_device_handlers* handlers = (ftd3_device_device_handlers*)capture_data->handle; - if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { - capture_error_print(print_failed, capture_data, "Write failed"); - preemptive_close_connection(capture_data); - return false; - } + if(firmware_id >= 2) + firmware_id = 1; - buf[0] = cfg_value; - buf[1] = 0x00; + if(!set_spi_access(print_failed, capture_data, true)) + return false; + + buf[0] += firmware_id; if(ftd3_is_error_compat(handlers, ftd3_write_pipe_compat(handlers, BULK_OUT, buf, 4, &transferred))) { capture_error_print(print_failed, capture_data, "Write failed"); preemptive_close_connection(capture_data); return false; } + default_sleep(FTD3_N3DSXL_CFG_WAIT_MS); + if(!set_spi_access(print_failed, capture_data, false)) + return false; + return true; } +static void drain_data(CaptureData* capture_data) { + uint8_t* in_buf = new uint8_t[0x100000]; + int transferred = 0; + ftd3_device_device_handlers* handlers = (ftd3_device_device_handlers*)capture_data->handle; + + set_spi_access(false, capture_data, true); + + const auto base_time = std::chrono::high_resolution_clock::now(); + + ftd3_read_pipe_compat(handlers, BULK_IN, in_buf, 0x100000, &transferred, FTD3_N3DSXL_CFG_WAIT_MS); + + const auto curr_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration diff = curr_time - base_time; + int ms_passed = (int)(diff.count() * 1000.0); + if(ms_passed < FTD3_N3DSXL_CFG_WAIT_MS) + default_sleep((float)(FTD3_N3DSXL_CFG_WAIT_MS - ms_passed + 1)); + + delete []in_buf; +} + bool connect_ftd3(bool print_failed, CaptureData* capture_data, CaptureDevice* device) { capture_data->handle = ftd3_reconnect_compat(device->serial_number, valid_3dscapture_descriptions); if(capture_data->handle == NULL) { capture_error_print(print_failed, capture_data, "Create failed"); return false; } + drain_data(capture_data); preemptive_close_connection(capture_data); capture_data->handle = ftd3_reconnect_compat(device->serial_number, valid_3dscapture_descriptions); if(capture_data->handle == NULL) { @@ -209,19 +250,10 @@ bool connect_ftd3(bool print_failed, CaptureData* capture_data, CaptureDevice* d return false; } - if(!reset_3ds_state(print_failed, capture_data)) + if(!spi_3ds_cc_stuff(print_failed, capture_data)) return false; - if(!set_3ds_state(print_failed, capture_data, 0x42)) - return false; - - if(!set_3ds_state(print_failed, capture_data, 0x40)) - return false; - - if(!set_3ds_state(print_failed, capture_data, 0x43)) - return false; - - if(!set_3ds_state(print_failed, capture_data, 0x40)) + if(!load_3ds_cc_firmware(print_failed, capture_data, 1)) return false; if(!read_3ds_config_3d(print_failed, capture_data, device)) @@ -257,13 +289,11 @@ bool ftd3_capture_3d_setup(CaptureData* capture_data, bool first_pass, bool& sto bool _3d_enabled_result = get_3d_enabled(&tmp_status); bool update_state = stored_3d_status != _3d_enabled_result; - if(update_state) { - int state_to_set = 0x42; + if(tmp_status.device.has_3d && update_state) { + uint8_t firmware_to_use = 0; if(_3d_enabled_result) - state_to_set = 0x43; - if(!set_3ds_state(print_failed, capture_data, state_to_set)) - return false; - if(!set_3ds_state(print_failed, capture_data, 0x40)) + firmware_to_use = 1; + if(!load_3ds_cc_firmware(print_failed, capture_data, firmware_to_use)) return false; }