mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
ams.tma: restore tma code to debugger_dev branch
This commit is contained in:
parent
a38927ec04
commit
37025258c6
47 changed files with 4511 additions and 3 deletions
4
Makefile
4
Makefile
|
@ -53,8 +53,9 @@ dist: all
|
|||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000036
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000034
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032
|
||||
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000007
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D
|
||||
cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin
|
||||
cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin
|
||||
cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin
|
||||
|
@ -70,6 +71,7 @@ dist: all
|
|||
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro
|
||||
mkdir -p atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags
|
||||
touch atmosphere-$(AMSVER)/atmosphere/titles/0100000000000032/flags/boot2.flag
|
||||
cp stratosphere/tma/tma.nsp atmosphere-$(AMSVER)/atmosphere/titles/0100000000000007/exefs.nsp
|
||||
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/atmosphere/titles/010000000000000D/exefs.nsp
|
||||
cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../;
|
||||
rm -r atmosphere-$(AMSVER)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt
|
||||
MODULES := loader pm sm boot ams_mitm eclct.stub creport fatal dmnt tma
|
||||
|
||||
SUBFOLDERS := libstratosphere $(MODULES)
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0fbc0e2f468762de62d2b14d6495247d04e80bc3
|
||||
Subproject commit fa37b70b0eca93be04e18636db25c9443e00d03b
|
159
stratosphere/tma/Makefile
Normal file
159
stratosphere/tma/Makefile
Normal file
|
@ -0,0 +1,159 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITPRO)/libnx/switch_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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
|
||||
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source source/test source/settings source/target_io
|
||||
DATA := data
|
||||
INCLUDES := include ../../common/include
|
||||
EXEFS_SRC := exefs_src
|
||||
|
||||
DEFINES := -DDISABLE_IPC
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lstratosphere -lnx
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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 := $(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)
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
.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).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).nsp
|
||||
|
||||
ifeq ($(strip $(APP_JSON)),)
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso
|
||||
else
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
endif
|
||||
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
35
stratosphere/tma/client/Main.py
Normal file
35
stratosphere/tma/client/Main.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Copyright (c) 2018 Atmosphere-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
from UsbConnection import UsbConnection
|
||||
import sys, time
|
||||
from Packet import Packet
|
||||
import ServiceId
|
||||
|
||||
def main(argc, argv):
|
||||
with UsbConnection(None) as c:
|
||||
print 'Waiting for connection...'
|
||||
c.wait_connected()
|
||||
print 'Connected!'
|
||||
print 'Reading atmosphere/BCT.ini...'
|
||||
c.intf.send_packet(Packet().set_service(ServiceId.TARGETIO_SERVICE).set_task(0x01000000).set_cmd(2).write_str('atmosphere/BCT.ini').write_u64(0x109).write_u64(0))
|
||||
resp = c.intf.read_packet()
|
||||
res_packet = c.intf.read_packet()
|
||||
read_res, size_read = resp.read_u32(), resp.read_u32()
|
||||
print 'Final Result: 0x%x' % res_packet.read_u32()
|
||||
print 'Size Read: 0x%x' % size_read
|
||||
print 'Data:\n%s' % resp.body[resp.offset:]
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(len(sys.argv), sys.argv))
|
120
stratosphere/tma/client/Packet.py
Normal file
120
stratosphere/tma/client/Packet.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# Copyright (c) 2018 Atmosphere-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import zlib
|
||||
import ServiceId
|
||||
from struct import unpack as up, pack as pk
|
||||
|
||||
HEADER_SIZE = 0x28
|
||||
|
||||
def crc32(s):
|
||||
return zlib.crc32(s) & 0xFFFFFFFF
|
||||
|
||||
class Packet():
|
||||
def __init__(self):
|
||||
self.service = 0
|
||||
self.task = 0
|
||||
self.cmd = 0
|
||||
self.continuation = 0
|
||||
self.version = 0
|
||||
self.body_len = 0
|
||||
self.body = ''
|
||||
self.offset = 0
|
||||
def load_header(self, header):
|
||||
assert len(header) == HEADER_SIZE
|
||||
self.service, self.task, self.cmd, self.continuation, self.version, self.body_len, \
|
||||
_, self.body_chk, self.hdr_chk = up('<IIHBBI16sII', header)
|
||||
if crc32(header[:-4]) != self.hdr_chk:
|
||||
raise ValueError('Invalid header checksum in received packet!')
|
||||
def load_body(self, body):
|
||||
assert len(body) == self.body_len
|
||||
if crc32(body) != self.body_chk:
|
||||
raise ValueError('Invalid body checksum in received packet!')
|
||||
self.body = body
|
||||
def get_data(self):
|
||||
assert len(self.body) == self.body_len and self.body_len <= 0xE000
|
||||
self.body_chk = crc32(self.body)
|
||||
hdr = pk('<IIHBBIIIIII', self.service, self.task, self.cmd, self.continuation, self.version, self.body_len, 0, 0, 0, 0, self.body_chk)
|
||||
self.hdr_chk = crc32(hdr)
|
||||
hdr += pk('<I', self.hdr_chk)
|
||||
return hdr + self.body
|
||||
def set_service(self, srv):
|
||||
if type(srv) is str:
|
||||
self.service = ServiceId.hash(srv)
|
||||
else:
|
||||
self.service = srv
|
||||
return self
|
||||
def set_task(self, t):
|
||||
self.task = t
|
||||
return self
|
||||
def set_cmd(self, x):
|
||||
self.cmd = x
|
||||
return self
|
||||
def set_continuation(self, c):
|
||||
self.continuation = c
|
||||
return self
|
||||
def set_version(self, v):
|
||||
self.version = v
|
||||
return self
|
||||
def reset_offset(self):
|
||||
self.offset = 0
|
||||
return self
|
||||
def write_str(self, s):
|
||||
if s[-1] != '\x00':
|
||||
s += '\x00'
|
||||
self.body += s
|
||||
self.body_len += len(s)
|
||||
return self
|
||||
def write_u8(self, x):
|
||||
self.body += pk('<B', x & 0xFF)
|
||||
self.body_len += 1
|
||||
return self
|
||||
def write_u16(self, x):
|
||||
self.body += pk('<H', x & 0xFFFF)
|
||||
self.body_len += 2
|
||||
return self
|
||||
def write_u32(self, x):
|
||||
self.body += pk('<I', x & 0xFFFFFFFF)
|
||||
self.body_len += 4
|
||||
return self
|
||||
def write_u64(self, x):
|
||||
self.body += pk('<Q', x & 0xFFFFFFFFFFFFFFFF)
|
||||
self.body_len += 8
|
||||
return self
|
||||
def read_str(self):
|
||||
s = ''
|
||||
while self.body[self.offset] != '\x00' and self.offset < self.body_len:
|
||||
s += self.body[self.offset]
|
||||
self.offset += 1
|
||||
if self.offset < self.body_len and self.body[self.offset] == '\x00':
|
||||
self.offset += 1
|
||||
def read_u8(self):
|
||||
x, = up('<B', self.body[self.offset:self.offset+1])
|
||||
self.offset += 1
|
||||
return x
|
||||
def read_u16(self):
|
||||
x, = up('<H', self.body[self.offset:self.offset+2])
|
||||
self.offset += 2
|
||||
return x
|
||||
def read_u32(self):
|
||||
x, = up('<I', self.body[self.offset:self.offset+4])
|
||||
self.offset += 4
|
||||
return x
|
||||
def read_u64(self):
|
||||
x, = up('<Q', self.body[self.offset:self.offset+8])
|
||||
self.offset += 8
|
||||
return x
|
||||
def read_struct(self, format, sz):
|
||||
x = up(format, self.body[self.offset:self.offset+sz])
|
||||
self.offset += sz
|
||||
return x
|
30
stratosphere/tma/client/ServiceId.py
Normal file
30
stratosphere/tma/client/ServiceId.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Copyright (c) 2018 Atmosphere-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
def hash(s):
|
||||
h = ord(s[0]) & 0xFFFFFFFF
|
||||
for c in s:
|
||||
h = ((1000003 * h) ^ ord(c)) & 0xFFFFFFFF
|
||||
h ^= len(s)
|
||||
return h
|
||||
|
||||
USB_QUERY_TARGET = hash("USBQueryTarget")
|
||||
USB_SEND_HOST_INFO = hash("USBSendHostInfo")
|
||||
USB_CONNECT = hash("USBConnect")
|
||||
USB_DISCONNECT = hash("USBDisconnect")
|
||||
|
||||
ATMOSPHERE_TEST_SERVICE = hash("AtmosphereTestService")
|
||||
SETTINGS_SERVICE = hash("SettingsService")
|
||||
TARGETIO_SERVICE = hash("TIOService")
|
||||
|
134
stratosphere/tma/client/UsbConnection.py
Normal file
134
stratosphere/tma/client/UsbConnection.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Copyright (c) 2018 Atmosphere-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
from UsbInterface import UsbInterface
|
||||
from threading import Thread, Condition
|
||||
from collections import deque
|
||||
import time
|
||||
import ServiceId
|
||||
from Packet import Packet
|
||||
|
||||
class UsbConnection(UsbInterface):
|
||||
# Auto connect thread func.
|
||||
def auto_connect(connection):
|
||||
while not connection.is_connected():
|
||||
try:
|
||||
connection.connect(UsbInterface())
|
||||
except ValueError as e:
|
||||
continue
|
||||
def recv_thread(connection):
|
||||
while connection.is_connected():
|
||||
try:
|
||||
connection.recv_packet()
|
||||
except Exception as e:
|
||||
print 'An exception occurred:'
|
||||
print 'Type: '+e.__class__.__name__
|
||||
print 'Msg: '+str(e)
|
||||
connection.disconnect()
|
||||
connection.send_packet(None)
|
||||
def send_thread(connection):
|
||||
while connection.is_connected():
|
||||
try:
|
||||
next_packet = connection.get_next_send_packet()
|
||||
if next_packet is not None:
|
||||
connection.intf.send_packet(next_packet)
|
||||
else:
|
||||
connection.disconnect()
|
||||
except Exception as e:
|
||||
print 'An exception occurred:'
|
||||
print 'Type: '+e.__class__.__name__
|
||||
print 'Msg: '+str(e)
|
||||
connection.disconnect()
|
||||
def __init__(self, manager):
|
||||
self.manager = manager
|
||||
self.connected = False
|
||||
self.intf = None
|
||||
self.conn_lock, self.send_lock = Condition(), Condition()
|
||||
self.send_queue = deque()
|
||||
def __enter__(self):
|
||||
self.conn_thrd = Thread(target=UsbConnection.auto_connect, args=(self,))
|
||||
self.conn_thrd.daemon = True
|
||||
self.conn_thrd.start()
|
||||
return self
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.disconnect()
|
||||
time.sleep(1)
|
||||
print 'Closing!'
|
||||
time.sleep(1)
|
||||
def wait_connected(self):
|
||||
self.conn_lock.acquire()
|
||||
if not self.is_connected():
|
||||
self.conn_lock.wait()
|
||||
self.conn_lock.release()
|
||||
def is_connected(self):
|
||||
return self.connected
|
||||
def connect(self, intf):
|
||||
# This indicates we have a connection.
|
||||
self.conn_lock.acquire()
|
||||
assert not self.connected
|
||||
self.intf = intf
|
||||
|
||||
try:
|
||||
# Perform Query + Connection handshake
|
||||
print 'Performing handshake...'
|
||||
self.intf.send_packet(Packet().set_service(ServiceId.USB_QUERY_TARGET))
|
||||
query_resp = self.intf.read_packet()
|
||||
print 'Found Switch, Protocol version 0x%x' % query_resp.read_u32()
|
||||
|
||||
self.intf.send_packet(Packet().set_service(ServiceId.USB_SEND_HOST_INFO).write_u32(0).write_u32(0))
|
||||
|
||||
self.intf.send_packet(Packet().set_service(ServiceId.USB_CONNECT))
|
||||
resp = self.intf.read_packet()
|
||||
|
||||
# Spawn threads
|
||||
self.recv_thrd = Thread(target=UsbConnection.recv_thread, args=(self,))
|
||||
self.send_thrd = Thread(target=UsbConnection.send_thread, args=(self,))
|
||||
self.recv_thrd.daemon = True
|
||||
self.send_thrd.daemon = True
|
||||
self.recv_thrd.start()
|
||||
self.send_thrd.start()
|
||||
self.connected = True
|
||||
finally:
|
||||
# Finish connection.
|
||||
self.conn_lock.notify()
|
||||
self.conn_lock.release()
|
||||
def disconnect(self):
|
||||
self.conn_lock.acquire()
|
||||
if self.connected:
|
||||
self.connected = False
|
||||
self.intf.send_packet(Packet().set_service(ServiceId.USB_DISCONNECT))
|
||||
self.conn_lock.release()
|
||||
def recv_packet(self):
|
||||
packet = self.intf.read_packet()
|
||||
assert type(packet) is Packet
|
||||
dat = packet.read_u64()
|
||||
print('Got Packet: %08x' % dat)
|
||||
def send_packet(self, packet):
|
||||
assert type(packet) is Packet
|
||||
self.send_lock.acquire()
|
||||
if len(self.send_queue) == 0x40:
|
||||
self.send_lock.wait()
|
||||
self.send_queue.append(packet)
|
||||
if len(self.send_queue) == 1:
|
||||
self.send_lock.notify()
|
||||
self.send_lock.release()
|
||||
def get_next_send_packet(self):
|
||||
self.send_lock.acquire()
|
||||
if len(self.send_queue) == 0:
|
||||
self.send_lock.wait()
|
||||
packet = self.send_queue.popleft()
|
||||
if len(self.send_queue) == 0x3F:
|
||||
self.send_lock.notify()
|
||||
self.send_lock.release()
|
||||
return packet
|
||||
|
62
stratosphere/tma/client/UsbInterface.py
Normal file
62
stratosphere/tma/client/UsbInterface.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Copyright (c) 2018 Atmosphere-NX
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms and conditions of the GNU General Public License,
|
||||
# version 2, as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
# more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import usb
|
||||
import Packet
|
||||
|
||||
class UsbInterface():
|
||||
def __init__(self):
|
||||
self.dev = usb.core.find(idVendor=0x057e, idProduct=0x3000)
|
||||
if self.dev is None:
|
||||
raise ValueError('Device not found')
|
||||
|
||||
self.dev.set_configuration()
|
||||
self.cfg = self.dev.get_active_configuration()
|
||||
self.intf = usb.util.find_descriptor(self.cfg, bInterfaceClass=0xff, bInterfaceSubClass=0xff, bInterfaceProtocol=0xfc)
|
||||
assert self.intf is not None
|
||||
|
||||
self.ep_in = usb.util.find_descriptor(
|
||||
self.intf,
|
||||
custom_match = \
|
||||
lambda e: \
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) == \
|
||||
usb.util.ENDPOINT_IN)
|
||||
assert self.ep_in is not None
|
||||
|
||||
self.ep_out = usb.util.find_descriptor(
|
||||
self.intf,
|
||||
custom_match = \
|
||||
lambda e: \
|
||||
usb.util.endpoint_direction(e.bEndpointAddress) == \
|
||||
usb.util.ENDPOINT_OUT)
|
||||
assert self.ep_out is not None
|
||||
def close(self):
|
||||
usb.util.dispose_resources(self.dev)
|
||||
def blocking_read(self, size):
|
||||
return ''.join(chr(x) for x in self.ep_in.read(size, 0xFFFFFFFFFFFFFFFF))
|
||||
def blocking_write(self, data):
|
||||
self.ep_out.write(data, 0xFFFFFFFFFFFFFFFF)
|
||||
def read_packet(self):
|
||||
packet = Packet.Packet()
|
||||
hdr = self.blocking_read(Packet.HEADER_SIZE)
|
||||
packet.load_header(hdr)
|
||||
if packet.body_len:
|
||||
packet.load_body(self.blocking_read(packet.body_len))
|
||||
return packet
|
||||
def send_packet(self, packet):
|
||||
data = packet.get_data()
|
||||
self.blocking_write(data[:Packet.HEADER_SIZE])
|
||||
if (len(data) > Packet.HEADER_SIZE):
|
||||
self.blocking_write(data[Packet.HEADER_SIZE:])
|
||||
|
||||
|
93
stratosphere/tma/source/crc.h
Normal file
93
stratosphere/tma/source/crc.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Code taken from Yazen Ghannam <yazen.ghannam@linaro.org>, licensed GPLv2. */
|
||||
|
||||
#define CRC32X(crc, value) __asm__("crc32x %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32W(crc, value) __asm__("crc32w %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32H(crc, value) __asm__("crc32h %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32B(crc, value) __asm__("crc32b %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CX(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CW(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CH(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CB(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
|
||||
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
|
||||
{
|
||||
return p[0] | p[1] << 8;
|
||||
}
|
||||
|
||||
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
|
||||
{
|
||||
return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
|
||||
}
|
||||
|
||||
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
|
||||
__get_unaligned_le32(p);
|
||||
}
|
||||
|
||||
static inline uint16_t get_unaligned_le16(const void *p)
|
||||
{
|
||||
return __get_unaligned_le16((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint32_t get_unaligned_le32(const void *p)
|
||||
{
|
||||
return __get_unaligned_le32((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint64_t get_unaligned_le64(const void *p)
|
||||
{
|
||||
return __get_unaligned_le64((const uint8_t *)p);
|
||||
}
|
||||
|
||||
|
||||
static u32 crc32_arm64_le_hw(const u8 *p, unsigned int len) {
|
||||
u32 crc = 0xFFFFFFFF;
|
||||
|
||||
s64 length = len;
|
||||
|
||||
while ((length -= sizeof(u64)) >= 0) {
|
||||
CRC32X(crc, get_unaligned_le64(p));
|
||||
p += sizeof(u64);
|
||||
}
|
||||
|
||||
/* The following is more efficient than the straight loop */
|
||||
if (length & sizeof(u32)) {
|
||||
CRC32W(crc, get_unaligned_le32(p));
|
||||
p += sizeof(u32);
|
||||
}
|
||||
if (length & sizeof(u16)) {
|
||||
CRC32H(crc, get_unaligned_le16(p));
|
||||
p += sizeof(u16);
|
||||
}
|
||||
if (length & sizeof(u8))
|
||||
CRC32B(crc, *p);
|
||||
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
286
stratosphere/tma/source/dmnt.c
Normal file
286
stratosphere/tma/source/dmnt.c
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include "dmnt.h"
|
||||
|
||||
static Service g_dmntSrv;
|
||||
static u64 g_refCnt;
|
||||
|
||||
Result dmntInitialize(void) {
|
||||
atomicIncrement64(&g_refCnt);
|
||||
|
||||
if (serviceIsActive(&g_dmntSrv))
|
||||
return 0;
|
||||
|
||||
return smGetService(&g_dmntSrv, "dmnt:-");
|
||||
}
|
||||
|
||||
void dmntExit(void) {
|
||||
if (atomicDecrement64(&g_refCnt) == 0)
|
||||
serviceClose(&g_dmntSrv);
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, path, FS_MAX_PATH, BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, out, sizeof(*out), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
int flags;
|
||||
u32 create_option;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 29;
|
||||
raw->flags = flags;
|
||||
raw->create_option = create_option;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileClose(DmntFile *f) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 30;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t *out_read) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, buf, len, BufferType_Type1);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 offset;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 31;
|
||||
raw->offset = off;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 out_read;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_read) {
|
||||
*out_read = resp->out_read;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t *out_written) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
ipcAddSendBuffer(&c, buf, len, BufferType_Type1);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 offset;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 32;
|
||||
raw->offset = off;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 out_written;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_written) {
|
||||
*out_written = resp->out_written;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileGetInformation(const char *path, bool *out_is_dir, DmntFileInformation *out_info) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, path, FS_MAX_PATH, BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, out_info, sizeof(*out_info), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 34;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
int is_dir;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*out_is_dir = resp->is_dir != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileGetSize(const char *path, u64 *out_size) {
|
||||
DmntFileInformation info;
|
||||
bool is_dir;
|
||||
Result rc = dmntTargetIOFileGetInformation(path, &is_dir, &info);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (is_dir) {
|
||||
/* TODO: error code? */
|
||||
rc = 0x202;
|
||||
} else {
|
||||
*out_size = info.size;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _dmntTargetIOFileSetSize(const void *arg, size_t arg_size, u64 size) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, arg, arg_size, BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 size;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 36;
|
||||
raw->size = size;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileSetSize(const char *path, u64 size) {
|
||||
return _dmntTargetIOFileSetSize(path, FS_MAX_PATH, size);
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileSetOpenFileSize(DmntFile *f, u64 size) {
|
||||
/* Atmosphere extension */
|
||||
return _dmntTargetIOFileSetSize(f, sizeof(*f), size);
|
||||
}
|
57
stratosphere/tma/source/dmnt.h
Normal file
57
stratosphere/tma/source/dmnt.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
u64 handle;
|
||||
} DmntFile;
|
||||
|
||||
typedef enum {
|
||||
DmntTIOCreateOption_CreateNew = 1,
|
||||
DmntTIOCreateOption_CreateAlways = 2,
|
||||
DmntTIOCreateOption_OpenExisting = 3,
|
||||
DmntTIOCreateOption_OpenAlways = 4,
|
||||
DmntTIOCreateOption_ResetSize = 5,
|
||||
} DmntTIOCreateOption;
|
||||
|
||||
typedef struct {
|
||||
u64 size;
|
||||
u64 create_time;
|
||||
u64 access_time;
|
||||
u64 modify_time;
|
||||
} DmntFileInformation;
|
||||
|
||||
Result dmntInitialize(void);
|
||||
void dmntExit(void);
|
||||
|
||||
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option);
|
||||
Result dmntTargetIOFileClose(DmntFile *f);
|
||||
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t* out_read);
|
||||
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t* out_written);
|
||||
Result dmntTargetIOFileGetInformation(const char *path, bool *out_is_dir, DmntFileInformation *out_info);
|
||||
Result dmntTargetIOFileGetSize(const char *path, u64 *out_size);
|
||||
Result dmntTargetIOFileSetSize(const char *path, u64 size);
|
||||
Result dmntTargetIOFileSetOpenFileSize(DmntFile *f, u64 size); /* Atmosphere extension */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
43
stratosphere/tma/source/settings/settings_service.cpp
Normal file
43
stratosphere/tma/source/settings/settings_service.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "settings_service.hpp"
|
||||
#include "settings_task.hpp"
|
||||
|
||||
TmaTask *SettingsService::NewTask(TmaPacket *packet) {
|
||||
TmaTask *new_task = nullptr;
|
||||
switch (packet->GetCommand()) {
|
||||
case SettingsServiceCmd_GetSetting:
|
||||
{
|
||||
new_task = new GetSettingTask(this->manager);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
new_task = nullptr;
|
||||
break;
|
||||
}
|
||||
if (new_task != nullptr) {
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
new_task->SetNeedsPackets(true);
|
||||
}
|
||||
|
||||
return new_task;
|
||||
}
|
34
stratosphere/tma/source/settings/settings_service.hpp
Normal file
34
stratosphere/tma/source/settings/settings_service.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
enum SettingsServiceCmd : u32 {
|
||||
SettingsServiceCmd_GetSetting = 0,
|
||||
};
|
||||
|
||||
class SettingsService : public TmaService {
|
||||
public:
|
||||
SettingsService(TmaServiceManager *m) : TmaService(m, "SettingsService") { }
|
||||
virtual ~SettingsService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
48
stratosphere/tma/source/settings/settings_task.cpp
Normal file
48
stratosphere/tma/source/settings/settings_task.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "settings_task.hpp"
|
||||
|
||||
void GetSettingTask::OnStart(TmaPacket *packet) {
|
||||
size_t length;
|
||||
packet->ReadString(this->name, sizeof(this->name), &length);
|
||||
packet->ReadString(this->item_key, sizeof(this->item_key), &length);
|
||||
|
||||
if (R_SUCCEEDED(setsysGetSettingsItemValueSize(this->name, this->item_key, &this->value_size))) {
|
||||
if (this->value_size <= sizeof(this->value)) {
|
||||
if (R_SUCCEEDED(setsysGetSettingsItemValue(this->name, this->item_key, this->value, this->value_size))) {
|
||||
this->succeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetSettingTask::OnReceivePacket(TmaPacket *packet) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void GetSettingTask::OnSendPacket(TmaPacket *packet) {
|
||||
packet->Write<u8>((u8)this->succeeded);
|
||||
if (this->succeeded) {
|
||||
packet->Write<u32>((u32)this->value_size);
|
||||
packet->Write(this->value, this->value_size);
|
||||
}
|
||||
|
||||
this->Complete();
|
||||
}
|
38
stratosphere/tma/source/settings/settings_task.hpp
Normal file
38
stratosphere/tma/source/settings/settings_task.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
|
||||
class GetSettingTask : public TmaTask {
|
||||
private:
|
||||
char name[0x40] = {0};
|
||||
char item_key[0x40] = {0};
|
||||
u8 value[0x40] = {0};
|
||||
u64 value_size = 0;
|
||||
bool succeeded = false;
|
||||
|
||||
public:
|
||||
GetSettingTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~GetSettingTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
};
|
47
stratosphere/tma/source/target_io/tio_service.cpp
Normal file
47
stratosphere/tma/source/target_io/tio_service.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tio_service.hpp"
|
||||
#include "tio_task.hpp"
|
||||
|
||||
TmaTask *TIOService::NewTask(TmaPacket *packet) {
|
||||
TmaTask *new_task = nullptr;
|
||||
switch (packet->GetCommand()) {
|
||||
case TIOServiceCmd_FileRead:
|
||||
{
|
||||
new_task = new TIOFileReadTask(this->manager);
|
||||
}
|
||||
break;
|
||||
case TIOServiceCmd_FileWrite:
|
||||
{
|
||||
new_task = new TIOFileWriteTask(this->manager);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
new_task = nullptr;
|
||||
break;
|
||||
}
|
||||
if (new_task != nullptr) {
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
}
|
||||
|
||||
return new_task;
|
||||
}
|
35
stratosphere/tma/source/target_io/tio_service.hpp
Normal file
35
stratosphere/tma/source/target_io/tio_service.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
enum TIOServiceCmd : u32 {
|
||||
TIOServiceCmd_FileRead = 2,
|
||||
TIOServiceCmd_FileWrite = 3,
|
||||
};
|
||||
|
||||
class TIOService : public TmaService {
|
||||
public:
|
||||
TIOService(TmaServiceManager *m) : TmaService(m, "TIOService") { }
|
||||
virtual ~TIOService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
173
stratosphere/tma/source/target_io/tio_task.cpp
Normal file
173
stratosphere/tma/source/target_io/tio_task.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tio_task.hpp"
|
||||
|
||||
|
||||
void TIOFileReadTask::OnStart(TmaPacket *packet) {
|
||||
char path[FS_MAX_PATH];
|
||||
|
||||
packet->ReadString(path, sizeof(path), nullptr);
|
||||
packet->Read<u64>(this->size_remaining);
|
||||
packet->Read<u64>(this->cur_offset);
|
||||
|
||||
Result rc = 0;
|
||||
if (strlen(path) == 0) {
|
||||
rc = 0x202;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u64 file_size;
|
||||
rc = dmntTargetIOFileGetSize(path, &file_size);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (file_size < this->cur_offset + this->size_remaining) {
|
||||
this->size_remaining = file_size - this->cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
|
||||
if (R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
return;
|
||||
} else {
|
||||
auto packet = this->AllocateSendPacket();
|
||||
rc = this->ProcessPacket(packet);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
this->manager->SendPacket(packet);
|
||||
if (this->size_remaining) {
|
||||
this->SetNeedsPackets(true);
|
||||
} else {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
} else {
|
||||
this->manager->FreePacket(packet);
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileReadTask::OnSendPacket(TmaPacket *packet) {
|
||||
Result rc = this->ProcessPacket(packet);
|
||||
|
||||
if (this->size_remaining == 0 || R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileReadTask::SendResult(Result rc) {
|
||||
dmntTargetIOFileClose(&this->handle);
|
||||
this->SetNeedsPackets(false);
|
||||
|
||||
auto packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
Complete();
|
||||
}
|
||||
|
||||
Result TIOFileReadTask::ProcessPacket(TmaPacket *packet) {
|
||||
Result rc = 0x196002;
|
||||
|
||||
size_t cur_read = static_cast<u32>((this->size_remaining > MaxDataSize) ? MaxDataSize : this->size_remaining);
|
||||
|
||||
u8 *buf = new u8[cur_read];
|
||||
if (buf != nullptr) {
|
||||
size_t actual_read = 0;
|
||||
rc = dmntTargetIOFileRead(&this->handle, this->cur_offset, buf, cur_read, &actual_read);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
packet->Write<Result>(rc);
|
||||
packet->Write<u32>(actual_read);
|
||||
packet->Write(buf, actual_read);
|
||||
this->cur_offset += actual_read;
|
||||
this->size_remaining -= actual_read;
|
||||
}
|
||||
|
||||
delete buf;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::OnStart(TmaPacket *packet) {
|
||||
char path[FS_MAX_PATH];
|
||||
|
||||
packet->ReadString(path, sizeof(path), nullptr);
|
||||
packet->Read<u64>(this->size_remaining);
|
||||
packet->Read<u64>(this->cur_offset);
|
||||
|
||||
Result rc = 0;
|
||||
if (strlen(path) == 0) {
|
||||
rc = 0x202;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u64 file_size;
|
||||
rc = dmntTargetIOFileGetSize(path, &file_size);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (file_size < this->cur_offset + this->size_remaining) {
|
||||
this->size_remaining = file_size - this->cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::OnReceivePacket(TmaPacket *packet) {
|
||||
Result rc = this->ProcessPacket(packet);
|
||||
|
||||
if (this->size_remaining == 0 || R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::SendResult(Result rc) {
|
||||
dmntTargetIOFileClose(&this->handle);
|
||||
|
||||
auto packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
Complete();
|
||||
}
|
||||
|
||||
Result TIOFileWriteTask::ProcessPacket(TmaPacket *packet) {
|
||||
Result rc = 0x196002;
|
||||
|
||||
/* Note: N does not bounds check this. We do. */
|
||||
u32 cur_write = 0;
|
||||
packet->Read<u32>(cur_write);
|
||||
|
||||
size_t actual_written = 0;
|
||||
if (cur_write < MaxDataSize) {
|
||||
if (cur_write > this->size_remaining) {
|
||||
cur_write = this->size_remaining;
|
||||
}
|
||||
rc = dmntTargetIOFileWrite(&this->handle, this->cur_offset, packet->GetCurrentBodyPtr(), cur_write, &actual_written);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
this->size_remaining -= actual_written;
|
||||
this->cur_offset += actual_written;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
85
stratosphere/tma/source/target_io/tio_task.hpp
Normal file
85
stratosphere/tma/source/target_io/tio_task.hpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
#include "../tma_service_manager.hpp"
|
||||
#include "../dmnt.h"
|
||||
|
||||
class TIOTask : public TmaTask {
|
||||
public:
|
||||
TIOTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~TIOTask() { }
|
||||
|
||||
virtual void SendResult(Result rc) {
|
||||
TmaPacket *packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
this->Complete();
|
||||
}
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) = 0;
|
||||
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override {
|
||||
this->Complete();
|
||||
}
|
||||
|
||||
virtual void OnSendPacket(TmaPacket *packet) override {
|
||||
this->Complete();
|
||||
}
|
||||
};
|
||||
|
||||
class TIOFileReadTask : public TIOTask {
|
||||
private:
|
||||
static constexpr size_t HeaderSize = sizeof(Result) + sizeof(u32);
|
||||
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
|
||||
private:
|
||||
DmntFile handle = {0};
|
||||
u64 size_remaining = 0;
|
||||
u64 cur_offset = 0;
|
||||
public:
|
||||
TIOFileReadTask(TmaServiceManager *m) : TIOTask(m) { }
|
||||
virtual ~TIOFileReadTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
virtual void SendResult(Result rc) override;
|
||||
|
||||
Result ProcessPacket(TmaPacket *packet);
|
||||
};
|
||||
|
||||
class TIOFileWriteTask : public TIOTask {
|
||||
private:
|
||||
static constexpr size_t HeaderSize = sizeof(u32);
|
||||
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
|
||||
private:
|
||||
DmntFile handle = {0};
|
||||
u64 size_remaining = 0;
|
||||
u64 cur_offset = 0;
|
||||
public:
|
||||
TIOFileWriteTask(TmaServiceManager *m) : TIOTask(m) { }
|
||||
virtual ~TIOFileWriteTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void SendResult(Result rc) override;
|
||||
|
||||
Result ProcessPacket(TmaPacket *packet);
|
||||
};
|
||||
|
31
stratosphere/tma/source/test/atmosphere_test_service.cpp
Normal file
31
stratosphere/tma/source/test/atmosphere_test_service.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "atmosphere_test_service.hpp"
|
||||
#include "atmosphere_test_task.hpp"
|
||||
|
||||
TmaTask *AtmosphereTestService::NewTask(TmaPacket *packet) {
|
||||
auto new_task = new AtmosphereTestTask(this->manager);
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
new_task->SetNeedsPackets(true);
|
||||
|
||||
return new_task;
|
||||
}
|
30
stratosphere/tma/source/test/atmosphere_test_service.hpp
Normal file
30
stratosphere/tma/source/test/atmosphere_test_service.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
class AtmosphereTestService : public TmaService {
|
||||
public:
|
||||
AtmosphereTestService(TmaServiceManager *m) : TmaService(m, "AtmosphereTestService") { }
|
||||
virtual ~AtmosphereTestService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
36
stratosphere/tma/source/test/atmosphere_test_task.cpp
Normal file
36
stratosphere/tma/source/test/atmosphere_test_task.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "atmosphere_test_task.hpp"
|
||||
|
||||
void AtmosphereTestTask::OnStart(TmaPacket *packet) {
|
||||
packet->Read<u32>(this->arg);
|
||||
}
|
||||
|
||||
void AtmosphereTestTask::OnReceivePacket(TmaPacket *packet) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void AtmosphereTestTask::OnSendPacket(TmaPacket *packet) {
|
||||
for (size_t i = 0; i < this->arg && i < 0x100; i++) {
|
||||
packet->Write<u8>('A');
|
||||
}
|
||||
|
||||
this->Complete();
|
||||
}
|
33
stratosphere/tma/source/test/atmosphere_test_task.hpp
Normal file
33
stratosphere/tma/source/test/atmosphere_test_task.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
|
||||
class AtmosphereTestTask : public TmaTask {
|
||||
private:
|
||||
u32 arg;
|
||||
public:
|
||||
AtmosphereTestTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~AtmosphereTestTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
};
|
64
stratosphere/tma/source/tma_conn_connection.cpp
Normal file
64
stratosphere/tma/source/tma_conn_connection.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *TmaConnection::AllocateSendPacket() {
|
||||
return this->service_manager->AllocateSendPacket();
|
||||
}
|
||||
|
||||
TmaPacket *TmaConnection::AllocateRecvPacket() {
|
||||
return this->service_manager->AllocateRecvPacket();
|
||||
}
|
||||
|
||||
void TmaConnection::FreePacket(TmaPacket *packet) {
|
||||
this->service_manager->FreePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnReceivePacket(TmaPacket *packet) {
|
||||
this->service_manager->OnReceivePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnDisconnected() {
|
||||
if (!this->is_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (this->service_manager != nullptr) {
|
||||
this->service_manager->OnDisconnect();
|
||||
}
|
||||
|
||||
this->has_woken_up = false;
|
||||
this->OnConnectionEvent(ConnectionEvent::Disconnected);
|
||||
}
|
||||
|
||||
void TmaConnection::OnConnectionEvent(ConnectionEvent event) {
|
||||
if (this->connection_event_callback != nullptr) {
|
||||
this->connection_event_callback(this->connection_event_arg, event);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaConnection::CancelTasks() {
|
||||
this->service_manager->CancelTasks();
|
||||
}
|
||||
|
||||
void TmaConnection::Tick() {
|
||||
this->service_manager->Tick();
|
||||
}
|
90
stratosphere/tma/source/tma_conn_connection.hpp
Normal file
90
stratosphere/tma/source/tma_conn_connection.hpp
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
enum class ConnectionEvent : u32 {
|
||||
Connected,
|
||||
Disconnected
|
||||
};
|
||||
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaConnection {
|
||||
protected:
|
||||
HosMutex lock;
|
||||
void (*connection_event_callback)(void *, ConnectionEvent) = nullptr;
|
||||
void *connection_event_arg = nullptr;
|
||||
bool has_woken_up = false;
|
||||
bool is_initialized = false;
|
||||
TmaServiceManager *service_manager = nullptr;
|
||||
protected:
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
void OnDisconnected();
|
||||
void OnConnectionEvent(ConnectionEvent event);
|
||||
void CancelTasks();
|
||||
void Tick();
|
||||
public:
|
||||
/* Setup */
|
||||
TmaConnection() { }
|
||||
virtual ~TmaConnection() { }
|
||||
|
||||
void Initialize() {
|
||||
if (this->is_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
this->is_initialized = true;
|
||||
}
|
||||
|
||||
void SetConnectionEventCallback(void (*callback)(void *, ConnectionEvent), void *arg) {
|
||||
this->connection_event_callback = callback;
|
||||
this->connection_event_arg = arg;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
if (this->is_initialized) {
|
||||
this->StopListening();
|
||||
if (this->IsConnected()) {
|
||||
this->Disconnect();
|
||||
}
|
||||
this->is_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetServiceManager(TmaServiceManager *manager) { this->service_manager = manager; }
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Sleep management. */
|
||||
bool HasWokenUp() const { return this->has_woken_up; }
|
||||
void SetWokenUp(bool woke) { this->has_woken_up = woke; }
|
||||
|
||||
/* For sub-interfaces to implement, connection management. */
|
||||
virtual void StartListening() { }
|
||||
virtual void StopListening() { }
|
||||
virtual bool IsConnected() = 0;
|
||||
virtual TmaConnResult Disconnect() = 0;
|
||||
virtual TmaConnResult SendPacket(TmaPacket *packet) = 0;
|
||||
};
|
279
stratosphere/tma/source/tma_conn_packet.hpp
Normal file
279
stratosphere/tma/source/tma_conn_packet.hpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "crc.h"
|
||||
|
||||
class TmaPacket {
|
||||
public:
|
||||
struct Header {
|
||||
u32 service_id;
|
||||
u32 task_id;
|
||||
u16 command;
|
||||
u8 is_continuation;
|
||||
u8 version;
|
||||
u32 body_len;
|
||||
u32 reserved[4]; /* This is where N's header ends. */
|
||||
u32 body_checksum;
|
||||
u32 header_checksum;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x28, "Packet::Header definition!");
|
||||
|
||||
static constexpr u32 MaxBodySize = 0xE000;
|
||||
static constexpr u32 MaxPacketSize = MaxBodySize + sizeof(Header);
|
||||
|
||||
private:
|
||||
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(MaxPacketSize);
|
||||
u32 offset = 0;
|
||||
HosMessageQueue *free_queue = nullptr;
|
||||
|
||||
Header *GetHeader() const {
|
||||
return reinterpret_cast<Header *>(buffer.get());
|
||||
}
|
||||
|
||||
u8 *GetBody(u32 ofs) const {
|
||||
return reinterpret_cast<u8 *>(buffer.get() + sizeof(Header) + ofs);
|
||||
}
|
||||
public:
|
||||
TmaPacket() {
|
||||
memset(buffer.get(), 0, MaxPacketSize);
|
||||
}
|
||||
|
||||
/* Implicit ~TmaPacket() */
|
||||
|
||||
/* These allow reading a packet in. */
|
||||
void CopyHeaderFrom(Header *hdr) {
|
||||
*GetHeader() = *hdr;
|
||||
}
|
||||
|
||||
TmaConnResult CopyBodyFrom(void *body, size_t size) {
|
||||
if (size >= MaxBodySize) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(GetBody(0), body, size);
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
void CopyHeaderTo(void *out) {
|
||||
memcpy(out, buffer.get(), sizeof(Header));
|
||||
}
|
||||
|
||||
void CopyBodyTo(void *out) const {
|
||||
memcpy(out, buffer.get() + sizeof(Header), GetBodyLength());
|
||||
}
|
||||
|
||||
bool IsHeaderValid() {
|
||||
Header *hdr = GetHeader();
|
||||
return crc32_arm64_le_hw(reinterpret_cast<const u8 *>(hdr), sizeof(*hdr) - sizeof(hdr->header_checksum)) == hdr->header_checksum;
|
||||
}
|
||||
|
||||
bool IsBodyValid() const {
|
||||
const u32 body_len = GetHeader()->body_len;
|
||||
if (body_len == 0) {
|
||||
return GetHeader()->body_checksum == 0;
|
||||
} else {
|
||||
return crc32_arm64_le_hw(GetBody(0), body_len) == GetHeader()->body_checksum;
|
||||
}
|
||||
}
|
||||
|
||||
HosMessageQueue *GetFreeQueue() const {
|
||||
return this->free_queue;
|
||||
}
|
||||
|
||||
void SetFreeQueue(HosMessageQueue *queue) {
|
||||
this->free_queue = queue;
|
||||
}
|
||||
|
||||
void SetChecksums() {
|
||||
Header *hdr = GetHeader();
|
||||
if (hdr->body_len) {
|
||||
hdr->body_checksum = crc32_arm64_le_hw(GetBody(0), hdr->body_len);
|
||||
} else {
|
||||
hdr->body_checksum = 0;
|
||||
}
|
||||
hdr->header_checksum = crc32_arm64_le_hw(reinterpret_cast<const u8 *>(hdr), sizeof(*hdr) - sizeof(hdr->header_checksum));
|
||||
}
|
||||
|
||||
u32 GetBodyLength() const {
|
||||
return GetHeader()->body_len;
|
||||
}
|
||||
|
||||
u32 GetLength() const {
|
||||
return GetBodyLength() + sizeof(Header);
|
||||
}
|
||||
|
||||
u32 GetBodyAvailableLength() const {
|
||||
return MaxPacketSize - this->offset;
|
||||
}
|
||||
|
||||
void SetServiceId(TmaServiceId srv) {
|
||||
GetHeader()->service_id = static_cast<u32>(srv);
|
||||
}
|
||||
|
||||
TmaServiceId GetServiceId() const {
|
||||
return static_cast<TmaServiceId>(GetHeader()->service_id);
|
||||
}
|
||||
|
||||
void SetTaskId(u32 id) {
|
||||
GetHeader()->task_id = id;
|
||||
}
|
||||
|
||||
u32 GetTaskId() const {
|
||||
return GetHeader()->task_id;
|
||||
}
|
||||
|
||||
void SetCommand(u16 cmd) {
|
||||
GetHeader()->command = cmd;
|
||||
}
|
||||
|
||||
u16 GetCommand() const {
|
||||
return GetHeader()->command;
|
||||
}
|
||||
|
||||
void SetContinuation(bool c) {
|
||||
GetHeader()->is_continuation = c ? 1 : 0;
|
||||
}
|
||||
|
||||
bool GetContinuation() const {
|
||||
return GetHeader()->is_continuation == 1;
|
||||
}
|
||||
|
||||
void SetVersion(u8 v) {
|
||||
GetHeader()->version = v;
|
||||
}
|
||||
|
||||
u8 GetVersion() const {
|
||||
return GetHeader()->version;
|
||||
}
|
||||
|
||||
u8 *GetCurrentBodyPtr() {
|
||||
return GetBody(this->offset);
|
||||
}
|
||||
|
||||
void ClearOffset() {
|
||||
this->offset = 0;
|
||||
}
|
||||
|
||||
void SetBodyLength() {
|
||||
GetHeader()->body_len = this->offset;
|
||||
}
|
||||
|
||||
TmaConnResult Write(const void *data, size_t size) {
|
||||
if (size > GetBodyAvailableLength()) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(GetBody(this->offset), data, size);
|
||||
this->offset += size;
|
||||
GetHeader()->body_len = this->offset;
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
TmaConnResult Read(void *data, size_t size) {
|
||||
if (size > GetBodyAvailableLength()) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(data, GetBody(this->offset), size);
|
||||
this->offset += size;
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TmaConnResult Write(const T &t) {
|
||||
return Write(&t, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TmaConnResult Read(T &t) {
|
||||
return Read(&t, sizeof(T));
|
||||
}
|
||||
|
||||
TmaConnResult WriteString(const char *s) {
|
||||
return Write(s, strlen(s) + 1);
|
||||
}
|
||||
|
||||
size_t WriteFormat(const char *format, ...) {
|
||||
va_list va_arg;
|
||||
va_start(va_arg, format);
|
||||
const size_t available = GetBodyAvailableLength();
|
||||
const int written = vsnprintf(reinterpret_cast<char *>(GetBody(this->offset)), available, format, va_arg);
|
||||
|
||||
size_t total_written;
|
||||
if (static_cast<size_t>(written) < available) {
|
||||
this->offset += written;
|
||||
*GetBody(this->offset++) = 0;
|
||||
total_written = written + 1;
|
||||
} else {
|
||||
this->offset += available;
|
||||
total_written = available;
|
||||
}
|
||||
|
||||
GetHeader()->body_len = this->offset;
|
||||
return total_written;
|
||||
}
|
||||
|
||||
TmaConnResult ReadString(char *buf, size_t buf_size, size_t *out_size) {
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
size_t available = GetBodyAvailableLength();
|
||||
size_t ofs = 0;
|
||||
while (ofs < buf_size) {
|
||||
if (ofs >= available) {
|
||||
res = TmaConnResult::PacketOverflow;
|
||||
break;
|
||||
}
|
||||
if (ofs == buf_size) {
|
||||
res = TmaConnResult::BufferOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
buf[ofs] = static_cast<char>(*GetBody(this->offset++));
|
||||
|
||||
if (buf[ofs++] == '\x00') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish reading the string if the user buffer is too small. */
|
||||
if (res == TmaConnResult::BufferOverflow) {
|
||||
u8 cur = *GetBody(this->offset);
|
||||
while (cur != 0) {
|
||||
if (ofs >= available) {
|
||||
res = TmaConnResult::PacketOverflow;
|
||||
break;
|
||||
}
|
||||
cur = *GetBody(this->offset++);
|
||||
ofs++;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_size != nullptr) {
|
||||
*out_size = ofs;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
40
stratosphere/tma/source/tma_conn_result.hpp
Normal file
40
stratosphere/tma/source/tma_conn_result.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
enum class TmaConnResult : u32 {
|
||||
Success = 0,
|
||||
NotImplemented,
|
||||
GeneralFailure,
|
||||
ConnectionFailure,
|
||||
AlreadyConnected,
|
||||
WrongConnectionVersion,
|
||||
PacketOverflow,
|
||||
BufferOverflow,
|
||||
Disconnected,
|
||||
ServiceAlreadyRegistered,
|
||||
ServiceUnknown,
|
||||
Timeout,
|
||||
NotInitialized,
|
||||
};
|
53
stratosphere/tma/source/tma_conn_service_ids.hpp
Normal file
53
stratosphere/tma/source/tma_conn_service_ids.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
|
||||
/* This is just python's hash function, but official TMA code uses it. */
|
||||
static constexpr u32 HashServiceName(const char *name) {
|
||||
u32 h = *name;
|
||||
u32 len = 0;
|
||||
|
||||
while (*name) {
|
||||
h = (1000003 * h) ^ *name;
|
||||
name++;
|
||||
len++;
|
||||
}
|
||||
|
||||
return h ^ len;
|
||||
}
|
||||
|
||||
enum class TmaServiceId : u32 {
|
||||
Invalid = 0,
|
||||
|
||||
/* Special nodes, for facilitating connection over USB. */
|
||||
UsbQueryTarget = HashServiceName("USBQueryTarget"),
|
||||
UsbSendHostInfo = HashServiceName("USBSendHostInfo"),
|
||||
UsbConnect = HashServiceName("USBConnect"),
|
||||
UsbDisconnect = HashServiceName("USBDisconnect"),
|
||||
|
||||
|
||||
TestService = HashServiceName("AtmosphereTestService"), /* Temporary service, will be used to debug communications. */
|
||||
};
|
||||
|
||||
static constexpr bool IsMetaService(TmaServiceId id) {
|
||||
return id == TmaServiceId::UsbQueryTarget ||
|
||||
id == TmaServiceId::UsbSendHostInfo ||
|
||||
id == TmaServiceId::UsbConnect ||
|
||||
id == TmaServiceId::UsbDisconnect;
|
||||
}
|
204
stratosphere/tma/source/tma_conn_usb_connection.cpp
Normal file
204
stratosphere/tma/source/tma_conn_usb_connection.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_conn_usb_connection.hpp"
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
static HosThread g_SendThread, g_RecvThread;
|
||||
|
||||
TmaConnResult TmaUsbConnection::InitializeComms() {
|
||||
return TmaUsbComms::Initialize();
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::FinalizeComms() {
|
||||
return TmaUsbComms::Finalize();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::ClearSendQueue() {
|
||||
uintptr_t _packet;
|
||||
while (this->send_queue.TryReceive(&_packet)) {
|
||||
TmaPacket *packet = reinterpret_cast<TmaPacket *>(_packet);
|
||||
if (packet != nullptr) {
|
||||
this->FreePacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaUsbConnection::SendThreadFunc(void *arg) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
while (res == TmaConnResult::Success) {
|
||||
/* Receive a packet from the send queue. */
|
||||
{
|
||||
uintptr_t _packet;
|
||||
this_ptr->send_queue.Receive(&_packet);
|
||||
packet = reinterpret_cast<TmaPacket *>(_packet);
|
||||
}
|
||||
|
||||
if (packet != nullptr) {
|
||||
/* Send the packet if we're connected. */
|
||||
if (this_ptr->IsConnected()) {
|
||||
res = TmaUsbComms::SendPacket(packet);
|
||||
}
|
||||
|
||||
this_ptr->FreePacket(packet);
|
||||
this_ptr->Tick();
|
||||
} else {
|
||||
res = TmaConnResult::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->OnDisconnected();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::RecvThreadFunc(void *arg) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
this_ptr->SetConnected(true);
|
||||
|
||||
while (res == TmaConnResult::Success) {
|
||||
TmaPacket *packet = this_ptr->AllocateRecvPacket();
|
||||
if (packet == nullptr) { std::abort(); }
|
||||
|
||||
res = TmaUsbComms::ReceivePacket(packet);
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (!IsMetaService(packet->GetServiceId())) {
|
||||
this_ptr->OnReceivePacket(packet);
|
||||
} else {
|
||||
switch (packet->GetServiceId()) {
|
||||
case TmaServiceId::UsbQueryTarget: {
|
||||
this_ptr->SetConnected(false);
|
||||
|
||||
res = this_ptr->SendQueryReply(packet);
|
||||
|
||||
if (!this_ptr->has_woken_up) {
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbSendHostInfo: {
|
||||
struct {
|
||||
u32 version;
|
||||
u32 sleeping;
|
||||
} host_info;
|
||||
packet->Read<decltype(host_info)>(host_info);
|
||||
|
||||
if (!this_ptr->has_woken_up || !host_info.sleeping) {
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbConnect: {
|
||||
res = this_ptr->SendQueryReply(packet);
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
this_ptr->SetConnected(true);
|
||||
this_ptr->OnConnectionEvent(ConnectionEvent::Connected);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbDisconnect: {
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->OnDisconnected();
|
||||
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this_ptr->FreePacket(packet);
|
||||
}
|
||||
} else {
|
||||
this_ptr->FreePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->send_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
|
||||
}
|
||||
|
||||
void TmaUsbConnection::OnUsbStateChange(void *arg, u32 state) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
switch (state) {
|
||||
case 0:
|
||||
case 6:
|
||||
this_ptr->StopThreads();
|
||||
break;
|
||||
case 5:
|
||||
this_ptr->StartThreads();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaUsbConnection::StartThreads() {
|
||||
g_SendThread.Join();
|
||||
g_RecvThread.Join();
|
||||
|
||||
g_SendThread.Initialize(&TmaUsbConnection::SendThreadFunc, this, 0x4000, 38);
|
||||
g_RecvThread.Initialize(&TmaUsbConnection::RecvThreadFunc, this, 0x4000, 38);
|
||||
|
||||
this->ClearSendQueue();
|
||||
g_SendThread.Start();
|
||||
g_RecvThread.Start();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::StopThreads() {
|
||||
TmaUsbComms::CancelComms();
|
||||
g_SendThread.Join();
|
||||
g_RecvThread.Join();
|
||||
}
|
||||
|
||||
bool TmaUsbConnection::IsConnected() {
|
||||
return this->is_connected;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::Disconnect() {
|
||||
TmaUsbComms::SetStateChangeCallback(nullptr, nullptr);
|
||||
|
||||
this->StopThreads();
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::SendPacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
if (this->IsConnected()) {
|
||||
this->send_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
return TmaConnResult::Success;
|
||||
} else {
|
||||
this->FreePacket(packet);
|
||||
this->Tick();
|
||||
return TmaConnResult::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::SendQueryReply(TmaPacket *packet) {
|
||||
packet->ClearOffset();
|
||||
struct {
|
||||
u32 version;
|
||||
} target_info;
|
||||
target_info.version = 0;
|
||||
packet->Write<decltype(target_info)>(target_info);
|
||||
return TmaUsbComms::SendPacket(packet);
|
||||
}
|
52
stratosphere/tma/source/tma_conn_usb_connection.hpp
Normal file
52
stratosphere/tma/source/tma_conn_usb_connection.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
class TmaUsbConnection : public TmaConnection {
|
||||
private:
|
||||
HosMessageQueue send_queue = HosMessageQueue(64);
|
||||
std::atomic<bool> is_connected = false;
|
||||
private:
|
||||
static void SendThreadFunc(void *arg);
|
||||
static void RecvThreadFunc(void *arg);
|
||||
static void OnUsbStateChange(void *this_ptr, u32 state);
|
||||
TmaConnResult SendQueryReply(TmaPacket *packet);
|
||||
void ClearSendQueue();
|
||||
void StartThreads();
|
||||
void StopThreads();
|
||||
void SetConnected(bool c) { this->is_connected = c; }
|
||||
public:
|
||||
static TmaConnResult InitializeComms();
|
||||
static TmaConnResult FinalizeComms();
|
||||
|
||||
TmaUsbConnection() {
|
||||
TmaUsbComms::SetStateChangeCallback(&TmaUsbConnection::OnUsbStateChange, this);
|
||||
}
|
||||
|
||||
virtual ~TmaUsbConnection() {
|
||||
this->Disconnect();
|
||||
}
|
||||
|
||||
virtual bool IsConnected() override;
|
||||
virtual TmaConnResult Disconnect() override;
|
||||
virtual TmaConnResult SendPacket(TmaPacket *packet) override;
|
||||
};
|
106
stratosphere/tma/source/tma_main.cpp
Normal file
106
stratosphere/tma/source/tma_main.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_target.hpp"
|
||||
|
||||
#include "dmnt.h"
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x400000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void);
|
||||
void __appInit(void);
|
||||
void __appExit(void);
|
||||
}
|
||||
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
void* addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib */
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
Result rc;
|
||||
|
||||
rc = smInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
rc = pscInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
rc = setsysInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
rc = dmntInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
|
||||
}
|
||||
|
||||
void __appExit(void) {
|
||||
/* Cleanup services. */
|
||||
dmntExit();
|
||||
setsysExit();
|
||||
pscExit();
|
||||
smExit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
consoleDebugInit(debugDevice_SVC);
|
||||
|
||||
/* This will initialize the target. */
|
||||
TmaTarget::Initialize();
|
||||
|
||||
while (true) {
|
||||
svcSleepThread(10000000UL);
|
||||
}
|
||||
|
||||
TmaTarget::Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
68
stratosphere/tma/source/tma_power_manager.cpp
Normal file
68
stratosphere/tma/source/tma_power_manager.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_power_manager.hpp"
|
||||
|
||||
static constexpr u16 PscPmModuleId_Usb = 0x04;
|
||||
static constexpr u16 PscPmModuleId_Pcie = 0x13;
|
||||
static constexpr u16 PscPmModuleId_Tma = 0x1E;
|
||||
|
||||
static const u16 g_tma_pm_dependencies[] = {
|
||||
PscPmModuleId_Usb,
|
||||
};
|
||||
|
||||
static void (*g_pm_callback)(PscPmState, u32) = nullptr;
|
||||
static HosThread g_pm_thread;
|
||||
|
||||
static void PowerManagerThread(void *arg) {
|
||||
/* Setup psc module. */
|
||||
Result rc;
|
||||
PscPmModule tma_module = {0};
|
||||
if (R_FAILED((rc = pscGetPmModule(&tma_module, PscPmModuleId_Tma, g_tma_pm_dependencies, sizeof(g_tma_pm_dependencies)/sizeof(u16), true)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
/* For now, just do what dummy tma does -- loop forever, acknowledging everything. */
|
||||
while (true) {
|
||||
if (R_FAILED((rc = eventWait(&tma_module.event, U64_MAX)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
PscPmState state;
|
||||
u32 flags;
|
||||
if (R_FAILED((rc = pscPmModuleGetRequest(&tma_module, &state, &flags)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
g_pm_callback(state, flags);
|
||||
|
||||
if (R_FAILED((rc = pscPmModuleAcknowledge(&tma_module, state)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaPowerManager::Initialize(void (*callback)(PscPmState, u32)) {
|
||||
g_pm_callback = callback;
|
||||
g_pm_thread.Initialize(PowerManagerThread, nullptr, 0x4000, 0x26);
|
||||
g_pm_thread.Start();
|
||||
}
|
||||
|
||||
void TmaPowerManager::Finalize() {
|
||||
/* TODO */
|
||||
}
|
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class TmaPowerManager {
|
||||
public:
|
||||
static void Initialize(void (*callback)(PscPmState, u32));
|
||||
static void Finalize();
|
||||
};
|
32
stratosphere/tma/source/tma_service.cpp
Normal file
32
stratosphere/tma/source/tma_service.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
u32 TmaService::GetNextTaskId() {
|
||||
return this->manager->GetNextTaskId();
|
||||
}
|
||||
|
||||
void TmaService::OnSleep() {
|
||||
/* Default service does nothing here. */
|
||||
}
|
||||
|
||||
void TmaService::OnWake() {
|
||||
/* Default service does nothing here. */
|
||||
}
|
43
stratosphere/tma/source/tma_service.hpp
Normal file
43
stratosphere/tma/source/tma_service.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
#include "tma_task.hpp"
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaService {
|
||||
protected:
|
||||
TmaServiceManager *manager;
|
||||
const char *service_name;
|
||||
const TmaServiceId id;
|
||||
protected:
|
||||
u32 GetNextTaskId();
|
||||
public:
|
||||
TmaService(TmaServiceManager *m, const char *n) : manager(m), service_name(n), id(static_cast<TmaServiceId>(HashServiceName(this->service_name))) { }
|
||||
virtual ~TmaService() { }
|
||||
|
||||
TmaServiceId GetServiceId() const { return this->id; }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) = 0;
|
||||
virtual void OnSleep();
|
||||
virtual void OnWake();
|
||||
};
|
406
stratosphere/tma/source/tma_service_manager.cpp
Normal file
406
stratosphere/tma/source/tma_service_manager.cpp
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
TmaServiceManager::TmaServiceManager() {
|
||||
/* Set up queues */
|
||||
for (size_t i = 0; i < TmaServiceManager::PacketQueueDepth; i++) {
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_send_packet_queue);
|
||||
this->free_send_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_recv_packet_queue);
|
||||
this->free_recv_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < TmaServiceManager::WorkQueueDepth; i++) {
|
||||
this->free_work_queue.Send(reinterpret_cast<uintptr_t>(new TmaWorkItem()));
|
||||
}
|
||||
}
|
||||
|
||||
TmaServiceManager::~TmaServiceManager() {
|
||||
/* Destroy queues. */
|
||||
TmaPacket *packet = nullptr;
|
||||
while (this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
while (this->free_recv_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
while (this->free_work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
while (this->work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Initialize() {
|
||||
this->initialized = true;
|
||||
this->work_thread.Initialize(TmaServiceManager::WorkThread, this, 0x4000, 0x26);
|
||||
this->work_thread.Start();
|
||||
}
|
||||
|
||||
void TmaServiceManager::Finalize() {
|
||||
if (this->initialized) {
|
||||
this->initialized = false;
|
||||
if (this->connection && this->connection->IsConnected()) {
|
||||
this->connection->Disconnect();
|
||||
}
|
||||
|
||||
/* Signal to work thread to end. */
|
||||
this->work_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
|
||||
this->work_thread.Join();
|
||||
|
||||
/* TODO: N tells services that they have no manager here. Do we want to do that? */
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this->free_work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
work_item->task = task;
|
||||
work_item->packet = packet;
|
||||
work_item->work_type = type;
|
||||
this->work_queue.Send(reinterpret_cast<uintptr_t>(work_item));
|
||||
}
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult TmaServiceManager::SendPacket(TmaPacket *packet) {
|
||||
TmaConnResult res = TmaConnResult::Disconnected;
|
||||
|
||||
if (this->connection != nullptr) {
|
||||
res = this->connection->SendPacket(packet);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void TmaServiceManager::OnReceivePacket(TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::ReceivePacket, nullptr, packet);
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateSendPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_send_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
packet->SetBodyLength();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateRecvPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_recv_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreePacket(TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (packet != nullptr) {
|
||||
packet->GetFreeQueue()->Send(reinterpret_cast<uintptr_t>(packet));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *TmaServiceManager::GetServiceById(TmaServiceId id) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (auto srv : this->services) {
|
||||
if (srv->GetServiceId() == id) {
|
||||
return srv;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddService(TmaService *service) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
this->services.push_back(service);
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddTask(TmaTask *task, TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::NewTask, task, packet);
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreeTask(TmaTask *task) {
|
||||
this->AddWork(TmaWorkType::FreeTask, task, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTask(u32 task_id) {
|
||||
if (this->initialized) {
|
||||
this->task_list.Cancel(task_id);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTasks() {
|
||||
if (this->initialized) {
|
||||
this->task_list.CancelAll();
|
||||
}
|
||||
}
|
||||
|
||||
u32 TmaServiceManager::GetNextTaskId() {
|
||||
while (true) {
|
||||
u32 id;
|
||||
{
|
||||
/* N only uses 16 bits for the task id. We'll use 24. */
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
id = (this->next_task_id++) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
if (this->task_list.IsIdFree(id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connection management. */
|
||||
void TmaServiceManager::Tick() {
|
||||
this->AddWork(TmaWorkType::Tick, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::SetConnection(TmaConnection *conn) {
|
||||
this->connection = conn;
|
||||
}
|
||||
|
||||
void TmaServiceManager::OnDisconnect() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->disconnect_signal.Reset();
|
||||
|
||||
this->AddWork(TmaWorkType::Disconnect, nullptr, nullptr);
|
||||
|
||||
/* TODO: why does N wait with a timeout of zero here? */
|
||||
this->disconnect_signal.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Sleep() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->wake_signal.Reset();
|
||||
this->sleep_signal.Reset();
|
||||
|
||||
/* Tell the work thread to stall, wait for ACK. */
|
||||
this->AddWork(TmaWorkType::Sleep, nullptr, nullptr);
|
||||
this->sleep_signal.Wait();
|
||||
|
||||
this->SetAsleep(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Wake(TmaConnection *conn) {
|
||||
if (this->connection != nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
if (this->GetAsleep()) {
|
||||
this->connection = conn;
|
||||
this->connection->SetWokenUp(true);
|
||||
this->connection->SetServiceManager(this);
|
||||
/* Tell the work thread to resume. */
|
||||
this->wake_signal.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool TmaServiceManager::GetConnected() const {
|
||||
return this->connection != nullptr && this->connection->IsConnected();
|
||||
}
|
||||
|
||||
/* Work thread. */
|
||||
void TmaServiceManager::WorkThread(void *_this) {
|
||||
TmaServiceManager *this_ptr = reinterpret_cast<TmaServiceManager *>(_this);
|
||||
if (!this_ptr->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
/* Receive a work item. */
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this_ptr->work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
if (work_item == nullptr) {
|
||||
/* We're done. */
|
||||
this_ptr->task_list.CancelAll();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (work_item->work_type) {
|
||||
case TmaWorkType::Tick:
|
||||
/* HandleTickWork called unconditionally. */
|
||||
break;
|
||||
case TmaWorkType::NewTask:
|
||||
this_ptr->HandleNewTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::FreeTask:
|
||||
this_ptr->HandleFreeTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::ReceivePacket:
|
||||
this_ptr->HandleReceivePacketWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::Disconnect:
|
||||
this_ptr->HandleDisconnectWork();
|
||||
break;
|
||||
case TmaWorkType::Sleep:
|
||||
this_ptr->HandleSleepWork();
|
||||
break;
|
||||
case TmaWorkType::None:
|
||||
default:
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
|
||||
this_ptr->free_work_queue.Send(reinterpret_cast<uintptr_t>(work_item));
|
||||
this_ptr->HandleTickWork();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleNewTaskWork(TmaWorkItem *work_item) {
|
||||
this->task_list.Add(work_item->task);
|
||||
if (this->GetConnected()) {
|
||||
if (work_item->packet != nullptr) {
|
||||
this->SendPacket(work_item->packet);
|
||||
}
|
||||
} else {
|
||||
work_item->task->Cancel();
|
||||
this->FreePacket(work_item->packet);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleFreeTaskWork(TmaWorkItem *work_item) {
|
||||
delete work_item->task;
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleReceivePacketWork(TmaWorkItem *work_item) {
|
||||
ON_SCOPE_EXIT { this->FreePacket(work_item->packet); };
|
||||
|
||||
/* Handle continuation packets. */
|
||||
if (work_item->packet->GetContinuation()) {
|
||||
this->task_list.ReceivePacket(work_item->packet);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a new task for the packet. */
|
||||
TmaService *srv = this->GetServiceById(work_item->packet->GetServiceId());
|
||||
if (srv != nullptr) {
|
||||
TmaTask *task = srv->NewTask(work_item->packet);
|
||||
if (task != nullptr) {
|
||||
this->task_list.Add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleTickWork() {
|
||||
if (this->connection == nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* N does this kind of manual cleanup if send isn't called. */
|
||||
/* It's pretty gross, but in lieu of a better idea... */
|
||||
bool needs_manual_cleanup = true;
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
while (this->connection != nullptr && this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
needs_manual_cleanup = false;
|
||||
|
||||
if (this->task_list.SendPacket(this->GetConnected(), packet)) {
|
||||
if (this->SendPacket(packet) != TmaConnResult::Success) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->FreePacket(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_manual_cleanup) {
|
||||
this->task_list.CleanupDoneTasks();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleDisconnectWork() {
|
||||
this->task_list.CancelAll();
|
||||
this->disconnect_signal.Signal();
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleSleepWork() {
|
||||
/* Put the task list to sleep. */
|
||||
this->task_list.Sleep();
|
||||
|
||||
/* Put services to sleep. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnSleep();
|
||||
}
|
||||
|
||||
/* Signal to main thread that we're sleeping. */
|
||||
this->sleep_signal.Signal();
|
||||
/* Wait for us to wake up. */
|
||||
this->wake_signal.Wait();
|
||||
|
||||
/* We're awake now... */
|
||||
this->SetAsleep(false);
|
||||
|
||||
/* Wake up services. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnWake();
|
||||
}
|
||||
|
||||
/* Wake up the task list. */
|
||||
this->task_list.Wake();
|
||||
}
|
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_task_list.hpp"
|
||||
#include "tma_conn_connection.hpp"
|
||||
|
||||
enum class TmaWorkType : u32 {
|
||||
None,
|
||||
NewTask,
|
||||
FreeTask,
|
||||
ReceivePacket,
|
||||
Tick,
|
||||
Disconnect,
|
||||
Sleep,
|
||||
};
|
||||
|
||||
struct TmaWorkItem {
|
||||
TmaTask *task;
|
||||
TmaPacket *packet;
|
||||
TmaWorkType work_type;
|
||||
};
|
||||
|
||||
class TmaServiceManager {
|
||||
public:
|
||||
static constexpr size_t PacketQueueDepth = 0x8;
|
||||
static constexpr size_t WorkQueueDepth = 0x80;
|
||||
private:
|
||||
HosMutex lock;
|
||||
bool initialized = false;
|
||||
TmaTaskList task_list;
|
||||
HosThread work_thread;
|
||||
std::vector<TmaService *> services;
|
||||
TmaConnection *connection = nullptr;
|
||||
u32 next_task_id = 0;
|
||||
|
||||
/* Work queues. */
|
||||
HosMessageQueue free_send_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue free_recv_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
HosMessageQueue free_work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
|
||||
/* Sleep management. */
|
||||
HosSignal disconnect_signal;
|
||||
HosSignal wake_signal;
|
||||
HosSignal sleep_signal;
|
||||
bool asleep = false;
|
||||
private:
|
||||
static void WorkThread(void *arg);
|
||||
void AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet);
|
||||
void HandleNewTaskWork(TmaWorkItem *work_item);
|
||||
void HandleFreeTaskWork(TmaWorkItem *work_item);
|
||||
void HandleReceivePacketWork(TmaWorkItem *work_item);
|
||||
void HandleTickWork();
|
||||
void HandleDisconnectWork();
|
||||
void HandleSleepWork();
|
||||
|
||||
void SetAsleep(bool s) { this->asleep = s; }
|
||||
public:
|
||||
TmaServiceManager();
|
||||
virtual ~TmaServiceManager();
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult SendPacket(TmaPacket *packet);
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *GetServiceById(TmaServiceId id);
|
||||
void AddService(TmaService *service);
|
||||
void AddTask(TmaTask *task, TmaPacket *packet);
|
||||
void FreeTask(TmaTask *task);
|
||||
void CancelTask(u32 task_id);
|
||||
void CancelTasks();
|
||||
u32 GetNextTaskId();
|
||||
|
||||
/* Connection management. */
|
||||
void Tick();
|
||||
void SetConnection(TmaConnection *conn);
|
||||
void OnDisconnect();
|
||||
void Sleep();
|
||||
void Wake(TmaConnection *conn);
|
||||
bool GetAsleep() const { return this->asleep; }
|
||||
bool GetConnected() const;
|
||||
};
|
234
stratosphere/tma/source/tma_target.cpp
Normal file
234
stratosphere/tma/source/tma_target.cpp
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_conn_usb_connection.hpp"
|
||||
|
||||
#include "tma_service_manager.hpp"
|
||||
#include "tma_power_manager.hpp"
|
||||
|
||||
#include "tma_target.hpp"
|
||||
|
||||
#include "test/atmosphere_test_service.hpp"
|
||||
#include "settings/settings_service.hpp"
|
||||
#include "target_io/tio_service.hpp"
|
||||
|
||||
struct TmaTargetConfig {
|
||||
char configuration_id1[0x80];
|
||||
char serial_number[0x80];
|
||||
};
|
||||
|
||||
static TmaConnection *g_active_connection = nullptr;
|
||||
static TmaServiceManager *g_service_manager = nullptr;
|
||||
static HosMutex g_connection_event_mutex;
|
||||
static bool g_has_woken_up = false;
|
||||
static bool g_connected_before_sleep = false;
|
||||
static bool g_signal_on_disconnect = false;
|
||||
|
||||
static TmaUsbConnection *g_usb_connection = nullptr;
|
||||
|
||||
static TmaTargetConfig g_target_config = {
|
||||
"Unknown",
|
||||
"SerialNumber",
|
||||
};
|
||||
|
||||
static void RefreshTargetConfig() {
|
||||
setsysInitialize();
|
||||
|
||||
/* TODO: setsysGetConfigurationId1(&g_target_config.configuration_id1); */
|
||||
|
||||
g_target_config.serial_number[0] = 0;
|
||||
setsysGetSerialNumber(g_target_config.serial_number);
|
||||
|
||||
setsysExit();
|
||||
}
|
||||
|
||||
static void InitializeServices() {
|
||||
g_service_manager->Initialize();
|
||||
}
|
||||
|
||||
static void FinalizeServices() {
|
||||
g_service_manager->Finalize();
|
||||
}
|
||||
|
||||
static void SetActiveConnection(TmaConnection *connection) {
|
||||
if (g_active_connection != connection) {
|
||||
if (g_active_connection != nullptr) {
|
||||
FinalizeServices();
|
||||
g_service_manager->SetConnection(nullptr);
|
||||
g_active_connection->Disconnect();
|
||||
g_active_connection = nullptr;
|
||||
}
|
||||
|
||||
if (connection != nullptr) {
|
||||
g_active_connection = connection;
|
||||
InitializeServices();
|
||||
g_service_manager->SetConnection(g_active_connection);
|
||||
g_active_connection->SetServiceManager(g_service_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnConnectionEvent(void *arg, ConnectionEvent evt) {
|
||||
std::scoped_lock<HosMutex> lk(g_connection_event_mutex);
|
||||
|
||||
switch (evt) {
|
||||
case ConnectionEvent::Connected:
|
||||
{
|
||||
bool has_active_connection = false;
|
||||
g_has_woken_up = false;
|
||||
|
||||
if (arg == g_usb_connection) {
|
||||
SetActiveConnection(g_usb_connection);
|
||||
has_active_connection = true;
|
||||
}
|
||||
|
||||
if (has_active_connection) {
|
||||
/* TODO: Signal connected */
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ConnectionEvent::Disconnected:
|
||||
if (g_signal_on_disconnect) {
|
||||
/* TODO: Signal disconnected */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void Wake() {
|
||||
if (g_service_manager->GetAsleep()) {
|
||||
g_has_woken_up = true;
|
||||
|
||||
/* N checks what kind of connection to use here. For now, we only use USB. */
|
||||
TmaUsbConnection::InitializeComms();
|
||||
g_usb_connection = new TmaUsbConnection();
|
||||
g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection);
|
||||
g_usb_connection->Initialize();
|
||||
g_active_connection = g_usb_connection;
|
||||
|
||||
g_service_manager->Wake(g_active_connection);
|
||||
}
|
||||
}
|
||||
|
||||
static void Sleep() {
|
||||
if (!g_service_manager->GetAsleep()) {
|
||||
if (g_active_connection->IsConnected()) {
|
||||
g_connected_before_sleep = true;
|
||||
|
||||
/* TODO: Send a packet saying we're going to sleep. */
|
||||
} else {
|
||||
g_connected_before_sleep = false;
|
||||
}
|
||||
|
||||
g_service_manager->Sleep();
|
||||
g_service_manager->SetConnection(nullptr);
|
||||
g_active_connection->Disconnect();
|
||||
g_active_connection = nullptr;
|
||||
g_service_manager->CancelTasks();
|
||||
|
||||
if (g_usb_connection != nullptr) {
|
||||
g_usb_connection->Finalize();
|
||||
delete g_usb_connection;
|
||||
g_usb_connection = nullptr;
|
||||
TmaUsbConnection::FinalizeComms();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPowerManagementEvent(PscPmState state, u32 flags) {
|
||||
switch (state) {
|
||||
case PscPmState_Awake:
|
||||
{
|
||||
Wake();
|
||||
}
|
||||
break;
|
||||
case PscPmState_ReadyAwaken:
|
||||
{
|
||||
if (g_service_manager->GetAsleep()) {
|
||||
Wake();
|
||||
if (g_connected_before_sleep)
|
||||
{
|
||||
/* Try to restore a connection. */
|
||||
bool connected = g_service_manager->GetConnected();
|
||||
|
||||
/* N uses a seven-second timeout, here. */
|
||||
TimeoutHelper timeout_helper(7000000000ULL);
|
||||
while (!connected && !timeout_helper.TimedOut()) {
|
||||
connected = g_service_manager->GetConnected();
|
||||
if (!connected) {
|
||||
/* Sleep for 1ms. */
|
||||
svcSleepThread(1000000ULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
/* TODO: Signal disconnected */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PscPmState_ReadySleep:
|
||||
{
|
||||
Sleep();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Don't handle ReadySleepCritical/ReadyAwakenCritical/ReadyShutdown */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTarget::Initialize() {
|
||||
/* Get current thread priority. */
|
||||
u32 cur_prio;
|
||||
if (R_FAILED(svcGetThreadPriority(&cur_prio, CUR_THREAD_HANDLE))) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
g_active_connection = nullptr;
|
||||
g_service_manager = new TmaServiceManager();
|
||||
/* TODO: Make this better. */
|
||||
g_service_manager->AddService(new AtmosphereTestService(g_service_manager));
|
||||
g_service_manager->AddService(new SettingsService(g_service_manager));
|
||||
g_service_manager->AddService(new TIOService(g_service_manager));
|
||||
|
||||
RefreshTargetConfig();
|
||||
|
||||
/* N checks what kind of connection to use here. For now, we only use USB. */
|
||||
TmaUsbConnection::InitializeComms();
|
||||
g_usb_connection = new TmaUsbConnection();
|
||||
g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection);
|
||||
g_usb_connection->Initialize();
|
||||
SetActiveConnection(g_usb_connection);
|
||||
|
||||
/* TODO: Initialize connection events */
|
||||
|
||||
/* TODO: Initialize IPC services */
|
||||
|
||||
TmaPowerManager::Initialize(OnPowerManagementEvent);
|
||||
}
|
||||
|
||||
void TmaTarget::Finalize() {
|
||||
/* TODO: Is implementing this actually worthwhile? It will never be called in practice... */
|
||||
}
|
25
stratosphere/tma/source/tma_target.hpp
Normal file
25
stratosphere/tma/source/tma_target.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class TmaTarget {
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
};
|
52
stratosphere/tma/source/tma_task.cpp
Normal file
52
stratosphere/tma/source/tma_task.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
void TmaTask::SetNeedsPackets(bool n) {
|
||||
this->needs_packets = n;
|
||||
this->manager->Tick();
|
||||
}
|
||||
|
||||
TmaPacket *TmaTask::AllocateSendPacket(bool continuation) {
|
||||
auto packet = this->manager->AllocateSendPacket();
|
||||
packet->SetServiceId(this->service_id);
|
||||
packet->SetTaskId(this->task_id);
|
||||
packet->SetCommand(this->command);
|
||||
packet->SetContinuation(continuation);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
void TmaTask::FreePacket(TmaPacket *packet) {
|
||||
this->manager->FreePacket(packet);
|
||||
}
|
||||
|
||||
void TmaTask::Complete() {
|
||||
SetNeedsPackets(false);
|
||||
this->state = TmaTaskState::Complete;
|
||||
this->manager->Tick();
|
||||
}
|
||||
|
||||
void TmaTask::Cancel() {
|
||||
SetNeedsPackets(false);
|
||||
this->state = TmaTaskState::Canceled;
|
||||
this->manager->Tick();
|
||||
}
|
80
stratosphere/tma/source/tma_task.hpp
Normal file
80
stratosphere/tma/source/tma_task.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
enum class TmaTaskState : u32 {
|
||||
InProgress,
|
||||
Complete,
|
||||
Canceled,
|
||||
};
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaTask {
|
||||
public:
|
||||
static constexpr u32 MaxPriority = 15;
|
||||
static constexpr u32 NumPriorities = MaxPriority + 1;
|
||||
protected:
|
||||
TmaServiceManager *manager;
|
||||
u32 priority = 0;
|
||||
TmaServiceId service_id = TmaServiceId::Invalid;
|
||||
u32 task_id = 0;
|
||||
u32 command = 0;
|
||||
TmaTaskState state = TmaTaskState::InProgress;
|
||||
HosSignal signal;
|
||||
bool owned_by_task_list = true;
|
||||
bool sleep_allowed = true;
|
||||
bool needs_packets = false;
|
||||
public:
|
||||
TmaTask(TmaServiceManager *m) : manager(m) { }
|
||||
virtual ~TmaTask() { }
|
||||
|
||||
u32 GetPriority() const { return this->priority; }
|
||||
TmaServiceId GetServiceId() const { return this->service_id; }
|
||||
u32 GetTaskId() const { return this->task_id; }
|
||||
u32 GetCommand() const { return this->command; }
|
||||
TmaTaskState GetState() const { return this->state; }
|
||||
bool GetOwnedByTaskList() const { return this->owned_by_task_list; }
|
||||
bool GetSleepAllowed() const { return this->sleep_allowed; }
|
||||
bool GetNeedsPackets() const { return this->needs_packets; }
|
||||
|
||||
void SetPriority(u32 p) { this->priority = p; }
|
||||
void SetServiceId(TmaServiceId s) { this->service_id = s; }
|
||||
void SetTaskId(u32 i) { this->task_id = i; }
|
||||
void SetCommand(u32 c) { this->command = c; }
|
||||
void SetOwnedByTaskList(bool o) { this->owned_by_task_list = o; }
|
||||
void SetSleepAllowed(bool a) { this->sleep_allowed = a; }
|
||||
void SetNeedsPackets(bool n);
|
||||
|
||||
void Signal() { this->signal.Signal(); }
|
||||
void ResetSignal() { this->signal.Reset(); }
|
||||
|
||||
TmaPacket *AllocateSendPacket(bool continuation = true);
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
void Complete();
|
||||
void Cancel();
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) = 0;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) = 0;
|
||||
virtual void OnSendPacket(TmaPacket *packet) = 0;
|
||||
};
|
218
stratosphere/tma/source/tma_task_list.cpp
Normal file
218
stratosphere/tma/source/tma_task_list.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <algorithm>
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_task_list.hpp"
|
||||
|
||||
TmaTask *TmaTaskList::GetById(u32 task_id) const {
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
for (auto task : this->tasks[i]) {
|
||||
if (task->GetTaskId() == task_id) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 TmaTaskList::GetNumTasks() const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
u32 count = 0;
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
count += this->tasks[i].size();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 TmaTaskList::GetNumSleepingTasks() const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
u32 count = 0;
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
count += this->sleeping_tasks[i].size();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool TmaTaskList::IsIdFree(u32 task_id) const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
return GetById(task_id) == nullptr;
|
||||
}
|
||||
|
||||
bool TmaTaskList::SendPacket(bool connected, TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
TmaTask *target_task = nullptr;
|
||||
|
||||
/* This loop both finds a target task, and cleans up finished tasks. */
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
switch (task->GetState()) {
|
||||
case TmaTaskState::InProgress:
|
||||
it++;
|
||||
if (target_task == nullptr && task->GetNeedsPackets()) {
|
||||
if (connected || IsMetaService(task->GetServiceId())) {
|
||||
target_task = task;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaTaskState::Complete:
|
||||
case TmaTaskState::Canceled:
|
||||
it = this->tasks[i].erase(it);
|
||||
if (task->GetOwnedByTaskList()) {
|
||||
delete task;
|
||||
} else {
|
||||
task->Signal();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target_task) {
|
||||
/* Setup packet. */
|
||||
packet->SetContinuation(true);
|
||||
packet->SetServiceId(target_task->GetServiceId());
|
||||
packet->SetTaskId(target_task->GetTaskId());
|
||||
packet->SetCommand(target_task->GetCommand());
|
||||
packet->ClearOffset();
|
||||
|
||||
/* Actually handle packet send. */
|
||||
target_task->OnSendPacket(packet);
|
||||
}
|
||||
|
||||
return target_task != nullptr;
|
||||
}
|
||||
|
||||
bool TmaTaskList::ReceivePacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
auto task = this->GetById(packet->GetTaskId());
|
||||
if (task != nullptr) {
|
||||
task->OnReceivePacket(packet);
|
||||
}
|
||||
return task != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TmaTaskList::CleanupDoneTasks() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
/* Clean up all tasks in Complete/Canceled state. */
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
switch (task->GetState()) {
|
||||
case TmaTaskState::InProgress:
|
||||
it++;
|
||||
break;
|
||||
case TmaTaskState::Complete:
|
||||
case TmaTaskState::Canceled:
|
||||
it = this->tasks[i].erase(it);
|
||||
if (task->GetOwnedByTaskList()) {
|
||||
delete task;
|
||||
} else {
|
||||
task->Signal();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Add(TmaTask *task) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
this->tasks[task->GetPriority()].push_back(task);
|
||||
}
|
||||
|
||||
void TmaTaskList::Remove(TmaTask *task) {
|
||||
const auto priority = task->GetPriority();
|
||||
|
||||
/* Nintendo iterates over all lists instead of just the correct one. */
|
||||
/* TODO: Is there actually any reason to do that? */
|
||||
auto ind = std::find(this->tasks[priority].begin(), this->tasks[priority].end(), task);
|
||||
if (ind != this->tasks[priority].end()) {
|
||||
this->tasks[priority].erase(ind);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void TmaTaskList::Cancel(u32 task_id) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
auto task = this->GetById(task_id);
|
||||
if (task != nullptr) {
|
||||
task->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::CancelAll() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
for (auto task : this->tasks[i]) {
|
||||
task->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Sleep() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
if (task->GetSleepAllowed()) {
|
||||
it = this->tasks[i].erase(it);
|
||||
this->sleeping_tasks[i].push_back(task);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Wake() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->sleeping_tasks[i].begin();
|
||||
while (it != this->sleeping_tasks[i].end()) {
|
||||
auto task = *it;
|
||||
it = this->sleeping_tasks[i].erase(it);
|
||||
this->tasks[i].push_back(task);
|
||||
}
|
||||
}
|
||||
}
|
50
stratosphere/tma/source/tma_task_list.hpp
Normal file
50
stratosphere/tma/source/tma_task_list.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_task.hpp"
|
||||
|
||||
class TmaTaskList {
|
||||
private:
|
||||
mutable HosMutex lock;
|
||||
std::vector<TmaTask *> tasks[TmaTask::NumPriorities];
|
||||
std::vector<TmaTask *> sleeping_tasks[TmaTask::NumPriorities];
|
||||
private:
|
||||
void Remove(TmaTask *task);
|
||||
TmaTask *GetById(u32 task_id) const;
|
||||
public:
|
||||
TmaTaskList() { }
|
||||
virtual ~TmaTaskList() { }
|
||||
|
||||
u32 GetNumTasks() const;
|
||||
u32 GetNumSleepingTasks() const;
|
||||
bool IsIdFree(u32 task_id) const;
|
||||
|
||||
bool SendPacket(bool connected, TmaPacket *packet);
|
||||
bool ReceivePacket(TmaPacket *packet);
|
||||
void CleanupDoneTasks();
|
||||
void Add(TmaTask *task);
|
||||
void Cancel(u32 task_id);
|
||||
void CancelAll();
|
||||
|
||||
void Sleep();
|
||||
void Wake();
|
||||
};
|
484
stratosphere/tma/source/tma_usb_comms.cpp
Normal file
484
stratosphere/tma/source/tma_usb_comms.cpp
Normal file
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
/* TODO: Is this actually allowed? */
|
||||
#define ATMOSPHERE_INTERFACE_PROTOCOL 0xFC
|
||||
|
||||
static std::atomic<bool> g_initialized = false;
|
||||
static UsbDsInterface *g_interface;
|
||||
static UsbDsEndpoint *g_endpoint_in, *g_endpoint_out;
|
||||
|
||||
/* USB State Change Tracking. */
|
||||
static HosThread g_state_change_thread;
|
||||
static WaitableManagerBase *g_state_change_manager = nullptr;
|
||||
static void (*g_state_change_callback)(void *arg, u32 state);
|
||||
static void *g_state_change_arg;
|
||||
|
||||
/* USB Send/Receive mutexes. */
|
||||
static HosMutex g_send_mutex;
|
||||
static HosMutex g_recv_mutex;
|
||||
|
||||
/* Static arrays to do USB DMA into. */
|
||||
static constexpr size_t DmaBufferAlign = 0x1000;
|
||||
static constexpr size_t HeaderBufferSize = DmaBufferAlign;
|
||||
static constexpr size_t DataBufferSize = 0x18000;
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_header_buffer[HeaderBufferSize];
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_recv_data_buf[DataBufferSize];
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_send_data_buf[DataBufferSize];
|
||||
|
||||
/* Taken from libnx usb comms. */
|
||||
static Result _usbCommsInterfaceInit1x()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
struct usb_interface_descriptor interface_descriptor = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 4,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceProtocol = ATMOSPHERE_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup interface.
|
||||
rc = usbDsGetDsInterface(&g_interface, &interface_descriptor, "usb");
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup endpoints.
|
||||
rc = usbDsInterface_GetDsEndpoint(g_interface, &g_endpoint_in, &endpoint_descriptor_in);//device->host
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsInterface_GetDsEndpoint(g_interface, &g_endpoint_out, &endpoint_descriptor_out);//host->device
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _usbCommsInterfaceInit5x() {
|
||||
Result rc = 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
|
||||
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 = 0x3000,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = iManufacturer,
|
||||
.iProduct = iProduct,
|
||||
.iSerialNumber = iSerialNumber,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
// 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_FAILED(rc)) return rc;
|
||||
|
||||
struct usb_interface_descriptor interface_descriptor = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 4,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceProtocol = ATMOSPHERE_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
rc = usbDsRegisterInterface(&g_interface);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
interface_descriptor.bInterfaceNumber = g_interface->interface_index;
|
||||
endpoint_descriptor_in.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||
|
||||
// Full Speed Config
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
// High Speed Config
|
||||
endpoint_descriptor_in.wMaxPacketSize = 0x200;
|
||||
endpoint_descriptor_out.wMaxPacketSize = 0x200;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
// Super Speed Config
|
||||
endpoint_descriptor_in.wMaxPacketSize = 0x400;
|
||||
endpoint_descriptor_out.wMaxPacketSize = 0x400;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup endpoints.
|
||||
rc = usbDsInterface_RegisterEndpoint(g_interface, &g_endpoint_in, endpoint_descriptor_in.bEndpointAddress);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsInterface_RegisterEndpoint(g_interface, &g_endpoint_out, endpoint_descriptor_out.bEndpointAddress);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Actual function implementations. */
|
||||
TmaConnResult TmaUsbComms::Initialize() {
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (g_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Result rc = usbDsInitialize();
|
||||
|
||||
/* Perform interface setup. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
|
||||
rc = _usbCommsInterfaceInit5x();
|
||||
} else {
|
||||
rc = _usbCommsInterfaceInit1x();
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the state change thread. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = g_state_change_thread.Initialize(&TmaUsbComms::UsbStateChangeThreadFunc, nullptr, 0x4000, 38);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = g_state_change_thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable USB communication. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = usbDsInterface_EnableInterface(g_interface);
|
||||
}
|
||||
if (R_SUCCEEDED(rc) && GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
|
||||
rc = usbDsEnable();
|
||||
}
|
||||
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
/* TODO: Should I not abort here? */
|
||||
std::abort();
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::Finalize() {
|
||||
Result rc = 0;
|
||||
/* We must have initialized before calling finalize. */
|
||||
if (!g_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* Kill the state change thread. */
|
||||
g_state_change_manager->RequestStop();
|
||||
if (R_FAILED(g_state_change_thread.Join())) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
CancelComms();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
usbDsExit();
|
||||
}
|
||||
|
||||
g_state_change_callback = nullptr;
|
||||
g_interface = nullptr;
|
||||
g_endpoint_in = nullptr;
|
||||
g_endpoint_out = nullptr;
|
||||
g_initialized = false;
|
||||
|
||||
return R_SUCCEEDED(rc) ? TmaConnResult::Success : TmaConnResult::ConnectionFailure;
|
||||
}
|
||||
|
||||
void TmaUsbComms::CancelComms() {
|
||||
if (!g_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
usbDsEndpoint_Cancel(g_endpoint_in);
|
||||
usbDsEndpoint_Cancel(g_endpoint_out);
|
||||
}
|
||||
|
||||
void TmaUsbComms::SetStateChangeCallback(void (*callback)(void *, u32), void *arg) {
|
||||
g_state_change_callback = callback;
|
||||
g_state_change_arg = arg;
|
||||
}
|
||||
|
||||
Result TmaUsbComms::UsbXfer(UsbDsEndpoint *ep, size_t *out_xferd, void *buf, size_t size) {
|
||||
Result rc = 0;
|
||||
u32 urbId = 0;
|
||||
u32 total_xferd = 0;
|
||||
UsbDsReportData reportdata;
|
||||
|
||||
if (size) {
|
||||
/* Start transfer. */
|
||||
rc = usbDsEndpoint_PostBufferAsync(ep, buf, size, &urbId);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
/* Wait for transfer to complete. */
|
||||
eventWait(&ep->CompletionEvent, U64_MAX);
|
||||
eventClear(&ep->CompletionEvent);
|
||||
|
||||
rc = usbDsEndpoint_GetReportData(ep, &reportdata);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsParseReportData(&reportdata, urbId, NULL, &total_xferd);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
if (out_xferd) *out_xferd = total_xferd;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::ReceivePacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk{g_recv_mutex};
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (!g_initialized || packet == nullptr) {
|
||||
return TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Read the header. */
|
||||
size_t read = 0;
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_out, &read, g_header_buffer, sizeof(TmaPacket::Header)))) {
|
||||
packet->CopyHeaderFrom(reinterpret_cast<TmaPacket::Header *>(g_header_buffer));
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Validate the read header data. */
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (read != sizeof(TmaPacket::Header) || !packet->IsHeaderValid()) {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the body! */
|
||||
if (res == TmaConnResult::Success) {
|
||||
const u32 body_len = packet->GetBodyLength();
|
||||
if (0 < body_len) {
|
||||
if (body_len <= sizeof(g_recv_data_buf)) {
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_out, &read, g_recv_data_buf, body_len))) {
|
||||
if (read == body_len) {
|
||||
res = packet->CopyBodyFrom(g_recv_data_buf, body_len);
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the body. */
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (!packet->IsBodyValid()) {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
packet->ClearOffset();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::SendPacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk{g_send_mutex};
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (!g_initialized || packet == nullptr) {
|
||||
return TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Ensure our packets have the correct checksums. */
|
||||
packet->SetChecksums();
|
||||
|
||||
/* Send the packet. */
|
||||
size_t written = 0;
|
||||
const u32 body_len = packet->GetBodyLength();
|
||||
if (body_len <= sizeof(g_send_data_buf)) {
|
||||
/* Copy header to send buffer. */
|
||||
packet->CopyHeaderTo(g_send_data_buf);
|
||||
|
||||
/* Send the packet header. */
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_in, &written, g_send_data_buf, sizeof(TmaPacket::Header)))) {
|
||||
if (written == sizeof(TmaPacket::Header)) {
|
||||
res = TmaConnResult::Success;
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
if (res == TmaConnResult::Success && 0 < body_len) {
|
||||
/* Copy body to send buffer. */
|
||||
packet->CopyBodyTo(g_send_data_buf);
|
||||
|
||||
|
||||
/* Send the packet body. */
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_in, &written, g_send_data_buf, body_len))) {
|
||||
if (written == body_len) {
|
||||
res = TmaConnResult::Success;
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TmaUsbComms::UsbStateChangeThreadFunc(void *arg) {
|
||||
u32 state;
|
||||
g_state_change_manager = new WaitableManager(1);
|
||||
|
||||
auto state_change_event = LoadReadOnlySystemEvent(usbDsGetStateChangeEvent()->revent, [&](u64 timeout) {
|
||||
if (R_SUCCEEDED(usbDsGetState(&state))) {
|
||||
if (g_state_change_callback != nullptr) {
|
||||
g_state_change_callback(g_state_change_arg, state);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}, true);
|
||||
|
||||
g_state_change_manager->AddWaitable(state_change_event);
|
||||
g_state_change_manager->Process();
|
||||
|
||||
/* If we get here, we're exiting. */
|
||||
state_change_event->r_h = 0;
|
||||
delete g_state_change_manager;
|
||||
g_state_change_manager = nullptr;
|
||||
|
||||
svcExitThread();
|
||||
}
|
36
stratosphere/tma/source/tma_usb_comms.hpp
Normal file
36
stratosphere/tma/source/tma_usb_comms.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
class TmaUsbComms {
|
||||
private:
|
||||
static void UsbStateChangeThreadFunc(void *arg);
|
||||
static Result UsbXfer(UsbDsEndpoint *ep, size_t *out_xferd, void *buf, size_t size);
|
||||
public:
|
||||
static TmaConnResult Initialize();
|
||||
static TmaConnResult Finalize();
|
||||
static void CancelComms();
|
||||
static TmaConnResult ReceivePacket(TmaPacket *packet);
|
||||
static TmaConnResult SendPacket(TmaPacket *packet);
|
||||
|
||||
static void SetStateChangeCallback(void (*callback)(void *, u32), void *arg);
|
||||
};
|
147
stratosphere/tma/tma.json
Normal file
147
stratosphere/tma/tma.json
Normal file
|
@ -0,0 +1,147 @@
|
|||
{
|
||||
"name": "tma",
|
||||
"title_id": "0x0100000000000007",
|
||||
"title_id_range_min": "0x0100000000000007",
|
||||
"title_id_range_max": "0x0100000000000007",
|
||||
"main_thread_stack_size": "0x00004000",
|
||||
"main_thread_priority": 38,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 1,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": [
|
||||
"bsd:s",
|
||||
"bsdcfg",
|
||||
"dmnt:-",
|
||||
"fatal:u",
|
||||
"i2c",
|
||||
"pcie",
|
||||
"psc:m",
|
||||
"set:cal",
|
||||
"set:fd",
|
||||
"set:sys",
|
||||
"sfdnsres",
|
||||
"spl:",
|
||||
"usb:ds"
|
||||
],
|
||||
"service_host": [
|
||||
"file_io",
|
||||
"gds",
|
||||
"htc",
|
||||
"htcs",
|
||||
"tma_log",
|
||||
"tmagent"
|
||||
],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 0,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
}, {
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcReadWriteRegister": "0x4E",
|
||||
"svcCreateSharedMemory": "0x50",
|
||||
"svcMapTransferMemory": "0x51",
|
||||
"svcUnmapTransferMemory": "0x52",
|
||||
"svcCreateInterruptEvent": "0x53",
|
||||
"svcQueryIoMapping": "0x55",
|
||||
"svcCreateDeviceAddressSpace": "0x56",
|
||||
"svcAttachDeviceAddressSpace": "0x57",
|
||||
"svcDetachDeviceAddressSpace": "0x58",
|
||||
"svcMapDeviceAddressSpaceByForce": "0x59",
|
||||
"svcMapDeviceAddressSpaceAligned": "0x5a",
|
||||
"svcMapDeviceAddressSpace": "0x5b",
|
||||
"svcUnmapDeviceAddressSpace": "0x5c",
|
||||
"svcInvalidateProcessDataCache": "0x5d",
|
||||
"svcStoreProcessDataCache": "0x5e",
|
||||
"svcFlushProcessDataCache": "0x5f",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}, {
|
||||
"type": "map",
|
||||
"value": {
|
||||
"address": "0x02000000",
|
||||
"is_ro": false,
|
||||
"size": "0x05000000",
|
||||
"is_io": true
|
||||
}
|
||||
}, {
|
||||
"type": "map",
|
||||
"value": {
|
||||
"address": "0x10000000",
|
||||
"is_ro": false,
|
||||
"size": "0x04000000",
|
||||
"is_io": true
|
||||
}
|
||||
}, {
|
||||
"type": "irq_pair",
|
||||
"value": [130, null]
|
||||
}, {
|
||||
"type": "irq_pair",
|
||||
"value": [131, 132]
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0030"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 256
|
||||
}]
|
||||
}
|
Loading…
Reference in a new issue