You've already forked dspico-usb-examples
mirror of
https://github.com/LNH-team/dspico-usb-examples.git
synced 2026-01-09 16:27:56 -08:00
Initial commit
This commit is contained in:
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Ignore build directories #
|
||||
############################
|
||||
Debug/
|
||||
Release/
|
||||
build/
|
||||
out/
|
||||
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.d
|
||||
*.map
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
*.nds
|
||||
*.elf
|
||||
*.a
|
||||
.vscode/
|
||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[submodule "libs/tinyusb"]
|
||||
path = libs/tinyusb
|
||||
url = https://github.com/hathach/tinyusb.git
|
||||
[submodule "libs/libtwl"]
|
||||
path = libs/libtwl
|
||||
url = https://github.com/Gericom/libtwl
|
||||
17
LICENSE.txt
Normal file
17
LICENSE.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Copyright (c) 2025 LNH team
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
17
README.md
Normal file
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# DSpico USB Examples
|
||||
This repository contains examples of using the DSpico USB port from a DS application.
|
||||
|
||||
The `examples` folder contains the following examples:
|
||||
- usb-speaker - Makes the DS/DSi/3DS work as a USB speaker for your PC
|
||||
- mass-storage - Allows access to the DSpico micro SD card over USB
|
||||
|
||||
The `platform` folder contains the tiny usb platform code for the DSpico.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> There is currently a bug in the DSpico Firmware that sometimes causes the DSpico the crash if you use USB, reset the console and use USB again. If this happens, unplug USB and reset the console such that the DSpico performs a power cycle. You can plug the USB back in afterwards.
|
||||
|
||||
## Environment
|
||||
The examples require a pre-calico devkitpro/libnds environment to build. With such an environment setup, just run `make` in the folder of the example to compile it.
|
||||
|
||||
## License
|
||||
The platform code and examples are licensed under the Zlib license. For details, see `LICENSE.txt`.
|
||||
60
examples/mass-storage/Makefile
Normal file
60
examples/mass-storage/Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
export TARGET := $(shell basename $(CURDIR))
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
# specify a directory which contains the nitro filesystem
|
||||
# this is relative to the Makefile
|
||||
NITRO_FILES :=
|
||||
|
||||
# These set the information text in the nds file
|
||||
#GAME_TITLE := My Wonderful Homebrew
|
||||
#GAME_SUBTITLE1 := built with devkitARM
|
||||
#GAME_SUBTITLE2 := http://devitpro.org
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
.PHONY: checklibtwl checkarm7 checkarm9 clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: checklibtwl checkarm7 checkarm9 $(TARGET).nds
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checklibtwl:
|
||||
$(MAKE) -C ../../libs/libtwl/libtwl7
|
||||
$(MAKE) -C ../../libs/libtwl/libtwl9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm7: checklibtwl
|
||||
$(MAKE) -C arm7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm9: checklibtwl
|
||||
$(MAKE) -C arm9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
|
||||
ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
|
||||
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \
|
||||
$(_ADDFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
arm7/$(TARGET).elf:
|
||||
$(MAKE) -C arm7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
arm9/$(TARGET).elf:
|
||||
$(MAKE) -C arm9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
$(MAKE) -C arm9 clean
|
||||
$(MAKE) -C arm7 clean
|
||||
rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9
|
||||
137
examples/mass-storage/arm7/Makefile
Normal file
137
examples/mass-storage/arm7/Makefile
Normal file
@@ -0,0 +1,137 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary files
|
||||
# all directories are relative to this makefile
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := source \
|
||||
../common/ipc \
|
||||
source/ipcServices \
|
||||
../../../platform \
|
||||
../../../libs/tinyusb/src \
|
||||
../../../libs/tinyusb/src/device \
|
||||
../../../libs/tinyusb/src/class/audio \
|
||||
../../../libs/tinyusb/src/class/cdc \
|
||||
../../../libs/tinyusb/src/class/msc \
|
||||
../../../libs/tinyusb/src/common
|
||||
INCLUDES := source include build ../common ../../../libs/tinyusb/src ../../../platform
|
||||
DATA :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM7 -DARM7
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
|
||||
-ffunction-sections -fdata-sections\
|
||||
-ffast-math \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
|
||||
-Wsuggest-override -Werror=suggest-override
|
||||
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=../dldi_ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map,--gc-sections -ffunction-sections -fdata-sections
|
||||
|
||||
LIBS := -ltwl7 -lnds7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl7 $(CURDIR)/../../../libs/libtwl/common
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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 ARM7ELF := $(CURDIR)/$(TARGET).elf
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
|
||||
|
||||
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)/*.*)))
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
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)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
.PHONY: $(BUILD) clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) *.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(ARM7ELF) : $(OFILES)
|
||||
@echo linking $(notdir $@)
|
||||
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
232
examples/mass-storage/arm7/dldi_ds_arm7.ld
Normal file
232
examples/mass-storage/arm7/dldi_ds_arm7.ld
Normal file
@@ -0,0 +1,232 @@
|
||||
/*--------------------------------------------------------------------------------
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
obtain one at https://mozilla.org/MPL/2.0/.
|
||||
--------------------------------------------------------------------------------*/
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_start)
|
||||
|
||||
|
||||
PHDRS {
|
||||
crt0 PT_LOAD FLAGS(7);
|
||||
arm7 PT_LOAD FLAGS(7);
|
||||
arm7i PT_LOAD FLAGS(0x100007);
|
||||
}
|
||||
|
||||
|
||||
MEMORY {
|
||||
ewram : ORIGIN = 0x02380000, LENGTH = 12M - 512K
|
||||
rom : ORIGIN = 0x08000000, LENGTH = 32M
|
||||
iwram : ORIGIN = 0x037fc000, LENGTH = (96K - 16K)
|
||||
|
||||
twl_ewram : ORIGIN = 0x02e80000, LENGTH = 512K - 64K
|
||||
twl_iwram : ORIGIN = 0x03000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
__iwram_start = ORIGIN(iwram);
|
||||
__iwram_top = ORIGIN(iwram)+ LENGTH(iwram);
|
||||
|
||||
__sp_irq = __iwram_top - 0x100;
|
||||
__sp_svc = __sp_irq - 0x100;
|
||||
__sp_usr = __sp_svc - 0x100;
|
||||
|
||||
__irq_flags = 0x04000000 - 8;
|
||||
__irq_flagsaux = 0x04000000 - 0x40;
|
||||
__irq_vector = 0x04000000 - 4;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
.twl :
|
||||
{
|
||||
__arm7i_lma__ = LOADADDR(.twl);
|
||||
__arm7i_start__ = .;
|
||||
*(.twl)
|
||||
*.twl*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
*.twl*(.rodata)
|
||||
*.twl*(.roda)
|
||||
*.twl*(.rodata.*)
|
||||
*.twl*(.data)
|
||||
*.twl*(.data.*)
|
||||
*.twl*(.gnu.linkonce.d*)
|
||||
. = ALIGN(4);
|
||||
__arm7i_end__ = .;
|
||||
} >twl_iwram AT>twl_ewram :arm7i
|
||||
|
||||
.twl_bss ALIGN(4) (NOLOAD) :
|
||||
{
|
||||
__twl_bss_start__ = .;
|
||||
*(.twl_bss)
|
||||
*.twl.*(.dynbss)
|
||||
*.twl.*(.gnu.linkonce.b*)
|
||||
*.twl.*(.bss*)
|
||||
*.twl.*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__twl_bss_end__ = .;
|
||||
} >twl_iwram :NONE
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >ewram :crt0
|
||||
|
||||
.text :
|
||||
{
|
||||
__arm7_lma__ = LOADADDR(.text);
|
||||
__arm7_start__ = .;
|
||||
KEEP (*(SORT_NONE(.init)))
|
||||
*(.plt)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
KEEP (*(.text.*personality*))
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
*(.glue_7t) *(.glue_7) *(.vfp11_veneer)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram :arm7
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP (*(.fini))
|
||||
} >iwram AT>ewram
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata)
|
||||
*all.rodata*(*)
|
||||
*(.roda)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r*)
|
||||
SORT(CONSTRUCTORS)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >iwram AT>ewram
|
||||
|
||||
.ARM.exidx : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
__exidx_end = .;
|
||||
} >iwram AT>ewram
|
||||
|
||||
/* Ensure the __preinit_array_start label is properly aligned. We
|
||||
could instead move the label definition inside the section, but
|
||||
the linker would then create the section even if it turns out to
|
||||
be empty, which isn't pretty. */
|
||||
.preinit_array : {
|
||||
. = ALIGN(32 / 8);
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.init_array : {
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.fini_array : {
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} >iwram AT>ewram
|
||||
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of the constructors, so
|
||||
we make sure it is first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not actually link against
|
||||
crtbegin.o; the linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it doesn't matter which
|
||||
directory crtbegin.o is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.eh_frame :
|
||||
{
|
||||
KEEP (*(.eh_frame))
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
|
||||
.gcc_except_table :
|
||||
{
|
||||
*(.gcc_except_table)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
} >iwram AT>ewram
|
||||
.jcr : { KEEP (*(.jcr)) } >iwram AT>ewram
|
||||
.got : { *(.got.plt) *(.got) } >iwram AT>ewram
|
||||
|
||||
.data ALIGN(4) : {
|
||||
__data_start = ABSOLUTE(.);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d*)
|
||||
CONSTRUCTORS
|
||||
. = ALIGN(4);
|
||||
__data_end = ABSOLUTE(.) ;
|
||||
} >iwram AT>ewram
|
||||
|
||||
.bss ALIGN(4) (NOLOAD) :
|
||||
{
|
||||
__arm7_end__ = .;
|
||||
__bss_start = ABSOLUTE(.);
|
||||
__bss_start__ = ABSOLUTE(.);
|
||||
*(.dynbss)
|
||||
*(.gnu.linkonce.b*)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4); /* REQUIRED. LD is flaky without it. */
|
||||
__bss_end__ = ABSOLUTE(.);
|
||||
__end__ = ABSOLUTE(.);
|
||||
} >iwram
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* These must appear regardless of . */
|
||||
}
|
||||
8
examples/mass-storage/arm7/dldi_ds_arm7.specs
Normal file
8
examples/mass-storage/arm7/dldi_ds_arm7.specs
Normal file
@@ -0,0 +1,8 @@
|
||||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T ../dldi_ds_arm7.ld%s --gc-sections --no-warn-rwx-segments
|
||||
|
||||
*startfile:
|
||||
ds_arm7_crt0%O%s crti%O%s crtbegin%O%s
|
||||
|
||||
8
examples/mass-storage/arm7/source/Arm7State.h
Normal file
8
examples/mass-storage/arm7/source/Arm7State.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the arm7 state.
|
||||
enum class Arm7State
|
||||
{
|
||||
Idle,
|
||||
ExitRequested
|
||||
};
|
||||
10
examples/mass-storage/arm7/source/ExitMode.h
Normal file
10
examples/mass-storage/arm7/source/ExitMode.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/// @brief Enum representing the exit mode of launcher.
|
||||
enum class ExitMode
|
||||
{
|
||||
/// @brief Reset the system (DSi mode only).
|
||||
Reset,
|
||||
/// @brief Power off the system.
|
||||
PowerOff
|
||||
};
|
||||
5
examples/mass-storage/arm7/source/common.h
Normal file
5
examples/mass-storage/arm7/source/common.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <nds.h>
|
||||
#include <libtwl/rtos/rtosMutex.h>
|
||||
|
||||
extern rtos_mutex_t gCardMutex;
|
||||
16
examples/mass-storage/arm7/source/dldi.s
Normal file
16
examples/mass-storage/arm7/source/dldi.s
Normal file
@@ -0,0 +1,16 @@
|
||||
.global _dldi_start
|
||||
.equ _dldi_start, 0x037F8000
|
||||
.global _io_dldi
|
||||
.equ _io_dldi, (_dldi_start + 0x60)
|
||||
.global _DLDI_startup_ptr
|
||||
.equ _DLDI_startup_ptr, (_io_dldi + 0x8)
|
||||
.global _DLDI_isInserted_ptr
|
||||
.equ _DLDI_isInserted_ptr, (_io_dldi + 0xC)
|
||||
.global _DLDI_readSectors_ptr
|
||||
.equ _DLDI_readSectors_ptr, (_io_dldi + 0x10)
|
||||
.global _DLDI_writeSectors_ptr
|
||||
.equ _DLDI_writeSectors_ptr, (_io_dldi + 0x14)
|
||||
.global _DLDI_clearStatus_ptr
|
||||
.equ _DLDI_clearStatus_ptr, (_io_dldi + 0x18)
|
||||
.global _DLDI_shutdown_ptr
|
||||
.equ _DLDI_shutdown_ptr, (_io_dldi + 0x1C)
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <nds/disc_io.h>
|
||||
#include "DldiIpcService.h"
|
||||
|
||||
extern FN_MEDIUM_STARTUP _DLDI_startup_ptr;
|
||||
extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr;
|
||||
extern FN_MEDIUM_WRITESECTORS _DLDI_writeSectors_ptr;
|
||||
|
||||
void DldiIpcService::HandleMessage(u32 data)
|
||||
{
|
||||
auto cmd = reinterpret_cast<const dldi_ipc_cmd_t*>(data << 2);
|
||||
switch (cmd->cmd)
|
||||
{
|
||||
case DLDI_IPC_CMD_SETUP:
|
||||
SetupDldi(cmd);
|
||||
break;
|
||||
|
||||
case DLDI_IPC_CMD_READ_SECTORS:
|
||||
ReadSectors(cmd);
|
||||
break;
|
||||
|
||||
case DLDI_IPC_CMD_WRITE_SECTORS:
|
||||
WriteSectors(cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DldiIpcService::SetupDldi(const dldi_ipc_cmd_t* cmd) const
|
||||
{
|
||||
memcpy((void*)0x037F8000, cmd->buffer, 16 * 1024);
|
||||
bool result;
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
result = _DLDI_startup_ptr();
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
SendResponseMessage(result);
|
||||
}
|
||||
|
||||
void DldiIpcService::ReadSectors(const dldi_ipc_cmd_t* cmd) const
|
||||
{
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
_DLDI_readSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
SendResponseMessage(0);
|
||||
}
|
||||
|
||||
void DldiIpcService::WriteSectors(const dldi_ipc_cmd_t* cmd) const
|
||||
{
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
_DLDI_writeSectors_ptr(cmd->sector, cmd->count, cmd->buffer);
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
SendResponseMessage(0);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "ipc/ThreadIpcService.h"
|
||||
#include "dldiIpcCommand.h"
|
||||
#include "ipcChannels.h"
|
||||
|
||||
class DldiIpcService : public ThreadIpcService
|
||||
{
|
||||
u32 _threadStack[128];
|
||||
|
||||
void SetupDldi(const dldi_ipc_cmd_t* cmd) const;
|
||||
void ReadSectors(const dldi_ipc_cmd_t* cmd) const;
|
||||
void WriteSectors(const dldi_ipc_cmd_t* cmd) const;
|
||||
|
||||
public:
|
||||
DldiIpcService()
|
||||
: ThreadIpcService(IPC_CHANNEL_DLDI, 6, _threadStack, sizeof(_threadStack)) { }
|
||||
|
||||
void HandleMessage(u32 data) override;
|
||||
};
|
||||
388
examples/mass-storage/arm7/source/main.cpp
Normal file
388
examples/mass-storage/arm7/source/main.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
#include "common.h"
|
||||
#include <nds/disc_io.h>
|
||||
#include <libtwl/rtos/rtosIrq.h>
|
||||
#include <libtwl/rtos/rtosThread.h>
|
||||
#include <libtwl/rtos/rtosEvent.h>
|
||||
#include <libtwl/sound/soundChannel.h>
|
||||
#include <libtwl/timer/timer.h>
|
||||
#include <libtwl/sound/sound.h>
|
||||
#include <libtwl/ipc/ipcSync.h>
|
||||
#include <libtwl/ipc/ipcFifoSystem.h>
|
||||
#include <libtwl/sys/sysPower.h>
|
||||
#include <libtwl/sio/sioRtc.h>
|
||||
#include <libtwl/sio/sio.h>
|
||||
#include <libtwl/gfx/gfxStatus.h>
|
||||
#include <libtwl/mem/memSwap.h>
|
||||
#include <libtwl/i2c/i2cMcu.h>
|
||||
#include <libtwl/spi/spiPmic.h>
|
||||
#include "ipcServices/DldiIpcService.h"
|
||||
#include "ExitMode.h"
|
||||
#include "Arm7State.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
static DldiIpcService sDldiIpcService;
|
||||
|
||||
rtos_mutex_t gCardMutex;
|
||||
|
||||
static rtos_event_t sVBlankEvent;
|
||||
static ExitMode sExitMode;
|
||||
static Arm7State sState;
|
||||
static volatile u8 sMcuIrqFlag = false;
|
||||
|
||||
static u32 sSdBlockCount;
|
||||
static u8 sSector0Buffer[512] alignas(4);
|
||||
|
||||
static rtos_thread_t sUsbThread;
|
||||
static u32 sUsbThreadStack[512];
|
||||
|
||||
extern FN_MEDIUM_READSECTORS _DLDI_readSectors_ptr;
|
||||
extern FN_MEDIUM_WRITESECTORS _DLDI_writeSectors_ptr;
|
||||
|
||||
static void vblankIrq(u32 irqMask)
|
||||
{
|
||||
rtos_signalEvent(&sVBlankEvent);
|
||||
}
|
||||
|
||||
static void mcuIrq(u32 irq2Mask)
|
||||
{
|
||||
sMcuIrqFlag = true;
|
||||
}
|
||||
|
||||
static void checkMcuIrq(void)
|
||||
{
|
||||
// mcu only exists in DSi mode
|
||||
if (isDSiMode())
|
||||
{
|
||||
// check and ack the flag atomically
|
||||
if (mem_swapByte(false, &sMcuIrqFlag))
|
||||
{
|
||||
// check the irq mask
|
||||
u32 irqMask = mcu_getIrqMask();
|
||||
if (irqMask & MCU_IRQ_RESET)
|
||||
{
|
||||
// power button was released
|
||||
sExitMode = ExitMode::Reset;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
else if (irqMask & MCU_IRQ_POWER_OFF)
|
||||
{
|
||||
// power button was held long to trigger a power off
|
||||
sExitMode = ExitMode::PowerOff;
|
||||
sState = Arm7State::ExitRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeVBlankIrq()
|
||||
{
|
||||
rtos_createEvent(&sVBlankEvent);
|
||||
rtos_setIrqFunc(RTOS_IRQ_VBLANK, vblankIrq);
|
||||
rtos_enableIrqMask(RTOS_IRQ_VBLANK);
|
||||
gfx_setVBlankIrqEnabled(true);
|
||||
}
|
||||
|
||||
static void usbThreadMain(void* arg)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
tud_task();
|
||||
}
|
||||
}
|
||||
|
||||
// Based on https://github.com/asiekierka/nrio-usb-disk/blob/main/source/msc.c msc_find_block_count by Asie
|
||||
// Note: This might not work correctly in some cases. It would be better if the DSpico would expose the actual SD capacity.
|
||||
static u32 findSdCardBlockCount()
|
||||
{
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
_DLDI_readSectors_ptr(0, 1, sSector0Buffer);
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
|
||||
u16 footer = *(u16*)(sSector0Buffer + 510);
|
||||
if (footer == 0xAA55)
|
||||
{
|
||||
u8 bootOpcode = sSector0Buffer[0];
|
||||
if (bootOpcode == 0xEB || bootOpcode == 0xE9 || bootOpcode == 0xE8)
|
||||
{
|
||||
if (!memcmp(sSector0Buffer + 54, "FAT", 3) || !memcmp(sSector0Buffer + 82, "FAT32 ", 8))
|
||||
{
|
||||
u32 totalSectors = *(u32*)(sSector0Buffer + 32);
|
||||
if (totalSectors < 0x10000)
|
||||
{
|
||||
totalSectors = sSector0Buffer[19] | (sSector0Buffer[20] << 8);
|
||||
}
|
||||
return totalSectors;
|
||||
}
|
||||
}
|
||||
|
||||
u32 blockCount = 0;
|
||||
for (u32 tableEntry = 0x1BE; tableEntry < 0x1FE; tableEntry += 16)
|
||||
{
|
||||
u32 pStart = *(u16*)(sSector0Buffer + tableEntry + 8) | (*(u16*)(sSector0Buffer + tableEntry + 10) << 16);
|
||||
u32 pCount = *(u16*)(sSector0Buffer + tableEntry + 12) | (*(u16*)(sSector0Buffer + tableEntry + 14) << 16);
|
||||
u32 pEnd = pStart + pCount;
|
||||
if (pEnd > blockCount)
|
||||
{
|
||||
blockCount = pEnd;
|
||||
}
|
||||
}
|
||||
|
||||
return blockCount;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void initializeArm7()
|
||||
{
|
||||
rtos_initIrq();
|
||||
rtos_startMainThread();
|
||||
ipc_initFifoSystem();
|
||||
|
||||
rtos_createMutex(&gCardMutex);
|
||||
|
||||
// clear sound registers
|
||||
dmaFillWords(0, (void*)0x04000400, 0x100);
|
||||
|
||||
pmic_setAmplifierEnable(true);
|
||||
sys_setSoundPower(true);
|
||||
|
||||
readUserSettings();
|
||||
pmic_setPowerLedBlink(PMIC_CONTROL_POWER_LED_BLINK_NONE);
|
||||
|
||||
sio_setGpioSiIrq(false);
|
||||
sio_setGpioMode(RCNT0_L_MODE_GPIO);
|
||||
|
||||
rtc_init();
|
||||
|
||||
sDldiIpcService.Start();
|
||||
|
||||
snd_setMasterVolume(127);
|
||||
snd_setMasterEnable(true);
|
||||
|
||||
initializeVBlankIrq();
|
||||
|
||||
if (isDSiMode())
|
||||
{
|
||||
rtos_setIrq2Func(RTOS_IRQ2_MCU, mcuIrq);
|
||||
rtos_enableIrq2Mask(RTOS_IRQ2_MCU);
|
||||
}
|
||||
|
||||
ipc_setArm7SyncBits(7);
|
||||
|
||||
while (ipc_getArm9SyncBits() != 6)
|
||||
{
|
||||
rtos_waitEvent(&sVBlankEvent, true, true);
|
||||
}
|
||||
|
||||
sSdBlockCount = findSdCardBlockCount();
|
||||
|
||||
tusb_rhport_init_t dev_init =
|
||||
{
|
||||
.role = TUSB_ROLE_DEVICE,
|
||||
.speed = TUSB_SPEED_AUTO
|
||||
};
|
||||
tusb_init(0, &dev_init);
|
||||
|
||||
rtos_createThread(&sUsbThread, 3, usbThreadMain, NULL, sUsbThreadStack, sizeof(sUsbThreadStack));
|
||||
rtos_wakeupThread(&sUsbThread);
|
||||
}
|
||||
|
||||
static void updateArm7IdleState()
|
||||
{
|
||||
checkMcuIrq();
|
||||
|
||||
if (sState == Arm7State::ExitRequested)
|
||||
{
|
||||
snd_setMasterVolume(0); // mute sound
|
||||
}
|
||||
}
|
||||
|
||||
static bool performExit(ExitMode exitMode)
|
||||
{
|
||||
switch (exitMode)
|
||||
{
|
||||
case ExitMode::Reset:
|
||||
{
|
||||
mcu_setWarmBootFlag(true);
|
||||
mcu_hardReset();
|
||||
break;
|
||||
}
|
||||
case ExitMode::PowerOff:
|
||||
{
|
||||
pmic_shutdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (true); // wait infinitely for exit
|
||||
}
|
||||
|
||||
static void updateArm7ExitRequestedState()
|
||||
{
|
||||
performExit(sExitMode);
|
||||
}
|
||||
|
||||
static void updateArm7()
|
||||
{
|
||||
switch (sState)
|
||||
{
|
||||
case Arm7State::Idle:
|
||||
{
|
||||
updateArm7IdleState();
|
||||
break;
|
||||
}
|
||||
case Arm7State::ExitRequested:
|
||||
{
|
||||
updateArm7ExitRequestedState();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
sState = Arm7State::Idle;
|
||||
initializeArm7();
|
||||
|
||||
while (true)
|
||||
{
|
||||
rtos_waitEvent(&sVBlankEvent, true, true);
|
||||
updateArm7();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
const char vid[] = "DSpico";
|
||||
const char pid[] = "Mass Storage";
|
||||
const char rev[] = "1.0";
|
||||
|
||||
memcpy(vendor_id , vid, strlen(vid));
|
||||
memcpy(product_id , pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
{
|
||||
(void) lun;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
*block_count = sSdBlockCount;
|
||||
*block_size = 512;
|
||||
}
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
{
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received READ10 command.
|
||||
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
if (offset != 0 || (bufsize % 512) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
_DLDI_readSectors_ptr(lba, bufsize / 512, buffer);
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
|
||||
return (int32_t)bufsize;
|
||||
}
|
||||
|
||||
bool tud_msc_is_writable_cb(uint8_t lun)
|
||||
{
|
||||
(void) lun;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback invoked when received WRITE10 command.
|
||||
// Process data in buffer to disk's storage and return number of written bytes
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
if (offset != 0 || (bufsize % 512) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtos_lockMutex(&gCardMutex);
|
||||
_DLDI_writeSectors_ptr(lba, bufsize / 512, buffer);
|
||||
rtos_unlockMutex(&gCardMutex);
|
||||
|
||||
return (int32_t)bufsize;
|
||||
}
|
||||
|
||||
// Callback invoked when received an SCSI command not in built-in list below
|
||||
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
|
||||
// - READ10 and WRITE10 has their own callbacks
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
|
||||
{
|
||||
// read10 & write10 has their own callback and MUST not be handled here
|
||||
|
||||
void const* response = NULL;
|
||||
int32_t resplen = 0;
|
||||
|
||||
// most scsi handled is input
|
||||
bool in_xfer = true;
|
||||
|
||||
switch (scsi_cmd[0])
|
||||
{
|
||||
default:
|
||||
{
|
||||
// Set Sense = Invalid Command Operation
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
|
||||
|
||||
// negative means error -> tinyusb could stall and/or response with failed status
|
||||
resplen = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return resplen must not larger than bufsize
|
||||
if (resplen > bufsize)
|
||||
{
|
||||
resplen = bufsize;
|
||||
}
|
||||
|
||||
if (response && (resplen > 0))
|
||||
{
|
||||
if(in_xfer)
|
||||
{
|
||||
memcpy(buffer, response, (size_t)resplen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SCSI output
|
||||
}
|
||||
}
|
||||
|
||||
return (int32_t)resplen;
|
||||
}
|
||||
112
examples/mass-storage/arm7/source/tusb_config.h
Normal file
112
examples/mass-storage/arm7/source/tusb_config.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_CONFIG_H_
|
||||
#define _TUSB_CONFIG_H_
|
||||
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Board Specific Configuration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// RHPort number used for device can be defined by board.mk, default to port 0
|
||||
#ifndef BOARD_TUD_RHPORT
|
||||
#define BOARD_TUD_RHPORT 0
|
||||
#endif
|
||||
|
||||
// RHPort max operational speed can defined by board.mk
|
||||
#ifndef BOARD_TUD_MAX_SPEED
|
||||
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// COMMON CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
// defined by compiler flags for flexibility
|
||||
#ifndef CFG_TUSB_MCU
|
||||
#define CFG_TUSB_MCU OPT_MCU_DSPICO
|
||||
#endif
|
||||
|
||||
#define TUP_DCD_ENDPOINT_MAX 16
|
||||
|
||||
#ifndef CFG_TUSB_OS
|
||||
#define CFG_TUSB_OS OPT_OS_CUSTOM
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_DEBUG
|
||||
#define CFG_TUSB_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Enable Device stack
|
||||
#define CFG_TUD_ENABLED 1
|
||||
|
||||
// Default is max speed that hardware controller could support with on-chip PHY
|
||||
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
|
||||
|
||||
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||
* Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||
* into those specific section.
|
||||
* e.g
|
||||
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||
*/
|
||||
#ifndef CFG_TUSB_MEM_SECTION
|
||||
#define CFG_TUSB_MEM_SECTION
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUSB_MEM_ALIGN
|
||||
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// DEVICE CONFIGURATION
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||
#define CFG_TUD_ENDPOINT0_SIZE 64
|
||||
#endif
|
||||
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC 0
|
||||
#define CFG_TUD_MSC 1
|
||||
#define CFG_TUD_HID 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_AUDIO 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
|
||||
// MSC Buffer size of Device Mass storage
|
||||
#define CFG_TUD_MSC_EP_BUFSIZE 8192
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_CONFIG_H_ */
|
||||
198
examples/mass-storage/arm7/source/usb_descriptors.c
Normal file
198
examples/mass-storage/arm7/source/usb_descriptors.c
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
// #include "bsp/board_api.h"
|
||||
#include "tusb.h"
|
||||
#include "usb_descriptors.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Device Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
tusb_desc_device_t const desc_device =
|
||||
{
|
||||
.bLength = sizeof(tusb_desc_device_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for Audio
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
|
||||
.idVendor = 0xCafe,
|
||||
.idProduct = USB_PID,
|
||||
.bcdDevice = 0x0100,
|
||||
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
// Invoked when received GET DEVICE DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
uint8_t const * tud_descriptor_device_cb(void)
|
||||
{
|
||||
return (uint8_t const *)&desc_device;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
|
||||
|
||||
#define EPNUM_MSC_OUT 0x01
|
||||
#define EPNUM_MSC_IN 0x81
|
||||
|
||||
uint8_t const desc_configuration[] =
|
||||
{
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 4, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
|
||||
|
||||
};
|
||||
|
||||
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||
// Application return pointer to descriptor
|
||||
// Descriptor contents must exist long enough for transfer to complete
|
||||
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
(void)index; // for multiple configurations
|
||||
return desc_configuration;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptors
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// String Descriptor Index
|
||||
enum {
|
||||
STRID_LANGID = 0,
|
||||
STRID_MANUFACTURER,
|
||||
STRID_PRODUCT,
|
||||
STRID_SERIAL,
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"LNH", // 1: Manufacturer
|
||||
"DSpico", // 2: Product
|
||||
NULL, // 3: Serials will use unique ID if possible
|
||||
"DSpico MSC" // 4: MSC Interface
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32 + 1];
|
||||
|
||||
// Get USB Serial number string from unique ID if available. Return number of character.
|
||||
// Input is string descriptor from index 1 (index 0 is type + len)
|
||||
static inline size_t board_usb_get_serial(uint16_t desc_str1[], size_t max_chars) {
|
||||
uint8_t uid[16] TU_ATTR_ALIGNED(4);
|
||||
size_t uid_len;
|
||||
|
||||
// TODO work with make, but not working with esp32s3 cmake
|
||||
// if ( board_get_unique_id ) {
|
||||
// uid_len = board_get_unique_id(uid, sizeof(uid));
|
||||
// }else {
|
||||
// fixed serial string is 01234567889ABCDEF
|
||||
uint32_t* uid32 = (uint32_t*) (uintptr_t) uid;
|
||||
uid32[0] = 0x67452301;
|
||||
uid32[1] = 0xEFCDAB89;
|
||||
uid_len = 8;
|
||||
// }
|
||||
|
||||
if ( uid_len > max_chars / 2 ) uid_len = max_chars / 2;
|
||||
|
||||
for ( size_t i = 0; i < uid_len; i++ ) {
|
||||
for ( size_t j = 0; j < 2; j++ ) {
|
||||
const char nibble_to_hex[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
uint8_t const nibble = (uid[i] >> (j * 4)) & 0xf;
|
||||
desc_str1[i * 2 + (1 - j)] = nibble_to_hex[nibble]; // UTF-16-LE
|
||||
}
|
||||
}
|
||||
|
||||
return 2 * uid_len;
|
||||
}
|
||||
|
||||
// Invoked when received GET STRING DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
(void) langid;
|
||||
size_t chr_count;
|
||||
|
||||
switch ( index ) {
|
||||
case STRID_LANGID:
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
break;
|
||||
|
||||
case STRID_SERIAL:
|
||||
chr_count = board_usb_get_serial(_desc_str + 1, 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
|
||||
|
||||
const char *str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
chr_count = strlen(str);
|
||||
size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
|
||||
if ( chr_count > max_count ) chr_count = max_count;
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for ( size_t i = 0; i < chr_count; i++ ) {
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
7
examples/mass-storage/arm7/source/usb_descriptors.h
Normal file
7
examples/mass-storage/arm7/source/usb_descriptors.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
ITF_NUM_MSC,
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
152
examples/mass-storage/arm9/Makefile
Normal file
152
examples/mass-storage/arm9/Makefile
Normal file
@@ -0,0 +1,152 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# INCLUDES is a list of directories containing extra header files
|
||||
# DATA is a list of directories containing binary files
|
||||
# all directories are relative to this makefile
|
||||
#---------------------------------------------------------------------------------
|
||||
BUILD := build
|
||||
SOURCES := source \
|
||||
source/core
|
||||
INCLUDES := include source ../common
|
||||
DATA := data
|
||||
GRAPHICS := gfx
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -marm -mthumb-interwork -DLIBTWL_ARM9 -DARM9
|
||||
|
||||
CFLAGS := -g -Wall -O2\
|
||||
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
|
||||
-ffunction-sections -fdata-sections\
|
||||
-ffast-math \
|
||||
-fno-devirtualize-speculatively \
|
||||
-Werror=return-type \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++23 -Wno-volatile -fno-rtti -fno-exceptions -fno-threadsafe-statics\
|
||||
-Wsuggest-override -Werror=suggest-override
|
||||
|
||||
CFLAGS += -Werror=implicit-function-declaration
|
||||
|
||||
ASFLAGS := -g $(ARCH) $(INCLUDE) -march=armv5te -mtune=arm946e-s
|
||||
|
||||
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),--gc-sections,--use-blx -ffunction-sections -fdata-sections
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBS := -ltwl9 -lnds9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(LIBNDS) $(CURDIR)/../../../libs/libtwl/libtwl9 $(CURDIR)/../../../libs/libtwl/common
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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 ARM9ELF := $(CURDIR)/$(TARGET).elf
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
|
||||
|
||||
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)/*.*)))
|
||||
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PNGFILES:.png=.o)\
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
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
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) *.elf *.nds* *.bin
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
$(ARM9ELF) : $(OFILES)
|
||||
@echo linking $(notdir $@)
|
||||
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.nft2.o : %.nft2
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# This rule creates assembly source files using grit
|
||||
# grit takes an image file and a .grit describing how the file is to be processed
|
||||
# add additional rules like this for each image extension
|
||||
# you use in the graphics folders
|
||||
#---------------------------------------------------------------------------------
|
||||
%.s %.h: %.png %.grit
|
||||
#---------------------------------------------------------------------------------
|
||||
grit $< -fts -o$*
|
||||
|
||||
-include $(DEPSDIR)/*.d
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
2
examples/mass-storage/arm9/source/common.h
Normal file
2
examples/mass-storage/arm9/source/common.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include <nds/ndstypes.h>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user