commit cd19e89db534116606a10eb4cbda6fade4a4743d Author: Maschell Date: Fri Mar 4 00:59:38 2022 +0100 First commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..56cc685 --- /dev/null +++ b/.clang-format @@ -0,0 +1,67 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9d2db0c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,65 @@ +name: CI-Release + +on: + push: + branches: + - main + +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source + build-binary: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" + deploy-binary: + needs: build-binary + runs-on: ubuntu-18.04 + steps: + - name: Get environment variables + id: get_repository_name + run: | + echo REPOSITORY_NAME=$(echo "$GITHUB_REPOSITORY" | awk -F / '{print $2}' | sed -e "s/:refs//") >> $GITHUB_ENV + echo DATETIME=$(echo $(date '+%Y%m%d-%H%M%S')) >> $GITHUB_ENV + - uses: actions/download-artifact@master + with: + name: binary + - name: zip artifact + run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.wms + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }} + draft: false + prerelease: true + body: | + Not a stable release: + ${{ github.event.head_commit.message }} + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip + asset_content_type: application/zip \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..19c3b14 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,25 @@ +name: CI-PR + +on: [pull_request] + +jobs: + clang-format: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: clang-format + run: | + docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source + build-binary: + runs-on: ubuntu-18.04 + needs: clang-format + steps: + - uses: actions/checkout@v2 + - name: build binary + run: | + docker build . -t builder + docker run --rm -v ${PWD}:/project builder make + - uses: actions/upload-artifact@master + with: + name: binary + path: "*.wms" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba57c28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.cbp +*.elf +*.layout +*.rpx +build/ +*.save-failed +.idea/ +cmake-build-debug/ +CMakeLists.txt +*.wms diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1458379 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM wiiuenv/devkitppc:20220303 + +COPY --from=wiiuenv/libkernel:20211031 /artifacts $DEVKITPRO +COPY --from=wiiuenv/wiiumodulesystem:20220123 /artifacts $DEVKITPRO +COPY --from=wiiuenv/libsdutils:20220303 /artifacts $DEVKITPRO + +WORKDIR project \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de9f891 --- /dev/null +++ b/Makefile @@ -0,0 +1,146 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +include $(DEVKITPRO)/wums/share/wums_rules + +WUMS_ROOT := $(DEVKITPRO)/wums +WUT_ROOT := $(DEVKITPRO)/wut +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#------------------------------------------------------------------------------- +TARGET := SDHotSwapModule +BUILD := build +SOURCES := source +DATA := data +INCLUDES := source + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -Wall -Wextra -O0 -ffunction-sections\ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) -std=c++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUMSSPECS) + +ifeq ($(DEBUG),1) +CXXFLAGS += -DDEBUG -g +CFLAGS += -DDEBUG -g +endif + +LIBS := -lwums -lwut -lkernel + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUMS_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#------------------------------------------------------------------------------- + export LD := $(CC) +#------------------------------------------------------------------------------- +else +#------------------------------------------------------------------------------- + export LD := $(CXX) +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wms $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wms + +$(OUTPUT).wms : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +%.o: %.s + @echo $(notdir $<) + @$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ $(ERROR_FILTER) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ff0684 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +[![CI-Release](https://github.com/wiiu-env/SDHotSwapModule/actions/workflows/ci.yml/badge.svg)](https://github.com/wiiu-env/SDHotSwapModule/actions/workflows/ci.yml) + +## SDHotSwapModule + +Automatically (un)mounts the sd card when the sd card was inserted/ejected. See [libsdutils](https://github.com/wiiu-env/libsdutils) to have an easy way to register a callback for these events. + +## Usage +(`[ENVIRONMENT]` is a placeholder for the actual environment name.) + +1. Copy the file `SDHotSwapModule.wms` into `sd:/wiiu/environments/[ENVIRONMENT]/modules`. +2. Requires the [WUMSLoader](https://github.com/wiiu-env/WUMSLoader) in `sd:/wiiu/environments/[ENVIRONMENT]/modules/setup`. + +## Building using the Dockerfile + +It's possible to use a docker image for building. This way you don't need anything installed on your host system. + +``` +# Build docker image (only needed once) +docker build . -t sdhotswapmodule-builder + +# make +docker run -it --rm -v ${PWD}:/project sdhotswapmodule-builder make + +# make clean +docker run -it --rm -v ${PWD}:/project sdhotswapmodule-builder make clean +``` + +## Format the code via docker + +`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i` diff --git a/source/exports.cpp b/source/exports.cpp new file mode 100644 index 0000000..3d5e155 --- /dev/null +++ b/source/exports.cpp @@ -0,0 +1,52 @@ +#include "exports.h" +#include +#include +#include + +#define MAX_HANDLERS 16 +static SDAttachHandlerFn sHandlers[MAX_HANDLERS] = {nullptr}; + +void callAttachCallbacks(SDUtilsAttachStatus status) { + int i; + for (i = 0; i < MAX_HANDLERS; ++i) { + if (sHandlers[i]) { + sHandlers[i](status); + } + } +} + +void cleanUpAttachCallbacks() { + memset(sHandlers, 0, sizeof(sHandlers)); +} + +bool SDUtilsAddAttachHandler(SDAttachHandlerFn fn) { + int i; + + for (i = 0; i < MAX_HANDLERS; ++i) { + if (sHandlers[i] == fn) { + return true; + } + if (!sHandlers[i]) { + sHandlers[i] = fn; + return true; + } + } + + return false; +} + +bool SDUtilsRemoveAttachHandler(SDAttachHandlerFn fn) { + int i; + + for (i = 0; i < MAX_HANDLERS; ++i) { + if (sHandlers[i] == fn) { + sHandlers[i] = nullptr; + return true; + } + } + + return false; +} + +WUMS_EXPORT_FUNCTION(SDUtilsAddAttachHandler); +WUMS_EXPORT_FUNCTION(SDUtilsRemoveAttachHandler); \ No newline at end of file diff --git a/source/exports.h b/source/exports.h new file mode 100644 index 0000000..432da4b --- /dev/null +++ b/source/exports.h @@ -0,0 +1,6 @@ +#pragma once +#include + +void callAttachCallbacks(SDUtilsAttachStatus status); + +void cleanUpAttachCallbacks(); \ No newline at end of file diff --git a/source/logger.c b/source/logger.c new file mode 100644 index 0000000..f700806 --- /dev/null +++ b/source/logger.c @@ -0,0 +1,36 @@ +#ifdef DEBUG +#include +#include +#include +#include + +uint32_t moduleLogInit = false; +uint32_t cafeLogInit = false; +uint32_t udpLogInit = false; +#endif // DEBUG + +void initLogging() { +#ifdef DEBUG + if (!(moduleLogInit = WHBLogModuleInit())) { + cafeLogInit = WHBLogCafeInit(); + udpLogInit = WHBLogUdpInit(); + } +#endif // DEBUG +} + +void deinitLogging() { +#ifdef DEBUG + if (moduleLogInit) { + WHBLogModuleDeinit(); + moduleLogInit = false; + } + if (cafeLogInit) { + WHBLogCafeDeinit(); + cafeLogInit = false; + } + if (udpLogInit) { + WHBLogUdpDeinit(); + udpLogInit = false; + } +#endif // DEBUG +} \ No newline at end of file diff --git a/source/logger.h b/source/logger.h new file mode 100644 index 0000000..061d52e --- /dev/null +++ b/source/logger.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG + +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \ + do { \ + WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ + } while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \ + do { \ + WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ + } while (0) + +#else + +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) + +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) + +#endif + +void initLogging(); + +void deinitLogging(); + +#ifdef __cplusplus +} +#endif diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..477c086 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,105 @@ +#include "exports.h" +#include "logger.h" +#include "sdcard.h" +#include +#include +#include +#include +#include +#include +#include +#include + +WUMS_MODULE_EXPORT_NAME("homebrew_sdhotswap"); +WUMS_MODULE_SKIP_INIT_FINI(); + +// This NEEDS to be on the heap. Global DTOR are never called for modules +// but we need a fresh instance of this condition_variable on each app change. +std::condition_variable *cv = nullptr; +std::mutex *cv_m = nullptr; + +std::thread *mountThread = nullptr; +bool sStopThread = false; +bool sIsSDInsertedAndMounted = false; + +int mount_thread() { + std::unique_lock lk(*cv_m); + // Wait until the main thread has checked the sd status once. + cv->wait(lk); + while (!sStopThread) { + auto newStatus = IsSDCardInserted(); + if (newStatus != sIsSDInsertedAndMounted) { + if (newStatus) { + if (MountSDCard()) { + callAttachCallbacks(SDUTILS_ATTACH_MOUNTED); + sIsSDInsertedAndMounted = true; + } + } else { + if (UnmountSDCard()) { + callAttachCallbacks(SDUTILS_ATTACH_UNMOUNTED); + sIsSDInsertedAndMounted = false; + } + } + OSMemoryBarrier(); + } + + OSSleepTicks(OSMillisecondsToTicks(100)); + } + return 0; +} + +WUMS_APPLICATION_STARTS() { + initLogging(); + + sStopThread = false; + + cv = new std::condition_variable; + cv_m = new std::mutex; + + OSMemoryBarrier(); + + mountThread = new std::thread(mount_thread); + auto nativeHandle = (OSThread *) mountThread->native_handle(); + OSSetThreadName(nativeHandle, "SDHotSwapModuleThread"); + while (!OSSetThreadAffinity(nativeHandle, OS_THREAD_ATTRIB_AFFINITY_CPU2)) { + OSSleepTicks(OSMillisecondsToTicks(16)); + } + + auto check = InitialSDCheck(); + if (check < 0) { + // On error stop thread. + sStopThread = true; + } else { + // Otherwise it retuns the current status. + sIsSDInsertedAndMounted = check == 1; + } + + OSMemoryBarrier(); + + DEBUG_FUNCTION_LINE("Wake up the thread"); + // Now we can wake up the thread! + cv->notify_all(); +} + +WUMS_APPLICATION_ENDS() { + sStopThread = true; + OSMemoryBarrier(); + cv->notify_all(); + + if (mountThread != nullptr) { + mountThread->join(); + + delete mountThread; + mountThread = nullptr; + } + + DeInitSDCheck(); + + delete cv; + delete cv_m; + cv = nullptr; + cv_m = nullptr; + + OSMemoryBarrier(); + deinitLogging(); +} diff --git a/source/sdcard.cpp b/source/sdcard.cpp new file mode 100644 index 0000000..3c9cfe0 --- /dev/null +++ b/source/sdcard.cpp @@ -0,0 +1,101 @@ +#include "exports.h" +#include "logger.h" +#include +#include +#include + +static FSClient sClient; +std::mutex *mutex; + +bool sFSClientAdded = false; + +int InitialSDCheck() { + mutex = new (std::nothrow) std::mutex; + if (!mutex) { + return -1; + } + FSStatus result = FSAddClient(&sClient, FS_ERROR_FLAG_ALL); + if (result != FS_STATUS_OK) { + return -2; + } else { + sFSClientAdded = true; + OSMemoryBarrier(); + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + FSMountSource mountSource; + return FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) == FS_STATUS_OK; + } +} + +void DeInitSDCheck() { + if (mutex) { + delete mutex; + mutex = nullptr; + } + + if (sFSClientAdded) { + FSDelClient(&sClient, FS_ERROR_FLAG_ALL); + memset(&sClient, 0, sizeof(sClient)); + sFSClientAdded = false; + } + + cleanUpAttachCallbacks(); + + OSMemoryBarrier(); +} + +int IsSDCardInserted() { + if (!sFSClientAdded || !mutex) { + return -1; + } + std::lock_guard lk(*mutex); + FSCmdBlock fsCmd; + FSMountSource mountSource; + FSInitCmdBlock(&fsCmd); + memset(&mountSource, 0, sizeof(mountSource)); + return FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) == FS_STATUS_OK; +} + +int MountSDCard() { + if (!sFSClientAdded || !mutex) { + return -1; + } + std::lock_guard lk(*mutex); + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + FSMountSource mountSource; + char mountPath[0x80]; + + if (FSGetMountSource(&sClient, &fsCmd, FS_MOUNT_SOURCE_SD, &mountSource, FS_ERROR_FLAG_ALL) != FS_STATUS_OK) { + DEBUG_FUNCTION_LINE("No SD Card found"); + return 0; + } + + FSStatus res; + if ((res = FSMount(&sClient, &fsCmd, &mountSource, mountPath, sizeof(mountPath), FS_ERROR_FLAG_ALL)) != FS_STATUS_OK) { + DEBUG_FUNCTION_LINE("FSMount failed %d", res); + return 0; + } + DEBUG_FUNCTION_LINE("Mounted SD Card"); + return 1; +} + +int UnmountSDCard() { + if (!sFSClientAdded || !mutex) { + return -1; + } + std::lock_guard lk(*mutex); + FSCmdBlock fsCmd; + FSInitCmdBlock(&fsCmd); + + FSStatus res = FS_STATUS_OK; + while (res == FS_STATUS_OK) { + res = FSUnmount(&sClient, &fsCmd, "/vol/external01", FS_ERROR_FLAG_ALL); + if (res != FS_STATUS_OK && res != FS_STATUS_NOT_FOUND) { + return 0; + } + } + DEBUG_FUNCTION_LINE("Unmounted SD Card"); + + return 1; +} diff --git a/source/sdcard.h b/source/sdcard.h new file mode 100644 index 0000000..d49b9a5 --- /dev/null +++ b/source/sdcard.h @@ -0,0 +1,27 @@ +#pragma once + +int InitialSDCheck(); + +void DeInitSDCheck(); + +/** + * @return < 0 on error. + * 0 when no FAT32 formatted sd card is inserted + * 1 when a FAT32 formatted sd card is inserted + */ + +int IsSDCardInserted(); + +/** + * @return < 0 on error. + * 0 when mounting failed, + * 1 when mounting was successful. + */ +int MountSDCard(); + +/** + * @return < 0 on error. + * 0 when unmounting failed, + * 1 when unmounting was successful. + */ +int UnmountSDCard(); \ No newline at end of file