2020-05-05 16:22:16 +01:00
/*
2020-07-03 10:31:22 +01:00
* usb . c
2020-05-05 16:22:16 +01:00
*
2020-07-03 10:31:22 +01:00
* Heavily based in usb_comms from libnx .
*
2020-07-06 01:10:07 +01:00
* Copyright ( c ) 2018 - 2020 , Switchbrew and libnx contributors .
2023-04-08 12:42:22 +01:00
* Copyright ( c ) 2020 - 2023 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 10:31:22 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-05-05 16:22:16 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2020-05-05 16:22:16 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-05-05 16:22:16 +01:00
*/
2021-03-26 04:35:14 +00:00
# include "nxdt_utils.h"
2020-07-03 10:31:22 +01:00
# include "usb.h"
2020-05-05 16:22:16 +01:00
2023-05-24 20:05:34 +01:00
# define USB_ABI_VERSION_MAJOR 1
2023-11-11 20:50:02 +00:00
# define USB_ABI_VERSION_MINOR 2
2023-05-24 20:05:34 +01:00
# define USB_ABI_VERSION ((USB_ABI_VERSION_MAJOR << 4) | USB_ABI_VERSION_MINOR)
2020-05-06 07:01:00 +01:00
2021-02-12 22:41:14 +00:00
# define USB_CMD_HEADER_MAGIC 0x4E584454 /* "NXDT". */
2020-05-05 16:22:16 +01:00
2021-02-12 22:41:14 +00:00
# define USB_TRANSFER_ALIGNMENT 0x1000 /* 4 KiB. */
2023-11-11 11:16:29 +00:00
# define USB_TRANSFER_TIMEOUT 10 /* 10 seconds. */
2020-05-05 16:22:16 +01:00
2021-02-12 22:41:14 +00:00
# define USB_DEV_VID 0x057E /* VID officially used by Nintendo in usb:ds. */
# define USB_DEV_PID 0x3000 /* PID officially used by Nintendo in usb:ds. */
# define USB_DEV_BCD_REL 0x0100 /* Device release number. Always 1.0. */
2020-08-17 22:30:47 +01:00
2021-02-12 22:41:14 +00:00
# define USB_FS_BCD_REVISION 0x0110 /* USB 1.1. */
# define USB_FS_EP0_MAX_PACKET_SIZE 0x40 /* 64 bytes. */
# define USB_FS_EP_MAX_PACKET_SIZE 0x40 /* 64 bytes. */
2020-08-17 22:30:47 +01:00
2021-02-12 22:41:14 +00:00
# define USB_HS_BCD_REVISION 0x0200 /* USB 2.0. */
2023-05-24 20:05:34 +01:00
# define USB_HS_EP0_MAX_PACKET_SIZE 0x40 /* 64 bytes. */
2021-02-12 22:41:14 +00:00
# define USB_HS_EP_MAX_PACKET_SIZE 0x200 /* 512 bytes. */
2020-08-17 22:31:24 +01:00
2021-02-12 22:41:14 +00:00
# define USB_SS_BCD_REVISION 0x0300 /* USB 3.0. */
# define USB_SS_EP0_MAX_PACKET_SIZE 9 /* 512 bytes (1 << 9). */
2021-05-11 07:00:33 +01:00
# define USB_SS_EP_MAX_PACKET_SIZE 0x400 /* 1024 bytes. */
2021-02-12 20:35:23 +00:00
2021-02-12 22:41:14 +00:00
# define USB_BOS_SIZE 0x16 /* usb_bos_descriptor + usb_2_0_extension_descriptor + usb_ss_usb_device_capability_descriptor. */
# define USB_LANGID_ENUS 0x0409
2021-02-12 20:35:23 +00:00
2020-05-05 16:22:16 +01:00
/* Type definitions. */
typedef enum {
2023-11-11 20:39:41 +00:00
UsbCommandType_StartSession = 0 ,
UsbCommandType_SendFileProperties = 1 ,
UsbCommandType_CancelFileTransfer = 2 ,
UsbCommandType_SendNspHeader = 3 ,
UsbCommandType_EndSession = 4 ,
UsbCommandType_StartExtractedFsDump = 5 ,
UsbCommandType_EndExtractedFsDump = 6 ,
UsbCommandType_Count = 7 ///< Total values supported by this enum.
2020-05-05 16:22:16 +01:00
} UsbCommandType ;
typedef struct {
u32 magic ;
u32 cmd ;
u32 cmd_block_size ;
u8 reserved [ 0x4 ] ;
} UsbCommandHeader ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( UsbCommandHeader , 0x10 ) ;
2020-05-05 16:22:16 +01:00
typedef struct {
2020-05-06 07:01:00 +01:00
u8 app_ver_major ;
u8 app_ver_minor ;
u8 app_ver_micro ;
u8 abi_version ;
2021-03-16 05:08:38 +00:00
char git_commit [ 8 ] ;
u8 reserved [ 0x4 ] ;
2020-05-08 04:48:22 +01:00
} UsbCommandStartSession ;
2020-05-05 16:22:16 +01:00
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( UsbCommandStartSession , 0x10 ) ;
2020-05-05 16:22:16 +01:00
typedef struct {
u64 file_size ;
u32 filename_length ;
2020-10-26 06:39:33 +00:00
u32 nsp_header_size ;
2020-05-05 16:22:16 +01:00
char filename [ FS_MAX_PATH ] ;
2023-11-11 20:39:41 +00:00
u8 reserved [ 0xF ] ;
2020-05-05 16:22:16 +01:00
} UsbCommandSendFileProperties ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( UsbCommandSendFileProperties , 0x320 ) ;
2023-11-11 20:39:41 +00:00
typedef struct {
u64 extracted_fs_size ;
char extracted_fs_root_path [ FS_MAX_PATH ] ;
u8 reserved [ 0x6 ] ;
} UsbCommandStartExtractedFsDump ;
NXDT_ASSERT ( UsbCommandStartExtractedFsDump , 0x310 ) ;
2020-05-05 16:22:16 +01:00
typedef enum {
2020-07-03 10:31:22 +01:00
///< Expected response code.
2020-05-07 12:08:54 +01:00
UsbStatusType_Success = 0 ,
2022-07-05 02:04:28 +01:00
2020-07-03 10:31:22 +01:00
///< Internal usage.
2020-05-07 12:08:54 +01:00
UsbStatusType_InvalidCommandSize = 1 ,
UsbStatusType_WriteCommandFailed = 2 ,
UsbStatusType_ReadStatusFailed = 3 ,
2022-07-05 02:04:28 +01:00
2020-07-03 10:31:22 +01:00
///< These can be returned by the host device.
2020-05-09 05:48:46 +01:00
UsbStatusType_InvalidMagicWord = 4 ,
UsbStatusType_UnsupportedCommand = 5 ,
2020-05-07 12:08:54 +01:00
UsbStatusType_UnsupportedAbiVersion = 6 ,
2020-05-09 07:32:01 +01:00
UsbStatusType_MalformedCommand = 7 ,
2023-07-17 00:03:05 +01:00
UsbStatusType_HostIoError = 8 ,
UsbStatusType_Count = 9 ///< Total values supported by this enum.
2020-05-05 16:22:16 +01:00
} UsbStatusType ;
typedef struct {
u32 magic ;
2020-10-25 15:42:53 +00:00
u32 status ; ///< UsbStatusType.
u16 max_packet_size ; ///< USB host endpoint max packet size.
u8 reserved [ 0x6 ] ;
2020-05-05 16:22:16 +01:00
} UsbStatus ;
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( UsbStatus , 0x10 ) ;
2021-02-12 20:35:23 +00:00
/// Imported from libusb, with some adjustments.
enum usb_bos_type {
USB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1 ,
USB_BT_USB_2_0_EXTENSION = 2 ,
USB_BT_SS_USB_DEVICE_CAPABILITY = 3 ,
USB_BT_CONTAINER_ID = 4
} ;
/// Imported from libusb, with some adjustments.
enum usb_2_0_extension_attributes {
USB_BM_LPM_SUPPORT = 2
} ;
/// Imported from libusb, with some adjustments.
enum usb_ss_usb_device_capability_attributes {
USB_BM_LTM_SUPPORT = 2
} ;
/// Imported from libusb, with some adjustments.
enum usb_supported_speed {
2021-02-12 22:01:33 +00:00
USB_LOW_SPEED_OPERATION = BIT ( 0 ) ,
USB_FULL_SPEED_OPERATION = BIT ( 1 ) ,
USB_HIGH_SPEED_OPERATION = BIT ( 2 ) ,
2021-02-12 20:35:23 +00:00
USB_SUPER_SPEED_OPERATION = BIT ( 3 )
} ;
/// Imported from libusb, with some adjustments.
2023-11-26 22:13:58 +00:00
struct NX_PACKED usb_bos_descriptor {
2021-02-12 20:35:23 +00:00
u8 bLength ;
u8 bDescriptorType ; ///< Must match USB_DT_BOS.
u16 wTotalLength ; ///< Length of this descriptor and all of its sub descriptors.
u8 bNumDeviceCaps ; ///< The number of separate device capability descriptors in the BOS.
2023-05-24 20:05:34 +01:00
} ;
2021-02-12 20:35:23 +00:00
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( struct usb_bos_descriptor , 0x5 ) ;
2021-02-12 20:35:23 +00:00
/// Imported from libusb, with some adjustments.
2023-11-26 22:13:58 +00:00
struct NX_PACKED usb_2_0_extension_descriptor {
2021-02-12 20:35:23 +00:00
u8 bLength ;
u8 bDescriptorType ; ///< Must match USB_DT_DEVICE_CAPABILITY.
u8 bDevCapabilityType ; ///< Must match USB_BT_USB_2_0_EXTENSION.
u32 bmAttributes ; ///< usb_2_0_extension_attributes.
2023-05-24 20:05:34 +01:00
} ;
2021-02-12 20:35:23 +00:00
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( struct usb_2_0_extension_descriptor , 0x7 ) ;
2021-02-12 20:35:23 +00:00
/// Imported from libusb, with some adjustments.
2023-11-26 22:13:58 +00:00
struct NX_PACKED usb_ss_usb_device_capability_descriptor {
2021-02-12 20:35:23 +00:00
u8 bLength ;
u8 bDescriptorType ; ///< Must match USB_DT_DEVICE_CAPABILITY.
u8 bDevCapabilityType ; ///< Must match USB_BT_SS_USB_DEVICE_CAPABILITY.
u8 bmAttributes ; ///< usb_ss_usb_device_capability_attributes.
u16 wSpeedsSupported ; ///< usb_supported_speed.
u8 bFunctionalitySupport ; ///< The lowest speed at which all the functionality that the device supports is available to the user.
u8 bU1DevExitLat ; ///< U1 Device Exit Latency.
u16 bU2DevExitLat ; ///< U2 Device Exit Latency.
2023-05-24 20:05:34 +01:00
} ;
2021-02-12 20:35:23 +00:00
2021-03-24 17:25:19 +00:00
NXDT_ASSERT ( struct usb_ss_usb_device_capability_descriptor , 0xA ) ;
2020-05-05 16:22:16 +01:00
/* Global variables. */
2021-05-18 13:32:43 +01:00
static Mutex g_usbInterfaceMutex = 0 ;
static UsbDsInterface * g_usbInterface = NULL ;
static UsbDsEndpoint * g_usbEndpointIn = NULL , * g_usbEndpointOut = NULL ;
2023-05-24 20:05:34 +01:00
static bool g_usbInterfaceInit = false , g_usbHos5xEnabled = false ;
2020-05-05 16:22:16 +01:00
2020-05-13 15:09:51 +01:00
static Event * g_usbStateChangeEvent = NULL ;
2020-08-18 06:04:13 +01:00
static Thread g_usbDetectionThread = { 0 } ;
2020-07-12 17:37:03 +01:00
static UEvent g_usbDetectionThreadExitEvent = { 0 } , g_usbTimeoutEvent = { 0 } ;
2020-10-26 06:39:33 +00:00
static bool g_usbHostAvailable = false , g_usbSessionStarted = false , g_usbDetectionThreadExitFlag = false , g_nspTransferMode = false ;
2020-07-12 17:37:03 +01:00
static atomic_bool g_usbDetectionThreadCreated = false ;
2020-05-08 04:48:22 +01:00
2020-05-05 16:22:16 +01:00
static u8 * g_usbTransferBuffer = NULL ;
2020-08-13 07:01:23 +01:00
static u64 g_usbTransferRemainingSize = 0 , g_usbTransferWrittenSize = 0 ;
2020-08-17 22:30:47 +01:00
static u16 g_usbEndpointMaxPacketSize = 0 ;
2020-05-05 16:22:16 +01:00
/* Function prototypes. */
2020-05-13 15:09:51 +01:00
static bool usbCreateDetectionThread ( void ) ;
static void usbDestroyDetectionThread ( void ) ;
2020-08-18 06:04:13 +01:00
static void usbDetectionThreadFunc ( void * arg ) ;
2020-05-13 15:09:51 +01:00
static bool usbStartSession ( void ) ;
static void usbEndSession ( void ) ;
2020-05-07 12:08:54 +01:00
2020-05-05 16:22:16 +01:00
NX_INLINE void usbPrepareCommandHeader ( u32 cmd , u32 cmd_block_size ) ;
2021-02-13 21:49:05 +00:00
static bool usbSendCommand ( void ) ;
2023-11-11 20:39:41 +00:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
2020-08-13 07:01:23 +01:00
static void usbLogStatusDetail ( u32 status ) ;
2022-09-13 01:53:11 +01:00
# endif
2020-05-05 16:22:16 +01:00
NX_INLINE bool usbAllocateTransferBuffer ( void ) ;
NX_INLINE void usbFreeTransferBuffer ( void ) ;
static bool usbInitializeComms ( void ) ;
2023-05-24 20:05:34 +01:00
static bool usbInitializeComms5x ( void ) ;
static bool usbInitializeComms1x ( void ) ;
2020-05-05 16:22:16 +01:00
static void usbCloseComms ( void ) ;
2023-05-24 20:05:34 +01:00
static bool _usbSendFileProperties ( u64 file_size , const char * filename , u32 nsp_header_size , bool enforce_nsp_mode ) ;
2020-05-05 16:22:16 +01:00
2020-05-07 12:08:54 +01:00
NX_INLINE bool usbIsHostAvailable ( void ) ;
2020-08-13 07:01:23 +01:00
NX_INLINE void usbSetZltPacket ( bool enable ) ;
2021-02-15 21:07:47 +00:00
NX_INLINE bool usbRead ( void * buf , size_t size ) ;
NX_INLINE bool usbWrite ( void * buf , size_t size ) ;
2020-05-05 16:22:16 +01:00
static bool usbTransferData ( void * buf , size_t size , UsbDsEndpoint * endpoint ) ;
bool usbInitialize ( void )
{
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
ret = g_usbInterfaceInit ;
if ( ret ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Allocate USB transfer buffer. */
if ( ! usbAllocateTransferBuffer ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to allocate memory for the USB transfer buffer! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
/* Initialize USB comms. */
2021-05-18 13:32:43 +01:00
if ( ! usbInitializeComms ( ) )
{
2023-05-24 20:05:34 +01:00
LOG_MSG_ERROR ( " Failed to initialize USB comms! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Retrieve USB state change kernel event. */
g_usbStateChangeEvent = usbDsGetStateChangeEvent ( ) ;
if ( ! g_usbStateChangeEvent )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to retrieve USB state change kernel event! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Create user-mode exit event. */
ueventCreate ( & g_usbDetectionThreadExitEvent , true ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Create user-mode USB timeout event. */
ueventCreate ( & g_usbTimeoutEvent , true ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Create USB detection thread. */
atomic_store ( & g_usbDetectionThreadCreated , usbCreateDetectionThread ( ) ) ;
if ( ! atomic_load ( & g_usbDetectionThreadCreated ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update flags. */
ret = g_usbInterfaceInit = true ;
2020-05-13 15:09:51 +01:00
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
return ret ;
}
void usbExit ( void )
{
2020-07-12 17:37:03 +01:00
/* Destroy USB detection thread before attempting to lock. */
if ( atomic_load ( & g_usbDetectionThreadCreated ) )
2020-05-13 15:09:51 +01:00
{
usbDestroyDetectionThread ( ) ;
2020-07-12 17:37:03 +01:00
atomic_store ( & g_usbDetectionThreadCreated , false ) ;
2020-05-13 15:09:51 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-12 17:37:03 +01:00
/* Now we can safely lock. */
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
{
/* Clear USB state change kernel event. */
g_usbStateChangeEvent = NULL ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Close USB device interface. */
usbCloseComms ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Free USB transfer buffer. */
usbFreeTransferBuffer ( ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Update flag. */
g_usbInterfaceInit = false ;
}
2020-05-05 16:22:16 +01:00
}
2020-05-07 12:08:54 +01:00
void * usbAllocatePageAlignedBuffer ( size_t size )
2020-05-05 16:22:16 +01:00
{
2020-05-07 12:08:54 +01:00
if ( ! size ) return NULL ;
return memalign ( USB_TRANSFER_ALIGNMENT , size ) ;
2020-05-05 16:22:16 +01:00
}
2021-06-25 22:01:15 +01:00
u8 usbIsReady ( void )
2020-05-05 16:22:16 +01:00
{
2021-06-25 22:01:15 +01:00
u8 ret = UsbHostSpeed_None ;
2022-07-05 02:04:28 +01:00
2021-06-25 22:01:15 +01:00
SCOPED_TRY_LOCK ( & g_usbInterfaceMutex )
{
if ( ! g_usbHostAvailable | | ! g_usbSessionStarted ) break ;
2022-07-05 02:04:28 +01:00
2021-06-25 22:01:15 +01:00
switch ( g_usbEndpointMaxPacketSize )
{
case USB_FS_EP_MAX_PACKET_SIZE : /* USB 1.x. */
ret = UsbHostSpeed_FullSpeed ;
break ;
case USB_HS_EP_MAX_PACKET_SIZE : /* USB 2.0. */
ret = UsbHostSpeed_HighSpeed ;
break ;
case USB_SS_EP_MAX_PACKET_SIZE : /* USB 3.0. */
ret = UsbHostSpeed_SuperSpeed ;
break ;
default :
break ;
}
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
return ret ;
}
2023-05-24 20:05:34 +01:00
bool usbSendFileProperties ( u64 file_size , const char * filename )
2020-05-05 16:22:16 +01:00
{
bool ret = false ;
2023-05-24 20:05:34 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex ) ret = _usbSendFileProperties ( file_size , filename , 0 , false ) ;
return ret ;
}
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
bool usbSendNspProperties ( u64 nsp_size , const char * filename , u32 nsp_header_size )
{
bool ret = false ;
SCOPED_LOCK ( & g_usbInterfaceMutex ) ret = _usbSendFileProperties ( nsp_size , filename , nsp_header_size , true ) ;
2020-05-05 16:22:16 +01:00
return ret ;
}
2020-05-07 12:08:54 +01:00
bool usbSendFileData ( void * data , u64 data_size )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
2020-08-13 07:01:23 +01:00
{
2021-05-18 13:32:43 +01:00
void * buf = NULL ;
bool zlt_required = false ;
2022-07-05 02:04:28 +01:00
2023-11-11 20:39:41 +00:00
if ( ! g_usbTransferBuffer | | ! g_usbInterfaceInit | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | ! g_usbTransferRemainingSize | | ! data | | ! data_size | | \
data_size > USB_TRANSFER_BUFFER_SIZE | | data_size > g_usbTransferRemainingSize )
2020-08-13 07:01:23 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
goto end ;
2020-08-13 07:01:23 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Optimization for buffers that already are page aligned. */
if ( IS_ALIGNED ( ( u64 ) data , USB_TRANSFER_ALIGNMENT ) )
2020-08-13 07:01:23 +01:00
{
2021-05-18 13:32:43 +01:00
buf = data ;
} else {
buf = g_usbTransferBuffer ;
memcpy ( buf , data , data_size ) ;
2020-08-13 07:01:23 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Determine if we'll need to set a Zero Length Termination (ZLT) packet. */
/* This is automatically handled by usbDsEndpoint_PostBufferAsync(), depending on the ZLT setting from the input (write) endpoint. */
/* First, check if this is the last data chunk for this file. */
if ( ( g_usbTransferRemainingSize - data_size ) = = 0 )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
/* Enable ZLT if the last chunk size is aligned to the USB endpoint max packet size. */
if ( IS_ALIGNED ( data_size , g_usbEndpointMaxPacketSize ) )
{
zlt_required = true ;
usbSetZltPacket ( true ) ;
2022-07-12 17:34:49 +01:00
LOG_MSG_DEBUG ( " ZLT enabled. Last chunk size: 0x%lX bytes. " , data_size ) ;
2021-05-18 13:32:43 +01:00
}
} else {
/* Disable ZLT if this is the first of multiple data chunks. */
if ( ! g_usbTransferWrittenSize )
{
usbSetZltPacket ( false ) ;
2022-07-12 17:34:49 +01:00
LOG_MSG_DEBUG ( " ZLT disabled (first chunk). " ) ;
2021-05-18 13:32:43 +01:00
}
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Send data chunk. */
if ( ! ( ret = usbWrite ( buf , data_size ) ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to write 0x%lX bytes long file data chunk from offset 0x%lX! (total size: 0x%lX). " , data_size , g_usbTransferWrittenSize , \
2023-05-24 20:05:34 +01:00
g_usbTransferRemainingSize + g_usbTransferWrittenSize ) ;
2020-07-13 07:36:17 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
g_usbTransferRemainingSize - = data_size ;
g_usbTransferWrittenSize + = data_size ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Check if this is the last chunk. */
if ( ! g_usbTransferRemainingSize )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
/* Check response from host device. */
if ( ! ( ret = usbRead ( g_usbTransferBuffer , sizeof ( UsbStatus ) ) ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to read 0x%lX bytes long status block! " , sizeof ( UsbStatus ) ) ;
2021-05-18 13:32:43 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
UsbStatus * cmd_status = ( UsbStatus * ) g_usbTransferBuffer ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
if ( ! ( ret = ( cmd_status - > magic = = __builtin_bswap32 ( USB_CMD_HEADER_MAGIC ) ) ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid status block magic word! (0x%08X). " , __builtin_bswap32 ( cmd_status - > magic ) ) ;
2021-05-18 13:32:43 +01:00
goto end ;
}
2022-07-05 02:04:28 +01:00
2022-09-13 01:53:11 +01:00
ret = ( cmd_status - > status = = UsbStatusType_Success ) ;
2023-11-11 20:39:41 +00:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
2022-09-13 01:53:11 +01:00
if ( ! ret ) usbLogStatusDetail ( cmd_status - > status ) ;
# endif
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-13 07:36:17 +01:00
end :
2021-05-18 13:32:43 +01:00
/* Disable ZLT if it was previously enabled. */
if ( zlt_required ) usbSetZltPacket ( false ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Reset variables in case of errors. */
if ( ! ret )
{
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0 ;
g_nspTransferMode = false ;
}
2020-10-26 06:39:33 +00:00
}
2022-07-05 02:04:28 +01:00
2020-10-26 06:39:33 +00:00
return ret ;
}
2021-02-15 21:07:47 +00:00
void usbCancelFileTransfer ( void )
{
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
{
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | ( ! g_usbTransferRemainingSize & & ! g_nspTransferMode ) ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Reset variables right away. */
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0 ;
g_nspTransferMode = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Prepare command data. */
usbPrepareCommandHeader ( UsbCommandType_CancelFileTransfer , 0 ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Send command. We don't care about the result here. */
usbSendCommand ( ) ;
}
2021-02-15 21:07:47 +00:00
}
2020-10-26 06:39:33 +00:00
bool usbSendNspHeader ( void * nsp_header , u32 nsp_header_size )
{
2021-02-13 21:49:05 +00:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
2020-10-26 06:39:33 +00:00
{
2023-11-11 20:39:41 +00:00
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | g_usbTransferRemainingSize | | ! g_nspTransferMode | | ! nsp_header | | \
! nsp_header_size | | nsp_header_size > ( USB_TRANSFER_BUFFER_SIZE - sizeof ( UsbCommandHeader ) ) )
2021-05-18 13:32:43 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-05-18 13:32:43 +01:00
break ;
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Disable NSP transfer mode right away. */
g_nspTransferMode = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Prepare command data. */
usbPrepareCommandHeader ( UsbCommandType_SendNspHeader , nsp_header_size ) ;
memcpy ( g_usbTransferBuffer + sizeof ( UsbCommandHeader ) , nsp_header , nsp_header_size ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Send command. */
ret = usbSendCommand ( ) ;
2020-10-26 06:39:33 +00:00
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
return ret ;
}
2023-11-11 20:39:41 +00:00
bool usbStartExtractedFsDump ( u64 extracted_fs_size , const char * extracted_fs_root_path )
{
bool ret = false ;
SCOPED_LOCK ( & g_usbInterfaceMutex )
{
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | g_usbTransferRemainingSize | | g_nspTransferMode | | ! extracted_fs_size | | \
! extracted_fs_root_path | | ! * extracted_fs_root_path ) break ;
/* Prepare command data. */
usbPrepareCommandHeader ( UsbCommandType_StartExtractedFsDump , ( u32 ) sizeof ( UsbCommandStartExtractedFsDump ) ) ;
UsbCommandStartExtractedFsDump * cmd_block = ( UsbCommandStartExtractedFsDump * ) ( g_usbTransferBuffer + sizeof ( UsbCommandHeader ) ) ;
memset ( cmd_block , 0 , sizeof ( UsbCommandStartExtractedFsDump ) ) ;
cmd_block - > extracted_fs_size = extracted_fs_size ;
snprintf ( cmd_block - > extracted_fs_root_path , sizeof ( cmd_block - > extracted_fs_root_path ) , " %s " , extracted_fs_root_path ) ;
/* Send command. */
ret = usbSendCommand ( ) ;
}
return ret ;
}
void usbEndExtractedFsDump ( void )
{
SCOPED_LOCK ( & g_usbInterfaceMutex )
{
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | g_usbTransferRemainingSize | | g_nspTransferMode ) break ;
/* Prepare command data. */
usbPrepareCommandHeader ( UsbCommandType_EndExtractedFsDump , 0 ) ;
/* Send command. We don't care about the result here. */
usbSendCommand ( ) ;
}
}
2020-05-13 15:09:51 +01:00
static bool usbCreateDetectionThread ( void )
2020-05-08 04:48:22 +01:00
{
2020-08-18 06:04:13 +01:00
if ( ! utilsCreateThread ( & g_usbDetectionThread , usbDetectionThreadFunc , NULL , 1 ) )
2020-05-08 04:48:22 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to create USB detection thread! " ) ;
2020-05-13 15:09:51 +01:00
return false ;
2020-05-08 04:48:22 +01:00
}
2022-07-05 02:04:28 +01:00
2020-05-13 15:09:51 +01:00
return true ;
}
static void usbDestroyDetectionThread ( void )
{
2020-07-12 17:37:03 +01:00
/* Signal the exit event to terminate the USB detection thread */
ueventSignal ( & g_usbDetectionThreadExitEvent ) ;
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Wait for the USB detection thread to exit. */
2020-08-18 06:04:13 +01:00
utilsJoinThread ( & g_usbDetectionThread ) ;
2020-05-13 15:09:51 +01:00
}
2020-08-18 06:04:13 +01:00
static void usbDetectionThreadFunc ( void * arg )
2020-05-13 15:09:51 +01:00
{
2023-12-20 19:32:48 +00:00
NX_IGNORE_ARG ( arg ) ;
2022-07-05 02:04:28 +01:00
2020-05-13 15:09:51 +01:00
Result rc = 0 ;
int idx = 0 ;
2022-07-05 02:04:28 +01:00
2020-07-03 10:31:22 +01:00
Waiter usb_change_event_waiter = waiterForEvent ( g_usbStateChangeEvent ) ;
Waiter usb_timeout_event_waiter = waiterForUEvent ( & g_usbTimeoutEvent ) ;
2020-07-12 17:37:03 +01:00
Waiter exit_event_waiter = waiterForUEvent ( & g_usbDetectionThreadExitEvent ) ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
bool exit_flag = false ;
2022-07-05 02:04:28 +01:00
2020-05-13 15:09:51 +01:00
while ( true )
{
2020-10-28 05:03:48 +00:00
/* Wait until an event is triggered. */
rc = waitMulti ( & idx , - 1 , usb_change_event_waiter , usb_timeout_event_waiter , exit_event_waiter ) ;
if ( R_FAILED ( rc ) ) continue ;
2022-07-05 02:04:28 +01:00
2020-07-12 17:37:03 +01:00
/* Exit event triggered. */
if ( idx = = 2 ) break ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
2020-07-12 17:37:03 +01:00
{
2021-05-18 13:32:43 +01:00
/* Retrieve current USB connection status. */
/* Only proceed if we're dealing with a status change. */
g_usbHostAvailable = usbIsHostAvailable ( ) ;
g_usbSessionStarted = false ;
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0 ;
g_usbEndpointMaxPacketSize = 0 ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Start a USB session if we're connected to a host device. */
/* This will essentially hang this thread and all other threads that call USB-related functions until: */
/* a) A session is successfully established. */
/* b) The console is disconnected from the USB host. */
/* c) The thread exit event is triggered. */
if ( g_usbHostAvailable )
2020-08-27 20:18:31 +01:00
{
2021-05-18 13:32:43 +01:00
/* Wait until a session is established. */
g_usbSessionStarted = usbStartSession ( ) ;
if ( g_usbSessionStarted )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " USB session successfully established. Endpoint max packet size: 0x%04X. " , g_usbEndpointMaxPacketSize ) ;
2021-05-18 13:32:43 +01:00
} else {
/* Update exit flag. */
exit_flag = g_usbDetectionThreadExitFlag ;
}
2020-08-27 20:18:31 +01:00
}
2020-07-12 17:37:03 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Check if the exit event was triggered while waiting for a session to be established. */
if ( exit_flag ) break ;
2020-05-13 15:09:51 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_usbInterfaceMutex )
{
/* Close USB session if needed. */
if ( g_usbHostAvailable & & g_usbSessionStarted ) usbEndSession ( ) ;
g_usbHostAvailable = g_usbSessionStarted = g_usbDetectionThreadExitFlag = false ;
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0 ;
g_usbEndpointMaxPacketSize = 0 ;
}
2022-07-05 02:04:28 +01:00
2020-08-18 06:04:13 +01:00
threadExit ( ) ;
2020-05-08 04:48:22 +01:00
}
2020-05-13 15:09:51 +01:00
static bool usbStartSession ( void )
2020-05-07 12:08:54 +01:00
{
2020-05-08 04:48:22 +01:00
UsbCommandStartSession * cmd_block = NULL ;
2021-02-13 21:49:05 +00:00
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer )
2020-05-07 12:08:54 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2021-02-13 21:49:05 +00:00
goto end ;
2020-05-07 12:08:54 +01:00
}
2022-07-05 02:04:28 +01:00
2020-05-08 04:48:22 +01:00
usbPrepareCommandHeader ( UsbCommandType_StartSession , ( u32 ) sizeof ( UsbCommandStartSession ) ) ;
2022-07-05 02:04:28 +01:00
2020-05-08 04:48:22 +01:00
cmd_block = ( UsbCommandStartSession * ) ( g_usbTransferBuffer + sizeof ( UsbCommandHeader ) ) ;
memset ( cmd_block , 0 , sizeof ( UsbCommandStartSession ) ) ;
2022-07-05 02:04:28 +01:00
2020-05-07 12:08:54 +01:00
cmd_block - > app_ver_major = VERSION_MAJOR ;
cmd_block - > app_ver_minor = VERSION_MINOR ;
cmd_block - > app_ver_micro = VERSION_MICRO ;
cmd_block - > abi_version = USB_ABI_VERSION ;
2023-03-20 16:48:21 +00:00
snprintf ( cmd_block - > git_commit , sizeof ( cmd_block - > git_commit ) , " %s " , GIT_COMMIT ) ;
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
ret = usbSendCommand ( ) ;
if ( ret )
2020-10-25 15:42:53 +00:00
{
/* Get the endpoint max packet size from the response sent by the USB host. */
/* This is done to accurately know when and where to enable Zero Length Termination (ZLT) packets during bulk transfers. */
2023-05-24 20:05:34 +01:00
/* As much as I'd like to avoid this, the GetUsbDeviceSpeed cmd from usb:ds is only available in HOS 8.0.0+ -- and we definitely want to provide USB comms under older versions. */
2021-05-18 13:32:43 +01:00
g_usbEndpointMaxPacketSize = ( ( UsbStatus * ) g_usbTransferBuffer ) - > max_packet_size ;
2020-10-25 15:42:53 +00:00
if ( g_usbEndpointMaxPacketSize ! = USB_FS_EP_MAX_PACKET_SIZE & & g_usbEndpointMaxPacketSize ! = USB_HS_EP_MAX_PACKET_SIZE & & g_usbEndpointMaxPacketSize ! = USB_SS_EP_MAX_PACKET_SIZE )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid endpoint max packet size value received from USB host: 0x%04X. " , g_usbEndpointMaxPacketSize ) ;
2022-07-05 02:04:28 +01:00
2020-10-25 15:42:53 +00:00
/* Reset flags. */
2021-02-13 21:49:05 +00:00
ret = false ;
2020-10-25 15:42:53 +00:00
g_usbEndpointMaxPacketSize = 0 ;
}
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
end :
return ret ;
2020-05-07 12:08:54 +01:00
}
2020-05-13 15:09:51 +01:00
static void usbEndSession ( void )
{
2021-05-18 13:32:43 +01:00
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted )
2020-05-13 15:09:51 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-05-13 15:09:51 +01:00
return ;
}
2022-07-05 02:04:28 +01:00
2021-02-15 21:07:47 +00:00
/* Prepare command data. */
2020-05-13 15:09:51 +01:00
usbPrepareCommandHeader ( UsbCommandType_EndSession , 0 ) ;
2022-07-05 02:04:28 +01:00
2021-02-15 21:07:47 +00:00
/* Send command. We don't care about the result here. */
usbSendCommand ( ) ;
2020-05-13 15:09:51 +01:00
}
2020-05-05 16:22:16 +01:00
NX_INLINE void usbPrepareCommandHeader ( u32 cmd , u32 cmd_block_size )
{
2023-11-11 20:39:41 +00:00
if ( cmd > = UsbCommandType_Count ) return ;
2020-05-05 16:22:16 +01:00
UsbCommandHeader * cmd_header = ( UsbCommandHeader * ) g_usbTransferBuffer ;
memset ( cmd_header , 0 , sizeof ( UsbCommandHeader ) ) ;
cmd_header - > magic = __builtin_bswap32 ( USB_CMD_HEADER_MAGIC ) ;
cmd_header - > cmd = cmd ;
cmd_header - > cmd_block_size = cmd_block_size ;
}
2021-02-13 21:49:05 +00:00
static bool usbSendCommand ( void )
2020-05-05 16:22:16 +01:00
{
2021-02-13 21:49:05 +00:00
UsbCommandHeader * cmd_header = ( UsbCommandHeader * ) g_usbTransferBuffer ;
2022-07-12 04:27:30 +01:00
u32 cmd_block_size = cmd_header - > cmd_block_size ;
# if LOG_LEVEL <= LOG_LEVEL_ERROR
u32 cmd = cmd_header - > cmd ;
# endif
2022-07-05 02:04:28 +01:00
2023-11-11 20:39:41 +00:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
UsbCommandHeader cmd_header_bkp = { 0 } ;
memcpy ( & cmd_header_bkp , cmd_header , sizeof ( UsbCommandHeader ) ) ;
# endif
2021-02-13 21:49:05 +00:00
UsbStatus * cmd_status = ( UsbStatus * ) g_usbTransferBuffer ;
u32 status = UsbStatusType_Success ;
2022-07-05 02:04:28 +01:00
2021-03-08 11:11:28 +00:00
bool ret = false , zlt_required = false , cmd_block_written = false ;
2022-07-05 02:04:28 +01:00
2021-02-16 12:22:14 +00:00
if ( ( sizeof ( UsbCommandHeader ) + cmd_block_size ) > USB_TRANSFER_BUFFER_SIZE )
2020-05-05 16:22:16 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid command size! " ) ;
2021-02-13 21:49:05 +00:00
status = UsbStatusType_InvalidCommandSize ;
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Write command header first. */
2021-02-15 21:07:47 +00:00
if ( ! usbWrite ( cmd_header , sizeof ( UsbCommandHeader ) ) )
2020-05-05 16:22:16 +01:00
{
2022-07-12 17:34:49 +01:00
if ( ! g_usbDetectionThreadExitFlag ) LOG_MSG_ERROR ( " Failed to write header for type 0x%X command! " , cmd ) ;
2021-02-13 21:49:05 +00:00
status = UsbStatusType_WriteCommandFailed ;
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Check if we need to transfer a command block. */
2021-02-16 12:22:14 +00:00
if ( cmd_block_size )
2020-05-05 16:22:16 +01:00
{
2021-02-16 12:22:14 +00:00
/* Move command block data within the transfer buffer to guarantee we'll work with proper alignment. */
memmove ( g_usbTransferBuffer , g_usbTransferBuffer + sizeof ( UsbCommandHeader ) , cmd_block_size ) ;
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Determine if we'll need to set a Zero Length Termination (ZLT) packet after sending the command block. */
2021-02-16 12:22:14 +00:00
zlt_required = IS_ALIGNED ( cmd_block_size , g_usbEndpointMaxPacketSize ) ;
2021-02-13 21:49:05 +00:00
if ( zlt_required ) usbSetZltPacket ( true ) ;
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Write command block. */
2021-02-16 12:22:14 +00:00
cmd_block_written = usbWrite ( g_usbTransferBuffer , cmd_block_size ) ;
2021-02-13 21:49:05 +00:00
if ( ! cmd_block_written )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to write command block for type 0x%X command! " , cmd ) ;
2021-02-13 21:49:05 +00:00
status = UsbStatusType_WriteCommandFailed ;
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Disable ZLT if it was previously enabled. */
if ( zlt_required ) usbSetZltPacket ( false ) ;
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Bail out if we failed to write the command block. */
if ( ! cmd_block_written ) goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Read status block. */
2021-02-15 21:07:47 +00:00
if ( ! usbRead ( cmd_status , sizeof ( UsbStatus ) ) )
2021-02-13 21:49:05 +00:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Failed to read 0x%lX bytes long status block for type 0x%X command! " , sizeof ( UsbStatus ) , cmd ) ;
2021-02-13 21:49:05 +00:00
status = UsbStatusType_ReadStatusFailed ;
goto end ;
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Verify magic word in status block. */
2020-05-05 16:22:16 +01:00
if ( cmd_status - > magic ! = __builtin_bswap32 ( USB_CMD_HEADER_MAGIC ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid status block magic word! (0x%08X). " , __builtin_bswap32 ( cmd_status - > magic ) ) ;
2021-02-13 21:49:05 +00:00
status = UsbStatusType_InvalidMagicWord ;
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
/* Update return value. */
ret = ( ( status = cmd_status - > status ) = = UsbStatusType_Success ) ;
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
end :
2023-11-11 20:39:41 +00:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
if ( ! ret )
{
usbLogStatusDetail ( status ) ;
if ( status > UsbStatusType_ReadStatusFailed )
{
LOG_DATA_INFO ( & cmd_header_bkp , sizeof ( cmd_header_bkp ) , " USB command header dump: " ) ;
if ( cmd_block_size ) LOG_DATA_INFO ( g_usbTransferBuffer , cmd_block_size , " USB command block dump: " ) ;
}
}
2022-09-13 01:53:11 +01:00
# endif
2022-07-05 02:04:28 +01:00
2021-02-13 21:49:05 +00:00
return ret ;
2020-05-05 16:22:16 +01:00
}
2022-09-13 01:53:11 +01:00
# if LOG_LEVEL <= LOG_LEVEL_INFO
2020-08-13 07:01:23 +01:00
static void usbLogStatusDetail ( u32 status )
2020-05-05 16:22:16 +01:00
{
switch ( status )
{
case UsbStatusType_Success :
case UsbStatusType_InvalidCommandSize :
case UsbStatusType_WriteCommandFailed :
case UsbStatusType_ReadStatusFailed :
2020-05-09 05:48:46 +01:00
break ;
2020-05-05 16:22:16 +01:00
case UsbStatusType_InvalidMagicWord :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Host replied with Invalid Magic Word status code. " ) ;
2020-05-05 16:22:16 +01:00
break ;
2020-05-09 05:48:46 +01:00
case UsbStatusType_UnsupportedCommand :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Host replied with Unsupported Command status code. " ) ;
2020-05-05 16:22:16 +01:00
break ;
2020-05-06 07:01:00 +01:00
case UsbStatusType_UnsupportedAbiVersion :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Host replied with Unsupported ABI Version status code. " ) ;
2020-05-05 16:22:16 +01:00
break ;
2020-05-09 07:32:01 +01:00
case UsbStatusType_MalformedCommand :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Host replied with Malformed Command status code. " ) ;
2020-05-09 07:32:01 +01:00
break ;
2020-05-05 16:22:16 +01:00
case UsbStatusType_HostIoError :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Host replied with I/O Error status code. " ) ;
2020-05-05 16:22:16 +01:00
break ;
default :
2022-07-12 17:34:49 +01:00
LOG_MSG_INFO ( " Unknown status code: 0x%X. " , status ) ;
2020-05-05 16:22:16 +01:00
break ;
}
}
2022-09-13 01:53:11 +01:00
# endif
2020-05-05 16:22:16 +01:00
NX_INLINE bool usbAllocateTransferBuffer ( void )
{
if ( g_usbTransferBuffer ) return true ;
g_usbTransferBuffer = memalign ( USB_TRANSFER_ALIGNMENT , USB_TRANSFER_BUFFER_SIZE ) ;
return ( g_usbTransferBuffer ! = NULL ) ;
}
NX_INLINE void usbFreeTransferBuffer ( void )
{
if ( ! g_usbTransferBuffer ) return ;
free ( g_usbTransferBuffer ) ;
g_usbTransferBuffer = NULL ;
}
static bool usbInitializeComms ( void )
{
Result rc = 0 ;
2023-05-24 20:05:34 +01:00
bool ret = false , is_5x = hosversionAtLeast ( 5 , 0 , 0 ) ;
/* Carry out USB comms initialization steps for this HOS version. */
ret = ( is_5x ? usbInitializeComms5x ( ) : usbInitializeComms1x ( ) ) ;
if ( ! ret ) goto end ;
/* Enable USB interface. */
/* This is always needed regardless of the HOS version. */
rc = usbDsInterface_EnableInterface ( g_usbInterface ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsInterface_EnableInterface failed! (0x%X). " , rc ) ;
goto end ;
}
/* Additional step needed under HOS 5.0.0+. */
if ( is_5x )
{
rc = usbDsEnable ( ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsEnable failed! (0x%X). " , rc ) ;
goto end ;
}
g_usbHos5xEnabled = true ;
}
ret = true ;
end :
if ( ! ret ) usbCloseComms ( ) ;
return ret ;
}
static bool usbInitializeComms5x ( void )
{
Result rc = 0 ;
bool ret = false ;
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
struct usb_device_descriptor device_descriptor = {
. bLength = USB_DT_DEVICE_SIZE ,
. bDescriptorType = USB_DT_DEVICE ,
2021-02-12 22:41:14 +00:00
. bcdUSB = USB_FS_BCD_REVISION , /* USB 1.1. Updated before setting new device descriptors for USB 2.0 and 3.0. */
. bDeviceClass = 0 , /* Defined at interface level. */
. bDeviceSubClass = 0 , /* Defined at interface level. */
. bDeviceProtocol = 0 , /* Defined at interface level. */
. bMaxPacketSize0 = USB_FS_EP0_MAX_PACKET_SIZE , /* Updated before setting the USB 3.0 device descriptor. */
. idVendor = USB_DEV_VID ,
. idProduct = USB_DEV_PID ,
. bcdDevice = USB_DEV_BCD_REL ,
. iManufacturer = 0 , /* Filled at a later time. */
. iProduct = 0 , /* Filled at a later time. */
. iSerialNumber = 0 , /* Filled at a later time. */
2021-02-12 20:35:23 +00:00
. bNumConfigurations = 1
} ;
2022-07-05 02:04:28 +01:00
2021-02-12 22:41:14 +00:00
static const u16 supported_langs [ ] = { USB_LANGID_ENUS } ;
2021-02-12 20:35:23 +00:00
static const u16 num_supported_langs = ( u16 ) MAX_ELEMENTS ( supported_langs ) ;
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
u8 bos [ USB_BOS_SIZE ] = { 0 } ;
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
struct usb_bos_descriptor * bos_desc = ( struct usb_bos_descriptor * ) bos ;
struct usb_2_0_extension_descriptor * usb2_ext_desc = ( struct usb_2_0_extension_descriptor * ) ( bos + sizeof ( struct usb_bos_descriptor ) ) ;
struct usb_ss_usb_device_capability_descriptor * usb3_devcap_desc = ( struct usb_ss_usb_device_capability_descriptor * ) ( ( u8 * ) usb2_ext_desc + sizeof ( struct usb_2_0_extension_descriptor ) ) ;
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
bos_desc - > bLength = sizeof ( struct usb_bos_descriptor ) ;
bos_desc - > bDescriptorType = USB_DT_BOS ;
2023-11-03 01:22:47 +00:00
bos_desc - > wTotalLength = sizeof ( bos ) ;
2021-02-12 20:35:23 +00:00
bos_desc - > bNumDeviceCaps = 2 ; /* USB 2.0 + USB 3.0. No extra capabilities for USB 1.x. */
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
usb2_ext_desc - > bLength = sizeof ( struct usb_2_0_extension_descriptor ) ;
usb2_ext_desc - > bDescriptorType = USB_DT_DEVICE_CAPABILITY ;
usb2_ext_desc - > bDevCapabilityType = USB_BT_USB_2_0_EXTENSION ;
usb2_ext_desc - > bmAttributes = USB_BM_LPM_SUPPORT ;
2022-07-05 02:04:28 +01:00
2021-02-12 20:35:23 +00:00
usb3_devcap_desc - > bLength = sizeof ( struct usb_ss_usb_device_capability_descriptor ) ;
usb3_devcap_desc - > bDescriptorType = USB_DT_DEVICE_CAPABILITY ;
usb3_devcap_desc - > bDevCapabilityType = USB_BT_SS_USB_DEVICE_CAPABILITY ;
usb3_devcap_desc - > bmAttributes = 0 ;
usb3_devcap_desc - > wSpeedsSupported = ( USB_SUPER_SPEED_OPERATION | USB_HIGH_SPEED_OPERATION | USB_FULL_SPEED_OPERATION ) ;
usb3_devcap_desc - > bFunctionalitySupport = 1 ; /* We can fully work under USB 1.x. */
usb3_devcap_desc - > bU1DevExitLat = 0 ;
usb3_devcap_desc - > bU2DevExitLat = 0 ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_interface_descriptor interface_descriptor = {
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
2021-02-12 20:35:23 +00:00
. bInterfaceNumber = USBDS_DEFAULT_InterfaceNumber ,
. bAlternateSetting = 0 ,
2020-05-05 16:22:16 +01:00
. bNumEndpoints = 2 ,
. bInterfaceClass = USB_CLASS_VENDOR_SPEC ,
. bInterfaceSubClass = USB_CLASS_VENDOR_SPEC ,
. bInterfaceProtocol = USB_CLASS_VENDOR_SPEC ,
2021-02-12 20:35:23 +00:00
. iInterface = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_endpoint_descriptor endpoint_descriptor_in = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_ENDPOINT_IN ,
. bmAttributes = USB_TRANSFER_TYPE_BULK ,
2021-02-12 20:35:23 +00:00
. wMaxPacketSize = USB_FS_EP_MAX_PACKET_SIZE , /* Updated before setting new device descriptors for USB 2.0 and 3.0. */
. bInterval = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_endpoint_descriptor endpoint_descriptor_out = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_ENDPOINT_OUT ,
. bmAttributes = USB_TRANSFER_TYPE_BULK ,
2021-02-12 20:35:23 +00:00
. wMaxPacketSize = USB_FS_EP_MAX_PACKET_SIZE , /* Updated before setting new device descriptors for USB 2.0 and 3.0. */
. bInterval = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
. bLength = sizeof ( struct usb_ss_endpoint_companion_descriptor ) ,
. bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION ,
. bMaxBurst = 0x0F ,
2021-02-12 22:41:14 +00:00
. bmAttributes = 0 ,
. wBytesPerInterval = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
/* Set language string descriptor. */
rc = usbDsAddUsbLanguageStringDescriptor ( NULL , supported_langs , num_supported_langs ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsAddUsbLanguageStringDescriptor failed! (0x%X). " , rc ) ;
goto end ;
}
/* Set manufacturer string descriptor. */
rc = usbDsAddUsbStringDescriptor ( & ( device_descriptor . iManufacturer ) , APP_AUTHOR ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsAddUsbStringDescriptor failed! (0x%X) (manufacturer). " , rc ) ;
goto end ;
}
/* Set product string descriptor. */
rc = usbDsAddUsbStringDescriptor ( & ( device_descriptor . iProduct ) , APP_TITLE ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsAddUsbStringDescriptor failed! (0x%X) (product). " , rc ) ;
goto end ;
}
/* Set serial number string descriptor. */
rc = usbDsAddUsbStringDescriptor ( & ( device_descriptor . iSerialNumber ) , APP_VERSION ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsAddUsbStringDescriptor failed! (0x%X) (serial number). " , rc ) ;
goto end ;
}
/* Set device descriptors. */
rc = usbDsSetUsbDeviceDescriptor ( UsbDeviceSpeed_Full , & device_descriptor ) ; /* Full Speed is USB 1.1. */
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsSetUsbDeviceDescriptor failed! (0x%X) (USB 1.1). " , rc ) ;
goto end ;
}
device_descriptor . bcdUSB = USB_HS_BCD_REVISION ;
rc = usbDsSetUsbDeviceDescriptor ( UsbDeviceSpeed_High , & device_descriptor ) ; /* High Speed is USB 2.0. */
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsSetUsbDeviceDescriptor failed! (0x%X) (USB 2.0). " , rc ) ;
goto end ;
}
device_descriptor . bcdUSB = USB_SS_BCD_REVISION ;
device_descriptor . bMaxPacketSize0 = USB_SS_EP0_MAX_PACKET_SIZE ;
rc = usbDsSetUsbDeviceDescriptor ( UsbDeviceSpeed_Super , & device_descriptor ) ; /* Super Speed is USB 3.0. */
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsSetUsbDeviceDescriptor failed! (0x%X) (USB 3.0). " , rc ) ;
goto end ;
}
/* Set Binary Object Store. */
2023-11-03 01:22:47 +00:00
rc = usbDsSetBinaryObjectStore ( bos , sizeof ( bos ) ) ;
2023-05-24 20:05:34 +01:00
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsSetBinaryObjectStore failed! (0x%X). " , rc ) ;
goto end ;
}
2020-07-06 01:10:07 +01:00
/* Setup interface. */
2021-05-18 13:32:43 +01:00
rc = usbDsRegisterInterface ( & g_usbInterface ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsRegisterInterface failed! (0x%X). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
interface_descriptor . bInterfaceNumber = g_usbInterface - > interface_index ;
2020-05-05 16:22:16 +01:00
endpoint_descriptor_in . bEndpointAddress + = ( interface_descriptor . bInterfaceNumber + 1 ) ;
endpoint_descriptor_out . bEndpointAddress + = ( interface_descriptor . bInterfaceNumber + 1 ) ;
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Full Speed config (USB 1.1). */
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Full , & interface_descriptor , USB_DT_INTERFACE_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 1.1) (interface). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Full , & endpoint_descriptor_in , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 1.1) (in endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Full , & endpoint_descriptor_out , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 1.1) (out endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* High Speed config (USB 2.0). */
2023-05-24 20:05:34 +01:00
endpoint_descriptor_in . wMaxPacketSize = endpoint_descriptor_out . wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_High , & interface_descriptor , USB_DT_INTERFACE_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 2.0) (interface). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_High , & endpoint_descriptor_in , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 2.0) (in endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_High , & endpoint_descriptor_out , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 2.0) (out endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Super Speed config (USB 3.0). */
2023-05-24 20:05:34 +01:00
endpoint_descriptor_in . wMaxPacketSize = endpoint_descriptor_out . wMaxPacketSize = USB_SS_EP_MAX_PACKET_SIZE ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Super , & interface_descriptor , USB_DT_INTERFACE_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 3.0) (interface). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Super , & endpoint_descriptor_in , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 3.0) (in endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Super , & endpoint_companion , USB_DT_SS_ENDPOINT_COMPANION_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 3.0) (in endpoint companion). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Super , & endpoint_descriptor_out , USB_DT_ENDPOINT_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 3.0) (out endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_AppendConfigurationData ( g_usbInterface , UsbDeviceSpeed_Super , & endpoint_companion , USB_DT_SS_ENDPOINT_COMPANION_SIZE ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_AppendConfigurationData failed! (0x%X) (USB 3.0) (out endpoint companion). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Setup endpoints. */
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_RegisterEndpoint ( g_usbInterface , & g_usbEndpointIn , endpoint_descriptor_in . bEndpointAddress ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_RegisterEndpoint failed! (0x%X) (in endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_RegisterEndpoint ( g_usbInterface , & g_usbEndpointOut , endpoint_descriptor_out . bEndpointAddress ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_RegisterEndpoint failed! (0x%X) (out endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
ret = true ;
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
end :
return ret ;
2020-05-05 16:22:16 +01:00
}
2023-05-24 20:05:34 +01:00
static bool usbInitializeComms1x ( void )
2020-05-05 16:22:16 +01:00
{
Result rc = 0 ;
2023-05-24 20:05:34 +01:00
bool ret = false ;
static const UsbDsDeviceInfo device_info = {
. idVendor = USB_DEV_VID ,
. idProduct = USB_DEV_PID ,
. bcdDevice = USB_DEV_BCD_REL ,
. Manufacturer = APP_AUTHOR ,
. Product = APP_TITLE ,
. SerialNumber = APP_VERSION
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_interface_descriptor interface_descriptor = {
. bLength = USB_DT_INTERFACE_SIZE ,
. bDescriptorType = USB_DT_INTERFACE ,
. bInterfaceNumber = 0 ,
2021-02-12 20:35:23 +00:00
. bAlternateSetting = 0 ,
2020-05-05 16:22:16 +01:00
. bInterfaceClass = USB_CLASS_VENDOR_SPEC ,
. bInterfaceSubClass = USB_CLASS_VENDOR_SPEC ,
. bInterfaceProtocol = USB_CLASS_VENDOR_SPEC ,
2021-02-12 20:35:23 +00:00
. iInterface = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_endpoint_descriptor endpoint_descriptor_in = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_ENDPOINT_IN ,
. bmAttributes = USB_TRANSFER_TYPE_BULK ,
2020-08-17 22:30:47 +01:00
. wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE ,
2021-02-12 20:35:23 +00:00
. bInterval = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
struct usb_endpoint_descriptor endpoint_descriptor_out = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_ENDPOINT_OUT ,
. bmAttributes = USB_TRANSFER_TYPE_BULK ,
2020-08-17 22:30:47 +01:00
. wMaxPacketSize = USB_HS_EP_MAX_PACKET_SIZE ,
2021-02-12 20:35:23 +00:00
. bInterval = 0
2020-05-05 16:22:16 +01:00
} ;
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
/* Set VID, PID and BCD. */
rc = usbDsSetVidPidBcd ( & device_info ) ;
if ( R_FAILED ( rc ) )
{
LOG_MSG_ERROR ( " usbDsSetVidPidBcd failed! (0x%X). " , rc ) ;
goto end ;
}
2020-07-06 01:10:07 +01:00
/* Setup interface. */
2021-05-18 13:32:43 +01:00
rc = usbDsGetDsInterface ( & g_usbInterface , & interface_descriptor , " usb " ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsGetDsInterface failed! (0x%X). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Setup endpoints. */
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_GetDsEndpoint ( g_usbInterface , & g_usbEndpointIn , & endpoint_descriptor_in ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_GetDsEndpoint failed! (0x%X) (in endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
rc = usbDsInterface_GetDsEndpoint ( g_usbInterface , & g_usbEndpointOut , & endpoint_descriptor_out ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsInterface_GetDsEndpoint failed! (0x%X) (out endpoint). " , rc ) ;
2023-05-24 20:05:34 +01:00
goto end ;
2020-05-05 16:22:16 +01:00
}
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
ret = true ;
end :
return ret ;
}
static void usbCloseComms ( void )
{
bool is_5x = hosversionAtLeast ( 5 , 0 , 0 ) ;
if ( is_5x & & g_usbHos5xEnabled )
2020-05-05 16:22:16 +01:00
{
2023-05-24 20:05:34 +01:00
usbDsDisable ( ) ;
g_usbHos5xEnabled = false ;
}
if ( g_usbEndpointOut )
{
usbDsEndpoint_Close ( g_usbEndpointOut ) ;
g_usbEndpointOut = NULL ;
}
if ( g_usbEndpointIn )
{
usbDsEndpoint_Close ( g_usbEndpointIn ) ;
g_usbEndpointIn = NULL ;
}
if ( g_usbInterface )
{
/* usbDsInterface_DisableInterface() is internally called here. */
usbDsInterface_Close ( g_usbInterface ) ;
g_usbInterface = NULL ;
}
if ( is_5x ) usbDsClearDeviceData ( ) ;
}
static bool _usbSendFileProperties ( u64 file_size , const char * filename , u32 nsp_header_size , bool enforce_nsp_mode )
{
bool ret = false ;
size_t filename_length = 0 ;
/* Disallow sending new files if we're not in NSP transfer mode and the remaining transfer size isn't zero. */
/* Allow empty files if we're not in NSP transfer mode. */
/* Disallow sending new NSPs if we're already in NSP transfer mode. */
if ( ! g_usbInterfaceInit | | ! g_usbTransferBuffer | | ! g_usbHostAvailable | | ! g_usbSessionStarted | | ( ! g_nspTransferMode & & g_usbTransferRemainingSize ) | | \
! filename | | ! ( filename_length = strlen ( filename ) ) | | filename_length > = FS_MAX_PATH | | ( ! enforce_nsp_mode & & nsp_header_size ) | | \
( enforce_nsp_mode & & ( g_nspTransferMode | | ! file_size | | ! nsp_header_size | | nsp_header_size > = file_size ) ) )
{
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2023-05-24 20:05:34 +01:00
/* Prepare command data. */
usbPrepareCommandHeader ( UsbCommandType_SendFileProperties , ( u32 ) sizeof ( UsbCommandSendFileProperties ) ) ;
UsbCommandSendFileProperties * cmd_block = ( UsbCommandSendFileProperties * ) ( g_usbTransferBuffer + sizeof ( UsbCommandHeader ) ) ;
memset ( cmd_block , 0 , sizeof ( UsbCommandSendFileProperties ) ) ;
cmd_block - > file_size = file_size ;
cmd_block - > filename_length = ( u32 ) filename_length ;
cmd_block - > nsp_header_size = nsp_header_size ;
snprintf ( cmd_block - > filename , sizeof ( cmd_block - > filename ) , " %s " , filename ) ;
/* Send command. */
ret = usbSendCommand ( ) ;
if ( ret )
{
g_usbTransferRemainingSize = file_size ;
g_usbTransferWrittenSize = 0 ;
if ( ! g_nspTransferMode & & enforce_nsp_mode ) g_nspTransferMode = true ;
} else {
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0 ;
g_nspTransferMode = false ;
}
return ret ;
2020-05-05 16:22:16 +01:00
}
2020-05-07 12:08:54 +01:00
NX_INLINE bool usbIsHostAvailable ( void )
{
2020-12-06 22:45:30 +00:00
UsbState state = UsbState_Detached ;
2020-05-07 12:08:54 +01:00
Result rc = usbDsGetState ( & state ) ;
2020-12-06 22:45:30 +00:00
return ( R_SUCCEEDED ( rc ) & & state = = UsbState_Configured ) ;
2020-05-07 12:08:54 +01:00
}
2020-08-13 07:01:23 +01:00
NX_INLINE void usbSetZltPacket ( bool enable )
{
2021-05-18 13:32:43 +01:00
usbDsEndpoint_SetZlt ( g_usbEndpointIn , enable ) ;
2020-08-13 07:01:23 +01:00
}
2021-02-15 21:07:47 +00:00
NX_INLINE bool usbRead ( void * buf , u64 size )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
return usbTransferData ( buf , size , g_usbEndpointOut ) ;
2020-05-05 16:22:16 +01:00
}
2021-02-15 21:07:47 +00:00
NX_INLINE bool usbWrite ( void * buf , u64 size )
2020-05-05 16:22:16 +01:00
{
2021-05-18 13:32:43 +01:00
return usbTransferData ( buf , size , g_usbEndpointIn ) ;
2020-05-05 16:22:16 +01:00
}
2020-05-07 12:08:54 +01:00
static bool usbTransferData ( void * buf , u64 size , UsbDsEndpoint * endpoint )
2020-05-05 16:22:16 +01:00
{
2020-05-11 20:12:03 +01:00
if ( ! buf | | ! IS_ALIGNED ( ( u64 ) buf , USB_TRANSFER_ALIGNMENT ) | | ! size | | ! endpoint )
2020-05-05 16:22:16 +01:00
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " Invalid parameters! " ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
if ( ! usbIsHostAvailable ( ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " USB host unavailable! " ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
Result rc = 0 ;
UsbDsReportData report_data = { 0 } ;
2021-02-15 21:07:47 +00:00
u32 urb_id = 0 , transferred_size = 0 ;
2020-07-12 16:29:08 +01:00
bool thread_exit = false ;
2022-07-05 02:04:28 +01:00
2021-05-18 13:32:43 +01:00
/* Start a USB transfer using the provided endpoint. */
2021-02-15 21:07:47 +00:00
rc = usbDsEndpoint_PostBufferAsync ( endpoint , buf , size , & urb_id ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsEndpoint_PostBufferAsync failed! (0x%X) (URB ID %u). " , rc , urb_id ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Wait for the transfer to finish. */
2020-07-12 16:29:08 +01:00
if ( g_usbSessionStarted )
{
2021-05-18 13:32:43 +01:00
/* If the USB session has already been established, then use a regular timeout value. */
2020-07-12 16:29:08 +01:00
rc = eventWait ( & ( endpoint - > CompletionEvent ) , USB_TRANSFER_TIMEOUT * ( u64 ) 1000000000 ) ;
} else {
2021-06-08 04:13:45 +01:00
/* If we're starting a USB session, wait indefinitely inside a loop to let the user start the host script. */
2020-07-12 17:37:03 +01:00
int idx = 0 ;
Waiter completion_event_waiter = waiterForEvent ( & ( endpoint - > CompletionEvent ) ) ;
Waiter exit_event_waiter = waiterForUEvent ( & g_usbDetectionThreadExitEvent ) ;
2022-07-05 02:04:28 +01:00
2020-07-12 17:37:03 +01:00
rc = waitMulti ( & idx , - 1 , completion_event_waiter , exit_event_waiter ) ;
if ( R_SUCCEEDED ( rc ) & & idx = = 1 )
2020-07-12 16:29:08 +01:00
{
2020-07-12 17:37:03 +01:00
/* Exit event triggered. */
rc = MAKERESULT ( Module_Kernel , KernelError_TimedOut ) ;
g_usbDetectionThreadExitFlag = thread_exit = true ;
2020-07-12 16:29:08 +01:00
}
}
2022-07-05 02:04:28 +01:00
2020-07-12 16:29:08 +01:00
/* Clear the endpoint completion event. */
if ( ! thread_exit ) eventClear ( & ( endpoint - > CompletionEvent ) ) ;
2022-07-05 02:04:28 +01:00
2020-05-11 13:11:06 +01:00
if ( R_FAILED ( rc ) )
{
2020-07-06 01:10:07 +01:00
/* Cancel transfer. */
2020-05-11 13:11:06 +01:00
usbDsEndpoint_Cancel ( endpoint ) ;
2022-07-05 02:04:28 +01:00
2020-07-06 01:10:07 +01:00
/* Safety measure: wait until the completion event is triggered again before proceeding. */
2020-05-11 20:12:03 +01:00
eventWait ( & ( endpoint - > CompletionEvent ) , UINT64_MAX ) ;
eventClear ( & ( endpoint - > CompletionEvent ) ) ;
2022-07-05 02:04:28 +01:00
2020-11-28 06:38:53 +00:00
/* Signal user-mode USB timeout event if needed. */
2020-07-06 01:10:07 +01:00
/* This will "reset" the USB connection by making the background thread wait until a new session is established. */
2020-07-03 10:31:22 +01:00
if ( g_usbSessionStarted ) ueventSignal ( & g_usbTimeoutEvent ) ;
2022-07-05 02:04:28 +01:00
2022-07-12 17:34:49 +01:00
if ( ! thread_exit ) LOG_MSG_ERROR ( " eventWait failed! (0x%X) (URB ID %u). " , rc , urb_id ) ;
2022-07-05 02:04:28 +01:00
2020-05-11 13:11:06 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
rc = usbDsEndpoint_GetReportData ( endpoint , & report_data ) ;
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsEndpoint_GetReportData failed! (0x%X) (URB ID %u). " , rc , urb_id ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2021-02-15 21:07:47 +00:00
rc = usbDsParseReportData ( & report_data , urb_id , NULL , & transferred_size ) ;
2020-05-05 16:22:16 +01:00
if ( R_FAILED ( rc ) )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " usbDsParseReportData failed! (0x%X) (URB ID %u). " , rc , urb_id ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
if ( transferred_size ! = size )
{
2022-07-12 17:34:49 +01:00
LOG_MSG_ERROR ( " USB transfer failed! Expected 0x%lX bytes, got 0x%X bytes (URB ID %u). " , size , transferred_size , urb_id ) ;
2020-05-05 16:22:16 +01:00
return false ;
}
2022-07-05 02:04:28 +01:00
2020-05-05 16:22:16 +01:00
return true ;
}