Add the switch port first code

This commit is contained in:
Luiz Pestana 2021-07-24 20:32:55 -03:00
parent 981f360371
commit dd152f6232
9 changed files with 1172 additions and 19 deletions

8
.gitignore vendored
View File

@ -58,6 +58,14 @@ packages/
*.bc *.bc
*.so *.so
# Ignore switch files
examples/*/*.d
examples/*/*.elf
examples/*/*.lst
examples/*/*.map
examples/*/*.nacp
examples/*/*.nro
# Ignore files build by xcode # Ignore files build by xcode
*.mode*v* *.mode*v*
*.pbxuser *.pbxuser

View File

@ -30,7 +30,7 @@ RAYLIB_PATH ?= ..
# Define default options # Define default options
# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB # One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
PLATFORM ?= PLATFORM_DESKTOP PLATFORM ?= PLATFORM_DESKTOP
# Locations of your newly installed library and associated headers. See ../src/Makefile # Locations of your newly installed library and associated headers. See ../src/Makefile
@ -101,6 +101,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
PLATFORM_OS=LINUX PLATFORM_OS=LINUX
endif endif
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
RAYLIB_LIBTYPE = STATIC
USE_EXTERNAL_GLFW = TRUE
endif
# RAYLIB_PATH adjustment for different platforms. # RAYLIB_PATH adjustment for different platforms.
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD? # If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
@ -131,6 +139,11 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH) export PATH = $(EMSDK_PATH);$(EMSCRIPTEN_PATH);$(CLANG_PATH);$(NODE_PATH);$(PYTHON_PATH);C:\raylib\MinGW\bin:$$(PATH)
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
RAYLIB_PREFIX ?= ..
RAYLIB_PATH = $(realpath $(RAYLIB_PREFIX))
endif
# Define raylib release directory for compiled library. # Define raylib release directory for compiled library.
# RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version # RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version
RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/src
@ -176,6 +189,12 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
# to use emscripten.h and emscripten_set_main_loop() # to use emscripten.h and emscripten_set_main_loop()
CC = emcc CC = emcc
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/libnx/switch_rules
endif
# Define default make program # Define default make program
MAKE = make MAKE = make
@ -205,6 +224,8 @@ ifeq ($(BUILD_MODE),DEBUG)
else else
ifeq ($(PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM),PLATFORM_WEB)
CFLAGS += -Os CFLAGS += -Os
else ifeq ($(PLATFORM),PLATFORM_NX)
CFLAGS += -O2
else else
CFLAGS += -s -O1 CFLAGS += -s -O1
endif endif
@ -260,6 +281,12 @@ endif
# NOTE: Some external/extras libraries could be required (stb, physac, easings...) # NOTE: Some external/extras libraries could be required (stb, physac, easings...)
INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external -I$(RAYLIB_PATH)/src/extras INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external -I$(RAYLIB_PATH)/src/extras
ifeq ($(PLATFORM),PLATFORM_NX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS += -ffunction-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
endif
# Define additional directories containing required header files # Define additional directories containing required header files
ifeq ($(PLATFORM),PLATFORM_RPI) ifeq ($(PLATFORM),PLATFORM_RPI)
# RPI required libraries # RPI required libraries
@ -308,6 +335,10 @@ ifeq ($(PLATFORM),PLATFORM_RPI)
LDFLAGS += -L/opt/vc/lib LDFLAGS += -L/opt/vc/lib
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
LDFLAGS += -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$*.map $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
endif
# Define any libraries required on linking # Define any libraries required on linking
# if you want to link libraries (libname.so or libname.a), use the -lname # if you want to link libraries (libname.so or libname.a), use the -lname
ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM),PLATFORM_DESKTOP)
@ -369,6 +400,10 @@ ifeq ($(PLATFORM),PLATFORM_WEB)
# Libraries for web (HTML5) compiling # Libraries for web (HTML5) compiling
LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a LDLIBS = $(RAYLIB_RELEASE_PATH)/libraylib.a
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
LDLIBS = -lraylib -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm
LDLIBS += `$(PREFIX)pkg-config --libs sdl2`
endif
# Define all object files required # Define all object files required
CORE = \ CORE = \
@ -521,6 +556,12 @@ physics: $(PHYSICS)
%: %.c %: %.c
ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(PLATFORM),PLATFORM_ANDROID)
$(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$< $(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$<
else ifeq ($(PLATFORM),PLATFORM_NX)
$(CC) -MMD -MP -MF $@.d -o $@.o -c $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
$(CXX) $@.o $(LDFLAGS) $(LDLIBS) -o $@.elf -D$(PLATFORM)
$(NM) -CSn $@.elf > $@.lst
nacptool --create "$(notdir $@)" "Raylib Example" "1.0.0" $@.nacp
elf2nro $@.elf $@.nro --icon=textures/resources/raylib_logo.jpg --nacp=$@.nacp
else else
$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
endif endif
@ -550,6 +591,16 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
endif endif
ifeq ($(PLATFORM),PLATFORM_WEB) ifeq ($(PLATFORM),PLATFORM_WEB)
del *.o *.html *.js del *.o *.html *.js
endif
ifeq ($(PLATFORM),PLATFORM_NX)
find . -type f \( \
-name "*.o" -o \
-name "*.d" -o \
-name "*.elf" -o \
-name "*.lst" -o \
-name "*.map" -o \
-name "*.nacp" -o \
-name "*.nro" \) -delete
endif endif
@echo Cleaning done @echo Cleaning done

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -11,6 +11,7 @@
# PLATFORM_RPI: Raspberry Pi (Raspbian) # PLATFORM_RPI: Raspberry Pi (Raspbian)
# PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver # PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
# PLATFORM_WEB: HTML5 (Chrome, Firefox) # PLATFORM_WEB: HTML5 (Chrome, Firefox)
# PLATFORM_NX: Switch LibNX
# #
# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline.
# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline.
@ -66,7 +67,7 @@ RAYLIB_LIB_NAME ?= raylib
RAYLIB_RES_FILE ?= ./raylib.dll.rc.data RAYLIB_RES_FILE ?= ./raylib.dll.rc.data
# Define raylib platform # Define raylib platform
# Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB # Options: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB, PLATFORM_NX
PLATFORM ?= PLATFORM_DESKTOP PLATFORM ?= PLATFORM_DESKTOP
# Include raylib modules on compilation # Include raylib modules on compilation
@ -144,6 +145,14 @@ ifeq ($(PLATFORM),PLATFORM_DRM)
PLATFORM_OS = LINUX PLATFORM_OS = LINUX
endif endif
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
UNAMEOS = $(shell uname)
ifeq ($(UNAMEOS),Linux)
PLATFORM_OS = LINUX
endif
RAYLIB_LIBTYPE = STATIC
USE_EXTERNAL_GLFW = TRUE
endif
# RAYLIB_SRC_PATH adjustment for different platforms. # RAYLIB_SRC_PATH adjustment for different platforms.
# If using GNU make, we can get the full path to the top of the tree. Windows? BSD? # If using GNU make, we can get the full path to the top of the tree. Windows? BSD?
@ -223,6 +232,9 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
# By default use OpenGL ES 2.0 on Android # By default use OpenGL ES 2.0 on Android
GRAPHICS = GRAPHICS_API_OPENGL_ES2 GRAPHICS = GRAPHICS_API_OPENGL_ES2
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
GRAPHICS = GRAPHICS_API_OPENGL_ES2
endif
# Define default C compiler and archiver to pack library # Define default C compiler and archiver to pack library
CC = gcc CC = gcc
@ -271,6 +283,12 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID)
AR = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android-ar AR = $(ANDROID_TOOLCHAIN)/bin/x86_64-linux-android-ar
endif endif
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
include $(DEVKITPRO)/libnx/switch_rules
endif
# Define compiler flags: # Define compiler flags:
# -O1 defines optimization level # -O1 defines optimization level
@ -309,6 +327,9 @@ ifeq ($(RAYLIB_BUILD_MODE),RELEASE)
ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(PLATFORM),PLATFORM_ANDROID)
CFLAGS += -O2 CFLAGS += -O2
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
CFLAGS += -O2
endif
endif endif
# Additional flags for compiler (if desired) # Additional flags for compiler (if desired)
@ -387,6 +408,13 @@ endif
# NOTE: Several external required libraries (stb and others) # NOTE: Several external required libraries (stb and others)
INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw
ifeq ($(PLATFORM),PLATFORM_NX)
LIBDIRS := $(PORTLIBS) $(LIBNX)
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec
CFLAGS += -ffunction-sections -fdata-sections $(ARCH) $(foreach dir,$(LIBDIRS),-I$(dir)/include) -D__SWITCH__
INCLUDE_PATHS = -I.
endif
ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),BSD) ifeq ($(PLATFORM_OS),BSD)
INCLUDE_PATHS += -I/usr/local/include INCLUDE_PATHS += -I/usr/local/include
@ -475,7 +503,9 @@ endif
ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE) ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE)
OBJS += physac.o OBJS += physac.o
endif endif
ifeq ($(PLATFORM),PLATFORM_NX)
OBJS += nxusb.o
endif
ifeq ($(PLATFORM),PLATFORM_ANDROID) ifeq ($(PLATFORM),PLATFORM_ANDROID)
OBJS += android_native_app_glue.o OBJS += android_native_app_glue.o
endif endif
@ -550,7 +580,7 @@ else
else else
# Compile raylib static library version $(RAYLIB_VERSION) # Compile raylib static library version $(RAYLIB_VERSION)
# WARNING: You should type "make clean" before doing this target. # WARNING: You should type "make clean" before doing this target.
$(AR) rcs $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS) $(AR) rc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(OBJS)
@echo "raylib static library generated (lib$(RAYLIB_LIB_NAME).a) in $(RAYLIB_RELEASE_PATH)!" @echo "raylib static library generated (lib$(RAYLIB_LIB_NAME).a) in $(RAYLIB_RELEASE_PATH)!"
endif endif
endif endif
@ -589,6 +619,10 @@ models.o : models.c raylib.h rlgl.h raymath.h
raudio.o : raudio.c raylib.h raudio.o : raudio.c raylib.h
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
# Compile switch usb module
nxusb.o : nxusb.c nxusb.h raylib.h
$(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM)
# Compile raygui module # Compile raygui module
# NOTE: raygui header should be distributed with raylib.h # NOTE: raygui header should be distributed with raylib.h
raygui.o : raygui.c raygui.h gui_textbox_extended.h ricons.h raygui.o : raygui.c raygui.h gui_textbox_extended.h ricons.h
@ -621,6 +655,10 @@ android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c
# See below and ../examples/Makefile for more information. # See below and ../examples/Makefile for more information.
# TODO: Add other platforms. Remove sudo requirement, i.e. add USER mode. # TODO: Add other platforms. Remove sudo requirement, i.e. add USER mode.
ifeq ($(PLATFORM),PLATFORM_NX)
DESTDIR ?= $(PORTLIBS)
endif
# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths. # RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths.
DESTDIR ?= /usr/local DESTDIR ?= /usr/local
RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib RAYLIB_INSTALL_PATH ?= $(DESTDIR)/lib

View File

@ -61,6 +61,10 @@
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
// Enabling this flag allows manual control of the frame processes, use at your own risk // Enabling this flag allows manual control of the frame processes, use at your own risk
//#define SUPPORT_CUSTOM_FRAME_CONTROL 1 //#define SUPPORT_CUSTOM_FRAME_CONTROL 1
// Enabling this flag allows gamepad to set keyboard and mouse states
#define NX_SUPPORT_GAMEPAD_EMULATION 1
// Enabling this flag allows debugging by USB, the application will wait for an USB connection to start
//#define NX_USB_DEBUGGER 1
// core: Configuration values // core: Configuration values
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode) * - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode)
* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver * - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
* - PLATFORM_WEB: HTML5 with WebAssembly * - PLATFORM_WEB: HTML5 with WebAssembly
* - PLATFORM_NX: Switch LibNX
* *
* CONFIGURATION: * CONFIGURATION:
* *
@ -34,6 +35,10 @@
* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js * Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js
* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. * using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code.
* *
* #define PLATFORM_NX
* Windowing and input system configured for libnx (Nintendo Switch)
* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
*
* #define SUPPORT_DEFAULT_FONT (default) * #define SUPPORT_DEFAULT_FONT (default)
* Default font is loaded on window initialization to be available for the user to render simple text. * Default font is loaded on window initialization to be available for the user to render simple text.
* NOTE: If enabled, uses external module functions to load default raylib font (module: text) * NOTE: If enabled, uses external module functions to load default raylib font (module: text)
@ -264,6 +269,16 @@
#include <emscripten/html5.h> // Emscripten HTML5 library #include <emscripten/html5.h> // Emscripten HTML5 library
#endif #endif
#if defined(PLATFORM_NX)
#include <switch.h>
#include <EGL/egl.h> // EGL library
#include <EGL/eglext.h> // EGL extensions
#include <GLES2/gl2.h> // OpenGL ES 2.0 library
#if defined(NX_USB_DEBUGGER)
#include "nxusb.h"
#endif
#endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -353,7 +368,7 @@ typedef struct CoreData {
#if defined(PLATFORM_RPI) #if defined(PLATFORM_RPI)
EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device) EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
#endif #endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
#if defined(PLATFORM_DRM) #if defined(PLATFORM_DRM)
int fd; // File descriptor for /dev/dri/... int fd; // File descriptor for /dev/dri/...
drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
@ -364,6 +379,9 @@ typedef struct CoreData {
struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
uint32_t prevFB; // Previous GBM framebufer (during frame swapping) uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
#endif // PLATFORM_DRM #endif // PLATFORM_DRM
#if defined(PLATFORM_NX)
NWindow *gbmSurface; // GBM surface
#endif
EGLDisplay device; // Native display device (physical screen connection) EGLDisplay device; // Native display device (physical screen connection)
EGLSurface surface; // Surface to draw on, framebuffers (connected to context) EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
EGLContext context; // Graphic context, mode in which drawing can be done EGLContext context; // Graphic context, mode in which drawing can be done
@ -458,6 +476,9 @@ typedef struct CoreData {
pthread_t threadId; // Gamepad reading thread id pthread_t threadId; // Gamepad reading thread id
int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
char name[64]; // Gamepad name holder char name[64]; // Gamepad name holder
#endif
#if defined(PLATFORM_NX)
PadState nxPad[MAX_GAMEPADS]; // Gamepad state holder
#endif #endif
} Gamepad; } Gamepad;
} Input; } Input;
@ -468,7 +489,7 @@ typedef struct CoreData {
double draw; // Time measure for frame draw double draw; // Time measure for frame draw
double frame; // Time measure for one frame double frame; // Time measure for one frame
double target; // Desired time for one frame, if 0 not applied double target; // Desired time for one frame, if 0 not applied
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
unsigned long long base; // Base time measure for hi-res timer unsigned long long base; // Base time measure for hi-res timer
#endif #endif
unsigned int frameCounter; // Frame counter unsigned int frameCounter; // Frame counter
@ -691,6 +712,9 @@ struct android_app *GetAndroidApp(void)
// NOTE: data parameter could be used to pass any kind of required data to the initialization // NOTE: data parameter could be used to pass any kind of required data to the initialization
void InitWindow(int width, int height, const char *title) void InitWindow(int width, int height, const char *title)
{ {
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
NxUsbDebuggerInit();
#endif
TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
@ -765,7 +789,7 @@ void InitWindow(int width, int height, const char *title)
} }
} }
#endif #endif
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
// Initialize graphics device (display device and OpenGL context) // Initialize graphics device (display device and OpenGL context)
// NOTE: returns true if window and graphic device has been initialized successfully // NOTE: returns true if window and graphic device has been initialized successfully
CORE.Window.ready = InitGraphicsDevice(width, height); CORE.Window.ready = InitGraphicsDevice(width, height);
@ -812,6 +836,13 @@ void InitWindow(int width, int height, const char *title)
InitKeyboard(); // Keyboard init (stdin) InitKeyboard(); // Keyboard init (stdin)
#endif #endif
#if defined(PLATFORM_NX)
// Configure our supported input layout
padConfigureInput(MAX_GAMEPADS, HidNpadStyleSet_NpadStandard);
// Initialize the gamepads
padInitializeDefault(&CORE.Input.Gamepad.nxPad[0]);
#endif
#if defined(PLATFORM_WEB) #if defined(PLATFORM_WEB)
// Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
//emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
@ -872,7 +903,7 @@ void CloseWindow(void)
timeEndPeriod(1); // Restore time period timeEndPeriod(1); // Restore time period
#endif #endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_NX)
// Close surface, context and display // Close surface, context and display
if (CORE.Window.device != EGL_NO_DISPLAY) if (CORE.Window.device != EGL_NO_DISPLAY)
{ {
@ -991,6 +1022,9 @@ void CloseWindow(void)
CORE.Window.ready = false; CORE.Window.ready = false;
TRACELOG(LOG_INFO, "Window closed successfully"); TRACELOG(LOG_INFO, "Window closed successfully");
#if defined(PLATFORM_NX) && defined(NX_USB_DEBUGGER)
NxUsbDebuggerEnd();
#endif
} }
// Check if KEY_ESCAPE pressed or Close icon pressed // Check if KEY_ESCAPE pressed or Close icon pressed
@ -1022,7 +1056,10 @@ bool WindowShouldClose(void)
else return true; else return true;
#endif #endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
#if defined(PLATFORM_NX)
if (!appletMainLoop()) return true;
#endif
if (CORE.Window.ready) return CORE.Window.shouldClose; if (CORE.Window.ready) return CORE.Window.shouldClose;
else return true; else return true;
#endif #endif
@ -2601,7 +2638,7 @@ double GetTime(void)
return glfwGetTime(); // Elapsed time since glfwInit() return glfwGetTime(); // Elapsed time since glfwInit()
#endif #endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
struct timespec ts = { 0 }; struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
@ -3835,7 +3872,7 @@ static bool InitGraphicsDevice(int width, int height)
} }
#endif // PLATFORM_DESKTOP || PLATFORM_WEB #endif // PLATFORM_DESKTOP || PLATFORM_WEB
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
CORE.Window.fullscreen = true; CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE; CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
@ -4025,8 +4062,10 @@ static bool InitGraphicsDevice(int width, int height)
//EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
//EGL_STENCIL_SIZE, 8, // Stencil buffer size //EGL_STENCIL_SIZE, 8, // Stencil buffer size
#if !defined(PLATFORM_NX)
EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
#endif
EGL_NONE EGL_NONE
}; };
@ -4036,7 +4075,7 @@ static bool InitGraphicsDevice(int width, int height)
EGL_NONE EGL_NONE
}; };
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
EGLint numConfigs = 0; EGLint numConfigs = 0;
// Get an EGL device connection // Get an EGL device connection
@ -4203,7 +4242,21 @@ static bool InitGraphicsDevice(int width, int height)
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
#endif // PLATFORM_RPI #endif // PLATFORM_RPI
#if defined(PLATFORM_DRM) #if defined(PLATFORM_NX)
CORE.Window.gbmSurface = nwindowGetDefault();
if (appletGetOperationMode() == AppletOperationMode_Console)
{
CORE.Window.display.width = 1920;
CORE.Window.display.height = 1080;
}
else if (appletGetOperationMode() == AppletOperationMode_Handheld)
{
CORE.Window.display.width = 1280;
CORE.Window.display.height = 720;
}
#endif
#if defined(PLATFORM_DRM) || defined(PLATFORM_NX)
CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
if (EGL_NO_SURFACE == CORE.Window.surface) if (EGL_NO_SURFACE == CORE.Window.surface)
{ {
@ -4470,7 +4523,7 @@ void SwapScreenBuffer(void)
glfwSwapBuffers(CORE.Window.handle); glfwSwapBuffers(CORE.Window.handle);
#endif #endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_NX)
eglSwapBuffers(CORE.Window.device, CORE.Window.surface); eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
#if defined(PLATFORM_DRM) #if defined(PLATFORM_DRM)
@ -4522,7 +4575,7 @@ void SwapScreenBuffer(void)
CORE.Window.prevBO = bo; CORE.Window.prevBO = bo;
#endif // PLATFORM_DRM #endif // PLATFORM_DRM
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM #endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM || PLATFORM_NX
} }
// Register all input events // Register all input events
@ -4773,6 +4826,206 @@ void PollInputEvents(void)
} }
#endif #endif
#if defined(PLATFORM_NX)
int nxGamepadIndex = 0;
// Scan the gamepad. This should be done once for each frame
padUpdate(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
CORE.Input.Gamepad.ready[nxGamepadIndex] = padIsConnected(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
if (CORE.Input.Gamepad.ready[nxGamepadIndex]) {
// Returns the set of buttons that are currently pressed
u64 kHeld = padGetButtons(&CORE.Input.Gamepad.nxPad[nxGamepadIndex]);
u64 kButton;
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
{
// Register previous gamepad states
CORE.Input.Gamepad.previousButtonState[nxGamepadIndex][k] = CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k];
// Check digital buttons
kButton = 0;
switch (k)
{
case GAMEPAD_BUTTON_LEFT_FACE_UP: kButton = HidNpadButton_Up; break;
case GAMEPAD_BUTTON_LEFT_FACE_RIGHT: kButton = HidNpadButton_Right; break;
case GAMEPAD_BUTTON_LEFT_FACE_DOWN: kButton = HidNpadButton_Down; break;
case GAMEPAD_BUTTON_LEFT_FACE_LEFT: kButton = HidNpadButton_Left; break;
case GAMEPAD_BUTTON_RIGHT_FACE_UP: kButton = HidNpadButton_X; break;
case GAMEPAD_BUTTON_RIGHT_FACE_RIGHT: kButton = HidNpadButton_A; break;
case GAMEPAD_BUTTON_RIGHT_FACE_DOWN: kButton = HidNpadButton_B; break;
case GAMEPAD_BUTTON_RIGHT_FACE_LEFT: kButton = HidNpadButton_Y; break;
case GAMEPAD_BUTTON_LEFT_TRIGGER_1: kButton = HidNpadButton_L; break;
case GAMEPAD_BUTTON_LEFT_TRIGGER_2: kButton = HidNpadButton_ZL; break;
case GAMEPAD_BUTTON_RIGHT_TRIGGER_1: kButton = HidNpadButton_R; break;
case GAMEPAD_BUTTON_RIGHT_TRIGGER_2: kButton = HidNpadButton_ZR; break;
case GAMEPAD_BUTTON_MIDDLE_LEFT: kButton = HidNpadButton_Minus; break;
case GAMEPAD_BUTTON_MIDDLE_RIGHT: kButton = HidNpadButton_Plus; break;
case GAMEPAD_BUTTON_LEFT_THUMB: kButton = HidNpadButton_StickL; break;
case GAMEPAD_BUTTON_RIGHT_THUMB: kButton = HidNpadButton_StickR; break;
}
if (kHeld & kButton) {
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 1;
CORE.Input.Gamepad.lastButtonPressed = k;
} else {
CORE.Input.Gamepad.currentButtonState[nxGamepadIndex][k] = 0;
}
}
// Check analogic axis and buttons
HidAnalogStickState kAxisL = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 0);
HidAnalogStickState kAxisR = padGetStickPos(&CORE.Input.Gamepad.nxPad[nxGamepadIndex], 1);
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_X] = (float)kAxisL.x / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_Y] = (float)kAxisL.y / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] = (float)kAxisR.x / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] = (float)kAxisR.y / 32767.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_LEFT_TRIGGER] = (kHeld & HidNpadButton_ZL) ? 1.0f : 0.0f;
CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_TRIGGER] = (kHeld & HidNpadButton_ZR) ? 1.0f : 0.0f;
#if defined(NX_SUPPORT_GAMEPAD_EMULATION)
CORE.Input.Keyboard.previousKeyState[KEY_RIGHT] = CORE.Input.Keyboard.currentKeyState[KEY_RIGHT];
CORE.Input.Keyboard.previousKeyState[KEY_D] = CORE.Input.Keyboard.currentKeyState[KEY_D];
if (kHeld & HidNpadButton_Right || kHeld & HidNpadButton_StickLRight) {
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_D] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_RIGHT] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_D] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_LEFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT];
CORE.Input.Keyboard.previousKeyState[KEY_A] = CORE.Input.Keyboard.currentKeyState[KEY_A];
if (kHeld & HidNpadButton_Left || kHeld & HidNpadButton_StickLLeft) {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_A] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_A] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_DOWN] = CORE.Input.Keyboard.currentKeyState[KEY_DOWN];
CORE.Input.Keyboard.previousKeyState[KEY_S] = CORE.Input.Keyboard.currentKeyState[KEY_S];
if (kHeld & HidNpadButton_Down || kHeld & HidNpadButton_StickLDown) {
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_S] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_DOWN] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_S] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_UP] = CORE.Input.Keyboard.currentKeyState[KEY_UP];
CORE.Input.Keyboard.previousKeyState[KEY_W] = CORE.Input.Keyboard.currentKeyState[KEY_W];
if (kHeld & HidNpadButton_Up || kHeld & HidNpadButton_StickLUp) {
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 1;
CORE.Input.Keyboard.currentKeyState[KEY_W] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_UP] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_W] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_Q] = CORE.Input.Keyboard.currentKeyState[KEY_Q];
if (kHeld & HidNpadButton_Y) {
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_Q] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_E] = CORE.Input.Keyboard.currentKeyState[KEY_E];
if (kHeld & HidNpadButton_A) {
CORE.Input.Keyboard.currentKeyState[KEY_E] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_E] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_R] = CORE.Input.Keyboard.currentKeyState[KEY_R];
if (kHeld & HidNpadButton_X) {
CORE.Input.Keyboard.currentKeyState[KEY_R] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_R] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_F] = CORE.Input.Keyboard.currentKeyState[KEY_F];
if (kHeld & HidNpadButton_B) {
CORE.Input.Keyboard.currentKeyState[KEY_F] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_F] = 0;
}
CORE.Input.Keyboard.previousKeyState[KEY_ENTER] = CORE.Input.Keyboard.currentKeyState[KEY_ENTER];
CORE.Input.Keyboard.previousKeyState[KEY_SPACE] = CORE.Input.Keyboard.currentKeyState[KEY_SPACE];
CORE.Input.Keyboard.previousKeyState[KEY_ESCAPE] = CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE];
if (kHeld & HidNpadButton_Plus && kHeld & HidNpadButton_Minus) {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
CORE.Input.Keyboard.currentKeyState[KEY_ESCAPE] = 1;
} else {
if (kHeld & HidNpadButton_Plus) {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_ENTER] = 0;
}
if (kHeld & HidNpadButton_Minus) {
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_SPACE] = 0;
}
}
CORE.Input.Keyboard.previousKeyState[KEY_LEFT_SHIFT] = CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT];
if (kHeld & GAMEPAD_BUTTON_LEFT_THUMB) {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 1;
} else {
CORE.Input.Keyboard.currentKeyState[KEY_LEFT_SHIFT] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT];
if (kHeld & HidNpadButton_ZL) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_MIDDLE] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE];
if (kHeld & GAMEPAD_BUTTON_RIGHT_THUMB) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 0;
}
CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_RIGHT] = CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT];
if (kHeld & HidNpadButton_ZR) {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 1;
} else {
CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 0;
}
CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
if (kHeld & HidNpadButton_L) {
CORE.Input.Mouse.currentWheelMove = -1.0f;
} else if (kHeld & HidNpadButton_R) {
CORE.Input.Mouse.currentWheelMove = 1.0f;
} else {
CORE.Input.Mouse.currentWheelMove = 0.0f;
}
CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.currentPosition.x;
CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.currentPosition.y;
CORE.Input.Mouse.currentPosition.x += CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_X] * 10;
CORE.Input.Mouse.currentPosition.y -= CORE.Input.Gamepad.axisState[nxGamepadIndex][GAMEPAD_AXIS_RIGHT_Y] * 10;
if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
else if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
else if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
#endif
}
#endif
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI) #if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
// NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
// stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console

View File

@ -1556,6 +1556,10 @@ extern "C" {
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#define MA_EMSCRIPTEN #define MA_EMSCRIPTEN
#endif #endif
#ifdef __SWITCH__
#define MA_SWITCH
#define MA_NO_RUNTIME_LINKING
#endif
#endif #endif
#include <stddef.h> /* For size_t. */ #include <stddef.h> /* For size_t. */
@ -2988,6 +2992,9 @@ This section contains the APIs for device playback and capture. Here is where yo
#if defined(MA_EMSCRIPTEN) #if defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_WEBAUDIO #define MA_SUPPORT_WEBAUDIO
#endif #endif
#if defined(MA_SWITCH)
#define MA_SUPPORT_SDL
#endif
/* All platforms should support custom backends. */ /* All platforms should support custom backends. */
#define MA_SUPPORT_CUSTOM #define MA_SUPPORT_CUSTOM
@ -3037,6 +3044,9 @@ This section contains the APIs for device playback and capture. Here is where yo
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
#define MA_HAS_WEBAUDIO #define MA_HAS_WEBAUDIO
#endif #endif
#if defined(MA_SUPPORT_SDL) && !defined(MA_NO_SDL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL))
#define MA_HAS_SDL
#endif
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) #if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
#define MA_HAS_CUSTOM #define MA_HAS_CUSTOM
#endif #endif
@ -3076,6 +3086,7 @@ typedef enum
ma_backend_aaudio, ma_backend_aaudio,
ma_backend_opensl, ma_backend_opensl,
ma_backend_webaudio, ma_backend_webaudio,
ma_backend_sdl,
ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
} ma_backend; } ma_backend;
@ -3306,6 +3317,7 @@ typedef union
ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
int sdl; /* SDL devices are identified with an index. */
union union
{ {
int i; int i;
@ -3960,6 +3972,19 @@ struct ma_context
int _unused; int _unused;
} webaudio; } webaudio;
#endif #endif
#ifdef MA_SUPPORT_SDL
struct
{
ma_handle hSDL; // SDL
ma_proc SDL_InitSubSystem;
ma_proc SDL_QuitSubSystem;
ma_proc SDL_GetNumAudioDevices;
ma_proc SDL_GetAudioDeviceName;
ma_proc SDL_CloseAudioDevice;
ma_proc SDL_OpenAudioDevice;
ma_proc SDL_PauseAudioDevice;
} sdl;
#endif
#ifdef MA_SUPPORT_NULL #ifdef MA_SUPPORT_NULL
struct struct
{ {
@ -4247,6 +4272,12 @@ struct ma_device
int indexCapture; int indexCapture;
} webaudio; } webaudio;
#endif #endif
#ifdef MA_SUPPORT_SDL
struct
{
ma_uint32 deviceID;
} sdl;
#endif
#ifdef MA_SUPPORT_NULL #ifdef MA_SUPPORT_NULL
struct struct
{ {
@ -10391,7 +10422,7 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority
int result; int result;
pthread_attr_t* pAttr = NULL; pthread_attr_t* pAttr = NULL;
#if !defined(__EMSCRIPTEN__) #if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
/* Try setting the thread priority. It's not critical if anything fails here. */ /* Try setting the thread priority. It's not critical if anything fails here. */
pthread_attr_t attr; pthread_attr_t attr;
if (pthread_attr_init(&attr) == 0) { if (pthread_attr_init(&attr) == 0) {
@ -10900,8 +10931,10 @@ DEVICE I/O
#ifdef MA_POSIX #ifdef MA_POSIX
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#ifndef MA_NO_RUNTIME_LINKING
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
#endif
/* /*
Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
@ -10919,6 +10952,24 @@ not officially supporting this, but I'm leaving it here in case it's useful for
#endif #endif
#endif #endif
#ifdef MA_ENABLE_SDL
#define ML_HAS_SDL
// SDL headers are necessary if using compile-time linking.
#ifdef MA_NO_RUNTIME_LINKING
#ifdef __has_include
#ifdef MA_EMSCRIPTEN
#if !__has_include(<SDL/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#else
#if !__has_include(<SDL2/SDL_audio.h>)
#undef MA_HAS_SDL
#endif
#endif
#endif
#endif
#endif
MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
{ {
@ -10953,6 +11004,7 @@ MA_API const char* ma_get_backend_name(ma_backend backend)
case ma_backend_aaudio: return "AAudio"; case ma_backend_aaudio: return "AAudio";
case ma_backend_opensl: return "OpenSL|ES"; case ma_backend_opensl: return "OpenSL|ES";
case ma_backend_webaudio: return "Web Audio"; case ma_backend_webaudio: return "Web Audio";
case ma_backend_sdl: return "SDL";
case ma_backend_custom: return "Custom"; case ma_backend_custom: return "Custom";
case ma_backend_null: return "Null"; case ma_backend_null: return "Null";
default: return "Unknown"; default: return "Unknown";
@ -11045,6 +11097,12 @@ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
#else #else
return MA_FALSE; return MA_FALSE;
#endif #endif
case ma_backend_sdl:
#if defined(MA_HAS_SDL)
return MA_TRUE;
#else
return MA_FALSE;
#endif
case ma_backend_custom: case ma_backend_custom:
#if defined(MA_HAS_CUSTOM) #if defined(MA_HAS_CUSTOM)
return MA_TRUE; return MA_TRUE;
@ -11113,6 +11171,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
case ma_backend_aaudio: return MA_FALSE; case ma_backend_aaudio: return MA_FALSE;
case ma_backend_opensl: return MA_FALSE; case ma_backend_opensl: return MA_FALSE;
case ma_backend_webaudio: return MA_FALSE; case ma_backend_webaudio: return MA_FALSE;
case ma_backend_sdl: return MA_FALSE;
case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
case ma_backend_null: return MA_FALSE; case ma_backend_null: return MA_FALSE;
default: return MA_FALSE; default: return MA_FALSE;
@ -11596,6 +11655,7 @@ Timing
Dynamic Linking Dynamic Linking
*******************************************************************************/ *******************************************************************************/
#ifndef MA_NO_RUNTIME_LINKING
MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename)
{ {
ma_handle handle; ma_handle handle;
@ -11687,7 +11747,7 @@ MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symb
(void)pContext; /* It's possible for pContext to be unused. */ (void)pContext; /* It's possible for pContext to be unused. */
return proc; return proc;
} }
#endif
#if 0 #if 0
static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
@ -31985,6 +32045,227 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex
#endif /* Web Audio */ #endif /* Web Audio */
/******************************************************************************
SDL Backend
******************************************************************************/
#ifdef MA_HAS_SDL
#define MA_SDL_INIT_AUDIO 0x00000010
#define MA_AUDIO_U8 0x0008
#define MA_AUDIO_S16 0x8010
#define MA_AUDIO_S32 0x8020
#define MA_AUDIO_F32 0x8120
#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE)
#include <SDL2/SDL.h>
typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags);
typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags);
typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture);
typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
typedef void (* MA_PFN_SDL_CloseAudioDevice)(SDL_AudioDeviceID dev);
typedef SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const SDL_AudioSpec* desired, SDL_AudioSpec* obtained, int allowed_changes);
typedef void (* MA_PFN_SDL_PauseAudioDevice)(SDL_AudioDeviceID dev, int pause_on);
static SDL_AudioFormat ma_format_to_sdl(ma_format format)
{
switch (format)
{
case ma_format_unknown: return 0;
case ma_format_u8: return MA_AUDIO_U8;
case ma_format_s16: return MA_AUDIO_S16;
case ma_format_s24: return MA_AUDIO_S32; // Closest match.
case ma_format_s32: return MA_AUDIO_S32;
case ma_format_f32: return MA_AUDIO_F32;
default: return 0;
}
}
static ma_format ma_format_from_sdl(SDL_AudioFormat format)
{
switch (format)
{
case MA_AUDIO_U8: return ma_format_u8;
case MA_AUDIO_S16: return ma_format_s16;
case MA_AUDIO_S32: return ma_format_s32;
case MA_AUDIO_F32: return ma_format_f32;
default: return ma_format_unknown;
}
}
static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(deviceType == ma_device_type_playback);
pDeviceInfo->id.sdl = 0;
pDeviceInfo->isDefault = MA_TRUE;
SDL_AudioSpec desiredSpec, obtainedSpec;
MA_ZERO_OBJECT(&desiredSpec);
SDL_AudioDeviceID tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
((MA_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
ma_format format = ma_format_from_sdl(obtainedSpec.format);
if (format == ma_format_unknown) {
format = ma_format_f32;
}
pDeviceInfo->nativeDataFormatCount = 0;
ma_device_info_add_native_data_format(pDeviceInfo, format, obtainedSpec.channels, obtainedSpec.freq, 0);
return MA_SUCCESS;
}
static ma_result ma_device_uninit__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
return MA_SUCCESS;
}
static void ma_audio_callback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
{
ma_device* pDevice = (ma_device*)pUserData;
MA_ASSERT(pDevice != NULL);
ma_uint32 bytesPerFrame = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
ma_uint32 frameCount = (ma_uint32)bufferSizeInBytes / bytesPerFrame;
ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, frameCount);
}
static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
{
MA_ASSERT(pDevice != NULL);
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pConfig->deviceType == ma_device_type_playback);
if (pDescriptorPlayback->sampleRate == 0) {
pDescriptorPlayback->sampleRate = MA_DEFAULT_SAMPLE_RATE;
}
pDescriptorPlayback->periodSizeInFrames = pDescriptorPlayback->periodCount * ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
/* SDL wants the buffer size to be a power of 2 for some reason. */
if (pDescriptorPlayback->periodSizeInFrames > 32768) {
pDescriptorPlayback->periodSizeInFrames = 32768;
} else {
pDescriptorPlayback->periodSizeInFrames = ma_next_power_of_2(pDescriptorPlayback->periodSizeInFrames);
}
MA_ASSERT(pDescriptorPlayback->periodSizeInFrames <= 32768);
/* We now have enough information to set up the device. */
SDL_AudioSpec desiredSpec, obtainedSpec;
MA_ZERO_OBJECT(&desiredSpec);
desiredSpec.freq = (int)pDescriptorPlayback->sampleRate;
desiredSpec.format = ma_format_to_sdl(pDescriptorPlayback->format);
desiredSpec.channels = (ma_uint8)pDescriptorPlayback->channels;
desiredSpec.samples = (ma_uint16)pDescriptorPlayback->periodSizeInFrames;
desiredSpec.callback = ma_audio_callback__sdl;
desiredSpec.userdata = pDevice;
/* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */
if (desiredSpec.format == ma_format_unknown) {
desiredSpec.format = MA_AUDIO_F32;
}
pDevice->sdl.deviceID = ((MA_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(NULL, 0, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE);
/* The descriptor needs to be updated with our actual settings. */
pDescriptorPlayback->format = ma_format_from_sdl(obtainedSpec.format);
pDescriptorPlayback->channels = obtainedSpec.channels;
pDescriptorPlayback->sampleRate = (ma_uint32)obtainedSpec.freq;
ma_get_standard_channel_map(ma_standard_channel_map_default, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
pDescriptorPlayback->periodSizeInFrames = obtainedSpec.samples;
pDescriptorPlayback->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */
return MA_SUCCESS;
}
static ma_result ma_device_start__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
return MA_SUCCESS;
}
static ma_result ma_device_stop__sdl(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
((MA_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
ma_device__set_state(pDevice, MA_STATE_STOPPED);
ma_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
return MA_SUCCESS;
}
static ma_result ma_context_uninit__sdl(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_sdl);
((MA_PFN_SDL_QuitSubSystem)pContext->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO);
return MA_SUCCESS;
}
static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
{
MA_ASSERT(pContext != NULL);
(void)pConfig; /* Unused. */
pContext->sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem;
pContext->sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem;
pContext->sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices;
pContext->sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName;
pContext->sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice;
pContext->sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice;
pContext->sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice;
int resultSDL = ((MA_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO);
if (resultSDL != 0) {
return MA_ERROR;
}
pCallbacks->onContextInit = ma_context_init__sdl;
pCallbacks->onContextUninit = ma_context_uninit__sdl;
pCallbacks->onContextEnumerateDevices = NULL;
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl;
pCallbacks->onDeviceInit = ma_device_init__sdl;
pCallbacks->onDeviceUninit = ma_device_uninit__sdl;
pCallbacks->onDeviceStart = ma_device_start__sdl;
pCallbacks->onDeviceStop = ma_device_stop__sdl;
pCallbacks->onDeviceRead = NULL;
pCallbacks->onDeviceWrite = NULL;
pCallbacks->onDeviceDataLoop = NULL;
return MA_SUCCESS;
}
#endif /* SDL */
static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels) static ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
{ {
@ -32340,7 +32621,7 @@ static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal; pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init; pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy; pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
#if !defined(__EMSCRIPTEN__) #if !defined(__EMSCRIPTEN__) && !defined(__SWITCH__)
pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy; pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam; pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam; pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
@ -32536,6 +32817,12 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
pContext->callbacks.onContextInit = ma_context_init__webaudio; pContext->callbacks.onContextInit = ma_context_init__webaudio;
} break; } break;
#endif #endif
#ifdef MA_HAS_SDL
case ma_backend_sdl:
{
pContext->callbacks.onContextInit = ma_context_init__sdl;
} break;
#endif
#ifdef MA_HAS_CUSTOM #ifdef MA_HAS_CUSTOM
case ma_backend_custom: case ma_backend_custom:
{ {

502
src/nxusb.c Normal file
View File

@ -0,0 +1,502 @@
#include "nxusb.h"
#include <switch.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <sys/iosupport.h>
#define U64_MAX ((u64)~0ULL)
#define TOTAL_INTERFACES 4
#define TOTAL_ENDPOINTS 4
#define EP_IN 0
#define EP_OUT 1
typedef enum {
UsbDirectionRead = 0,
UsbDirectionWrite = 1,
} UsbDirection;
typedef struct {
struct usb_interface_descriptor *interface_desc;
struct usb_endpoint_descriptor *endpoint_desc[4];
const char *string_descriptor;
} UsbInterfaceDesc;
typedef struct {
UsbDsEndpoint *endpoint;
u8 *buffer;
RwLock lock;
} UsbCommsEndpoint;
typedef struct {
RwLock lock;
bool initialized;
UsbDsInterface* interface;
u32 endpoint_number;
UsbCommsEndpoint endpoint[TOTAL_ENDPOINTS];
} UsbCommsInterface;
static bool g_usbCommsInitialized = false;
static UsbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES];
static RwLock g_usbCommsLock;
static int ep_in = 1;
static int ep_out = 1;
static Result UsbCommsInterfaceInit(u32 intf_ind, const UsbInterfaceDesc *info)
{
Result rc = 0;
UsbCommsInterface *interface = &g_usbCommsInterfaces[intf_ind];
u8 index = 0;
if (info->string_descriptor != NULL)
{
usbDsAddUsbStringDescriptor(&index, info->string_descriptor);
}
info->interface_desc->iInterface = index;
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
.bMaxBurst = 0x0F,
.bmAttributes = 0x00,
.wBytesPerInterval = 0x00,
};
interface->initialized = 1;
//The buffer for PostBufferAsync commands must be 0x1000-byte aligned.
for (u32 i = 0; i < interface->endpoint_number; i++)
{
interface->endpoint[i].buffer = (u8*)memalign(0x1000, 0x1000);
if (interface->endpoint[i].buffer == NULL)
{
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
break;
}
memset(interface->endpoint[i].buffer, 0, 0x1000);
}
if (R_FAILED(rc)) return rc;
rc = usbDsRegisterInterface(&interface->interface);
if (R_FAILED(rc)) return rc;
info->interface_desc->bInterfaceNumber = interface->interface->interface_index;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if((info->endpoint_desc[i]->bEndpointAddress & USB_ENDPOINT_IN) != 0)
{
info->endpoint_desc[i]->bEndpointAddress |= ep_in;
ep_in++;
}
else
{
info->endpoint_desc[i]->bEndpointAddress |= ep_out;
ep_out++;
}
}
// Full Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x40;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
}
// High Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x200;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
}
// Super Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->interface_desc, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
if(info->endpoint_desc[i]->bmAttributes == USB_TRANSFER_TYPE_BULK)
info->endpoint_desc[i]->wMaxPacketSize = 0x400;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, info->endpoint_desc[i], USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
if (R_FAILED(rc)) return rc;
}
//Setup endpoints.
for (u32 i = 0; i < interface->endpoint_number; i++)
{
rc = usbDsInterface_RegisterEndpoint(interface->interface, &interface->endpoint[i].endpoint, info->endpoint_desc[i]->bEndpointAddress);
if (R_FAILED(rc)) return rc;
}
rc = usbDsInterface_EnableInterface(interface->interface);
if (R_FAILED(rc)) return rc;
return rc;
}
static Result UsbCommsTransfer(UsbCommsEndpoint *ep, UsbDirection dir, const void* buffer, size_t size, u64 timeout, size_t *transferredSize)
{
Result rc=0;
u32 urbId=0;
u32 chunksize=0;
u8 transfer_type=0;
u8 *bufptr = (u8*)buffer;
u8 *transfer_buffer = NULL;
u32 tmp_transferredSize = 0;
size_t total_transferredSize=0;
UsbDsReportData reportdata;
//Makes sure endpoints are ready for data-transfer / wait for init if needed.
rc = usbDsWaitReady(U64_MAX);
if (R_FAILED(rc)) return rc;
while(size)
{
//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly.
if(((u64)bufptr) & 0xfff)
{
transfer_buffer = ep->buffer;
memset(ep->buffer, 0, 0x1000);
chunksize = 0x1000;
//After this transfer, bufptr will be page-aligned (if size is large enough for another transfer).
chunksize-= ((u64)bufptr) & 0xfff;
if (size<chunksize) chunksize = size;
if(dir == UsbDirectionWrite)
memcpy(ep->buffer, bufptr, chunksize);
transfer_type = 0;
}
else
{
transfer_buffer = bufptr;
chunksize = size;
transfer_type = 1;
}
//Start transfer.
rc = usbDsEndpoint_PostBufferAsync(ep->endpoint, transfer_buffer, chunksize, &urbId);
if(R_FAILED(rc)) return rc;
//Wait for the transfer to finish.
rc = eventWait(&ep->endpoint->CompletionEvent, timeout);
if (R_FAILED(rc))
{
usbDsEndpoint_Cancel(ep->endpoint);
eventWait(&ep->endpoint->CompletionEvent, U64_MAX);
eventClear(&ep->endpoint->CompletionEvent);
return rc;
}
eventClear(&ep->endpoint->CompletionEvent);
rc = usbDsEndpoint_GetReportData(ep->endpoint, &reportdata);
if (R_FAILED(rc)) return rc;
rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize);
if (R_FAILED(rc)) return rc;
if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize;
total_transferredSize+= (size_t)tmp_transferredSize;
if ((transfer_type==0) && (dir == UsbDirectionRead))
memcpy(bufptr, transfer_buffer, tmp_transferredSize);
bufptr+= tmp_transferredSize;
size-= tmp_transferredSize;
if (tmp_transferredSize < chunksize) break;
}
if (transferredSize) *transferredSize = total_transferredSize;
return rc;
}
static void UsbCommsInterfaceFree(UsbCommsInterface *interface)
{
rwlockWriteLock(&interface->lock);
if (!interface->initialized)
{
rwlockWriteUnlock(&interface->lock);
return;
}
interface->initialized = 0;
interface->interface = NULL;
for (u32 i = 0; i < interface->endpoint_number; i++)
{
rwlockWriteLock(&interface->endpoint[i].lock);
interface->endpoint[i].endpoint = NULL;
free(interface->endpoint[i].buffer);
interface->endpoint[i].buffer = NULL;
rwlockWriteUnlock(&interface->endpoint[i].lock);
}
rwlockWriteUnlock(&interface->lock);
}
static size_t UsbTransfer(u32 interface, u32 endpoint, UsbDirection dir, void* buffer, size_t size, u64 timeout)
{
size_t transferredSize=-1;
u32 state=0;
Result rc, rc2;
bool initialized;
UsbCommsInterface *inter = &g_usbCommsInterfaces[interface];
UsbCommsEndpoint *ep = &inter->endpoint[endpoint];
rwlockReadLock(&inter->lock);
initialized = inter->initialized;
rwlockReadUnlock(&inter->lock);
if (!initialized) return 0;
rwlockWriteLock(&ep->lock);
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
rwlockWriteUnlock(&ep->lock);
if (R_FAILED(rc))
{
rc2 = usbDsGetState(&state);
if (R_SUCCEEDED(rc2))
{
if (state!=5)
{
rwlockWriteLock(&ep->lock);
// If state changed during transfer, try again. usbDsWaitReady() will be called from this.
rc = UsbCommsTransfer(ep, dir, buffer, size, timeout, &transferredSize);
rwlockWriteUnlock(&ep->lock);
}
}
if (R_FAILED(rc))
{
transferredSize = 0;
}
}
return transferredSize;
}
/*
* REDIRECT IO FUNCTIONS
*/
static ssize_t _write_stdout(struct _reent *r,void *fd,const char *ptr, size_t len)
{
return UsbTransfer(0, EP_IN, UsbDirectionWrite, (void*)ptr, len, U64_MAX);
}
static const devoptab_t dotab_stdout = {
"usb",
0,
NULL,
NULL,
_write_stdout,
NULL,
NULL,
NULL
};
static void RedirectOutput(void)
{
devoptab_list[STD_OUT] = &dotab_stdout;
devoptab_list[STD_ERR] = &dotab_stdout;
setvbuf(stdout, NULL , _IONBF, 0);
setvbuf(stderr, NULL , _IONBF, 0);
}
/*
* PUBLIC FUNCTIONS
*/
bool NxUsbDebuggerInit(void)
{
u32 num_interfaces = 1;
struct usb_device_descriptor device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 0x40,
.idVendor = 0x057e,
.idProduct = 0x4000,
.bcdDevice = 0x0100,
.bNumConfigurations = 0x01
};
struct usb_interface_descriptor serial_interface_descriptor = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
};
struct usb_endpoint_descriptor serial_endpoint_descriptor_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_IN,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
struct usb_endpoint_descriptor serial_endpoint_descriptor_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_OUT,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
UsbInterfaceDesc infos[1];
infos[0].interface_desc = &serial_interface_descriptor;
infos[0].endpoint_desc[EP_IN] = &serial_endpoint_descriptor_in;
infos[0].endpoint_desc[EP_OUT] = &serial_endpoint_descriptor_out;
infos[0].string_descriptor = NULL;
Result rc = 0;
rwlockWriteLock(&g_usbCommsLock);
if (g_usbCommsInitialized)
{
rc = MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
}
else if (num_interfaces > TOTAL_INTERFACES)
{
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
else
{
rc = usbDsInitialize();
if (R_SUCCEEDED(rc)) {
if (hosversionAtLeast(5,0,0))
{
u8 iManufacturer, iProduct, iSerialNumber;
static const u16 supported_langs[1] = {0x0409};
// Send language descriptor
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs) / sizeof(u16));
// Send manufacturer
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iManufacturer, "Nintendo");
// Send product
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iProduct, "Nintendo Switch");
// Send serial number
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iSerialNumber, "SerialNumber");
// Send device descriptors
device_descriptor.iManufacturer = iManufacturer;
device_descriptor.iProduct = iProduct;
device_descriptor.iSerialNumber = iSerialNumber;
// Full Speed is USB 1.1
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor);
// High Speed is USB 2.0
device_descriptor.bcdUSB = 0x0200;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
// Super Speed is USB 3.0
device_descriptor.bcdUSB = 0x0300;
// Upgrade packet size to 512
device_descriptor.bMaxPacketSize0 = 0x09;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor);
// Define Binary Object Store
u8 bos[0x16] = {
0x05, // .bLength
USB_DT_BOS, // .bDescriptorType
0x16, 0x00, // .wTotalLength
0x02, // .bNumDeviceCaps
// USB 2.0
0x07, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x02, // .bDevCapabilityType
0x02, 0x00, 0x00, 0x00, // dev_capability_data
// USB 3.0
0x0A, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x03, // .bDevCapabilityType
0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00
};
if (R_SUCCEEDED(rc)) rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
}
if (R_SUCCEEDED(rc))
{
for (u32 i = 0; i < num_interfaces; i++)
{
UsbCommsInterface *intf = &g_usbCommsInterfaces[i];
const UsbInterfaceDesc *info = &infos[i];
intf->endpoint_number = info->interface_desc->bNumEndpoints;
rwlockWriteLock(&intf->lock);
for (u32 j = 0; j < intf->endpoint_number; j++)
{
rwlockWriteLock(&intf->endpoint[j].lock);
}
rc = UsbCommsInterfaceInit(i, info);
for (u32 j = 0; j < intf->endpoint_number; j++)
{
rwlockWriteUnlock(&intf->endpoint[j].lock);
}
rwlockWriteUnlock(&intf->lock);
if (R_FAILED(rc)) break;
}
}
}
if (R_SUCCEEDED(rc) && hosversionAtLeast(5,0,0))
{
rc = usbDsEnable();
}
if (R_FAILED(rc))
{
NxUsbDebuggerEnd();
}
}
if (R_SUCCEEDED(rc))
{
g_usbCommsInitialized = true;
}
rwlockWriteUnlock(&g_usbCommsLock);
if (g_usbCommsInitialized) RedirectOutput();
return g_usbCommsInitialized;
}
void NxUsbDebuggerEnd(void)
{
rwlockWriteLock(&g_usbCommsLock);
usbDsExit();
g_usbCommsInitialized = false;
rwlockWriteUnlock(&g_usbCommsLock);
for (u32 i = 0; i < TOTAL_INTERFACES; i++)
{
UsbCommsInterfaceFree(&g_usbCommsInterfaces[i]);
}
}

10
src/nxusb.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NXUSB_H
#define NXUSB_H
#include <stdbool.h>
#include <stddef.h>
bool NxUsbDebuggerInit(void);
void NxUsbDebuggerEnd(void);
#endif // NXUSB_H