mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 20:22:17 +00:00
Manage USB transfer session using a background thread.
This commit is contained in:
parent
a3790dd0d6
commit
1ebc106b79
3 changed files with 135 additions and 78 deletions
|
@ -366,7 +366,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
consolePrint("lrOpenLocationResolver succeeded\n");
|
consolePrint("lrOpenLocationResolver succeeded\n");
|
||||||
|
|
||||||
rc = lrLrResolveProgramPath(&resolver, (u64)0x01007EF00011E000, path); // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000
|
rc = lrLrResolveProgramPath(&resolver, (u64)0x01004AB00A260000, path); // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
{
|
{
|
||||||
consolePrint("lrLrResolveProgramPath failed\n");
|
consolePrint("lrLrResolveProgramPath failed\n");
|
||||||
|
@ -423,16 +423,13 @@ int main(int argc, char *argv[])
|
||||||
shared_data.data_written = 0;
|
shared_data.data_written = 0;
|
||||||
romfsGetTotalDataSize(&romfs_ctx, &(shared_data.total_size));
|
romfsGetTotalDataSize(&romfs_ctx, &(shared_data.total_size));
|
||||||
|
|
||||||
consolePrint("waiting for usb connection... ");
|
consolePrint("waiting for usb connection...\n");
|
||||||
|
|
||||||
if (!usbStartSession())
|
while(appletMainLoop())
|
||||||
{
|
{
|
||||||
consolePrint("failed\n");
|
if (usbIsReady()) break;
|
||||||
goto out2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
consolePrint("success\n");
|
|
||||||
|
|
||||||
consolePrint("creating threads\n");
|
consolePrint("creating threads\n");
|
||||||
thrd_create(&read_thread, read_thread_func, &shared_data);
|
thrd_create(&read_thread, read_thread_func, &shared_data);
|
||||||
thrd_create(&write_thread, write_thread_func, &shared_data);
|
thrd_create(&write_thread, write_thread_func, &shared_data);
|
||||||
|
@ -510,10 +507,6 @@ int main(int argc, char *argv[])
|
||||||
goto out2;
|
goto out2;
|
||||||
}
|
}
|
||||||
|
|
||||||
consolePrint("ending usb session... ");
|
|
||||||
usbEndSession();
|
|
||||||
consolePrint("done\n");
|
|
||||||
|
|
||||||
consolePrint("process completed in %lu seconds\n", start);
|
consolePrint("process completed in %lu seconds\n", start);
|
||||||
|
|
||||||
|
|
||||||
|
|
186
source/usb.c
186
source/usb.c
|
@ -20,6 +20,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <threads.h>
|
||||||
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
@ -28,8 +29,6 @@
|
||||||
|
|
||||||
#define USB_CMD_HEADER_MAGIC 0x4E584454 /* "NXDT" */
|
#define USB_CMD_HEADER_MAGIC 0x4E584454 /* "NXDT" */
|
||||||
|
|
||||||
#define USB_SESSION_START_TIMEOUT 10 /* 10 seconds */
|
|
||||||
|
|
||||||
#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB */
|
#define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB */
|
||||||
#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds */
|
#define USB_TRANSFER_TIMEOUT 5 /* 5 seconds */
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ typedef struct {
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UsbCommandType_StartSession = 0,
|
UsbCommandType_StartSession = 0,
|
||||||
UsbCommandType_SendFileProperties = 1,
|
UsbCommandType_SendFileProperties = 1,
|
||||||
UsbCommandType_SendNspHeader = 2, /* Needs to be implemented */
|
UsbCommandType_SendNspHeader = 2, ///< Needs to be implemented.
|
||||||
UsbCommandType_EndSession = 3
|
UsbCommandType_EndSession = 3
|
||||||
} UsbCommandType;
|
} UsbCommandType;
|
||||||
|
|
||||||
|
@ -73,15 +72,15 @@ typedef struct {
|
||||||
} UsbCommandSendFileProperties;
|
} UsbCommandSendFileProperties;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
/* Expected response code */
|
/// Expected response code.
|
||||||
UsbStatusType_Success = 0,
|
UsbStatusType_Success = 0,
|
||||||
|
|
||||||
/* Internal usage */
|
/// Internal usage.
|
||||||
UsbStatusType_InvalidCommandSize = 1,
|
UsbStatusType_InvalidCommandSize = 1,
|
||||||
UsbStatusType_WriteCommandFailed = 2,
|
UsbStatusType_WriteCommandFailed = 2,
|
||||||
UsbStatusType_ReadStatusFailed = 3,
|
UsbStatusType_ReadStatusFailed = 3,
|
||||||
|
|
||||||
/* These can be returned by the host device */
|
/// These can be returned by the host device.
|
||||||
UsbStatusType_InvalidMagicWord = 4,
|
UsbStatusType_InvalidMagicWord = 4,
|
||||||
UsbStatusType_UnsupportedCommand = 5,
|
UsbStatusType_UnsupportedCommand = 5,
|
||||||
UsbStatusType_UnsupportedAbiVersion = 6,
|
UsbStatusType_UnsupportedAbiVersion = 6,
|
||||||
|
@ -97,18 +96,26 @@ typedef struct {
|
||||||
|
|
||||||
/* Global variables. */
|
/* Global variables. */
|
||||||
|
|
||||||
static bool g_usbDeviceInterfaceInitialized = false;
|
|
||||||
static usbDeviceInterface g_usbDeviceInterface = {0};
|
|
||||||
static RwLock g_usbDeviceLock = {0};
|
static RwLock g_usbDeviceLock = {0};
|
||||||
|
static usbDeviceInterface g_usbDeviceInterface = {0};
|
||||||
|
static bool g_usbDeviceInterfaceInitialized = false;
|
||||||
|
|
||||||
static bool g_usbSessionStarted = false;
|
static Event *g_usbStateChangeEvent = NULL;
|
||||||
|
static thrd_t g_usbDetectionThread;
|
||||||
|
static UEvent g_usbDetectionThreadExitEvent = {0};
|
||||||
|
static bool g_usbDetectionThreadCreated = false, g_usbHostAvailable = false, g_usbSessionStarted = false;
|
||||||
|
|
||||||
static u8 *g_usbTransferBuffer = NULL;
|
static u8 *g_usbTransferBuffer = NULL;
|
||||||
static u64 g_usbTransferRemainingSize = 0;
|
static u64 g_usbTransferRemainingSize = 0;
|
||||||
|
|
||||||
/* Function prototypes. */
|
/* Function prototypes. */
|
||||||
|
|
||||||
static bool _usbStartSession(void);
|
static bool usbCreateDetectionThread(void);
|
||||||
|
static void usbDestroyDetectionThread(void);
|
||||||
|
static int usbDetectionThreadFunc(void *arg);
|
||||||
|
|
||||||
|
static bool usbStartSession(void);
|
||||||
|
static void usbEndSession(void);
|
||||||
|
|
||||||
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size);
|
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size);
|
||||||
static u32 usbSendCommand(size_t cmd_size);
|
static u32 usbSendCommand(size_t cmd_size);
|
||||||
|
@ -152,6 +159,20 @@ bool usbInitialize(void)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retrieve USB state change kernel event */
|
||||||
|
g_usbStateChangeEvent = usbDsGetStateChangeEvent();
|
||||||
|
if (!g_usbStateChangeEvent)
|
||||||
|
{
|
||||||
|
LOGFILE("Failed to retrieve USB state change kernel event!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create usermode exit event */
|
||||||
|
ueventCreate(&g_usbDetectionThreadExitEvent, true);
|
||||||
|
|
||||||
|
/* Create USB detection thread */
|
||||||
|
if (!(g_usbDetectionThreadCreated = usbCreateDetectionThread())) goto exit;
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
@ -164,6 +185,16 @@ void usbExit(void)
|
||||||
{
|
{
|
||||||
rwlockWriteLock(&g_usbDeviceLock);
|
rwlockWriteLock(&g_usbDeviceLock);
|
||||||
|
|
||||||
|
/* Destroy USB detection thread */
|
||||||
|
if (g_usbDetectionThreadCreated)
|
||||||
|
{
|
||||||
|
usbDestroyDetectionThread();
|
||||||
|
g_usbDetectionThreadCreated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear USB state change kernel event */
|
||||||
|
g_usbStateChangeEvent = NULL;
|
||||||
|
|
||||||
/* Close USB device interface */
|
/* Close USB device interface */
|
||||||
usbCloseComms();
|
usbCloseComms();
|
||||||
|
|
||||||
|
@ -183,38 +214,13 @@ void *usbAllocatePageAlignedBuffer(size_t size)
|
||||||
return memalign(USB_TRANSFER_ALIGNMENT, size);
|
return memalign(USB_TRANSFER_ALIGNMENT, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usbStartSession(void)
|
bool usbIsReady(void)
|
||||||
{
|
{
|
||||||
rwlockWriteLock(&g_usbDeviceLock);
|
rwlockWriteLock(&g_usbDeviceLock);
|
||||||
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
||||||
|
bool ret = (g_usbHostAvailable && g_usbSessionStarted);
|
||||||
bool ret = g_usbSessionStarted;
|
|
||||||
if (ret) goto exit;
|
|
||||||
|
|
||||||
time_t start = time(NULL);
|
|
||||||
time_t now = start;
|
|
||||||
|
|
||||||
while((now - start) < USB_SESSION_START_TIMEOUT)
|
|
||||||
{
|
|
||||||
if (usbIsHostAvailable())
|
|
||||||
{
|
|
||||||
/* Once the console has been connected to a host device, there's no need to keep running this loop */
|
|
||||||
/* usbTransferData() implements its own timeout */
|
|
||||||
ret = _usbStartSession();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Continuously running usbDsGetState() can potentially hang up the system, so let's add a small sleep */
|
|
||||||
utilsSleep(1);
|
|
||||||
now = time(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret) g_usbSessionStarted = true;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
||||||
rwlockWriteUnlock(&g_usbDeviceLock);
|
rwlockWriteUnlock(&g_usbDeviceLock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +235,7 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
|
||||||
u32 status = UsbStatusType_Success;
|
u32 status = UsbStatusType_Success;
|
||||||
u32 filename_length = 0;
|
u32 filename_length = 0;
|
||||||
|
|
||||||
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbSessionStarted || g_usbTransferRemainingSize > 0 || !filename || \
|
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || g_usbTransferRemainingSize > 0 || !filename || \
|
||||||
!(filename_length = (u32)strlen(filename)) || filename_length >= FS_MAX_PATH)
|
!(filename_length = (u32)strlen(filename)) || filename_length >= FS_MAX_PATH)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid parameters!");
|
LOGFILE("Invalid parameters!");
|
||||||
|
@ -272,8 +278,8 @@ bool usbSendFileData(void *data, u64 data_size)
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
UsbStatus *cmd_status = NULL;
|
UsbStatus *cmd_status = NULL;
|
||||||
|
|
||||||
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbSessionStarted || !g_usbTransferRemainingSize || !data || !data_size || \
|
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || !g_usbTransferRemainingSize || !data || \
|
||||||
data_size > USB_TRANSFER_BUFFER_SIZE || data_size > g_usbTransferRemainingSize)
|
!data_size || data_size > USB_TRANSFER_BUFFER_SIZE || data_size > g_usbTransferRemainingSize)
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid parameters!");
|
LOGFILE("Invalid parameters!");
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -329,29 +335,78 @@ exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usbEndSession(void)
|
static bool usbCreateDetectionThread(void)
|
||||||
{
|
{
|
||||||
rwlockWriteLock(&g_usbDeviceLock);
|
if (thrd_create(&g_usbDetectionThread, usbDetectionThreadFunc, NULL) != thrd_success)
|
||||||
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
|
||||||
|
|
||||||
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbSessionStarted)
|
|
||||||
{
|
{
|
||||||
LOGFILE("Invalid parameters!");
|
LOGFILE("Failed to create USB detection thread!");
|
||||||
goto exit;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
usbPrepareCommandHeader(UsbCommandType_EndSession, 0);
|
return true;
|
||||||
|
|
||||||
if (!usbWrite(g_usbTransferBuffer, sizeof(UsbCommandHeader))) LOGFILE("Failed to send EndSession command!");
|
|
||||||
|
|
||||||
g_usbSessionStarted = false;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
|
||||||
rwlockWriteUnlock(&g_usbDeviceLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _usbStartSession(void)
|
static void usbDestroyDetectionThread(void)
|
||||||
|
{
|
||||||
|
/* Signal the exit event to terminate the USB detection thread */
|
||||||
|
ueventSignal(&g_usbDetectionThreadExitEvent);
|
||||||
|
|
||||||
|
/* Wait for the USB detection thread to exit */
|
||||||
|
thrd_join(g_usbDetectionThread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usbDetectionThreadFunc(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
Result rc = 0;
|
||||||
|
int idx = 0;
|
||||||
|
bool prev_status = false;
|
||||||
|
|
||||||
|
Waiter usb_event_waiter = waiterForEvent(g_usbStateChangeEvent);
|
||||||
|
Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
/* Wait until an event is triggered */
|
||||||
|
rc = waitMulti(&idx, -1, usb_event_waiter, exit_event_waiter);
|
||||||
|
if (R_FAILED(rc)) continue;
|
||||||
|
|
||||||
|
/* Exit event triggered */
|
||||||
|
if (idx == 1) break;
|
||||||
|
|
||||||
|
/* Retrieve current USB connection status */
|
||||||
|
/* Only proceed if we're dealing with a status change */
|
||||||
|
rwlockWriteLock(&g_usbDeviceLock);
|
||||||
|
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
||||||
|
|
||||||
|
g_usbHostAvailable = usbIsHostAvailable();
|
||||||
|
g_usbTransferRemainingSize = 0;
|
||||||
|
|
||||||
|
if (!prev_status && g_usbHostAvailable)
|
||||||
|
{
|
||||||
|
/* Start a USB session */
|
||||||
|
/* This will essentially hang this thread and all other threads that call USB-related functions until a session is established */
|
||||||
|
g_usbSessionStarted = usbStartSession();
|
||||||
|
} else {
|
||||||
|
/* Update USB session flag */
|
||||||
|
g_usbSessionStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_status = g_usbHostAvailable;
|
||||||
|
|
||||||
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
||||||
|
rwlockWriteUnlock(&g_usbDeviceLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close USB session if needed */
|
||||||
|
if (g_usbHostAvailable && g_usbSessionStarted) usbEndSession();
|
||||||
|
g_usbHostAvailable = g_usbSessionStarted = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool usbStartSession(void)
|
||||||
{
|
{
|
||||||
UsbCommandStartSession *cmd_block = NULL;
|
UsbCommandStartSession *cmd_block = NULL;
|
||||||
size_t cmd_size = 0;
|
size_t cmd_size = 0;
|
||||||
|
@ -381,6 +436,19 @@ static bool _usbStartSession(void)
|
||||||
return (status == UsbStatusType_Success);
|
return (status == UsbStatusType_Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usbEndSession(void)
|
||||||
|
{
|
||||||
|
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted)
|
||||||
|
{
|
||||||
|
LOGFILE("Invalid parameters!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
usbPrepareCommandHeader(UsbCommandType_EndSession, 0);
|
||||||
|
|
||||||
|
if (!usbWrite(g_usbTransferBuffer, sizeof(UsbCommandHeader))) LOGFILE("Failed to send EndSession command!");
|
||||||
|
}
|
||||||
|
|
||||||
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size)
|
NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size)
|
||||||
{
|
{
|
||||||
if (cmd > UsbCommandType_EndSession) return;
|
if (cmd > UsbCommandType_EndSession) return;
|
||||||
|
@ -903,9 +971,7 @@ NX_INLINE bool usbIsHostAvailable(void)
|
||||||
{
|
{
|
||||||
u32 state = 0;
|
u32 state = 0;
|
||||||
Result rc = usbDsGetState(&state);
|
Result rc = usbDsGetState(&state);
|
||||||
bool ret = (R_SUCCEEDED(rc) && state == 5);
|
return (R_SUCCEEDED(rc) && state == 5);
|
||||||
if (!ret) g_usbSessionStarted = false;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NX_INLINE bool usbRead(void *buf, u64 size)
|
NX_INLINE bool usbRead(void *buf, u64 size)
|
||||||
|
|
12
source/usb.h
12
source/usb.h
|
@ -32,18 +32,16 @@ void usbExit(void);
|
||||||
/// Returns a pointer to a heap-allocated, page-aligned memory buffer that's suitable for USB transfers.
|
/// Returns a pointer to a heap-allocated, page-aligned memory buffer that's suitable for USB transfers.
|
||||||
void *usbAllocatePageAlignedBuffer(size_t size);
|
void *usbAllocatePageAlignedBuffer(size_t size);
|
||||||
|
|
||||||
/// Starts a data transfer session with the connected host device. Returns true if the host device replies with valid data within a certain time span.
|
/// Used to check if the console has been connected to an USB host device and if a valid USB session has been established.
|
||||||
/// This should be called before usbSendFileProperties().
|
/// Bear in mind this call will block the calling thread if the console is connected to an USB host device but no USB session has been established.
|
||||||
bool usbStartSession(void);
|
/// If the console is disconnected during this block, the function will return false.
|
||||||
|
bool usbIsReady(void);
|
||||||
|
|
||||||
/// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData(), and after usbStartSession().
|
/// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData().
|
||||||
bool usbSendFileProperties(u64 file_size, const char *filename);
|
bool usbSendFileProperties(u64 file_size, const char *filename);
|
||||||
|
|
||||||
/// Performs a file data transfer. Must be continuously called after usbSendFileProperties() until all file data has been transferred.
|
/// Performs a file data transfer. Must be continuously called after usbSendFileProperties() until all file data has been transferred.
|
||||||
/// Data chunk size must not exceed USB_TRANSFER_BUFFER_SIZE.
|
/// Data chunk size must not exceed USB_TRANSFER_BUFFER_SIZE.
|
||||||
bool usbSendFileData(void *data, u64 data_size);
|
bool usbSendFileData(void *data, u64 data_size);
|
||||||
|
|
||||||
/// Ends a previously started data transfer session with the connected host device.
|
|
||||||
void usbEndSession(void);
|
|
||||||
|
|
||||||
#endif /* __USB_H__ */
|
#endif /* __USB_H__ */
|
||||||
|
|
Loading…
Reference in a new issue