Compare commits
85 Commits
1600_suppo
...
1.5.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85c23b5781 | ||
|
|
8e042f2262 | ||
|
|
81e9154a52 | ||
|
|
e9b9dbc2aa | ||
|
|
7e6c849ca4 | ||
|
|
8ec7c096d0 | ||
|
|
6e5b901a9b | ||
|
|
b800953d66 | ||
|
|
f0240db75a | ||
|
|
1f5ec68a5c | ||
|
|
ed9e60acb9 | ||
|
|
a7300b0fa4 | ||
|
|
8e2eca2004 | ||
|
|
9f83b3c838 | ||
|
|
434c8cefc4 | ||
|
|
d8aed7de6d | ||
|
|
a346014dc7 | ||
|
|
4b3c801e9f | ||
|
|
90db1223f6 | ||
|
|
fa64a6ff4d | ||
|
|
0c6a06a0cf | ||
|
|
5efb4a2a98 | ||
|
|
3b662122f9 | ||
|
|
e9b28ab4b1 | ||
|
|
1afb184c14 | ||
|
|
5e070600a9 | ||
|
|
8274081e39 | ||
|
|
f1ad26ce84 | ||
|
|
e4c314146e | ||
|
|
52f00731d9 | ||
|
|
476d658a79 | ||
|
|
7263022bac | ||
|
|
e0e7aa1e2f | ||
|
|
bd9d8fff46 | ||
|
|
61e3f0b391 | ||
|
|
cd9b173318 | ||
|
|
a8df400825 | ||
|
|
68040e2922 | ||
|
|
8da4d14e15 | ||
|
|
e2ebf9c0ff | ||
|
|
5fb6f52b9e | ||
|
|
982389dceb | ||
|
|
f636596ee2 | ||
|
|
0a2440522f | ||
|
|
3292ea5970 | ||
|
|
33d42f4831 | ||
|
|
46094cfb3e | ||
|
|
618691a500 | ||
|
|
356d89244f | ||
|
|
1ce3611695 | ||
|
|
1ab8b23444 | ||
|
|
06b4738d54 | ||
|
|
b92c614347 | ||
|
|
99175c1149 | ||
|
|
8876eedfb0 | ||
|
|
bbcee8c77c | ||
|
|
f7915c2c05 | ||
|
|
668cabd4a2 | ||
|
|
d64ab354ad | ||
|
|
59b518783d | ||
|
|
6462101b6f | ||
|
|
0ea5dbcfbb | ||
|
|
bb6446aada | ||
|
|
e46e7e0eb1 | ||
|
|
cb89c66bd8 | ||
|
|
e85a512cf4 | ||
|
|
7d9fea01c3 | ||
|
|
406320f6ec | ||
|
|
aad2be0a01 | ||
|
|
25383db524 | ||
|
|
48f4c526f3 | ||
|
|
1279d236f3 | ||
|
|
008eb974d4 | ||
|
|
035cebef9d | ||
|
|
8db22967bf | ||
|
|
db510f96c3 | ||
|
|
6e2dd791b2 | ||
|
|
bff61c68ab | ||
|
|
ca7734ffaf | ||
|
|
ed22f802ee | ||
|
|
8ffc177b44 | ||
|
|
c058376b3b | ||
|
|
d5ebf13094 | ||
|
|
695c125721 | ||
|
|
e7e3e7b374 |
@@ -51,6 +51,8 @@ dist: dist-no-debug
|
|||||||
cp $(CURRENT_DIRECTORY)/stratosphere/spl/$(ATMOSPHERE_OUT_DIR)/spl.elf $(DIST_DIR)/spl.elf
|
cp $(CURRENT_DIRECTORY)/stratosphere/spl/$(ATMOSPHERE_OUT_DIR)/spl.elf $(DIST_DIR)/spl.elf
|
||||||
cp $(CURRENT_DIRECTORY)/stratosphere/TioServer/$(ATMOSPHERE_OUT_DIR)/TioServer.elf $(DIST_DIR)/TioServer.elf
|
cp $(CURRENT_DIRECTORY)/stratosphere/TioServer/$(ATMOSPHERE_OUT_DIR)/TioServer.elf $(DIST_DIR)/TioServer.elf
|
||||||
cp $(CURRENT_DIRECTORY)/troposphere/daybreak/daybreak.elf $(DIST_DIR)/daybreak.elf
|
cp $(CURRENT_DIRECTORY)/troposphere/daybreak/daybreak.elf $(DIST_DIR)/daybreak.elf
|
||||||
|
cp $(CURRENT_DIRECTORY)/troposphere/haze/haze.elf $(DIST_DIR)/haze.elf
|
||||||
|
cp $(CURRENT_DIRECTORY)/troposphere/reboot_to_payload/reboot_to_payload.elf $(DIST_DIR)/reboot_to_payload.elf
|
||||||
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION)-debug.zip ./*; cd ../;
|
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION)-debug.zip ./*; cd ../;
|
||||||
rm -rf $(DIST_DIR)
|
rm -rf $(DIST_DIR)
|
||||||
|
|
||||||
@@ -106,6 +108,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
|
|||||||
rm -r $(DIST_DIR)/stratosphere_romfs
|
rm -r $(DIST_DIR)/stratosphere_romfs
|
||||||
cp troposphere/reboot_to_payload/reboot_to_payload.nro $(DIST_DIR)/switch/reboot_to_payload.nro
|
cp troposphere/reboot_to_payload/reboot_to_payload.nro $(DIST_DIR)/switch/reboot_to_payload.nro
|
||||||
cp troposphere/daybreak/daybreak.nro $(DIST_DIR)/switch/daybreak.nro
|
cp troposphere/daybreak/daybreak.nro $(DIST_DIR)/switch/daybreak.nro
|
||||||
|
cp troposphere/haze/haze.nro $(DIST_DIR)/switch/haze.nro
|
||||||
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION).zip ./*; cd ../;
|
cd $(DIST_DIR); zip -r ../atmosphere-$(ATMOSPHERE_VERSION).zip ./*; cd ../;
|
||||||
rm -rf $(DIST_DIR)
|
rm -rf $(DIST_DIR)
|
||||||
cp fusee/$(ATMOSPHERE_BOOT_OUT_DIR)/fusee.bin $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)/fusee.bin
|
cp fusee/$(ATMOSPHERE_BOOT_OUT_DIR)/fusee.bin $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)/fusee.bin
|
||||||
|
|||||||
@@ -67,6 +67,10 @@
|
|||||||
; Note that this setting is ignored (and treated as 1) when htc is enabled.
|
; Note that this setting is ignored (and treated as 1) when htc is enabled.
|
||||||
; 0 = Disabled, 1 = Enabled
|
; 0 = Disabled, 1 = Enabled
|
||||||
; enable_log_manager = u8!0x0
|
; enable_log_manager = u8!0x0
|
||||||
|
; Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs)
|
||||||
|
; NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database.
|
||||||
|
; 0 = Disabled, 1 = Enabled
|
||||||
|
; enable_external_bluetooth_db = u8!0x0
|
||||||
[hbloader]
|
[hbloader]
|
||||||
; Controls the size of the homebrew heap when running as applet.
|
; Controls the size of the homebrew heap when running as applet.
|
||||||
; If set to zero, all available applet memory is used as heap.
|
; If set to zero, all available applet memory is used as heap.
|
||||||
|
|||||||
@@ -1,4 +1,43 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
## 1.5.3
|
||||||
|
+ Support was added for 16.0.3.
|
||||||
|
+ Atmosphère was updated to use GCC 13/newlib (latest devkitA64/devkitARM releases).
|
||||||
|
+ **Please note**: This introduces a known issue, which is currently being worked on.
|
||||||
|
+ As you may recall from the 1.4.1 changelog, Fire Emblem: Engage requires enormous amounts of memory to support using layeredfs mods with the game.
|
||||||
|
+ Latest GCC/newlib slightly increases malloc overhead size, which makes the previous memory increase insufficient.
|
||||||
|
+ A general-case solution to this is in the works, which should hopefully fix the problem in a way that doesn't jinx me for the future.
|
||||||
|
+ A number of minor issues were fixed and improvements were made, including:
|
||||||
|
+ An issue was fixed that caused system font replacement to not work on 16.0.0+.
|
||||||
|
+ An minor accuracy issue was addressed in mesosphere's management of certain memory ranges; this issue would have had zero visible impact to the end-user.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
|
## 1.5.2
|
||||||
|
+ A homebrew application (`haze`) was added for performing USB file transfer (with thanks to @liamwhite for both design and implementation).
|
||||||
|
+ `haze` is included with atmosphère, and provides access to the SD card via the PTP/MTP protocol.
|
||||||
|
+ **Please note**: haze will show inside the homebrew menu under the name "USB File Transfer".
|
||||||
|
+ **Please note**: Atmosphère cannot be updated at runtime, and trying to install an atmosphère update via haze will fail as usual.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
|
## 1.5.1
|
||||||
|
+ `fatal` was updated to reduce memory footprint.
|
||||||
|
+ Starting in 16.0.0, official `fatal` has no framebuffer or rendering logic, and instead calls other system service commands to draw the screen.
|
||||||
|
+ However, these commands aren't usable by atmosphère (too small rendering window, bad color support).
|
||||||
|
+ To reduce the relative memory footprint differential between atmosphère and official code, the framebuffer (2 MB) is now dynamically allocated when needed.
|
||||||
|
+ This will try to allocate from multiple pools (preferring System > System_NonSecure > Application).
|
||||||
|
+ This technically requires that 2 MB be available in at least one of these pools for the fatal screen to render (otherwise, a reboot-to-black-and-white-fatal will occur), but this should be a non-issue in almost all cases.
|
||||||
|
+ A feature was added to optionally mirror the bluetooth pairing database to the SD card (thanks @ndeadly).
|
||||||
|
+ This allows device pairings to be automatically kept in-sync across sysmmc/all emummcs.
|
||||||
|
+ This is opt-in, and can be controlled by setting `atmosphere!enable_external_bluetooth_db = u8!0x1`.
|
||||||
|
+ When enabled, the pairing database will be synchronized to `/atmosphere/bluetooth_devices.db`.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
|
## 1.5.0
|
||||||
|
+ Support was added for 16.0.0
|
||||||
|
+ `mesosphère` was updated to reflect the latest official kernel behavior.
|
||||||
|
+ `ncm` was updated to reflect the latest official behavior.
|
||||||
|
+ Many FS apis were updated under the hood to reflect the latest official behavior.
|
||||||
|
+ **Please Note**: 16.0.0 made breaking changes to a number of system APIs, including in FS/NCM/Shared Font commands that some homebrew programs may use.
|
||||||
|
+ These programs may encounter strange errors, and may need to be recompiled with a libnx updated to support 16.0.0's changes to function properly.
|
||||||
|
+ A number of minor issues were fixed and improvements were made, including:
|
||||||
|
+ An issue was fixed that could cause GPIO outputs to be misconfigured under certain circumstances.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
## 1.4.1
|
## 1.4.1
|
||||||
+ A number of minor issues were fixed and improvements were made, including:
|
+ A number of minor issues were fixed and improvements were made, including:
|
||||||
+ `dmnt` cheat toggle files are no longer ignored when they are missing a trailing newline.
|
+ `dmnt` cheat toggle files are no longer ignored when they are missing a trailing newline.
|
||||||
@@ -12,10 +51,11 @@
|
|||||||
+ The additional memory here is taken from the applet pool; no issues are expected to arise from this, but please report anything you may run into.
|
+ The additional memory here is taken from the applet pool; no issues are expected to arise from this, but please report anything you may run into.
|
||||||
+ As usual, if you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
|
+ As usual, if you encounter a game that exhausts ams.mitm's memory (crashing it) when loading layeredfs mods, please contact `SciresM#0524`.
|
||||||
+ I am jinxing myself by saying this, but it's really hard to imagine any game being worse than Fire Emblem: Engage, but if it happens again I will drop everything to fix it as usual.
|
+ I am jinxing myself by saying this, but it's really hard to imagine any game being worse than Fire Emblem: Engage, but if it happens again I will drop everything to fix it as usual.
|
||||||
|
+ General system stability improvements to enhance the user's experience.
|
||||||
## 1.4.0
|
## 1.4.0
|
||||||
+ Support was added for 15.0.0.
|
+ Support was added for 15.0.0.
|
||||||
+ `mesosphère` was updated to reflect the latest official kernel behavior.
|
+ `mesosphère` was updated to reflect the latest official kernel behavior.
|
||||||
+ `ncm` was updated to reflect the latest official kernel behavior.
|
+ `ncm` was updated to reflect the latest official behavior.
|
||||||
+ A number of minor issues were fixed and improvements were made, including:
|
+ A number of minor issues were fixed and improvements were made, including:
|
||||||
+ The capacity limit on registered add-on contents was fixed in NCM to reflect the increase that occurred in 12.0.0.
|
+ The capacity limit on registered add-on contents was fixed in NCM to reflect the increase that occurred in 12.0.0.
|
||||||
+ An off-by-one was fixed in mesosphere when computing the new value for an address arbiter signaled with ModifyByWaitingCountIfEqual.
|
+ An off-by-one was fixed in mesosphere when computing the new value for an address arbiter signaled with ModifyByWaitingCountIfEqual.
|
||||||
|
|||||||
18
docs/faq.md
Normal file
18
docs/faq.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
This document serves as a place to store answers for common questions received about Atmosphère.
|
||||||
|
|
||||||
|
## What does "June 15th" mean?
|
||||||
|
When Atmosphère began development in February 2018, "June 15" was given as the estimate/target date for a first release, to coincide with the planned disclosure of a vulnerability.
|
||||||
|
|
||||||
|
This deadline was missed, hard.
|
||||||
|
|
||||||
|
People made rather a lot of fun of me (SciresM) for this.
|
||||||
|
|
||||||
|
Several months later, when the first Atmosphère release occurred, I captioned it "Happy June 15th!" and pretended like I hadn't missed the first deadline.
|
||||||
|
|
||||||
|
This amused me a lot, and so the practice has been kept up for every single release since.
|
||||||
|
|
||||||
|
Depending on who you ask, you may be told that this is a dumb joke and it is not funny.
|
||||||
|
|
||||||
|
This is incorrect. It is definitely a dumb joke, but it is also hilarious.
|
||||||
@@ -27,3 +27,6 @@ A list of planned features for Atmosphère can be found [here](roadmap.md).
|
|||||||
|
|
||||||
## Release History
|
## Release History
|
||||||
A changelog of previous versions of Atmosphère can be found [here](changelog.md).
|
A changelog of previous versions of Atmosphère can be found [here](changelog.md).
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
Answers to one or more frequently asked questions may be found [here](faq.md).
|
||||||
|
|||||||
4
emummc/.gitrepo
vendored
4
emummc/.gitrepo
vendored
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/m4xw/emummc
|
remote = https://github.com/m4xw/emummc
|
||||||
branch = develop
|
branch = develop
|
||||||
commit = 4714b2df9eaf68fb85516b35f7f4265ab0413825
|
commit = 30205111ee375bef96f0f76cb6a3130a2f0fc85c
|
||||||
parent = 99f6a96845b6097d50f5059eea9245d27876f26a
|
parent = 81e9154a52a976f85317bddd0131426599d26a62
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
2
emummc/README.md
vendored
2
emummc/README.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
*A SDMMC driver replacement for Nintendo's Filesystem Services, by **m4xw***
|
||||||
|
|
||||||
### Supported Horizon Versions
|
### Supported Horizon Versions
|
||||||
**1.0.0 - 16.0.0**
|
**1.0.0 - 16.0.3**
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Arbitrary SDMMC backend selection
|
* Arbitrary SDMMC backend selection
|
||||||
|
|||||||
8
emummc/source/FS/FS_offsets.c
vendored
8
emummc/source/FS/FS_offsets.c
vendored
@@ -65,6 +65,8 @@
|
|||||||
#include "offsets/1500_exfat.h"
|
#include "offsets/1500_exfat.h"
|
||||||
#include "offsets/1600.h"
|
#include "offsets/1600.h"
|
||||||
#include "offsets/1600_exfat.h"
|
#include "offsets/1600_exfat.h"
|
||||||
|
#include "offsets/1603.h"
|
||||||
|
#include "offsets/1603_exfat.h"
|
||||||
#include "../utils/fatal.h"
|
#include "../utils/fatal.h"
|
||||||
|
|
||||||
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
#define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers
|
||||||
@@ -141,6 +143,8 @@ DEFINE_OFFSET_STRUCT(_1500);
|
|||||||
DEFINE_OFFSET_STRUCT(_1500_EXFAT);
|
DEFINE_OFFSET_STRUCT(_1500_EXFAT);
|
||||||
DEFINE_OFFSET_STRUCT(_1600);
|
DEFINE_OFFSET_STRUCT(_1600);
|
||||||
DEFINE_OFFSET_STRUCT(_1600_EXFAT);
|
DEFINE_OFFSET_STRUCT(_1600_EXFAT);
|
||||||
|
DEFINE_OFFSET_STRUCT(_1603);
|
||||||
|
DEFINE_OFFSET_STRUCT(_1603_EXFAT);
|
||||||
|
|
||||||
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
@@ -242,6 +246,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) {
|
|||||||
return &(GET_OFFSET_STRUCT_NAME(_1600));
|
return &(GET_OFFSET_STRUCT_NAME(_1600));
|
||||||
case FS_VER_16_0_0_EXFAT:
|
case FS_VER_16_0_0_EXFAT:
|
||||||
return &(GET_OFFSET_STRUCT_NAME(_1600_EXFAT));
|
return &(GET_OFFSET_STRUCT_NAME(_1600_EXFAT));
|
||||||
|
case FS_VER_16_0_3:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_1603));
|
||||||
|
case FS_VER_16_0_3_EXFAT:
|
||||||
|
return &(GET_OFFSET_STRUCT_NAME(_1603_EXFAT));
|
||||||
default:
|
default:
|
||||||
fatal_abort(Fatal_UnknownVersion);
|
fatal_abort(Fatal_UnknownVersion);
|
||||||
}
|
}
|
||||||
|
|||||||
3
emummc/source/FS/FS_versions.h
vendored
3
emummc/source/FS/FS_versions.h
vendored
@@ -95,6 +95,9 @@ enum FS_VER
|
|||||||
FS_VER_16_0_0,
|
FS_VER_16_0_0,
|
||||||
FS_VER_16_0_0_EXFAT,
|
FS_VER_16_0_0_EXFAT,
|
||||||
|
|
||||||
|
FS_VER_16_0_3,
|
||||||
|
FS_VER_16_0_3_EXFAT,
|
||||||
|
|
||||||
FS_VER_MAX,
|
FS_VER_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
59
emummc/source/FS/offsets/1603.h
vendored
Normal file
59
emummc/source/FS/offsets/1603.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_1603_H__
|
||||||
|
#define __FS_1603_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_1603_SDMMC_ACCESSOR_GC 0x1862F0
|
||||||
|
#define FS_OFFSET_1603_SDMMC_ACCESSOR_SD 0x187F70
|
||||||
|
#define FS_OFFSET_1603_SDMMC_ACCESSOR_NAND 0x1867B0
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_1603_SDMMC_WRAPPER_READ 0x182240
|
||||||
|
#define FS_OFFSET_1603_SDMMC_WRAPPER_WRITE 0x1822A0
|
||||||
|
#define FS_OFFSET_1603_RTLD 0x269B0
|
||||||
|
#define FS_OFFSET_1603_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||||
|
|
||||||
|
#define FS_OFFSET_1603_CLKRST_SET_MIN_V_CLK_RATE 0x1A2D80
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_1603_LOCK_MUTEX 0x17B780
|
||||||
|
#define FS_OFFSET_1603_UNLOCK_MUTEX 0x17B7D0
|
||||||
|
|
||||||
|
#define FS_OFFSET_1603_SDMMC_WRAPPER_CONTROLLER_OPEN 0x182200
|
||||||
|
#define FS_OFFSET_1603_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x182220
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_1603_SD_MUTEX 0xFFB3F0
|
||||||
|
#define FS_OFFSET_1603_NAND_MUTEX 0xFF6B58
|
||||||
|
#define FS_OFFSET_1603_ACTIVE_PARTITION 0xFF6B98
|
||||||
|
#define FS_OFFSET_1603_SDMMC_DAS_HANDLE 0xFDC8B0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_1603_SD_DAS_INIT 0x258D4
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_1603_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00063B98, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070DBC, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0007795C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008A7A4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_1603_H__
|
||||||
59
emummc/source/FS/offsets/1603_exfat.h
vendored
Normal file
59
emummc/source/FS/offsets/1603_exfat.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||||
|
* Copyright (c) 2019 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/>.
|
||||||
|
*/
|
||||||
|
#ifndef __FS_1603_EXFAT_H__
|
||||||
|
#define __FS_1603_EXFAT_H__
|
||||||
|
|
||||||
|
// Accessor vtable getters
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_GC 0x190FD0
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_SD 0x192C50
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_ACCESSOR_NAND 0x191490
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_READ 0x18CF20
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_WRITE 0x18CF80
|
||||||
|
#define FS_OFFSET_1603_EXFAT_RTLD 0x269B0
|
||||||
|
#define FS_OFFSET_1603_EXFAT_RTLD_DESTINATION ((uintptr_t)(INT64_C(-0x3C)))
|
||||||
|
|
||||||
|
#define FS_OFFSET_1603_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x1ADA60
|
||||||
|
|
||||||
|
// Misc funcs
|
||||||
|
#define FS_OFFSET_1603_EXFAT_LOCK_MUTEX 0x186460
|
||||||
|
#define FS_OFFSET_1603_EXFAT_UNLOCK_MUTEX 0x1864B0
|
||||||
|
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_CONTROLLER_OPEN 0x18CEE0
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x18CF00
|
||||||
|
|
||||||
|
// Misc Data
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SD_MUTEX 0x100D3F0
|
||||||
|
#define FS_OFFSET_1603_EXFAT_NAND_MUTEX 0x1008B58
|
||||||
|
#define FS_OFFSET_1603_EXFAT_ACTIVE_PARTITION 0x1008B98
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SDMMC_DAS_HANDLE 0xFE98B0
|
||||||
|
|
||||||
|
// NOPs
|
||||||
|
#define FS_OFFSET_1603_EXFAT_SD_DAS_INIT 0x258D4
|
||||||
|
|
||||||
|
// Nintendo Paths
|
||||||
|
#define FS_OFFSET_1603_EXFAT_NINTENDO_PATHS \
|
||||||
|
{ \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00063B98, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 3, .adrp_offset = 0x00070DBC, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0007795C, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 4, .adrp_offset = 0x0008A7A4, .add_rel_offset = 0x00000004}, \
|
||||||
|
{.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __FS_1603_EXFAT_H__
|
||||||
@@ -162,6 +162,9 @@ namespace ams::nxboot {
|
|||||||
FsVersion_16_0_0,
|
FsVersion_16_0_0,
|
||||||
FsVersion_16_0_0_Exfat,
|
FsVersion_16_0_0_Exfat,
|
||||||
|
|
||||||
|
FsVersion_16_0_3,
|
||||||
|
FsVersion_16_0_3_Exfat,
|
||||||
|
|
||||||
FsVersion_Count,
|
FsVersion_Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,6 +242,9 @@ namespace ams::nxboot {
|
|||||||
|
|
||||||
{ 0x56, 0xE8, 0x56, 0x56, 0x6C, 0x38, 0xD8, 0xBE }, /* FsVersion_16_0_0 */
|
{ 0x56, 0xE8, 0x56, 0x56, 0x6C, 0x38, 0xD8, 0xBE }, /* FsVersion_16_0_0 */
|
||||||
{ 0xCF, 0xAB, 0x45, 0x0C, 0x2C, 0x53, 0x9D, 0xA9 }, /* FsVersion_16_0_0_Exfat */
|
{ 0xCF, 0xAB, 0x45, 0x0C, 0x2C, 0x53, 0x9D, 0xA9 }, /* FsVersion_16_0_0_Exfat */
|
||||||
|
|
||||||
|
{ 0x39, 0xEE, 0x1F, 0x1E, 0x0E, 0xA7, 0x32, 0x5D }, /* FsVersion_16_0_3 */
|
||||||
|
{ 0x62, 0xC6, 0x5E, 0xFD, 0x9A, 0xBF, 0x7C, 0x43 }, /* FsVersion_16_0_3_Exfat */
|
||||||
};
|
};
|
||||||
|
|
||||||
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
|
const InitialProcessBinaryHeader *FindInitialProcessBinary(const pkg2::Package2Header *header, const u8 *data, ams::TargetFirmware target_firmware) {
|
||||||
@@ -656,6 +662,14 @@ namespace ams::nxboot {
|
|||||||
AddPatch(fs_meta, 0x1913B9, NogcPatch0, sizeof(NogcPatch0));
|
AddPatch(fs_meta, 0x1913B9, NogcPatch0, sizeof(NogcPatch0));
|
||||||
AddPatch(fs_meta, 0x16B950, NogcPatch1, sizeof(NogcPatch1));
|
AddPatch(fs_meta, 0x16B950, NogcPatch1, sizeof(NogcPatch1));
|
||||||
break;
|
break;
|
||||||
|
case FsVersion_16_0_3:
|
||||||
|
AddPatch(fs_meta, 0x186729, NogcPatch0, sizeof(NogcPatch0));
|
||||||
|
AddPatch(fs_meta, 0x160CC0, NogcPatch1, sizeof(NogcPatch1));
|
||||||
|
break;
|
||||||
|
case FsVersion_16_0_3_Exfat:
|
||||||
|
AddPatch(fs_meta, 0x191409, NogcPatch0, sizeof(NogcPatch0));
|
||||||
|
AddPatch(fs_meta, 0x16B9A0, NogcPatch1, sizeof(NogcPatch1));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[subrepo]
|
[subrepo]
|
||||||
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||||
branch = master
|
branch = master
|
||||||
commit = b7711b8fbcec5013e1738218267d69b2cb44f85e
|
commit = cd0fc2c1d5728ec45414aaaa6efa28c269695992
|
||||||
parent = 590f22933db38f089ab2224f7fd86658a9533e8d
|
parent = 8ec7c096d04e6f9586be2cb785840cd482d4b900
|
||||||
method = merge
|
method = merge
|
||||||
cmdver = 0.4.1
|
cmdver = 0.4.1
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_WINDOWS
|
export ATMOSPHERE_DEFINES += -DATMOSPHERE_OS_WINDOWS
|
||||||
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer
|
export ATMOSPHERE_SETTINGS += -fno-omit-frame-pointer -fno-data-sections
|
||||||
export ATMOSPHERE_CFLAGS +=
|
export ATMOSPHERE_CFLAGS +=
|
||||||
export ATMOSPHERE_CXXFLAGS +=
|
export ATMOSPHERE_CXXFLAGS +=
|
||||||
export ATMOSPHERE_ASFLAGS +=
|
export ATMOSPHERE_ASFLAGS +=
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
|
|||||||
-Wl,--wrap,exit
|
-Wl,--wrap,exit
|
||||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||||
export CXXREQUIRED :=
|
export CXXREQUIRED :=
|
||||||
export CXXWRAPS := -Wl,--wrap,__p__acmdln
|
export CXXWRAPS := -Wl,--wrap,__p__acmdln -Wl,--wrap,_set_invalid_parameter_handler
|
||||||
else
|
else
|
||||||
export CXXREQUIRED :=
|
export CXXREQUIRED :=
|
||||||
export CXXWRAPS :=
|
export CXXWRAPS :=
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ namespace ams::kern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE bool Close() {
|
ALWAYS_INLINE bool Close() {
|
||||||
/* Atomically decrement the reference count, not allowing it to become negative. */
|
/* Atomically decrement the reference count, not allowing it to decrement past zero. */
|
||||||
u32 cur = m_value.Load<std::memory_order_relaxed>();
|
u32 cur = m_value.Load<std::memory_order_relaxed>();
|
||||||
do {
|
do {
|
||||||
MESOSPHERE_ABORT_UNLESS(cur > 0);
|
MESOSPHERE_ABORT_UNLESS(cur > 0);
|
||||||
@@ -257,7 +257,7 @@ namespace ams::kern {
|
|||||||
class KScopedAutoObject {
|
class KScopedAutoObject {
|
||||||
NON_COPYABLE(KScopedAutoObject);
|
NON_COPYABLE(KScopedAutoObject);
|
||||||
private:
|
private:
|
||||||
template<typename U>
|
template<typename U> requires std::derived_from<U, KAutoObject>
|
||||||
friend class KScopedAutoObject;
|
friend class KScopedAutoObject;
|
||||||
private:
|
private:
|
||||||
T *m_obj;
|
T *m_obj;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace ams::kern {
|
|||||||
/* Check that the object is closed. */
|
/* Check that the object is closed. */
|
||||||
R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState());
|
R_UNLESS(derived->IsServerClosed(), svc::ResultInvalidState());
|
||||||
|
|
||||||
return Delete(obj.GetPointerUnsafe(), name);
|
R_RETURN(Delete(obj.GetPointerUnsafe(), name));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Derived> requires std::derived_from<Derived, KAutoObject>
|
template<typename Derived> requires std::derived_from<Derived, KAutoObject>
|
||||||
|
|||||||
@@ -57,11 +57,26 @@ namespace ams::kern {
|
|||||||
using TraversalEntry = KPageTableImpl::TraversalEntry;
|
using TraversalEntry = KPageTableImpl::TraversalEntry;
|
||||||
using TraversalContext = KPageTableImpl::TraversalContext;
|
using TraversalContext = KPageTableImpl::TraversalContext;
|
||||||
|
|
||||||
struct MemoryRange {
|
class MemoryRange {
|
||||||
KPhysicalAddress address;
|
private:
|
||||||
size_t size;
|
KPhysicalAddress m_address;
|
||||||
|
size_t m_size;
|
||||||
|
bool m_heap;
|
||||||
|
public:
|
||||||
|
constexpr MemoryRange() : m_address(Null<KPhysicalAddress>), m_size(0), m_heap(false) { /* ... */ }
|
||||||
|
|
||||||
void Close();
|
void Set(KPhysicalAddress address, size_t size, bool heap) {
|
||||||
|
m_address = address;
|
||||||
|
m_size = size;
|
||||||
|
m_heap = heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr KPhysicalAddress GetAddress() const { return m_address; }
|
||||||
|
constexpr size_t GetSize() const { return m_size; }
|
||||||
|
constexpr bool IsHeap() const { return m_heap; }
|
||||||
|
|
||||||
|
void Open();
|
||||||
|
void Close();
|
||||||
};
|
};
|
||||||
protected:
|
protected:
|
||||||
enum MemoryFillValue {
|
enum MemoryFillValue {
|
||||||
|
|||||||
@@ -234,11 +234,11 @@ namespace ams::kern {
|
|||||||
KPriorityQueueImpl m_scheduled_queue;
|
KPriorityQueueImpl m_scheduled_queue;
|
||||||
KPriorityQueueImpl m_suggested_queue;
|
KPriorityQueueImpl m_suggested_queue;
|
||||||
private:
|
private:
|
||||||
constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
|
static constexpr ALWAYS_INLINE void ClearAffinityBit(u64 &affinity, s32 core) {
|
||||||
affinity &= ~(UINT64_C(1) << core);
|
affinity &= ~(UINT64_C(1) << core);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
|
static constexpr ALWAYS_INLINE s32 GetNextCore(u64 &affinity) {
|
||||||
const s32 core = __builtin_ctzll(static_cast<unsigned long long>(affinity));
|
const s32 core = __builtin_ctzll(static_cast<unsigned long long>(affinity));
|
||||||
ClearAffinityBit(affinity, core);
|
ClearAffinityBit(affinity, core);
|
||||||
return core;
|
return core;
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ namespace ams::kern {
|
|||||||
template<typename T> requires (std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
|
template<typename T> requires (std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
|
||||||
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KThread &rhs) {
|
static constexpr ALWAYS_INLINE int Compare(const T &lhs, const KThread &rhs) {
|
||||||
if (lhs.GetPriority() < rhs.GetPriority()) {
|
if (lhs.GetPriority() < rhs.GetPriority()) {
|
||||||
/* And then by priority. */
|
/* Sort by priority. */
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -120,10 +120,6 @@ namespace ams::kern {
|
|||||||
return m_address == rhs;
|
return m_address == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(uintptr_t rhs) const {
|
|
||||||
return m_address != rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow getting the address explicitly, for use in accessors. */
|
/* Allow getting the address explicitly, for use in accessors. */
|
||||||
constexpr ALWAYS_INLINE uintptr_t GetValue() const {
|
constexpr ALWAYS_INLINE uintptr_t GetValue() const {
|
||||||
return m_address;
|
return m_address;
|
||||||
|
|||||||
@@ -164,8 +164,9 @@ namespace ams::kern {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename Derived, typename Base, bool SupportDynamicExpansion = false> requires std::derived_from<Base, KAutoObjectWithList>
|
template<typename Derived, typename Base, bool SupportDynamicExpansion = false>
|
||||||
class KAutoObjectWithSlabHeapAndContainer : public KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion> {
|
class KAutoObjectWithSlabHeapAndContainer : public KAutoObjectWithSlabHeapBase<Derived, Base, SupportDynamicExpansion> {
|
||||||
|
static_assert(std::derived_from<Base, KAutoObjectWithList>);
|
||||||
private:
|
private:
|
||||||
static constinit inline KAutoObjectWithListContainer<Derived> s_container;
|
static constinit inline KAutoObjectWithListContainer<Derived> s_container;
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1133,17 +1133,17 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
size_t cur_size;
|
size_t cur_size;
|
||||||
{
|
{
|
||||||
/* Get the current contiguous range. */
|
/* Get the current contiguous range. */
|
||||||
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
KPageTableBase::MemoryRange contig_range;
|
||||||
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
|
R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned));
|
||||||
|
|
||||||
/* Ensure we close the range when we're done. */
|
/* Ensure we close the range when we're done. */
|
||||||
ON_SCOPE_EXIT { contig_range.Close(); };
|
ON_SCOPE_EXIT { contig_range.Close(); };
|
||||||
|
|
||||||
/* Get the current size. */
|
/* Get the current size. */
|
||||||
cur_size = contig_range.size;
|
cur_size = contig_range.GetSize();
|
||||||
|
|
||||||
/* Map the device page. */
|
/* Map the device page. */
|
||||||
R_TRY(this->MapDevicePage(contig_range.address, contig_range.size, cur_addr, device_perm));
|
R_TRY(this->MapDevicePage(contig_range.GetAddress(), cur_size, cur_addr, device_perm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advance. */
|
/* Advance. */
|
||||||
@@ -1288,7 +1288,7 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0);
|
||||||
|
|
||||||
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
|
/* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */
|
||||||
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
KPageTableBase::MemoryRange contig_range;
|
||||||
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
|
if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1300,8 +1300,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
/* Walk the directory. */
|
/* Walk the directory. */
|
||||||
KProcessAddress cur_process_address = process_address;
|
KProcessAddress cur_process_address = process_address;
|
||||||
size_t remaining_size = size;
|
size_t remaining_size = size;
|
||||||
KPhysicalAddress cur_phys_address = contig_range.address;
|
KPhysicalAddress cur_phys_address = contig_range.GetAddress();
|
||||||
size_t remaining_in_range = contig_range.size;
|
size_t remaining_in_range = contig_range.GetSize();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
u32 first_attr = 0;
|
u32 first_attr = 0;
|
||||||
while (remaining_size > 0) {
|
while (remaining_size > 0) {
|
||||||
@@ -1347,8 +1347,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
range_open = true;
|
range_open = true;
|
||||||
|
|
||||||
cur_phys_address = contig_range.address;
|
cur_phys_address = contig_range.GetAddress();
|
||||||
remaining_in_range = contig_range.size;
|
remaining_in_range = contig_range.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the physical address is expected. */
|
/* Check that the physical address is expected. */
|
||||||
@@ -1390,8 +1390,8 @@ namespace ams::kern::board::nintendo::nx {
|
|||||||
}
|
}
|
||||||
range_open = true;
|
range_open = true;
|
||||||
|
|
||||||
cur_phys_address = contig_range.address;
|
cur_phys_address = contig_range.GetAddress();
|
||||||
remaining_in_range = contig_range.size;
|
remaining_in_range = contig_range.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the physical address is expected, and there's enough in the range. */
|
/* Check that the physical address is expected, and there's enough in the range. */
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace ams::kern {
|
|||||||
|
|
||||||
/* Sleep the thread. */
|
/* Sleep the thread. */
|
||||||
{
|
{
|
||||||
KScopedSchedulerLockAndSleep lk(&timer, owner, timeout);
|
KScopedSchedulerLockAndSleep lk(std::addressof(timer), owner, timeout);
|
||||||
|
|
||||||
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
if (!allow_terminating_thread && owner->IsTerminationRequested()) {
|
||||||
lk.CancelSleep();
|
lk.CancelSleep();
|
||||||
|
|||||||
@@ -74,8 +74,18 @@ namespace ams::kern {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KPageTableBase::MemoryRange::Open() {
|
||||||
|
/* If the range contains heap pages, open them. */
|
||||||
|
if (this->IsHeap()) {
|
||||||
|
Kernel::GetMemoryManager().Open(this->GetAddress(), this->GetSize() / PageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void KPageTableBase::MemoryRange::Close() {
|
void KPageTableBase::MemoryRange::Close() {
|
||||||
Kernel::GetMemoryManager().Close(address, size / PageSize);
|
/* If the range contains heap pages, close them. */
|
||||||
|
if (this->IsHeap()) {
|
||||||
|
Kernel::GetMemoryManager().Close(this->GetAddress(), this->GetSize() / PageSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
|
Result KPageTableBase::InitializeForKernel(bool is_64_bit, void *table, KVirtualAddress start, KVirtualAddress end) {
|
||||||
@@ -1504,16 +1514,13 @@ namespace ams::kern {
|
|||||||
|
|
||||||
/* Check that the memory is contiguous (modulo the reference count bit). */
|
/* Check that the memory is contiguous (modulo the reference count bit). */
|
||||||
const u32 test_state_mask = state_mask | KMemoryState_FlagReferenceCounted;
|
const u32 test_state_mask = state_mask | KMemoryState_FlagReferenceCounted;
|
||||||
if (R_FAILED(this->CheckMemoryStateContiguous(address, size, test_state_mask, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr))) {
|
const bool is_heap = R_SUCCEEDED(this->CheckMemoryStateContiguous(address, size, test_state_mask, state | KMemoryState_FlagReferenceCounted, perm_mask, perm, attr_mask, attr));
|
||||||
|
if (!is_heap) {
|
||||||
R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask, perm, attr_mask, attr));
|
R_TRY(this->CheckMemoryStateContiguous(address, size, test_state_mask, state, perm_mask, perm, attr_mask, attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The memory is contiguous, so set the output range. */
|
/* The memory is contiguous, so set the output range. */
|
||||||
*out = {
|
out->Set(phys_address, size, is_heap);
|
||||||
.address = phys_address,
|
|
||||||
.size = size,
|
|
||||||
};
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2932,7 +2939,7 @@ namespace ams::kern {
|
|||||||
KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None));
|
KMemoryAttribute_IpcLocked | KMemoryAttribute_Locked, KMemoryAttribute_None));
|
||||||
|
|
||||||
/* We got the range, so open it. */
|
/* We got the range, so open it. */
|
||||||
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
|
out->Open();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -2949,7 +2956,7 @@ namespace ams::kern {
|
|||||||
KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared));
|
KMemoryAttribute_DeviceShared | KMemoryAttribute_Locked, KMemoryAttribute_DeviceShared));
|
||||||
|
|
||||||
/* We got the range, so open it. */
|
/* We got the range, so open it. */
|
||||||
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
|
out->Open();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -3020,7 +3027,7 @@ namespace ams::kern {
|
|||||||
KMemoryAttribute_Uncached, KMemoryAttribute_None));
|
KMemoryAttribute_Uncached, KMemoryAttribute_None));
|
||||||
|
|
||||||
/* We got the range, so open it. */
|
/* We got the range, so open it. */
|
||||||
Kernel::GetMemoryManager().Open(out->address, out->size / PageSize);
|
out->Open();
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,15 @@ namespace ams::kern::svc {
|
|||||||
size_t remaining = size;
|
size_t remaining = size;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
/* Get a contiguous range to operate on. */
|
/* Get a contiguous range to operate on. */
|
||||||
KPageTableBase::MemoryRange contig_range = { .address = Null<KPhysicalAddress>, .size = 0 };
|
KPageTableBase::MemoryRange contig_range;
|
||||||
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address));
|
||||||
|
|
||||||
/* Close the range when we're done operating on it. */
|
/* Close the range when we're done operating on it. */
|
||||||
ON_SCOPE_EXIT { contig_range.Close(); };
|
ON_SCOPE_EXIT { contig_range.Close(); };
|
||||||
|
|
||||||
/* Adjust to remain within range. */
|
/* Adjust to remain within range. */
|
||||||
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.address);
|
KVirtualAddress operate_address = KMemoryLayout::GetLinearVirtualAddress(contig_range.GetAddress());
|
||||||
size_t operate_size = contig_range.size;
|
size_t operate_size = contig_range.GetSize();
|
||||||
if (cur_address < address) {
|
if (cur_address < address) {
|
||||||
operate_address += (address - cur_address);
|
operate_address += (address - cur_address);
|
||||||
operate_size -= (address - cur_address);
|
operate_size -= (address - cur_address);
|
||||||
@@ -57,7 +57,7 @@ namespace ams::kern::svc {
|
|||||||
operation.Operate(GetVoidPointer(operate_address), operate_size);
|
operation.Operate(GetVoidPointer(operate_address), operate_size);
|
||||||
|
|
||||||
/* Advance. */
|
/* Advance. */
|
||||||
cur_address += contig_range.size;
|
cur_address += contig_range.GetSize();
|
||||||
remaining -= operate_size;
|
remaining -= operate_size;
|
||||||
}
|
}
|
||||||
MESOSPHERE_ASSERT(remaining == 0);
|
MESOSPHERE_ASSERT(remaining == 0);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace ams::kern::svc {
|
|||||||
R_UNLESS(size < ams::kern::MainMemorySizeMax, svc::ResultInvalidSize());
|
R_UNLESS(size < ams::kern::MainMemorySizeMax, svc::ResultInvalidSize());
|
||||||
|
|
||||||
/* Set the heap size. */
|
/* Set the heap size. */
|
||||||
KProcessAddress address;
|
KProcessAddress address = Null<KProcessAddress>;
|
||||||
R_TRY(GetCurrentProcess().GetPageTable().SetHeapSize(std::addressof(address), size));
|
R_TRY(GetCurrentProcess().GetPageTable().SetHeapSize(std::addressof(address), size));
|
||||||
|
|
||||||
/* Set the output. */
|
/* Set the output. */
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ namespace ams::hos {
|
|||||||
Version_15_0_0 = ::ams::TargetFirmware_15_0_0,
|
Version_15_0_0 = ::ams::TargetFirmware_15_0_0,
|
||||||
Version_15_0_1 = ::ams::TargetFirmware_15_0_1,
|
Version_15_0_1 = ::ams::TargetFirmware_15_0_1,
|
||||||
Version_16_0_0 = ::ams::TargetFirmware_16_0_0,
|
Version_16_0_0 = ::ams::TargetFirmware_16_0_0,
|
||||||
|
Version_16_0_1 = ::ams::TargetFirmware_16_0_1,
|
||||||
|
Version_16_0_2 = ::ams::TargetFirmware_16_0_2,
|
||||||
|
Version_16_0_3 = ::ams::TargetFirmware_16_0_3,
|
||||||
|
|
||||||
Version_Current = ::ams::TargetFirmware_Current,
|
Version_Current = ::ams::TargetFirmware_Current,
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <stratosphere/os/os_process_memory_api.hpp>
|
#include <stratosphere/os/os_process_memory_api.hpp>
|
||||||
#include <stratosphere/os/os_process_code_memory_api.hpp>
|
#include <stratosphere/os/os_process_code_memory_api.hpp>
|
||||||
#include <stratosphere/os/os_insecure_memory_api.hpp>
|
#include <stratosphere/os/os_insecure_memory_api.hpp>
|
||||||
|
#include <stratosphere/os/os_unsafe_memory_api.hpp>
|
||||||
#include <stratosphere/os/os_random.hpp>
|
#include <stratosphere/os/os_random.hpp>
|
||||||
#include <stratosphere/os/os_mutex.hpp>
|
#include <stratosphere/os/os_mutex.hpp>
|
||||||
#include <stratosphere/os/os_condition_variable.hpp>
|
#include <stratosphere/os/os_condition_variable.hpp>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <stratosphere/os/os_memory_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
Result AllocateUnsafeMemory(uintptr_t *out_address, size_t size);
|
||||||
|
Result FreeUnsafeMemory(uintptr_t address, size_t size);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -235,4 +235,31 @@ namespace ams::settings {
|
|||||||
return !(lhs <= rhs);
|
return !(lhs <= rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BluetoothDevicesSettings : public sf::LargeData {
|
||||||
|
u8 address[0x6];
|
||||||
|
char name[0x20];
|
||||||
|
u8 class_of_device[0x3];
|
||||||
|
u8 link_key[0x10];
|
||||||
|
u8 link_key_present;
|
||||||
|
u16 version;
|
||||||
|
u32 trusted_services;
|
||||||
|
u16 vid;
|
||||||
|
u16 pid;
|
||||||
|
u8 sub_class;
|
||||||
|
u8 attribute_mask;
|
||||||
|
u16 descriptor_length;
|
||||||
|
u8 descriptor[0x80];
|
||||||
|
u8 key_type;
|
||||||
|
u8 device_type;
|
||||||
|
u16 brr_size;
|
||||||
|
u8 brr[0x9];
|
||||||
|
u8 reserved0;
|
||||||
|
char name2[0xF9];
|
||||||
|
u8 reserved1[0x31];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||||
|
static_assert(sizeof(BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,10 +154,12 @@ spl_secure_monitor_api.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/l
|
|||||||
fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
|
fs_id_string_impl.os.generic.o: CXXFLAGS += -I$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/include
|
||||||
|
|
||||||
ifeq ($(ATMOSPHERE_OS_NAME),windows)
|
ifeq ($(ATMOSPHERE_OS_NAME),windows)
|
||||||
os_%.o: CXXFLAGS += -fno-lto
|
# I do not remember why these had fno-lto, but it appears to
|
||||||
fssystem_%.o: CXXFLAGS += -fno-lto
|
# work without no-lto (2023/03/09), so I am disabling these. I may regret this later.
|
||||||
fssrv_%.o: CXXFLAGS += -fno-lto
|
#os_%.o: CXXFLAGS += -fno-lto
|
||||||
fs_%.o: CXXFLAGS += -fno-lto
|
#fssystem_%.o: CXXFLAGS += -fno-lto
|
||||||
|
#fssrv_%.o: CXXFLAGS += -fno-lto
|
||||||
|
#fs_%.o: CXXFLAGS += -fno-lto
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
extern "C" char **__real___p__acmdln(void);
|
extern "C" char **__real___p__acmdln(void);
|
||||||
|
extern "C" _invalid_parameter_handler __real__set_invalid_parameter_handler(_invalid_parameter_handler);
|
||||||
|
|
||||||
namespace ams {
|
namespace ams {
|
||||||
|
|
||||||
@@ -65,4 +66,11 @@ extern "C" {
|
|||||||
return __real___p__acmdln();
|
return __real___p__acmdln();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* On some mingw gcc versions, acmdln isn't used, so we need to hook a different part of crt init. */
|
||||||
|
_invalid_parameter_handler __wrap__set_invalid_parameter_handler(_invalid_parameter_handler handler) {
|
||||||
|
::ams::os::Initialize();
|
||||||
|
return __real__set_invalid_parameter_handler(handler);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace ams::erpt::srv {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Context(CategoryId cat, u32 max_records) : m_category(cat), m_max_record_count(max_records), m_record_count(0) {
|
Context::Context(CategoryId cat) : m_category(cat) {
|
||||||
g_category_list.push_front(*this);
|
g_category_list.push_front(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,12 +38,11 @@ namespace ams::erpt::srv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result Context::AddCategoryToReport(Report *report) {
|
Result Context::AddCategoryToReport(Report *report) {
|
||||||
R_SUCCEED_IF(m_record_list.empty());
|
if (m_record != nullptr) {
|
||||||
|
const auto *entry = m_record->GetContextEntryPtr();
|
||||||
for (auto it = m_record_list.begin(); it != m_record_list.end(); it++) {
|
for (u32 i = 0; i < entry->field_count; i++) {
|
||||||
for (u32 i = 0; i < it->m_ctx.field_count; i++) {
|
auto *field = std::addressof(entry->fields[i]);
|
||||||
auto *field = std::addressof(it->m_ctx.fields[i]);
|
u8 *arr_buf = entry->array_buffer;
|
||||||
u8 *arr_buf = it->m_ctx.array_buffer;
|
|
||||||
|
|
||||||
switch (field->type) {
|
switch (field->type) {
|
||||||
case FieldType_Bool: R_TRY(Cipher::AddField(report, field->id, field->value_bool)); break;
|
case FieldType_Bool: R_TRY(Cipher::AddField(report, field->id, field->value_bool)); break;
|
||||||
@@ -70,46 +69,23 @@ namespace ams::erpt::srv {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Context::AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
||||||
auto record = std::make_unique<ContextRecord>();
|
auto record = std::make_unique<ContextRecord>();
|
||||||
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
|
||||||
|
|
||||||
R_TRY(record->Initialize(entry, data, data_size));
|
R_TRY(record->Initialize(entry, data, data_size));
|
||||||
|
|
||||||
this->AddContextRecordToCategory(std::move(record));
|
R_RETURN(SubmitContextRecord(std::move(record)));
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Context::AddContextRecordToCategory(std::unique_ptr<ContextRecord> record) {
|
|
||||||
if (m_record_count < m_max_record_count) {
|
|
||||||
m_record_list.push_front(*record.release());
|
|
||||||
m_record_count++;
|
|
||||||
} else {
|
|
||||||
ContextRecord *back = std::addressof(m_record_list.back());
|
|
||||||
m_record_list.pop_back();
|
|
||||||
m_record_list.push_front(*record.release());
|
|
||||||
delete back;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result Context::SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size) {
|
|
||||||
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
|
|
||||||
return cur.m_category == entry->category;
|
|
||||||
});
|
|
||||||
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
|
|
||||||
|
|
||||||
R_RETURN(it->AddContextToCategory(entry, data, data_size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Context::SubmitContextRecord(std::unique_ptr<ContextRecord> record) {
|
Result Context::SubmitContextRecord(std::unique_ptr<ContextRecord> record) {
|
||||||
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
|
auto it = util::range::find_if(g_category_list, [&](const Context &cur) {
|
||||||
return cur.m_category == record->m_ctx.category;
|
return cur.m_category == record->GetContextEntryPtr()->category;
|
||||||
});
|
});
|
||||||
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
|
R_UNLESS(it != g_category_list.end(), erpt::ResultCategoryNotFound());
|
||||||
|
|
||||||
R_RETURN(it->AddContextRecordToCategory(std::move(record)));
|
it->m_record = std::move(record);
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Context::WriteContextsToReport(Report *report) {
|
Result Context::WriteContextsToReport(Report *report) {
|
||||||
|
|||||||
@@ -26,16 +26,12 @@ namespace ams::erpt::srv {
|
|||||||
class Context : public Allocator, public util::IntrusiveListBaseNode<Context> {
|
class Context : public Allocator, public util::IntrusiveListBaseNode<Context> {
|
||||||
private:
|
private:
|
||||||
const CategoryId m_category;
|
const CategoryId m_category;
|
||||||
const u32 m_max_record_count;
|
std::unique_ptr<ContextRecord> m_record;
|
||||||
u32 m_record_count;
|
|
||||||
util::IntrusiveListBaseTraits<ContextRecord>::ListType m_record_list;
|
|
||||||
public:
|
public:
|
||||||
Context(CategoryId cat, u32 max_records);
|
Context(CategoryId cat);
|
||||||
~Context();
|
~Context();
|
||||||
|
|
||||||
Result AddCategoryToReport(Report *report);
|
Result AddCategoryToReport(Report *report);
|
||||||
Result AddContextToCategory(const ContextEntry *entry, const u8 *data, u32 data_size);
|
|
||||||
Result AddContextRecordToCategory(std::unique_ptr<ContextRecord> record);
|
|
||||||
public:
|
public:
|
||||||
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
|
static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size);
|
||||||
static Result SubmitContextRecord(std::unique_ptr<ContextRecord> record);
|
static Result SubmitContextRecord(std::unique_ptr<ContextRecord> record);
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ namespace ams::erpt::srv {
|
|||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
|
|
||||||
class ContextRecord : public Allocator, public util::IntrusiveListBaseNode<ContextRecord> {
|
class ContextRecord : public Allocator {
|
||||||
friend class Context;
|
|
||||||
private:
|
private:
|
||||||
static u32 s_record_count;
|
static u32 s_record_count;
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ namespace ams::erpt::srv {
|
|||||||
g_sf_allocator.Attach(g_heap_handle);
|
g_sf_allocator.Attach(g_heap_handle);
|
||||||
|
|
||||||
for (auto i = 0; i < CategoryId_Count; i++) {
|
for (auto i = 0; i < CategoryId_Count; i++) {
|
||||||
Context *ctx = new Context(static_cast<CategoryId>(i), 1);
|
Context *ctx = new Context(static_cast<CategoryId>(i));
|
||||||
AMS_ABORT_UNLESS(ctx != nullptr);
|
AMS_ABORT_UNLESS(ctx != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace ams::os::impl {
|
|||||||
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
|
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
|
||||||
[](uintptr_t map_address, size_t map_size) -> Result {
|
[](uintptr_t map_address, size_t map_size) -> Result {
|
||||||
R_TRY_CATCH(svc::MapInsecureMemory(map_address, map_size)) {
|
R_TRY_CATCH(svc::MapInsecureMemory(map_address, map_size)) {
|
||||||
|
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
|
||||||
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
class UnsafeMemoryImpl {
|
||||||
|
public:
|
||||||
|
static Result AllocateUnsafeMemoryImpl(uintptr_t *out_address, size_t size);
|
||||||
|
static Result FreeUnsafeMemoryImpl(uintptr_t address, size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <stratosphere.hpp>
|
||||||
|
#include "os_unsafe_memory_impl.hpp"
|
||||||
|
#include "os_aslr_space_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::os::impl {
|
||||||
|
|
||||||
|
Result UnsafeMemoryImpl::AllocateUnsafeMemoryImpl(uintptr_t *out_address, size_t size) {
|
||||||
|
/* Map at a random address. */
|
||||||
|
R_RETURN(impl::GetAslrSpaceManager().MapAtRandomAddress(out_address,
|
||||||
|
[](uintptr_t map_address, size_t map_size) -> Result {
|
||||||
|
R_TRY_CATCH(svc::MapPhysicalMemoryUnsafe(map_address, map_size)) {
|
||||||
|
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||||
|
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfMemory())
|
||||||
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultInvalidCurrentMemoryState())
|
||||||
|
R_CONVERT(svc::ResultLimitReached, os::ResultOutOfMemory())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
},
|
||||||
|
[](uintptr_t map_address, size_t map_size) -> void {
|
||||||
|
R_ABORT_UNLESS(UnsafeMemoryImpl::FreeUnsafeMemoryImpl(map_address, map_size));
|
||||||
|
},
|
||||||
|
size,
|
||||||
|
0
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UnsafeMemoryImpl::FreeUnsafeMemoryImpl(uintptr_t address, size_t size) {
|
||||||
|
R_TRY_CATCH(svc::UnmapPhysicalMemoryUnsafe(address, size)) {
|
||||||
|
R_CONVERT(svc::ResultOutOfResource, os::ResultOutOfResource())
|
||||||
|
R_CONVERT(svc::ResultOutOfMemory, os::ResultOutOfResource())
|
||||||
|
R_CONVERT(svc::ResultInvalidCurrentMemory, os::ResultBusy())
|
||||||
|
R_CONVERT(svc::ResultInvalidMemoryRegion, os::ResultInvalidParameter())
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
libraries/libstratosphere/source/os/os_unsafe_memory.cpp
Normal file
39
libraries/libstratosphere/source/os/os_unsafe_memory.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <stratosphere.hpp>
|
||||||
|
#include "impl/os_unsafe_memory_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::os {
|
||||||
|
|
||||||
|
Result AllocateUnsafeMemory(uintptr_t *out_address, size_t size) {
|
||||||
|
/* Check arguments. */
|
||||||
|
AMS_ASSERT(size > 0);
|
||||||
|
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
|
||||||
|
|
||||||
|
/* Allocate memory. */
|
||||||
|
R_RETURN(impl::UnsafeMemoryImpl::AllocateUnsafeMemoryImpl(out_address, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FreeUnsafeMemory(uintptr_t address, size_t size) {
|
||||||
|
/* Check arguments. */
|
||||||
|
AMS_ASSERT(util::IsAligned(address, os::MemoryPageSize));
|
||||||
|
AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize));
|
||||||
|
|
||||||
|
/* Free memory. */
|
||||||
|
R_RETURN(impl::UnsafeMemoryImpl::FreeUnsafeMemoryImpl(address, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ namespace ams::sf::hipc {
|
|||||||
AMS_ABORT_UNLESS(ServerManagerBase::CanAnyManageMitmServers());
|
AMS_ABORT_UNLESS(ServerManagerBase::CanAnyManageMitmServers());
|
||||||
|
|
||||||
/* Clone the forward service. */
|
/* Clone the forward service. */
|
||||||
std::shared_ptr<::Service> new_forward_service = std::move(ServerSession::CreateForwardService());
|
std::shared_ptr<::Service> new_forward_service = ServerSession::CreateForwardService();
|
||||||
R_ABORT_UNLESS(serviceClone(util::GetReference(m_session->m_forward_service).get(), new_forward_service.get()));
|
R_ABORT_UNLESS(serviceClone(util::GetReference(m_session->m_forward_service).get(), new_forward_service.get()));
|
||||||
R_ABORT_UNLESS(tagged_manager->RegisterMitmSession(server_handle, std::move(clone), std::move(new_forward_service)));
|
R_ABORT_UNLESS(tagged_manager->RegisterMitmSession(server_handle, std::move(clone), std::move(new_forward_service)));
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ namespace ams::sf::hipc {
|
|||||||
R_ABORT_UNLESS(hipc::CreateSession(std::addressof(server_handle), std::addressof(client_handle)));
|
R_ABORT_UNLESS(hipc::CreateSession(std::addressof(server_handle), std::addressof(client_handle)));
|
||||||
|
|
||||||
/* Register. */
|
/* Register. */
|
||||||
std::shared_ptr<::Service> new_forward_service = std::move(ServerSession::CreateForwardService());
|
std::shared_ptr<::Service> new_forward_service = ServerSession::CreateForwardService();
|
||||||
serviceCreate(new_forward_service.get(), new_forward_target);
|
serviceCreate(new_forward_service.get(), new_forward_target);
|
||||||
R_ABORT_UNLESS(m_manager->RegisterMitmSession(server_handle, std::move(object), std::move(new_forward_service)));
|
R_ABORT_UNLESS(m_manager->RegisterMitmSession(server_handle, std::move(object), std::move(new_forward_service)));
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MINOR 5
|
#define ATMOSPHERE_RELEASE_VERSION_MINOR 5
|
||||||
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
|
#define ATMOSPHERE_RELEASE_VERSION_MICRO 3
|
||||||
|
|
||||||
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO
|
||||||
|
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 16
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 16
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 0
|
||||||
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0
|
#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 3
|
||||||
|
|||||||
@@ -74,8 +74,11 @@
|
|||||||
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_0 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 0)
|
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_0 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 0)
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_1 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 1)
|
#define ATMOSPHERE_TARGET_FIRMWARE_15_0_1 ATMOSPHERE_TARGET_FIRMWARE(15, 0, 1)
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_0 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 0)
|
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_0 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 0)
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_1 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 1)
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_2 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 2)
|
||||||
|
#define ATMOSPHERE_TARGET_FIRMWARE_16_0_3 ATMOSPHERE_TARGET_FIRMWARE(16, 0, 3)
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_16_0_0
|
#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_16_0_3
|
||||||
|
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
#define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0)
|
||||||
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
#define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT
|
||||||
@@ -142,6 +145,9 @@ namespace ams {
|
|||||||
TargetFirmware_15_0_0 = ATMOSPHERE_TARGET_FIRMWARE_15_0_0,
|
TargetFirmware_15_0_0 = ATMOSPHERE_TARGET_FIRMWARE_15_0_0,
|
||||||
TargetFirmware_15_0_1 = ATMOSPHERE_TARGET_FIRMWARE_15_0_1,
|
TargetFirmware_15_0_1 = ATMOSPHERE_TARGET_FIRMWARE_15_0_1,
|
||||||
TargetFirmware_16_0_0 = ATMOSPHERE_TARGET_FIRMWARE_16_0_0,
|
TargetFirmware_16_0_0 = ATMOSPHERE_TARGET_FIRMWARE_16_0_0,
|
||||||
|
TargetFirmware_16_0_1 = ATMOSPHERE_TARGET_FIRMWARE_16_0_1,
|
||||||
|
TargetFirmware_16_0_2 = ATMOSPHERE_TARGET_FIRMWARE_16_0_2,
|
||||||
|
TargetFirmware_16_0_3 = ATMOSPHERE_TARGET_FIRMWARE_16_0_3,
|
||||||
|
|
||||||
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT,
|
||||||
|
|
||||||
|
|||||||
@@ -457,10 +457,10 @@ namespace ams::result::impl {
|
|||||||
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
|
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) do { ::ams::diag::impl::FatalErrorByResultForNx(val); AMS_INFINITE_LOOP(); AMS_ASSUME(false); } while (false)
|
||||||
#elif defined(ATMOSPHERE_OS_HORIZON)
|
#elif defined(ATMOSPHERE_OS_HORIZON)
|
||||||
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
|
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
|
||||||
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
|
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32, cond, static_cast<::ams::Result>(val).GetModule(), static_cast<::ams::Result>(val).GetDescription(), static_cast<::ams::Result>(val).GetInnerValue())
|
||||||
#else
|
#else
|
||||||
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
|
#define AMS_CALL_ON_RESULT_ASSERTION_IMPL(cond, val) AMS_CALL_ASSERT_FAIL_IMPL(::ams::diag::AssertionType_Assert, "ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(static_cast<::ams::Result>(val)))
|
||||||
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, val.GetModule(), val.GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(val))
|
#define AMS_CALL_ON_RESULT_ABORT_IMPL(cond, val) AMS_CALL_ABORT_IMPL("ams::Result::IsSuccess()", "Failed: %s\n Module: %d\n Description: %d\n InnerValue: 0x%08" PRIX32 "\n Name: %s", cond, static_cast<::ams::Result>(val).GetModule(), static_cast<::ams::Result>(val).GetDescription(), static_cast<::ams::Result>(val).GetInnerValue(), ::ams::GetResultName(static_cast<::ams::Result>(val)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Evaluates an expression that returns a result, and asserts the result if it would fail.
|
/// Evaluates an expression that returns a result, and asserts the result if it would fail.
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ namespace ams::svc {
|
|||||||
|
|
||||||
constexpr inline size_t AddressMemoryRegionSmall36Size = 2_GB;
|
constexpr inline size_t AddressMemoryRegionSmall36Size = 2_GB;
|
||||||
constexpr inline size_t AddressMemoryRegionLarge36Size = 64_GB - AddressMemoryRegionSmall36Size;
|
constexpr inline size_t AddressMemoryRegionLarge36Size = 64_GB - AddressMemoryRegionSmall36Size;
|
||||||
constexpr inline size_t AddressMemoryRegionHeap36Size = 6_GB;
|
constexpr inline size_t AddressMemoryRegionHeap36Size = 8_GB;
|
||||||
constexpr inline size_t AddressMemoryRegionAlias36Size = 6_GB;
|
constexpr inline size_t AddressMemoryRegionAlias36Size = 6_GB;
|
||||||
|
|
||||||
constexpr inline size_t AddressMemoryRegionSmall39Size = 64_GB;
|
constexpr inline size_t AddressMemoryRegionSmall39Size = 64_GB;
|
||||||
constexpr inline size_t AddressMemoryRegionHeap39Size = 6_GB;
|
constexpr inline size_t AddressMemoryRegionHeap39Size = 8_GB;
|
||||||
constexpr inline size_t AddressMemoryRegionAlias39Size = 64_GB;
|
constexpr inline size_t AddressMemoryRegionAlias39Size = 64_GB;
|
||||||
constexpr inline size_t AddressMemoryRegionStack39Size = 2_GB;
|
constexpr inline size_t AddressMemoryRegionStack39Size = 2_GB;
|
||||||
|
|
||||||
|
|||||||
@@ -144,10 +144,6 @@ namespace ams::util {
|
|||||||
return m_node == rhs.m_node;
|
return m_node == rhs.m_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||||
return m_node;
|
return m_node;
|
||||||
}
|
}
|
||||||
@@ -355,10 +351,6 @@ namespace ams::util {
|
|||||||
return m_iterator == rhs.m_iterator;
|
return m_iterator == rhs.m_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||||
return std::addressof(Traits::GetParent(*m_iterator));
|
return std::addressof(Traits::GetParent(*m_iterator));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,10 +94,6 @@ namespace ams::util {
|
|||||||
return m_node == rhs.m_node;
|
return m_node == rhs.m_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||||
return m_node;
|
return m_node;
|
||||||
}
|
}
|
||||||
@@ -304,10 +300,6 @@ namespace ams::util {
|
|||||||
return m_impl == rhs.m_impl;
|
return m_impl == rhs.m_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE bool operator!=(const Iterator &rhs) const {
|
|
||||||
return !(*this == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE pointer operator->() const {
|
constexpr ALWAYS_INLINE pointer operator->() const {
|
||||||
return Traits::GetParent(std::addressof(*m_impl));
|
return Traits::GetParent(std::addressof(*m_impl));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ _ZN3ams4kern10KScheduler12ScheduleImplEv:
|
|||||||
mov x1, x0
|
mov x1, x0
|
||||||
|
|
||||||
/* First, clear the need's scheduling bool (and dmb ish after, as it's an atomic). */
|
/* First, clear the need's scheduling bool (and dmb ish after, as it's an atomic). */
|
||||||
/* TODO: Should this be a stlrb? Nintendo does not do one. */
|
|
||||||
strb wzr, [x1]
|
strb wzr, [x1]
|
||||||
dmb ish
|
dmb ish
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ namespace ams::mitm::fs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
||||||
if (program_id == ncm::SystemProgramId::Sdb) {
|
/* NOTE: In 16.0.0+, this was moved to glue. */
|
||||||
|
if (program_id == ncm::SystemProgramId::Sdb || program_id == ncm::SystemProgramId::Glue) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,12 +251,11 @@ namespace ams::mitm::fs {
|
|||||||
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
|
using DirectoryTableWriter = TableWriter<DirectoryEntry>;
|
||||||
using FileTableWriter = TableWriter<FileEntry>;
|
using FileTableWriter = TableWriter<FileEntry>;
|
||||||
|
|
||||||
constexpr inline u32 CalculatePathHash(u32 parent, const char *_path, u32 start, size_t path_len) {
|
constexpr inline u32 CalculatePathHash(u32 parent, const char *path, u32 start, size_t path_len) {
|
||||||
const unsigned char *path = reinterpret_cast<const unsigned char *>(_path);
|
|
||||||
u32 hash = parent ^ 123456789;
|
u32 hash = parent ^ 123456789;
|
||||||
for (size_t i = 0; i < path_len; i++) {
|
for (size_t i = 0; i < path_len; i++) {
|
||||||
hash = (hash >> 5) | (hash << 27);
|
hash = (hash >> 5) | (hash << 27);
|
||||||
hash ^= path[start + i];
|
hash ^= static_cast<unsigned char>(path[start + i]);
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "setsys_mitm_service.hpp"
|
#include "setsys_mitm_service.hpp"
|
||||||
#include "settings_sd_kvs.hpp"
|
#include "settings_sd_kvs.hpp"
|
||||||
|
#include "setsys_shim.h"
|
||||||
|
|
||||||
namespace ams::mitm::settings {
|
namespace ams::mitm::settings {
|
||||||
|
|
||||||
@@ -23,6 +24,8 @@ namespace ams::mitm::settings {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char ExternalBluetoothDatabasePath[] = "@Sdcard:/atmosphere/bluetooth_devices.db";
|
||||||
|
|
||||||
constinit os::SdkMutex g_firmware_version_lock;
|
constinit os::SdkMutex g_firmware_version_lock;
|
||||||
constinit bool g_cached_firmware_version;
|
constinit bool g_cached_firmware_version;
|
||||||
constinit settings::FirmwareVersion g_firmware_version;
|
constinit settings::FirmwareVersion g_firmware_version;
|
||||||
@@ -77,7 +80,7 @@ namespace ams::mitm::settings {
|
|||||||
CacheFirmwareVersion();
|
CacheFirmwareVersion();
|
||||||
|
|
||||||
/* We want to give a special firmware version to the home menu title, and nothing else. */
|
/* We want to give a special firmware version to the home menu title, and nothing else. */
|
||||||
/* This means Qlaunch + Maintenance Menu, and nothing else. */
|
/* This means qlaunch + maintenance menu, and nothing else. */
|
||||||
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
||||||
*out = g_ams_firmware_version;
|
*out = g_ams_firmware_version;
|
||||||
} else {
|
} else {
|
||||||
@@ -87,6 +90,99 @@ namespace ams::mitm::settings {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsExternalBluetoothDatabaseEnabled() {
|
||||||
|
u8 en = 0;
|
||||||
|
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db");
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasExternalBluetoothDatabase() {
|
||||||
|
bool file_exists;
|
||||||
|
R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), ExternalBluetoothDatabasePath));
|
||||||
|
return file_exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadExternalBluetoothDatabase(s32 *entries_read, settings::BluetoothDevicesSettings *db, size_t db_max_size) {
|
||||||
|
/* Open the external database file. */
|
||||||
|
fs::FileHandle file;
|
||||||
|
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Read));
|
||||||
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
|
/* Read the number of database entries stored in external database. */
|
||||||
|
u64 total_entries;
|
||||||
|
R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries)));
|
||||||
|
|
||||||
|
u64 db_offset = sizeof(total_entries);
|
||||||
|
if (total_entries > db_max_size) {
|
||||||
|
/* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit. */
|
||||||
|
db_offset += (total_entries - db_max_size) * sizeof(settings::BluetoothDevicesSettings);
|
||||||
|
|
||||||
|
/* Cap number of database entries read to size of database on this firmware. */
|
||||||
|
total_entries = db_max_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read database entries. */
|
||||||
|
R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||||
|
|
||||||
|
/* Convert entries to the old format if running on a firmware below 13.0.0. */
|
||||||
|
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||||
|
for (size_t i = 0; i < total_entries; ++i) {
|
||||||
|
/* Copy the newer name field to the older one. */
|
||||||
|
util::TSNPrintf(db[i].name, sizeof(db[i].name), "%s", db[i].name2);
|
||||||
|
|
||||||
|
/* Clear the newer name field. */
|
||||||
|
std::memset(db[i].name2, 0, sizeof(db[i].name2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the output. */
|
||||||
|
*entries_read = static_cast<s32>(total_entries);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result StoreExternalBluetoothDatabase(const settings::BluetoothDevicesSettings *db, u64 total_entries) {
|
||||||
|
/* Open the external database file. */
|
||||||
|
fs::FileHandle file;
|
||||||
|
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Write));
|
||||||
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
|
/* Ensure the file is the appropriate size for the number of entries. */
|
||||||
|
R_TRY(fs::SetFileSize(file, sizeof(total_entries) + total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||||
|
|
||||||
|
/* Write the number of database entries. */
|
||||||
|
R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None));
|
||||||
|
|
||||||
|
/* Write the database entries. */
|
||||||
|
u64 db_offset = sizeof(total_entries);
|
||||||
|
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||||
|
/* Convert entries to the new format if running on a firmware below 13.0.0. */
|
||||||
|
for (size_t i = 0; i < total_entries; ++i) {
|
||||||
|
/* Make a copy of the current database entry. */
|
||||||
|
settings::BluetoothDevicesSettings tmp = db[i];
|
||||||
|
|
||||||
|
/* Copy the older name field to the newer one. */
|
||||||
|
util::TSNPrintf(tmp.name2, sizeof(tmp.name2), "%s", tmp.name);
|
||||||
|
|
||||||
|
/* Clear the original name field. */
|
||||||
|
std::memset(tmp.name, 0, sizeof(tmp.name));
|
||||||
|
|
||||||
|
/* Write the converted database entry. */
|
||||||
|
R_TRY(fs::WriteFile(file, db_offset, std::addressof(tmp), sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::None));
|
||||||
|
|
||||||
|
/* Advance to the next database entry. */
|
||||||
|
db_offset += sizeof(settings::BluetoothDevicesSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush the data we've written. */
|
||||||
|
R_TRY(fs::FlushFile(file));
|
||||||
|
} else {
|
||||||
|
/* We can just write the database to the file. */
|
||||||
|
R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::Flush));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
|
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
|
||||||
@@ -102,6 +198,47 @@ namespace ams::mitm::settings {
|
|||||||
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray<settings::BluetoothDevicesSettings> &settings) {
|
||||||
|
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||||
|
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
/* Create the external database if it doesn't exist. */
|
||||||
|
if (!HasExternalBluetoothDatabase()) {
|
||||||
|
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Backup the local database to the sd card. */
|
||||||
|
R_TRY(StoreExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize()));
|
||||||
|
|
||||||
|
/* Ensure that the updated database is stored to the system save as usual. */
|
||||||
|
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||||
|
R_TRY(setsysSetBluetoothDevicesSettingsFwd(m_forward_service.get(), reinterpret_cast<const ::SetSysBluetoothDevicesSettings *>(settings.GetPointer()), settings.GetSize()));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<settings::BluetoothDevicesSettings> &out) {
|
||||||
|
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||||
|
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||||
|
|
||||||
|
if (!HasExternalBluetoothDatabase()) {
|
||||||
|
/* Fetch the local database from the system save. */
|
||||||
|
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||||
|
R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), reinterpret_cast<::SetSysBluetoothDevicesSettings *>(out.GetPointer()), out.GetSize()));
|
||||||
|
|
||||||
|
/* Create the external database file. */
|
||||||
|
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||||
|
|
||||||
|
/* Backup the local database to the sd card. */
|
||||||
|
R_TRY(StoreExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue()));
|
||||||
|
} else {
|
||||||
|
/* Read the external database from the sd card. */
|
||||||
|
R_TRY(ReadExternalBluetoothDatabase(out_count.GetPointer(), out.GetPointer(), out.GetSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
||||||
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
|
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
|
||||||
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
||||||
|
|||||||
@@ -16,12 +16,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
|
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
|
AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings), (settings)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
|
AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out), (out_count, out)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
|
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
|
||||||
|
|
||||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
|
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
|
||||||
|
|
||||||
@@ -41,6 +43,8 @@ namespace ams::mitm::settings {
|
|||||||
public:
|
public:
|
||||||
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
|
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
|
||||||
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
|
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
|
||||||
|
Result SetBluetoothDevicesSettings(const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings);
|
||||||
|
Result GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out);
|
||||||
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||||
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||||
Result GetDebugModeFlag(sf::Out<bool> out);
|
Result GetDebugModeFlag(sf::Out<bool> out);
|
||||||
|
|||||||
30
stratosphere/ams_mitm/source/set_mitm/setsys_shim.c
Normal file
30
stratosphere/ams_mitm/source/set_mitm/setsys_shim.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 "setsys_shim.h"
|
||||||
|
|
||||||
|
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count) {
|
||||||
|
return serviceDispatch(s, 11,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||||
|
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count) {
|
||||||
|
return serviceDispatchOut(s, 12, *total_out,
|
||||||
|
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||||
|
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
|
||||||
|
);
|
||||||
|
}
|
||||||
20
stratosphere/ams_mitm/source/set_mitm/setsys_shim.h
Normal file
20
stratosphere/ams_mitm/source/set_mitm/setsys_shim.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @file setsys_shim.h
|
||||||
|
* @brief Settings Services (fs) IPC wrapper for setsys.mitm.
|
||||||
|
* @author ndeadly
|
||||||
|
* @copyright libnx Authors
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Forwarding shims. */
|
||||||
|
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count);
|
||||||
|
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -393,6 +393,11 @@ namespace ams::settings::fwdbg {
|
|||||||
/* 0 = Disabled, 1 = Enabled */
|
/* 0 = Disabled, 1 = Enabled */
|
||||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0"));
|
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0"));
|
||||||
|
|
||||||
|
/* Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs) */
|
||||||
|
/* NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database. */
|
||||||
|
/* 0 = Disabled, 1 = Enabled */
|
||||||
|
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_external_bluetooth_db", "u8!0x0"));
|
||||||
|
|
||||||
/* Hbloader custom settings. */
|
/* Hbloader custom settings. */
|
||||||
|
|
||||||
/* Controls the size of the homebrew heap when running as applet. */
|
/* Controls the size of the homebrew heap when running as applet. */
|
||||||
|
|||||||
@@ -78,6 +78,9 @@
|
|||||||
"svcReplyAndReceive": "0x43",
|
"svcReplyAndReceive": "0x43",
|
||||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||||
"svcCreateEvent": "0x45",
|
"svcCreateEvent": "0x45",
|
||||||
|
"svcMapPhysicalMemoryUnsafe": "0x48",
|
||||||
|
"svcUnmapPhysicalMemoryUnsafe": "0x48",
|
||||||
|
"svcSetUnsafeLimit": "0x4A",
|
||||||
"svcReadWriteRegister": "0x4E",
|
"svcReadWriteRegister": "0x4E",
|
||||||
"svcDebugActiveProcess": "0x60",
|
"svcDebugActiveProcess": "0x60",
|
||||||
"svcGetDebugEvent": "0x63",
|
"svcGetDebugEvent": "0x63",
|
||||||
@@ -86,7 +89,9 @@
|
|||||||
"svcQueryDebugProcessMemory": "0x69",
|
"svcQueryDebugProcessMemory": "0x69",
|
||||||
"svcReadDebugProcessMemory": "0x6a",
|
"svcReadDebugProcessMemory": "0x6a",
|
||||||
"svcGetDebugThreadParam": "0x6d",
|
"svcGetDebugThreadParam": "0x6d",
|
||||||
"svcCallSecureMonitor": "0x7f"
|
"svcCallSecureMonitor": "0x7f",
|
||||||
|
"svcMapInsecureMemory": "0x90",
|
||||||
|
"svcUnmapInsecureMemory": "0x91"
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
"type": "min_kernel_version",
|
"type": "min_kernel_version",
|
||||||
|
|||||||
@@ -99,6 +99,41 @@ namespace ams {
|
|||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Result InitializePlatformServiceWithoutSessionCountForHomebrewCompatibility() {
|
||||||
|
/* NOTE: This implements a hack, to keep a session to pl:u without counting against the global limit. */
|
||||||
|
/* This is done because as of 16.0.0, pl:u is the only way to get shared font access, and there are */
|
||||||
|
/* not enough sessions for all reasonable the clients when homebrew gets involved. */
|
||||||
|
/* Please do not do similar things for other services; this is a hack which may not always work, */
|
||||||
|
/* and which could cause problems in other contexts where the ServerManager doesn't have enough */
|
||||||
|
/* slots in truth. */
|
||||||
|
|
||||||
|
/* Initialize pl. */
|
||||||
|
R_ABORT_UNLESS(plInitialize(::PlServiceType_User));
|
||||||
|
|
||||||
|
/* Get the service session for pl. */
|
||||||
|
Service *srv = plGetServiceSession();
|
||||||
|
|
||||||
|
/* Next, create a clone. */
|
||||||
|
/* Because this doesn't go through sm, this does not count against the session limit. */
|
||||||
|
Service clone;
|
||||||
|
R_TRY(serviceClone(srv, std::addressof(clone)));
|
||||||
|
|
||||||
|
/* Next, close the pl service session from sm. */
|
||||||
|
/* This decrements the used session count by one, since the session is from sm. */
|
||||||
|
serviceClose(srv);
|
||||||
|
|
||||||
|
/* HACK: replace the session with the clone we made, to restore functionality. */
|
||||||
|
*srv = clone;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void InitializeSystemModule() {
|
void InitializeSystemModule() {
|
||||||
/* Initialize heap. */
|
/* Initialize heap. */
|
||||||
fatal::srv::InitializeFsHeap();
|
fatal::srv::InitializeFsHeap();
|
||||||
@@ -126,7 +161,7 @@ namespace ams {
|
|||||||
|
|
||||||
R_ABORT_UNLESS(psmInitialize());
|
R_ABORT_UNLESS(psmInitialize());
|
||||||
R_ABORT_UNLESS(spsmInitialize());
|
R_ABORT_UNLESS(spsmInitialize());
|
||||||
R_ABORT_UNLESS(plInitialize(::PlServiceType_User));
|
R_ABORT_UNLESS(InitializePlatformServiceWithoutSessionCountForHomebrewCompatibility());
|
||||||
gpio::Initialize();
|
gpio::Initialize();
|
||||||
|
|
||||||
/* Mount the SD card. */
|
/* Mount the SD card. */
|
||||||
|
|||||||
@@ -42,7 +42,46 @@ namespace ams::fatal::srv {
|
|||||||
alignas(os::MemoryPageSize) constinit u8 g_nv_transfer_memory[0x40000];
|
alignas(os::MemoryPageSize) constinit u8 g_nv_transfer_memory[0x40000];
|
||||||
|
|
||||||
/* There should only be a single (1280*768) framebuffer. */
|
/* There should only be a single (1280*768) framebuffer. */
|
||||||
alignas(os::MemoryPageSize) constinit u8 g_framebuffer_memory[FatalScreenWidthAlignedBytes * util::AlignUp(FatalScreenHeight, 128)];
|
constexpr size_t FrameBufferRequiredSizeBytes = FatalScreenWidthAlignedBytes * util::AlignUp(FatalScreenHeight, 128);
|
||||||
|
constexpr size_t FrameBufferRequiredSizePageAligned = util::AlignUp(FrameBufferRequiredSizeBytes, os::MemoryPageSize);
|
||||||
|
constexpr size_t FrameBufferRequiredSizeHeapAligned = util::AlignUp(FrameBufferRequiredSizeBytes, os::MemoryHeapUnitSize);
|
||||||
|
|
||||||
|
constinit u8 *g_framebuffer_pointer = nullptr;
|
||||||
|
|
||||||
|
void InitializeFrameBufferPointer() {
|
||||||
|
/* Try to get a framebuffer from heap. */
|
||||||
|
{
|
||||||
|
if (R_SUCCEEDED(os::SetMemoryHeapSize(FrameBufferRequiredSizeHeapAligned))) {
|
||||||
|
g_framebuffer_pointer = reinterpret_cast<u8 *>(os::GetMemoryHeapAddress());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We couldn't use heap, so try insecure memory, from the system nonsecure pool. */
|
||||||
|
{
|
||||||
|
uintptr_t address = 0;
|
||||||
|
if (R_SUCCEEDED(os::AllocateInsecureMemory(std::addressof(address), FrameBufferRequiredSizePageAligned))) {
|
||||||
|
g_framebuffer_pointer = reinterpret_cast<u8 *>(address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neither heap nor insecure is available, so we're going to have to try to raid the unsafe pool. */
|
||||||
|
{
|
||||||
|
/* First, increase the limit to an extremely high value. */
|
||||||
|
size_t large_size = std::max(64_MB, FrameBufferRequiredSizeHeapAligned);
|
||||||
|
while (svc::ResultLimitReached::Includes(svc::SetUnsafeLimit(large_size))) {
|
||||||
|
large_size *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next, map some unsafe memory. */
|
||||||
|
uintptr_t address = 0;
|
||||||
|
if (R_SUCCEEDED(os::AllocateUnsafeMemory(std::addressof(address), FrameBufferRequiredSizePageAligned))) {
|
||||||
|
g_framebuffer_pointer = reinterpret_cast<u8 *>(address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,8 +241,12 @@ namespace ams::fatal::srv {
|
|||||||
void ShowFatalTask::PreRenderFrameBuffer() {
|
void ShowFatalTask::PreRenderFrameBuffer() {
|
||||||
const FatalConfig &config = GetFatalConfig();
|
const FatalConfig &config = GetFatalConfig();
|
||||||
|
|
||||||
|
/* Allocate a frame buffer. */
|
||||||
|
InitializeFrameBufferPointer();
|
||||||
|
AMS_ABORT_UNLESS(g_framebuffer_pointer != nullptr);
|
||||||
|
|
||||||
/* Pre-render the image into the static framebuffer. */
|
/* Pre-render the image into the static framebuffer. */
|
||||||
u16 *tiled_buf = reinterpret_cast<u16 *>(g_framebuffer_memory);
|
u16 *tiled_buf = reinterpret_cast<u16 *>(g_framebuffer_pointer);
|
||||||
|
|
||||||
/* Temporarily use the NV transfer memory as font backing heap. */
|
/* Temporarily use the NV transfer memory as font backing heap. */
|
||||||
font::SetHeapMemory(g_nv_transfer_memory, sizeof(g_nv_transfer_memory));
|
font::SetHeapMemory(g_nv_transfer_memory, sizeof(g_nv_transfer_memory));
|
||||||
@@ -214,7 +257,7 @@ namespace ams::fatal::srv {
|
|||||||
font::SetFontColor(0xFFFF);
|
font::SetFontColor(0xFFFF);
|
||||||
|
|
||||||
/* Draw a background. */
|
/* Draw a background. */
|
||||||
for (size_t i = 0; i < sizeof(g_framebuffer_memory) / sizeof(*tiled_buf); i++) {
|
for (size_t i = 0; i < FrameBufferRequiredSizeBytes / sizeof(*tiled_buf); i++) {
|
||||||
tiled_buf[i] = 0x39C9;
|
tiled_buf[i] = 0x39C9;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,7 +482,7 @@ namespace ams::fatal::srv {
|
|||||||
R_TRY(nvFenceInit());
|
R_TRY(nvFenceInit());
|
||||||
|
|
||||||
/* Create nvmap. */
|
/* Create nvmap. */
|
||||||
R_TRY(nvMapCreate(std::addressof(m_map), g_framebuffer_memory, sizeof(g_framebuffer_memory), 0x20000, NvKind_Pitch, true));
|
R_TRY(nvMapCreate(std::addressof(m_map), g_framebuffer_pointer, FrameBufferRequiredSizeBytes, 0x20000, NvKind_Pitch, true));
|
||||||
|
|
||||||
/* Setup graphics buffer. */
|
/* Setup graphics buffer. */
|
||||||
{
|
{
|
||||||
@@ -460,9 +503,9 @@ namespace ams::fatal::srv {
|
|||||||
grbuf.planes[0].block_height_log2 = 4;
|
grbuf.planes[0].block_height_log2 = 4;
|
||||||
grbuf.nvmap_id = nvMapGetId(std::addressof(m_map));
|
grbuf.nvmap_id = nvMapGetId(std::addressof(m_map));
|
||||||
grbuf.stride = FatalScreenWidthAligned;
|
grbuf.stride = FatalScreenWidthAligned;
|
||||||
grbuf.total_size = sizeof(g_framebuffer_memory);
|
grbuf.total_size = FrameBufferRequiredSizeBytes;
|
||||||
grbuf.planes[0].pitch = FatalScreenWidthAlignedBytes;
|
grbuf.planes[0].pitch = FatalScreenWidthAlignedBytes;
|
||||||
grbuf.planes[0].size = sizeof(g_framebuffer_memory);
|
grbuf.planes[0].size = FrameBufferRequiredSizeBytes;
|
||||||
grbuf.planes[0].offset = 0;
|
grbuf.planes[0].offset = 0;
|
||||||
|
|
||||||
R_TRY(nwindowConfigureBuffer(std::addressof(m_win), 0, std::addressof(grbuf)));
|
R_TRY(nwindowConfigureBuffer(std::addressof(m_win), 0, std::addressof(grbuf)));
|
||||||
@@ -474,7 +517,7 @@ namespace ams::fatal::srv {
|
|||||||
void ShowFatalTask::DisplayPreRenderedFrame() {
|
void ShowFatalTask::DisplayPreRenderedFrame() {
|
||||||
s32 slot;
|
s32 slot;
|
||||||
R_ABORT_UNLESS(nwindowDequeueBuffer(std::addressof(m_win), std::addressof(slot), nullptr));
|
R_ABORT_UNLESS(nwindowDequeueBuffer(std::addressof(m_win), std::addressof(slot), nullptr));
|
||||||
dd::FlushDataCache(g_framebuffer_memory, sizeof(g_framebuffer_memory));
|
dd::FlushDataCache(g_framebuffer_pointer, FrameBufferRequiredSizeBytes);
|
||||||
R_ABORT_UNLESS(nwindowQueueBuffer(std::addressof(m_win), m_win.cur_slot, NULL));
|
R_ABORT_UNLESS(nwindowQueueBuffer(std::addressof(m_win), m_win.cur_slot, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
APPLICATIONS := daybreak reboot_to_payload
|
APPLICATIONS := daybreak haze reboot_to_payload
|
||||||
|
|
||||||
SUBFOLDERS := $(APPLICATIONS)
|
SUBFOLDERS := $(APPLICATIONS)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "ui_util.hpp"
|
#include "ui_util.hpp"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace dbk {
|
namespace dbk {
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ namespace dbk {
|
|||||||
nvgFill(vg);
|
nvgFill(vg);
|
||||||
|
|
||||||
/* Setup the font. */
|
/* Setup the font. */
|
||||||
nvgFontSize(vg, 32.0f);
|
nvgFontSize(vg, std::min(32.0f, -(strlen(title)*0.5f) + 47.0f));
|
||||||
nvgFontFace(vg, SwitchStandardFont);
|
nvgFontFace(vg, SwitchStandardFont);
|
||||||
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||||
nvgFillColor(vg, nvgRGB(0, 0, 0));
|
nvgFillColor(vg, nvgRGB(0, 0, 0));
|
||||||
|
|||||||
226
troposphere/haze/Makefile
Normal file
226
troposphere/haze/Makefile
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.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
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := $(notdir $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include ../../libraries/libvapours/include
|
||||||
|
#ROMFS := romfs
|
||||||
|
|
||||||
|
APP_TITLE := USB File Transfer
|
||||||
|
APP_AUTHOR := Atmosphere-NX
|
||||||
|
APP_VERSION := 1.0.0
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# 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++20
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
BIN
troposphere/haze/icon.jpg
Normal file
BIN
troposphere/haze/icon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
2
troposphere/haze/icon.svg
Normal file
2
troposphere/haze/icon.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="svg1500" width="182" height="182" version="1.1" viewBox="0 0 182 182" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs id="defs1484"><linearGradient id="a" x1="36.532" x2="36.532" y1="39.898" y2="80.671" gradientTransform="scale(1.0484 .95387)" gradientUnits="userSpaceOnUse"><stop id="stop1474" stop-color="#fff" offset="0"/><stop id="stop1476" stop-color="#9FC1DB" offset="1"/></linearGradient><linearGradient id="linearGradient2139" x1="451.42" x2="426.88" y1="93.955" y2="145.12" gradientTransform="matrix(1.4611 0 0 1.4611 -557.72 -38.455)" gradientUnits="userSpaceOnUse"><stop id="stop1479" stop-color="#90B9D9" offset="0"/><stop id="stop1481" stop-color="#554BBA" offset="1"/></linearGradient><linearGradient id="linearGradient2147" x1="436.58" x2="451.74" y1="96.001" y2="108.43" gradientTransform="matrix(1.4611 0 0 1.4611 -557.72 -34.137)" gradientUnits="userSpaceOnUse" xlink:href="#a"/><linearGradient id="linearGradient5197" x1="438.54" x2="455.23" y1="117.18" y2="124.38" gradientTransform="matrix(1.5947 0 0 1.5947 -617.05 -51.062)" gradientUnits="userSpaceOnUse" xlink:href="#a"/></defs><rect id="rect6884" width="182" height="182" ry="0" fill="#37394c" stop-color="#000000"/><g id="g1716" transform="matrix(1.8352 0 0 1.8352 -75.998 -148.92)" stroke-width=".54491"><rect id="rect1998" x="80.151" y="91.556" width="21.697" height="21.879" ry="1.4596" fill="url(#linearGradient2147)" stop-color="#000000"/><path id="rect1828" d="m107.41 151.77v-41.952c0-1.4858-1.1967-2.6824-2.6825-2.6824h-27.447c-1.4858 0-2.6825 1.1967-2.6825 2.6824v41.952c0 13.67 13.523 9.3797 13.523 23.423v10.594h5.7652v-10.594c0-14.043 13.523-9.7531 13.523-23.423" fill="url(#linearGradient2139)" stop-color="#000000"/><path id="path1334" d="m91 116.2-2.5153 4.3556h1.7943v22.263l-4.5798-4.3348c-0.2957-0.3689-0.50311-0.85159-0.51463-1.3481 0-2.0086-5.2e-4 -3.2015-8.82e-4 -3.6405 0.84792-0.29761 1.46-1.0971 1.46-2.0475 0-1.2023-0.97564-2.178-2.1784-2.178-1.2033 0-2.1786 0.97563-2.1786 2.178 0 0.9504 0.61169 1.7499 1.4589 2.0475l-6.01e-4 3.5979c0 0.97511 0.53498 1.9969 1.1622 2.6472-0.01854-0.0177-0.0384-0.0363 3.53e-4 8.9e-4 0.01589 0.0143 4.8585 4.5991 4.8585 4.5991 0.29527 0.36811 0.50139 0.85054 0.51324 1.3467v2.5185c-1.6637 0.33382-2.9172 1.803-2.9172 3.5654 0 2.0093 1.6288 3.6381 3.6375 3.6381 2.0093 0 3.6382-1.6288 3.6382-3.6381 0-1.7627-1.2545-3.2319-2.9197-3.5657v-2.4743c0-6e-3 3.53e-4 -0.0121 0-0.0191v-5.4728c0.01209-0.49523 0.21903-0.97704 0.51464-1.3448 0 0 4.8427-4.5843 4.8583-4.5985 0.03901-0.0367 0.01854-0.0184 3.54e-4 -3.5e-4 0.62708-0.65035 1.1617-1.6726 1.1617-2.6478l-7.86e-4 -3.4673h1.4604v-4.3568h-4.3563v4.3568h1.4585s-8.83e-4 0.91324-8.83e-4 3.5098c-0.01095 0.49662-0.21857 0.9798-0.51427 1.3486l-4.5807 4.3358v-16.818h1.7972z" fill="url(#linearGradient5197)" stroke-width=".54491"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
27
troposphere/haze/include/haze.hpp
Normal file
27
troposphere/haze/include/haze.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/async_usb_server.hpp>
|
||||||
|
#include <haze/common.hpp>
|
||||||
|
#include <haze/device_properties.hpp>
|
||||||
|
#include <haze/event_reactor.hpp>
|
||||||
|
#include <haze/file_system_proxy.hpp>
|
||||||
|
#include <haze/ptp.hpp>
|
||||||
|
#include <haze/ptp_object_database.hpp>
|
||||||
|
#include <haze/ptp_object_heap.hpp>
|
||||||
|
#include <haze/ptp_responder.hpp>
|
||||||
|
#include <haze/usb_session.hpp>
|
||||||
32
troposphere/haze/include/haze/assert.hpp
Normal file
32
troposphere/haze/include/haze/assert.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
|
||||||
|
#define HAZE_ASSERT(expr) \
|
||||||
|
{ \
|
||||||
|
const bool __tmp_haze_assert_val = static_cast<bool>(expr); \
|
||||||
|
if (AMS_UNLIKELY(!__tmp_haze_assert_val)) { \
|
||||||
|
svcBreak(BreakReason_Assert, 0, 0); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HAZE_R_ABORT_UNLESS(res_expr) \
|
||||||
|
{ \
|
||||||
|
const auto _tmp_r_abort_rc = (res_expr); \
|
||||||
|
HAZE_ASSERT(R_SUCCEEDED(_tmp_r_abort_rc)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HAZE_UNREACHABLE_DEFAULT_CASE() default: HAZE_ASSERT(false)
|
||||||
44
troposphere/haze/include/haze/async_usb_server.hpp
Normal file
44
troposphere/haze/include/haze/async_usb_server.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
#include <haze/event_reactor.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class AsyncUsbServer final {
|
||||||
|
private:
|
||||||
|
EventReactor *m_reactor;
|
||||||
|
public:
|
||||||
|
constexpr explicit AsyncUsbServer() : m_reactor() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(const UsbCommsInterfaceInfo *interface_info, u16 id_vendor, u16 id_product, EventReactor *reactor);
|
||||||
|
void Finalize();
|
||||||
|
private:
|
||||||
|
Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred) const;
|
||||||
|
public:
|
||||||
|
Result ReadPacket(void *page, u32 size, u32 *out_size_transferred) const {
|
||||||
|
R_RETURN(this->TransferPacketImpl(true, page, size, out_size_transferred));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WritePacket(void *page, u32 size) const {
|
||||||
|
u32 size_transferred;
|
||||||
|
R_RETURN(this->TransferPacketImpl(false, page, size, std::addressof(size_transferred)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
47
troposphere/haze/include/haze/common.hpp
Normal file
47
troposphere/haze/include/haze/common.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
|
||||||
|
#define ATMOSPHERE_OS_HORIZON
|
||||||
|
#define ATMOSPHERE_ARCH_ARM64
|
||||||
|
#define ATMOSPHERE_ARCH_ARM_V8A
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <bit>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <haze/results.hpp>
|
||||||
|
#include <haze/assert.hpp>
|
||||||
|
|
||||||
|
#include <vapours/literals.hpp>
|
||||||
|
#include <vapours/svc/svc_common.hpp>
|
||||||
|
#include <vapours/svc/svc_types_common.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
using namespace ::ams::literals;
|
||||||
|
using namespace ::ams;
|
||||||
|
|
||||||
|
using Result = ::ams::Result;
|
||||||
|
|
||||||
|
static constexpr u32 UsbBulkPacketBufferSize = 1_MB;
|
||||||
|
|
||||||
|
}
|
||||||
249
troposphere/haze/include/haze/console_main_loop.hpp
Normal file
249
troposphere/haze/include/haze/console_main_loop.hpp
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/event_reactor.hpp>
|
||||||
|
#include <haze/ptp_object_heap.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class ConsoleMainLoop : public EventConsumer {
|
||||||
|
private:
|
||||||
|
static constexpr size_t FrameDelayNs = 33'333'333;
|
||||||
|
private:
|
||||||
|
EventReactor *m_reactor;
|
||||||
|
PtpObjectHeap *m_object_heap;
|
||||||
|
|
||||||
|
PadState m_pad;
|
||||||
|
|
||||||
|
Thread m_thread;
|
||||||
|
UEvent m_event;
|
||||||
|
UEvent m_cancel_event;
|
||||||
|
|
||||||
|
u32 m_last_heap_used;
|
||||||
|
u32 m_last_heap_total;
|
||||||
|
bool m_is_applet_mode;
|
||||||
|
private:
|
||||||
|
static void Run(void *arg) {
|
||||||
|
static_cast<ConsoleMainLoop *>(arg)->Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Run() {
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Wait for up to 1 frame delay time to be cancelled. */
|
||||||
|
Waiter cancel_waiter = waiterForUEvent(std::addressof(m_cancel_event));
|
||||||
|
Result rc = waitObjects(std::addressof(idx), std::addressof(cancel_waiter), 1, FrameDelayNs);
|
||||||
|
|
||||||
|
/* Finish if we were cancelled. */
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, signal the console update event. */
|
||||||
|
if (svc::ResultTimedOut::Includes(rc)) {
|
||||||
|
ueventSignal(std::addressof(m_event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
explicit ConsoleMainLoop() : m_reactor(), m_pad(), m_thread(), m_event(), m_cancel_event(), m_last_heap_used(), m_last_heap_total(), m_is_applet_mode() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(EventReactor *reactor, PtpObjectHeap *object_heap) {
|
||||||
|
/* Register event reactor and heap. */
|
||||||
|
m_reactor = reactor;
|
||||||
|
m_object_heap = object_heap;
|
||||||
|
|
||||||
|
/* Set cached use amounts to invalid values. */
|
||||||
|
m_last_heap_used = 0xffffffffu;
|
||||||
|
m_last_heap_total = 0xffffffffu;
|
||||||
|
|
||||||
|
/* Get whether we are launched in applet mode. */
|
||||||
|
AppletType applet_type = appletGetAppletType();
|
||||||
|
m_is_applet_mode = applet_type != AppletType_Application && applet_type != AppletType_SystemApplication;
|
||||||
|
|
||||||
|
/* Initialize events. */
|
||||||
|
ueventCreate(std::addressof(m_event), true);
|
||||||
|
ueventCreate(std::addressof(m_cancel_event), true);
|
||||||
|
|
||||||
|
/* Set up pad inputs to allow exiting the program. */
|
||||||
|
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
|
||||||
|
padInitializeAny(std::addressof(m_pad));
|
||||||
|
|
||||||
|
/* Create the delay thread with higher priority than the main thread (which runs at priority 0x2c). */
|
||||||
|
R_TRY(threadCreate(std::addressof(m_thread), ConsoleMainLoop::Run, this, nullptr, 4_KB, 0x2b, svc::IdealCoreUseProcessValue));
|
||||||
|
|
||||||
|
/* Ensure we close the thread on failure. */
|
||||||
|
ON_RESULT_FAILURE { threadClose(std::addressof(m_thread)); };
|
||||||
|
|
||||||
|
/* Connect ourselves to the event loop. */
|
||||||
|
R_UNLESS(m_reactor->AddConsumer(this, waiterForUEvent(std::addressof(m_event))), haze::ResultRegistrationFailed());
|
||||||
|
|
||||||
|
/* Start the delay thread. */
|
||||||
|
R_RETURN(threadStart(std::addressof(m_thread)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize() {
|
||||||
|
/* Signal the delay thread to shut down. */
|
||||||
|
ueventSignal(std::addressof(m_cancel_event));
|
||||||
|
|
||||||
|
/* Wait for the delay thread to exit and close it. */
|
||||||
|
HAZE_R_ABORT_UNLESS(threadWaitForExit(std::addressof(m_thread)));
|
||||||
|
|
||||||
|
HAZE_R_ABORT_UNLESS(threadClose(std::addressof(m_thread)));
|
||||||
|
|
||||||
|
/* Disconnect from the event loop.*/
|
||||||
|
m_reactor->RemoveConsumer(this);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void RedrawConsole() {
|
||||||
|
/* Get use amounts from the heap. */
|
||||||
|
u32 heap_used = m_object_heap->GetUsedSize();
|
||||||
|
u32 heap_total = m_object_heap->GetTotalSize();
|
||||||
|
u32 heap_pct = heap_total > 0 ? static_cast<u32>((heap_used * 100ul) / heap_total) : 0;
|
||||||
|
|
||||||
|
if (heap_used == m_last_heap_used && heap_total == m_last_heap_total) {
|
||||||
|
/* If usage didn't change, skip redrawing the console. */
|
||||||
|
/* This provides a substantial performance improvement in file transfer speed. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update cached use amounts. */
|
||||||
|
m_last_heap_used = heap_used;
|
||||||
|
m_last_heap_total = heap_total;
|
||||||
|
|
||||||
|
/* Determine units to use for printing to the console. */
|
||||||
|
const char *used_unit = "B";
|
||||||
|
if (heap_used >= 1_KB) { heap_used >>= 10; used_unit = "KiB"; }
|
||||||
|
if (heap_used >= (1_MB / 1_KB)) { heap_used >>= 10; used_unit = "MiB"; }
|
||||||
|
|
||||||
|
const char *total_unit = "B";
|
||||||
|
if (heap_total >= 1_KB) { heap_total >>= 10; total_unit = "KiB"; }
|
||||||
|
if (heap_total >= (1_MB / 1_KB)) { heap_total >>= 10; total_unit = "MiB"; }
|
||||||
|
|
||||||
|
/* Draw the console UI. */
|
||||||
|
consoleClear();
|
||||||
|
printf("USB File Transfer\n\n");
|
||||||
|
printf("Connect console to computer. Press [+] to exit.\n");
|
||||||
|
printf("Heap used: %u %s / %u %s (%u%%)\n", heap_used, used_unit, heap_total, total_unit, heap_pct);
|
||||||
|
|
||||||
|
if (m_is_applet_mode) {
|
||||||
|
/* Print "Applet Mode" in red text. */
|
||||||
|
printf("\n" CONSOLE_ESC(38;5;196m) "Applet Mode" CONSOLE_ESC(0m) "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(nullptr);
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
void ProcessEvent() override {
|
||||||
|
/* Update the console. */
|
||||||
|
this->RedrawConsole();
|
||||||
|
|
||||||
|
/* Check buttons. */
|
||||||
|
padUpdate(std::addressof(m_pad));
|
||||||
|
|
||||||
|
/* If the plus button is held, request immediate exit. */
|
||||||
|
if (padGetButtonsDown(std::addressof(m_pad)) & HidNpadButton_Plus) {
|
||||||
|
m_reactor->SetResult(haze::ResultStopRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pump applet events, and check if exit was requested. */
|
||||||
|
if (!appletMainLoop()) {
|
||||||
|
m_reactor->SetResult(haze::ResultStopRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if focus was lost. */
|
||||||
|
if (appletGetFocusState() == AppletFocusState_Background) {
|
||||||
|
m_reactor->SetResult(haze::ResultFocusLost());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static bool SuspendAndWaitForFocus() {
|
||||||
|
/* Enable suspension with resume notification. */
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify);
|
||||||
|
|
||||||
|
/* Pump applet events. */
|
||||||
|
while (appletMainLoop()) {
|
||||||
|
/* Check if focus was regained. */
|
||||||
|
if (appletGetFocusState() != AppletFocusState_Background) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit was requested. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
static void RunApplication() {
|
||||||
|
/* Declare the object heap, to hold the database for an active session. */
|
||||||
|
PtpObjectHeap ptp_object_heap;
|
||||||
|
|
||||||
|
/* Declare the event reactor, and components which use it. */
|
||||||
|
EventReactor event_reactor;
|
||||||
|
PtpResponder ptp_responder;
|
||||||
|
ConsoleMainLoop console_main_loop;
|
||||||
|
|
||||||
|
/* Initialize the console.*/
|
||||||
|
consoleInit(nullptr);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Disable suspension. */
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
|
||||||
|
/* Declare result from serving to use. */
|
||||||
|
Result rc;
|
||||||
|
{
|
||||||
|
/* Ensure we don't go to sleep while transferring files. */
|
||||||
|
appletSetAutoSleepDisabled(true);
|
||||||
|
|
||||||
|
/* Clear the event reactor. */
|
||||||
|
event_reactor.SetResult(ResultSuccess());
|
||||||
|
|
||||||
|
/* Configure the PTP responder and console main loop. */
|
||||||
|
ptp_responder.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap));
|
||||||
|
console_main_loop.Initialize(std::addressof(event_reactor), std::addressof(ptp_object_heap));
|
||||||
|
|
||||||
|
/* Ensure we maintain a clean state on exit. */
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
/* Finalize the console main loop and PTP responder. */
|
||||||
|
console_main_loop.Finalize();
|
||||||
|
ptp_responder.Finalize();
|
||||||
|
|
||||||
|
/* Restore auto sleep setting. */
|
||||||
|
appletSetAutoSleepDisabled(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Begin processing requests. */
|
||||||
|
rc = ptp_responder.LoopProcess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If focus was lost, try to pump the applet main loop until we receive focus again. */
|
||||||
|
if (haze::ResultFocusLost::Includes(rc) && SuspendAndWaitForFocus()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, enable suspension and finish. */
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize the console. */
|
||||||
|
consoleExit(nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
26
troposphere/haze/include/haze/device_properties.hpp
Normal file
26
troposphere/haze/include/haze/device_properties.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
Result LoadDeviceProperties();
|
||||||
|
|
||||||
|
const char *GetSerialNumber();
|
||||||
|
|
||||||
|
const char *GetFirmwareVersion();
|
||||||
|
|
||||||
|
}
|
||||||
52
troposphere/haze/include/haze/event_reactor.hpp
Normal file
52
troposphere/haze/include/haze/event_reactor.hpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class EventConsumer {
|
||||||
|
public:
|
||||||
|
virtual ~EventConsumer() = default;
|
||||||
|
virtual void ProcessEvent() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EventReactor {
|
||||||
|
private:
|
||||||
|
EventConsumer *m_consumers[svc::ArgumentHandleCountMax];
|
||||||
|
Waiter m_waiters[svc::ArgumentHandleCountMax];
|
||||||
|
s32 m_num_wait_objects;
|
||||||
|
Result m_result;
|
||||||
|
public:
|
||||||
|
constexpr explicit EventReactor() : m_consumers(), m_waiters(), m_num_wait_objects(), m_result(ResultSuccess()) { /* ... */ }
|
||||||
|
|
||||||
|
bool AddConsumer(EventConsumer *consumer, Waiter waiter);
|
||||||
|
void RemoveConsumer(EventConsumer *consumer);
|
||||||
|
public:
|
||||||
|
void SetResult(Result r) { m_result = r; }
|
||||||
|
Result GetResult() const { return m_result; }
|
||||||
|
public:
|
||||||
|
template <typename... Args> requires (sizeof...(Args) > 0)
|
||||||
|
Result WaitFor(s32 *out_arg_waiter, Args &&... arg_waiters) {
|
||||||
|
const Waiter arg_waiter_array[] = { arg_waiters... };
|
||||||
|
return this->WaitForImpl(out_arg_waiter, arg_waiter_array, sizeof...(Args));
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Result WaitForImpl(s32 *out_arg_waiter, const Waiter *arg_waiters, s32 num_arg_waiters);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
131
troposphere/haze/include/haze/file_system_proxy.hpp
Normal file
131
troposphere/haze/include/haze/file_system_proxy.hpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
#include <haze/event_reactor.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class FileSystemProxy final {
|
||||||
|
private:
|
||||||
|
EventReactor *m_reactor;
|
||||||
|
FsFileSystem *m_filesystem;
|
||||||
|
public:
|
||||||
|
constexpr explicit FileSystemProxy() : m_reactor(), m_filesystem() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(EventReactor *reactor, FsFileSystem *fs) {
|
||||||
|
HAZE_ASSERT(fs != nullptr);
|
||||||
|
|
||||||
|
m_reactor = reactor;
|
||||||
|
m_filesystem = fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize() {
|
||||||
|
m_reactor = nullptr;
|
||||||
|
m_filesystem = nullptr;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template <typename F, typename... Args>
|
||||||
|
Result ForwardResult(F func, Args &&... args) {
|
||||||
|
/* Perform the method call, collecting its result. */
|
||||||
|
const Result rc = func(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
/* If the event loop was stopped, return that here. */
|
||||||
|
R_TRY(m_reactor->GetResult());
|
||||||
|
|
||||||
|
/* Otherwise, return the call result. */
|
||||||
|
R_RETURN(rc);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
Result GetTotalSpace(const char *path, s64 *out) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsGetTotalSpace, m_filesystem, path, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetFreeSpace(const char *path, s64 *out) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsGetFreeSpace, m_filesystem, path, out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetEntryType(const char *path, FsDirEntryType *out_entry_type) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsGetEntryType, m_filesystem, path, out_entry_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateFile(const char* path, s64 size, u32 option) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsCreateFile, m_filesystem, path, size, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeleteFile(const char* path) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsDeleteFile, m_filesystem, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result RenameFile(const char *old_path, const char *new_path) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsRenameFile, m_filesystem, old_path, new_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenFile(const char *path, u32 mode, FsFile *out_file) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsOpenFile, m_filesystem, path, mode, out_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetFileSize(FsFile *file, s64 *out_size) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFileGetSize, file, out_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SetFileSize(FsFile *file, s64 size) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFileSetSize, file, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadFile(FsFile *file, s64 off, void *buf, u64 read_size, u32 option, u64 *out_bytes_read) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFileRead, file, off, buf, read_size, option, out_bytes_read));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result WriteFile(FsFile *file, s64 off, const void *buf, u64 write_size, u32 option) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFileWrite, file, off, buf, write_size, option));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseFile(FsFile *file) {
|
||||||
|
fsFileClose(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateDirectory(const char* path) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsCreateDirectory, m_filesystem, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DeleteDirectoryRecursively(const char* path) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsDeleteDirectoryRecursively, m_filesystem, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result RenameDirectory(const char *old_path, const char *new_path) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsRenameDirectory, m_filesystem, old_path, new_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result OpenDirectory(const char *path, u32 mode, FsDir *out_dir) {
|
||||||
|
R_RETURN(this->ForwardResult(fsFsOpenDirectory, m_filesystem, path, mode, out_dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadDirectory(FsDir *d, s64 *out_total_entries, size_t max_entries, FsDirectoryEntry *buf) {
|
||||||
|
R_RETURN(this->ForwardResult(fsDirRead, d, out_total_entries, max_entries, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetDirectoryEntryCount(FsDir *d, s64 *out_count) {
|
||||||
|
R_RETURN(this->ForwardResult(fsDirGetEntryCount, d, out_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseDirectory(FsDir *d) {
|
||||||
|
fsDirClose(d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
488
troposphere/haze/include/haze/ptp.hpp
Normal file
488
troposphere/haze/include/haze/ptp.hpp
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Atmosphère-NX
|
||||||
|
* Copyright (c) libmtp project
|
||||||
|
*
|
||||||
|
* 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 <haze/common.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
constexpr inline u32 PtpUsbBulkHighSpeedMaxPacketLength = 0x200;
|
||||||
|
constexpr inline u32 PtpUsbBulkSuperSpeedMaxPacketLength = 0x400;
|
||||||
|
constexpr inline u32 PtpUsbBulkHeaderLength = 2 * sizeof(u32) + 2 * sizeof(u16);
|
||||||
|
constexpr inline u32 PtpStringMaxLength = 255;
|
||||||
|
|
||||||
|
enum PtpUsbBulkContainerType : u16 {
|
||||||
|
PtpUsbBulkContainerType_Undefined = 0x0000,
|
||||||
|
PtpUsbBulkContainerType_Command = 0x0001,
|
||||||
|
PtpUsbBulkContainerType_Data = 0x0002,
|
||||||
|
PtpUsbBulkContainerType_Response = 0x0003,
|
||||||
|
PtpUsbBulkContainerType_Event = 0x0004,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpOperationCode : u16 {
|
||||||
|
PtpOperationCode_Undefined = 0x1000,
|
||||||
|
PtpOperationCode_GetDeviceInfo = 0x1001,
|
||||||
|
PtpOperationCode_OpenSession = 0x1002,
|
||||||
|
PtpOperationCode_CloseSession = 0x1003,
|
||||||
|
PtpOperationCode_GetStorageIds = 0x1004,
|
||||||
|
PtpOperationCode_GetStorageInfo = 0x1005,
|
||||||
|
PtpOperationCode_GetNumObjects = 0x1006,
|
||||||
|
PtpOperationCode_GetObjectHandles = 0x1007,
|
||||||
|
PtpOperationCode_GetObjectInfo = 0x1008,
|
||||||
|
PtpOperationCode_GetObject = 0x1009,
|
||||||
|
PtpOperationCode_GetThumb = 0x100a,
|
||||||
|
PtpOperationCode_DeleteObject = 0x100b,
|
||||||
|
PtpOperationCode_SendObjectInfo = 0x100c,
|
||||||
|
PtpOperationCode_SendObject = 0x100d,
|
||||||
|
PtpOperationCode_InitiateCapture = 0x100e,
|
||||||
|
PtpOperationCode_FormatStore = 0x100f,
|
||||||
|
PtpOperationCode_ResetDevice = 0x1010,
|
||||||
|
PtpOperationCode_SelfTest = 0x1011,
|
||||||
|
PtpOperationCode_SetObjectProtection = 0x1012,
|
||||||
|
PtpOperationCode_PowerDown = 0x1013,
|
||||||
|
PtpOperationCode_GetDevicePropDesc = 0x1014,
|
||||||
|
PtpOperationCode_GetDevicePropValue = 0x1015,
|
||||||
|
PtpOperationCode_SetDevicePropValue = 0x1016,
|
||||||
|
PtpOperationCode_ResetDevicePropValue = 0x1017,
|
||||||
|
PtpOperationCode_TerminateOpenCapture = 0x1018,
|
||||||
|
PtpOperationCode_MoveObject = 0x1019,
|
||||||
|
PtpOperationCode_CopyObject = 0x101a,
|
||||||
|
PtpOperationCode_GetPartialObject = 0x101b,
|
||||||
|
PtpOperationCode_InitiateOpenCapture = 0x101c,
|
||||||
|
PtpOperationCode_StartEnumHandles = 0x101d,
|
||||||
|
PtpOperationCode_EnumHandles = 0x101e,
|
||||||
|
PtpOperationCode_StopEnumHandles = 0x101f,
|
||||||
|
PtpOperationCode_GetVendorExtensionMaps = 0x1020,
|
||||||
|
PtpOperationCode_GetVendorDeviceInfo = 0x1021,
|
||||||
|
PtpOperationCode_GetResizedImageObject = 0x1022,
|
||||||
|
PtpOperationCode_GetFilesystemManifest = 0x1023,
|
||||||
|
PtpOperationCode_GetStreamInfo = 0x1024,
|
||||||
|
PtpOperationCode_GetStream = 0x1025,
|
||||||
|
PtpOperationCode_MtpGetObjectPropsSupported = 0x9801,
|
||||||
|
PtpOperationCode_MtpGetObjectPropDesc = 0x9802,
|
||||||
|
PtpOperationCode_MtpGetObjectPropValue = 0x9803,
|
||||||
|
PtpOperationCode_MtpSetObjectPropValue = 0x9804,
|
||||||
|
PtpOperationCode_MtpGetObjPropList = 0x9805,
|
||||||
|
PtpOperationCode_MtpSetObjPropList = 0x9806,
|
||||||
|
PtpOperationCode_MtpGetInterdependendPropdesc = 0x9807,
|
||||||
|
PtpOperationCode_MtpSendObjectPropList = 0x9808,
|
||||||
|
PtpOperationCode_MtpGetObjectReferences = 0x9810,
|
||||||
|
PtpOperationCode_MtpSetObjectReferences = 0x9811,
|
||||||
|
PtpOperationCode_MtpUpdateDeviceFirmware = 0x9812,
|
||||||
|
PtpOperationCode_MtpSkip = 0x9820,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpResponseCode : u16 {
|
||||||
|
PtpResponseCode_Undefined = 0x2000,
|
||||||
|
PtpResponseCode_Ok = 0x2001,
|
||||||
|
PtpResponseCode_GeneralError = 0x2002,
|
||||||
|
PtpResponseCode_SessionNotOpen = 0x2003,
|
||||||
|
PtpResponseCode_InvalidTransactionId = 0x2004,
|
||||||
|
PtpResponseCode_OperationNotSupported = 0x2005,
|
||||||
|
PtpResponseCode_ParameterNotSupported = 0x2006,
|
||||||
|
PtpResponseCode_IncompleteTransfer = 0x2007,
|
||||||
|
PtpResponseCode_InvalidStorageId = 0x2008,
|
||||||
|
PtpResponseCode_InvalidObjectHandle = 0x2009,
|
||||||
|
PtpResponseCode_DevicePropNotSupported = 0x200a,
|
||||||
|
PtpResponseCode_InvalidObjectFormatCode = 0x200b,
|
||||||
|
PtpResponseCode_StoreFull = 0x200c,
|
||||||
|
PtpResponseCode_ObjectWriteProtected = 0x200d,
|
||||||
|
PtpResponseCode_StoreReadOnly = 0x200e,
|
||||||
|
PtpResponseCode_AccessDenied = 0x200f,
|
||||||
|
PtpResponseCode_NoThumbnailPresent = 0x2010,
|
||||||
|
PtpResponseCode_SelfTestFailed = 0x2011,
|
||||||
|
PtpResponseCode_PartialDeletion = 0x2012,
|
||||||
|
PtpResponseCode_StoreNotAvailable = 0x2013,
|
||||||
|
PtpResponseCode_SpecificationByFormatUnsupported = 0x2014,
|
||||||
|
PtpResponseCode_NoValidObjectInfo = 0x2015,
|
||||||
|
PtpResponseCode_InvalidCodeFormat = 0x2016,
|
||||||
|
PtpResponseCode_UnknownVendorCode = 0x2017,
|
||||||
|
PtpResponseCode_CaptureAlreadyTerminated = 0x2018,
|
||||||
|
PtpResponseCode_DeviceBusy = 0x2019,
|
||||||
|
PtpResponseCode_InvalidParentObject = 0x201a,
|
||||||
|
PtpResponseCode_InvalidDevicePropFormat = 0x201b,
|
||||||
|
PtpResponseCode_InvalidDevicePropValue = 0x201c,
|
||||||
|
PtpResponseCode_InvalidParameter = 0x201d,
|
||||||
|
PtpResponseCode_SessionAlreadyOpened = 0x201e,
|
||||||
|
PtpResponseCode_TransactionCanceled = 0x201f,
|
||||||
|
PtpResponseCode_SpecificationOfDestinationUnsupported = 0x2020,
|
||||||
|
PtpResponseCode_InvalidEnumHandle = 0x2021,
|
||||||
|
PtpResponseCode_NoStreamEnabled = 0x2022,
|
||||||
|
PtpResponseCode_InvalidDataSet = 0x2023,
|
||||||
|
PtpResponseCode_MtpUndefined = 0xa800,
|
||||||
|
PtpResponseCode_MtpInvalidObjectPropCode = 0xa801,
|
||||||
|
PtpResponseCode_MtpInvalidObjectPropFormat = 0xa802,
|
||||||
|
PtpResponseCode_MtpInvalidObjectPropValue = 0xa803,
|
||||||
|
PtpResponseCode_MtpInvalidObjectReference = 0xa804,
|
||||||
|
PtpResponseCode_MtpInvalidDataset = 0xa806,
|
||||||
|
PtpResponseCode_MtpSpecificationByGroupUnsupported = 0xa807,
|
||||||
|
PtpResponseCode_MtpSpecificationByDepthUnsupported = 0xa808,
|
||||||
|
PtpResponseCode_MtpObjectTooLarge = 0xa809,
|
||||||
|
PtpResponseCode_MtpObjectPropNotSupported = 0xa80a,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpEventCode : u16 {
|
||||||
|
PtpEventCode_Undefined = 0x4000,
|
||||||
|
PtpEventCode_CancelTransaction = 0x4001,
|
||||||
|
PtpEventCode_ObjectAdded = 0x4002,
|
||||||
|
PtpEventCode_ObjectRemoved = 0x4003,
|
||||||
|
PtpEventCode_StoreAdded = 0x4004,
|
||||||
|
PtpEventCode_StoreRemoved = 0x4005,
|
||||||
|
PtpEventCode_DevicePropChanged = 0x4006,
|
||||||
|
PtpEventCode_ObjectInfoChanged = 0x4007,
|
||||||
|
PtpEventCode_DeviceInfoChanged = 0x4008,
|
||||||
|
PtpEventCode_RequestObjectTransfer = 0x4009,
|
||||||
|
PtpEventCode_StoreFull = 0x400a,
|
||||||
|
PtpEventCode_DeviceReset = 0x400b,
|
||||||
|
PtpEventCode_StorageInfoChanged = 0x400c,
|
||||||
|
PtpEventCode_CaptureComplete = 0x400d,
|
||||||
|
PtpEventCode_UnreportedStatus = 0x400e,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpDataTypeCode : u16 {
|
||||||
|
PtpDataTypeCode_Undefined = 0x0000,
|
||||||
|
PtpDataTypeCode_S8 = 0x0001,
|
||||||
|
PtpDataTypeCode_U8 = 0x0002,
|
||||||
|
PtpDataTypeCode_S16 = 0x0003,
|
||||||
|
PtpDataTypeCode_U16 = 0x0004,
|
||||||
|
PtpDataTypeCode_S32 = 0x0005,
|
||||||
|
PtpDataTypeCode_U32 = 0x0006,
|
||||||
|
PtpDataTypeCode_S64 = 0x0007,
|
||||||
|
PtpDataTypeCode_U64 = 0x0008,
|
||||||
|
PtpDataTypeCode_S128 = 0x0009,
|
||||||
|
PtpDataTypeCode_U128 = 0x000a,
|
||||||
|
|
||||||
|
PtpDataTypeCode_ArrayMask = (1u << 14),
|
||||||
|
|
||||||
|
PtpDataTypeCode_S8Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S8,
|
||||||
|
PtpDataTypeCode_U8Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U8,
|
||||||
|
PtpDataTypeCode_S16Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S16,
|
||||||
|
PtpDataTypeCode_U16Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U16,
|
||||||
|
PtpDataTypeCode_S32Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S32,
|
||||||
|
PtpDataTypeCode_U32Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U32,
|
||||||
|
PtpDataTypeCode_S64Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S64,
|
||||||
|
PtpDataTypeCode_U64Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U64,
|
||||||
|
PtpDataTypeCode_S128Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_S128,
|
||||||
|
PtpDataTypeCode_U128Array = PtpDataTypeCode_ArrayMask | PtpDataTypeCode_U128,
|
||||||
|
|
||||||
|
PtpDataTypeCode_String = 0xffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpPropertyGetSetFlag : u8 {
|
||||||
|
PtpPropertyGetSetFlag_Get = 0x00,
|
||||||
|
PtpPropertyGetSetFlag_GetSet = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpPropertyGroupCode : u32 {
|
||||||
|
PtpPropertyGroupCode_Default = 0x00000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpPropertyFormFlag : u8 {
|
||||||
|
PtpPropertyFormFlag_None = 0x00,
|
||||||
|
PtpPropertyFormFlag_Range = 0x01,
|
||||||
|
PtpPropertyFormFlag_Enumeration = 0x02,
|
||||||
|
PtpPropertyFormFlag_DateTime = 0x03,
|
||||||
|
PtpPropertyFormFlag_FixedLengthArray = 0x04,
|
||||||
|
PtpPropertyFormFlag_RegularExpression = 0x05,
|
||||||
|
PtpPropertyFormFlag_ByteArray = 0x06,
|
||||||
|
PtpPropertyFormFlag_LongString = 0xff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpObjectPropertyCode : u16 {
|
||||||
|
PtpObjectPropertyCode_StorageId = 0xdc01,
|
||||||
|
PtpObjectPropertyCode_ObjectFormat = 0xdc02,
|
||||||
|
PtpObjectPropertyCode_ProtectionStatus = 0xdc03,
|
||||||
|
PtpObjectPropertyCode_ObjectSize = 0xdc04,
|
||||||
|
PtpObjectPropertyCode_AssociationType = 0xdc05,
|
||||||
|
PtpObjectPropertyCode_AssociationDesc = 0xdc06,
|
||||||
|
PtpObjectPropertyCode_ObjectFileName = 0xdc07,
|
||||||
|
PtpObjectPropertyCode_DateCreated = 0xdc08,
|
||||||
|
PtpObjectPropertyCode_DateModified = 0xdc09,
|
||||||
|
PtpObjectPropertyCode_Keywords = 0xdc0a,
|
||||||
|
PtpObjectPropertyCode_ParentObject = 0xdc0b,
|
||||||
|
PtpObjectPropertyCode_AllowedFolderContents = 0xdc0c,
|
||||||
|
PtpObjectPropertyCode_Hidden = 0xdc0d,
|
||||||
|
PtpObjectPropertyCode_SystemObject = 0xdc0e,
|
||||||
|
PtpObjectPropertyCode_PersistentUniqueObjectIdentifier = 0xdc41,
|
||||||
|
PtpObjectPropertyCode_SyncId = 0xdc42,
|
||||||
|
PtpObjectPropertyCode_PropertyBag = 0xdc43,
|
||||||
|
PtpObjectPropertyCode_Name = 0xdc44,
|
||||||
|
PtpObjectPropertyCode_CreatedBy = 0xdc45,
|
||||||
|
PtpObjectPropertyCode_Artist = 0xdc46,
|
||||||
|
PtpObjectPropertyCode_DateAuthored = 0xdc47,
|
||||||
|
PtpObjectPropertyCode_Description = 0xdc48,
|
||||||
|
PtpObjectPropertyCode_UrlReference = 0xdc49,
|
||||||
|
PtpObjectPropertyCode_LanguageLocale = 0xdc4a,
|
||||||
|
PtpObjectPropertyCode_CopyrightInformation = 0xdc4b,
|
||||||
|
PtpObjectPropertyCode_Source = 0xdc4c,
|
||||||
|
PtpObjectPropertyCode_OriginLocation = 0xdc4d,
|
||||||
|
PtpObjectPropertyCode_DateAdded = 0xdc4e,
|
||||||
|
PtpObjectPropertyCode_NonConsumable = 0xdc4f,
|
||||||
|
PtpObjectPropertyCode_CorruptOrUnplayable = 0xdc50,
|
||||||
|
PtpObjectPropertyCode_ProducerSerialNumber = 0xdc51,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleFormat = 0xdc81,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleSize = 0xdc82,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleHeight = 0xdc83,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleWidth = 0xdc84,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleDuration = 0xdc85,
|
||||||
|
PtpObjectPropertyCode_RepresentativeSampleData = 0xdc86,
|
||||||
|
PtpObjectPropertyCode_Width = 0xdc87,
|
||||||
|
PtpObjectPropertyCode_Height = 0xdc88,
|
||||||
|
PtpObjectPropertyCode_Duration = 0xdc89,
|
||||||
|
PtpObjectPropertyCode_Rating = 0xdc8a,
|
||||||
|
PtpObjectPropertyCode_Track = 0xdc8b,
|
||||||
|
PtpObjectPropertyCode_Genre = 0xdc8c,
|
||||||
|
PtpObjectPropertyCode_Credits = 0xdc8d,
|
||||||
|
PtpObjectPropertyCode_Lyrics = 0xdc8e,
|
||||||
|
PtpObjectPropertyCode_SubscriptionContentId = 0xdc8f,
|
||||||
|
PtpObjectPropertyCode_ProducedBy = 0xdc90,
|
||||||
|
PtpObjectPropertyCode_UseCount = 0xdc91,
|
||||||
|
PtpObjectPropertyCode_SkipCount = 0xdc92,
|
||||||
|
PtpObjectPropertyCode_LastAccessed = 0xdc93,
|
||||||
|
PtpObjectPropertyCode_ParentalRating = 0xdc94,
|
||||||
|
PtpObjectPropertyCode_MetaGenre = 0xdc95,
|
||||||
|
PtpObjectPropertyCode_Composer = 0xdc96,
|
||||||
|
PtpObjectPropertyCode_EffectiveRating = 0xdc97,
|
||||||
|
PtpObjectPropertyCode_Subtitle = 0xdc98,
|
||||||
|
PtpObjectPropertyCode_OriginalReleaseDate = 0xdc99,
|
||||||
|
PtpObjectPropertyCode_AlbumName = 0xdc9a,
|
||||||
|
PtpObjectPropertyCode_AlbumArtist = 0xdc9b,
|
||||||
|
PtpObjectPropertyCode_Mood = 0xdc9c,
|
||||||
|
PtpObjectPropertyCode_DrmStatus = 0xdc9d,
|
||||||
|
PtpObjectPropertyCode_SubDescription = 0xdc9e,
|
||||||
|
PtpObjectPropertyCode_IsCropped = 0xdcd1,
|
||||||
|
PtpObjectPropertyCode_IsColorCorrected = 0xdcd2,
|
||||||
|
PtpObjectPropertyCode_ImageBitDepth = 0xdcd3,
|
||||||
|
PtpObjectPropertyCode_Fnumber = 0xdcd4,
|
||||||
|
PtpObjectPropertyCode_ExposureTime = 0xdcd5,
|
||||||
|
PtpObjectPropertyCode_ExposureIndex = 0xdcd6,
|
||||||
|
PtpObjectPropertyCode_DisplayName = 0xdce0,
|
||||||
|
PtpObjectPropertyCode_BodyText = 0xdce1,
|
||||||
|
PtpObjectPropertyCode_Subject = 0xdce2,
|
||||||
|
PtpObjectPropertyCode_Priority = 0xdce3,
|
||||||
|
PtpObjectPropertyCode_GivenName = 0xdd00,
|
||||||
|
PtpObjectPropertyCode_MiddleNames = 0xdd01,
|
||||||
|
PtpObjectPropertyCode_FamilyName = 0xdd02,
|
||||||
|
PtpObjectPropertyCode_Prefix = 0xdd03,
|
||||||
|
PtpObjectPropertyCode_Suffix = 0xdd04,
|
||||||
|
PtpObjectPropertyCode_PhoneticGivenName = 0xdd05,
|
||||||
|
PtpObjectPropertyCode_PhoneticFamilyName = 0xdd06,
|
||||||
|
PtpObjectPropertyCode_EmailPrimary = 0xdd07,
|
||||||
|
PtpObjectPropertyCode_EmailPersonal1 = 0xdd08,
|
||||||
|
PtpObjectPropertyCode_EmailPersonal2 = 0xdd09,
|
||||||
|
PtpObjectPropertyCode_EmailBusiness1 = 0xdd0a,
|
||||||
|
PtpObjectPropertyCode_EmailBusiness2 = 0xdd0b,
|
||||||
|
PtpObjectPropertyCode_EmailOthers = 0xdd0c,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberPrimary = 0xdd0d,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberPersonal = 0xdd0e,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberPersonal2 = 0xdd0f,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberBusiness = 0xdd10,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberBusiness2 = 0xdd11,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberMobile = 0xdd12,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberMobile2 = 0xdd13,
|
||||||
|
PtpObjectPropertyCode_FaxNumberPrimary = 0xdd14,
|
||||||
|
PtpObjectPropertyCode_FaxNumberPersonal = 0xdd15,
|
||||||
|
PtpObjectPropertyCode_FaxNumberBusiness = 0xdd16,
|
||||||
|
PtpObjectPropertyCode_PagerNumber = 0xdd17,
|
||||||
|
PtpObjectPropertyCode_PhoneNumberOthers = 0xdd18,
|
||||||
|
PtpObjectPropertyCode_PrimaryWebAddress = 0xdd19,
|
||||||
|
PtpObjectPropertyCode_PersonalWebAddress = 0xdd1a,
|
||||||
|
PtpObjectPropertyCode_BusinessWebAddress = 0xdd1b,
|
||||||
|
PtpObjectPropertyCode_InstantMessengerAddress = 0xdd1c,
|
||||||
|
PtpObjectPropertyCode_InstantMessengerAddress2 = 0xdd1d,
|
||||||
|
PtpObjectPropertyCode_InstantMessengerAddress3 = 0xdd1e,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFull = 0xdd1f,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullLine1 = 0xdd20,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullLine2 = 0xdd21,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullCity = 0xdd22,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullRegion = 0xdd23,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullPostalCode = 0xdd24,
|
||||||
|
PtpObjectPropertyCode_PostalAddressPersonalFullCountry = 0xdd25,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessFull = 0xdd26,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessLine1 = 0xdd27,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessLine2 = 0xdd28,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessCity = 0xdd29,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessRegion = 0xdd2a,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessPostalCode = 0xdd2b,
|
||||||
|
PtpObjectPropertyCode_PostalAddressBusinessCountry = 0xdd2c,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherFull = 0xdd2d,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherLine1 = 0xdd2e,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherLine2 = 0xdd2f,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherCity = 0xdd30,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherRegion = 0xdd31,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherPostalCode = 0xdd32,
|
||||||
|
PtpObjectPropertyCode_PostalAddressOtherCountry = 0xdd33,
|
||||||
|
PtpObjectPropertyCode_OrganizationName = 0xdd34,
|
||||||
|
PtpObjectPropertyCode_PhoneticOrganizationName = 0xdd35,
|
||||||
|
PtpObjectPropertyCode_Role = 0xdd36,
|
||||||
|
PtpObjectPropertyCode_Birthdate = 0xdd37,
|
||||||
|
PtpObjectPropertyCode_MessageTo = 0xdd40,
|
||||||
|
PtpObjectPropertyCode_MessageCC = 0xdd41,
|
||||||
|
PtpObjectPropertyCode_MessageBCC = 0xdd42,
|
||||||
|
PtpObjectPropertyCode_MessageRead = 0xdd43,
|
||||||
|
PtpObjectPropertyCode_MessageReceivedTime = 0xdd44,
|
||||||
|
PtpObjectPropertyCode_MessageSender = 0xdd45,
|
||||||
|
PtpObjectPropertyCode_ActivityBeginTime = 0xdd50,
|
||||||
|
PtpObjectPropertyCode_ActivityEndTime = 0xdd51,
|
||||||
|
PtpObjectPropertyCode_ActivityLocation = 0xdd52,
|
||||||
|
PtpObjectPropertyCode_ActivityRequiredAttendees = 0xdd54,
|
||||||
|
PtpObjectPropertyCode_ActivityOptionalAttendees = 0xdd55,
|
||||||
|
PtpObjectPropertyCode_ActivityResources = 0xdd56,
|
||||||
|
PtpObjectPropertyCode_ActivityAccepted = 0xdd57,
|
||||||
|
PtpObjectPropertyCode_Owner = 0xdd5d,
|
||||||
|
PtpObjectPropertyCode_Editor = 0xdd5e,
|
||||||
|
PtpObjectPropertyCode_Webmaster = 0xdd5f,
|
||||||
|
PtpObjectPropertyCode_UrlSource = 0xdd60,
|
||||||
|
PtpObjectPropertyCode_UrlDestination = 0xdd61,
|
||||||
|
PtpObjectPropertyCode_TimeBookmark = 0xdd62,
|
||||||
|
PtpObjectPropertyCode_ObjectBookmark = 0xdd63,
|
||||||
|
PtpObjectPropertyCode_ByteBookmark = 0xdd64,
|
||||||
|
PtpObjectPropertyCode_LastBuildDate = 0xdd70,
|
||||||
|
PtpObjectPropertyCode_TimetoLive = 0xdd71,
|
||||||
|
PtpObjectPropertyCode_MediaGuid = 0xdd72,
|
||||||
|
PtpObjectPropertyCode_TotalBitRate = 0xde91,
|
||||||
|
PtpObjectPropertyCode_BitRateType = 0xde92,
|
||||||
|
PtpObjectPropertyCode_SampleRate = 0xde93,
|
||||||
|
PtpObjectPropertyCode_NumberOfChannels = 0xde94,
|
||||||
|
PtpObjectPropertyCode_AudioBitDepth = 0xde95,
|
||||||
|
PtpObjectPropertyCode_ScanDepth = 0xde97,
|
||||||
|
PtpObjectPropertyCode_AudioWaveCodec = 0xde99,
|
||||||
|
PtpObjectPropertyCode_AudioBitRate = 0xde9a,
|
||||||
|
PtpObjectPropertyCode_VideoFourCcCodec = 0xde9b,
|
||||||
|
PtpObjectPropertyCode_VideoBitRate = 0xde9c,
|
||||||
|
PtpObjectPropertyCode_FramesPerThousandSeconds = 0xde9d,
|
||||||
|
PtpObjectPropertyCode_KeyFrameDistance = 0xde9e,
|
||||||
|
PtpObjectPropertyCode_BufferSize = 0xde9f,
|
||||||
|
PtpObjectPropertyCode_EncodingQuality = 0xdea0,
|
||||||
|
PtpObjectPropertyCode_EncodingProfile = 0xdea1,
|
||||||
|
PtpObjectPropertyCode_BuyFlag = 0xd901,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpDevicePropertyCode : u16 {
|
||||||
|
PtpDevicePropertyCode_Undefined = 0x5000,
|
||||||
|
PtpDevicePropertyCode_BatteryLevel = 0x5001,
|
||||||
|
PtpDevicePropertyCode_FunctionalMode = 0x5002,
|
||||||
|
PtpDevicePropertyCode_ImageSize = 0x5003,
|
||||||
|
PtpDevicePropertyCode_CompressionSetting = 0x5004,
|
||||||
|
PtpDevicePropertyCode_WhiteBalance = 0x5005,
|
||||||
|
PtpDevicePropertyCode_RgbGain = 0x5006,
|
||||||
|
PtpDevicePropertyCode_FNumber = 0x5007,
|
||||||
|
PtpDevicePropertyCode_FocalLength = 0x5008,
|
||||||
|
PtpDevicePropertyCode_FocusDistance = 0x5009,
|
||||||
|
PtpDevicePropertyCode_FocusMode = 0x500a,
|
||||||
|
PtpDevicePropertyCode_ExposureMeteringMode = 0x500b,
|
||||||
|
PtpDevicePropertyCode_FlashMode = 0x500c,
|
||||||
|
PtpDevicePropertyCode_ExposureTime = 0x500d,
|
||||||
|
PtpDevicePropertyCode_ExposureProgramMode = 0x500e,
|
||||||
|
PtpDevicePropertyCode_ExposureIndex = 0x500f,
|
||||||
|
PtpDevicePropertyCode_ExposureBiasCompensation = 0x5010,
|
||||||
|
PtpDevicePropertyCode_DateTime = 0x5011,
|
||||||
|
PtpDevicePropertyCode_CaptureDelay = 0x5012,
|
||||||
|
PtpDevicePropertyCode_StillCaptureMode = 0x5013,
|
||||||
|
PtpDevicePropertyCode_Contrast = 0x5014,
|
||||||
|
PtpDevicePropertyCode_Sharpness = 0x5015,
|
||||||
|
PtpDevicePropertyCode_DigitalZoom = 0x5016,
|
||||||
|
PtpDevicePropertyCode_EffectMode = 0x5017,
|
||||||
|
PtpDevicePropertyCode_BurstNumber = 0x5018,
|
||||||
|
PtpDevicePropertyCode_BurstInterval = 0x5019,
|
||||||
|
PtpDevicePropertyCode_TimelapseNumber = 0x501a,
|
||||||
|
PtpDevicePropertyCode_TimelapseInterval = 0x501b,
|
||||||
|
PtpDevicePropertyCode_FocusMeteringMode = 0x501c,
|
||||||
|
PtpDevicePropertyCode_UploadUrl = 0x501d,
|
||||||
|
PtpDevicePropertyCode_Artist = 0x501e,
|
||||||
|
PtpDevicePropertyCode_CopyrightInfo = 0x501f,
|
||||||
|
PtpDevicePropertyCode_SupportedStreams = 0x5020,
|
||||||
|
PtpDevicePropertyCode_EnabledStreams = 0x5021,
|
||||||
|
PtpDevicePropertyCode_VideoFormat = 0x5022,
|
||||||
|
PtpDevicePropertyCode_VideoResolution = 0x5023,
|
||||||
|
PtpDevicePropertyCode_VideoQuality = 0x5024,
|
||||||
|
PtpDevicePropertyCode_VideoFrameRate = 0x5025,
|
||||||
|
PtpDevicePropertyCode_VideoContrast = 0x5026,
|
||||||
|
PtpDevicePropertyCode_VideoBrightness = 0x5027,
|
||||||
|
PtpDevicePropertyCode_AudioFormat = 0x5028,
|
||||||
|
PtpDevicePropertyCode_AudioBitrate = 0x5029,
|
||||||
|
PtpDevicePropertyCode_AudioSamplingRate = 0x502a,
|
||||||
|
PtpDevicePropertyCode_AudioBitPerSample = 0x502b,
|
||||||
|
PtpDevicePropertyCode_AudioVolume = 0x502c,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpObjectFormatCode : u16 {
|
||||||
|
PtpObjectFormatCode_Undefined = 0x3000,
|
||||||
|
PtpObjectFormatCode_Association = 0x3001,
|
||||||
|
PtpObjectFormatCode_Defined = 0x3800,
|
||||||
|
PtpObjectFormatCode_MtpMediaCard = 0xb211,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpAssociationType : u16 {
|
||||||
|
PtpAssociationType_Undefined = 0x0000,
|
||||||
|
PtpAssociationType_GenericFolder = 0x0001,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpGetObjectHandles : u32 {
|
||||||
|
PtpGetObjectHandles_AllFormats = 0x00000000,
|
||||||
|
PtpGetObjectHandles_AllAssocs = 0x00000000,
|
||||||
|
PtpGetObjectHandles_AllStorage = 0xffffffff,
|
||||||
|
PtpGetObjectHandles_RootParent = 0xffffffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpStorageType : u16 {
|
||||||
|
PtpStorageType_Undefined = 0x0000,
|
||||||
|
PtpStorageType_FixedRom = 0x0001,
|
||||||
|
PtpStorageType_RemovableRom = 0x0002,
|
||||||
|
PtpStorageType_FixedRam = 0x0003,
|
||||||
|
PtpStorageType_RemovableRam = 0x0004,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpFilesystemType : u16 {
|
||||||
|
PtpFilesystemType_Undefined = 0x0000,
|
||||||
|
PtpFilesystemType_GenericFlat = 0x0001,
|
||||||
|
PtpFilesystemType_GenericHierarchical = 0x0002,
|
||||||
|
PtpFilesystemType_Dcf = 0x0003,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpAccessCapability : u16 {
|
||||||
|
PtpAccessCapability_ReadWrite = 0x0000,
|
||||||
|
PtpAccessCapability_ReadOnly = 0x0001,
|
||||||
|
PtpAccessCapability_ReadOnlyWithObjectDeletion = 0x0002,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpProtectionStatus : u16 {
|
||||||
|
PtpProtectionStatus_NoProtection = 0x0000,
|
||||||
|
PtpProtectionStatus_ReadOnly = 0x0001,
|
||||||
|
PtpProtectionStatus_MtpReadOnlyData = 0x8002,
|
||||||
|
PtpProtectionStatus_MtpNonTransferableData = 0x8003,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PtpThumbFormat : u16 {
|
||||||
|
PtpThumbFormat_Undefined = 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PtpUsbBulkContainer {
|
||||||
|
u32 length;
|
||||||
|
u16 type;
|
||||||
|
u16 code;
|
||||||
|
u32 trans_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PtpUsbBulkContainer) == PtpUsbBulkHeaderLength);
|
||||||
|
|
||||||
|
struct PtpNewObjectInfo {
|
||||||
|
u32 storage_id;
|
||||||
|
u32 parent_object_id;
|
||||||
|
u32 object_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
174
troposphere/haze/include/haze/ptp_data_builder.hpp
Normal file
174
troposphere/haze/include/haze/ptp_data_builder.hpp
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/async_usb_server.hpp>
|
||||||
|
#include <haze/common.hpp>
|
||||||
|
#include <haze/ptp.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class PtpDataBuilder final {
|
||||||
|
private:
|
||||||
|
AsyncUsbServer *m_server;
|
||||||
|
u32 m_transmitted_size;
|
||||||
|
u32 m_offset;
|
||||||
|
u8 *m_data;
|
||||||
|
bool m_disabled;
|
||||||
|
private:
|
||||||
|
Result Flush() {
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
m_transmitted_size += m_offset;
|
||||||
|
m_offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If we're disabled, we have nothing to do. */
|
||||||
|
R_SUCCEED_IF(m_disabled);
|
||||||
|
|
||||||
|
/* Otherwise, we should write our buffered data. */
|
||||||
|
R_RETURN(m_server->WritePacket(m_data, m_offset));
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
constexpr explicit PtpDataBuilder(void *data, AsyncUsbServer *server) : m_server(server), m_transmitted_size(), m_offset(), m_data(static_cast<u8 *>(data)), m_disabled() { /* ... */ }
|
||||||
|
|
||||||
|
Result Commit() {
|
||||||
|
if (m_offset > 0) {
|
||||||
|
/* If there is remaining data left to write, write it now. */
|
||||||
|
R_TRY(this->Flush());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (util::IsAligned(m_transmitted_size, PtpUsbBulkHighSpeedMaxPacketLength)) {
|
||||||
|
/* If the transmission size was a multiple of wMaxPacketSize, send a zero length packet. */
|
||||||
|
R_TRY(this->Flush());
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AddBuffer(const u8 *buffer, u32 count) {
|
||||||
|
while (count > 0) {
|
||||||
|
/* Calculate how many bytes we can write now. */
|
||||||
|
const u32 write_size = std::min<u32>(count, haze::UsbBulkPacketBufferSize - m_offset);
|
||||||
|
|
||||||
|
/* Write this number of bytes. */
|
||||||
|
std::memcpy(m_data + m_offset, buffer, write_size);
|
||||||
|
m_offset += write_size;
|
||||||
|
buffer += write_size;
|
||||||
|
count -= write_size;
|
||||||
|
|
||||||
|
/* If our buffer is full, flush it. */
|
||||||
|
if (m_offset == haze::UsbBulkPacketBufferSize) {
|
||||||
|
R_TRY(this->Flush());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Result Add(T value) {
|
||||||
|
u8 bytes[sizeof(T)];
|
||||||
|
|
||||||
|
std::memcpy(bytes, std::addressof(value), sizeof(T));
|
||||||
|
|
||||||
|
R_RETURN(this->AddBuffer(bytes, sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AddDataHeader(PtpUsbBulkContainer &request, u32 data_size) {
|
||||||
|
R_TRY(this->Add<u32>(PtpUsbBulkHeaderLength + data_size));
|
||||||
|
R_TRY(this->Add<u16>(PtpUsbBulkContainerType_Data));
|
||||||
|
R_TRY(this->Add<u16>(request.code));
|
||||||
|
R_TRY(this->Add<u32>(request.trans_id));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AddResponseHeader(PtpUsbBulkContainer &request, PtpResponseCode code, u32 params_size) {
|
||||||
|
R_TRY(this->Add<u32>(PtpUsbBulkHeaderLength + params_size));
|
||||||
|
R_TRY(this->Add<u16>(PtpUsbBulkContainerType_Response));
|
||||||
|
R_TRY(this->Add<u16>(code));
|
||||||
|
R_TRY(this->Add<u32>(request.trans_id));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
Result WriteVariableLengthData(PtpUsbBulkContainer &request, F &&func) {
|
||||||
|
HAZE_ASSERT(m_offset == 0 && m_transmitted_size == 0);
|
||||||
|
|
||||||
|
/* Declare how many bytes the data will require to write. */
|
||||||
|
u32 data_size = 0;
|
||||||
|
{
|
||||||
|
/* Temporarily disable writing to calculate the size.*/
|
||||||
|
m_disabled = true;
|
||||||
|
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
/* Report how many bytes were required. */
|
||||||
|
data_size = m_transmitted_size;
|
||||||
|
|
||||||
|
/* On exit, enable writing and reset sizes. */
|
||||||
|
m_transmitted_size = 0;
|
||||||
|
m_disabled = false;
|
||||||
|
m_offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
R_TRY(func());
|
||||||
|
R_TRY(this->Commit());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actually copy and write the data. */
|
||||||
|
R_TRY(this->AddDataHeader(request, data_size));
|
||||||
|
R_TRY(func());
|
||||||
|
R_TRY(this->Commit());
|
||||||
|
|
||||||
|
/* We succeeded. */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Result AddString(const T *str) {
|
||||||
|
/* Use one less than the maximum string length for maximum length with null terminator. */
|
||||||
|
const u8 len = static_cast<u8>(std::min<s32>(util::Strlen(str), PtpStringMaxLength - 1));
|
||||||
|
|
||||||
|
if (len > 0) {
|
||||||
|
/* Length is padded by null terminator for non-empty strings. */
|
||||||
|
R_TRY(this->Add<u8>(len + 1));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
R_TRY(this->Add<u16>(str[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_TRY(this->Add<u16>(0));
|
||||||
|
} else {
|
||||||
|
R_TRY(this->Add<u8>(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Result AddArray(const T *arr, u32 count) {
|
||||||
|
R_TRY(this->Add<u32>(count));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
R_TRY(this->Add<T>(arr[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
114
troposphere/haze/include/haze/ptp_data_parser.hpp
Normal file
114
troposphere/haze/include/haze/ptp_data_parser.hpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/async_usb_server.hpp>
|
||||||
|
#include <haze/common.hpp>
|
||||||
|
#include <haze/ptp.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class PtpDataParser final {
|
||||||
|
private:
|
||||||
|
AsyncUsbServer *m_server;
|
||||||
|
u32 m_received_size;
|
||||||
|
u32 m_offset;
|
||||||
|
u8 *m_data;
|
||||||
|
bool m_eot;
|
||||||
|
private:
|
||||||
|
Result Flush() {
|
||||||
|
R_UNLESS(!m_eot, haze::ResultEndOfTransmission());
|
||||||
|
|
||||||
|
m_received_size = 0;
|
||||||
|
m_offset = 0;
|
||||||
|
|
||||||
|
ON_SCOPE_EXIT {
|
||||||
|
/* End of transmission occurs when receiving a bulk transfer less than the buffer size. */
|
||||||
|
/* PTP uses zero-length termination, so zero is a possible size to receive. */
|
||||||
|
m_eot = m_received_size < haze::UsbBulkPacketBufferSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
R_RETURN(m_server->ReadPacket(m_data, haze::UsbBulkPacketBufferSize, std::addressof(m_received_size)));
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
constexpr explicit PtpDataParser(void *data, AsyncUsbServer *server) : m_server(server), m_received_size(), m_offset(), m_data(static_cast<u8 *>(data)), m_eot() { /* ... */ }
|
||||||
|
|
||||||
|
Result Finalize() {
|
||||||
|
/* Read until the transmission completes. */
|
||||||
|
while (true) {
|
||||||
|
Result rc = this->Flush();
|
||||||
|
|
||||||
|
R_SUCCEED_IF(m_eot || haze::ResultEndOfTransmission::Includes(rc));
|
||||||
|
R_TRY(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReadBuffer(u8 *buffer, u32 count, u32 *out_read_count) {
|
||||||
|
*out_read_count = 0;
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
/* If we cannot read more bytes now, flush. */
|
||||||
|
if (m_offset == m_received_size) {
|
||||||
|
R_TRY(this->Flush());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate how many bytes we can read now. */
|
||||||
|
u32 read_size = std::min<u32>(count, m_received_size - m_offset);
|
||||||
|
|
||||||
|
/* Read this number of bytes. */
|
||||||
|
std::memcpy(buffer + *out_read_count, m_data + m_offset, read_size);
|
||||||
|
*out_read_count += read_size;
|
||||||
|
m_offset += read_size;
|
||||||
|
count -= read_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Result Read(T *out_t) {
|
||||||
|
u32 read_count;
|
||||||
|
u8 bytes[sizeof(T)];
|
||||||
|
|
||||||
|
R_TRY(this->ReadBuffer(bytes, sizeof(T), std::addressof(read_count)));
|
||||||
|
|
||||||
|
std::memcpy(out_t, bytes, sizeof(T));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: out_string must contain room for 256 bytes. */
|
||||||
|
/* The result will be null-terminated on successful completion. */
|
||||||
|
Result ReadString(char *out_string) {
|
||||||
|
u8 len;
|
||||||
|
R_TRY(this->Read(std::addressof(len)));
|
||||||
|
|
||||||
|
/* Read characters one by one. */
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
u16 chr;
|
||||||
|
R_TRY(this->Read(std::addressof(chr)));
|
||||||
|
|
||||||
|
*out_string++ = static_cast<char>(chr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write null terminator. */
|
||||||
|
*out_string++ = '\x00';
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
107
troposphere/haze/include/haze/ptp_object_database.hpp
Normal file
107
troposphere/haze/include/haze/ptp_object_database.hpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
#include <haze/ptp_object_heap.hpp>
|
||||||
|
#include <vapours/util.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
struct PtpObject {
|
||||||
|
public:
|
||||||
|
util::IntrusiveRedBlackTreeNode m_name_node;
|
||||||
|
util::IntrusiveRedBlackTreeNode m_object_id_node;
|
||||||
|
u32 m_parent_id;
|
||||||
|
u32 m_object_id;
|
||||||
|
char m_name[];
|
||||||
|
public:
|
||||||
|
const char *GetName() const { return m_name; }
|
||||||
|
u32 GetParentId() const { return m_parent_id; }
|
||||||
|
u32 GetObjectId() const { return m_object_id; }
|
||||||
|
public:
|
||||||
|
bool GetIsRegistered() const { return m_object_id != 0; }
|
||||||
|
void Register(u32 object_id) { m_object_id = object_id; }
|
||||||
|
void Unregister() { m_object_id = 0; }
|
||||||
|
public:
|
||||||
|
struct NameComparator {
|
||||||
|
struct RedBlackKeyType {
|
||||||
|
const char *m_name;
|
||||||
|
|
||||||
|
constexpr RedBlackKeyType(const char *name) : m_name(name) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr const char *GetName() const {
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires (std::same_as<T, PtpObject> || std::same_as<T, RedBlackKeyType>)
|
||||||
|
static constexpr int Compare(const T &lhs, const PtpObject &rhs) {
|
||||||
|
/* All SD card filesystems supported by fs are case-insensitive and case-preserving. */
|
||||||
|
/* Account for that in collation here. */
|
||||||
|
return strcasecmp(lhs.GetName(), rhs.GetName());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObjectIdComparator {
|
||||||
|
struct RedBlackKeyType {
|
||||||
|
u32 m_object_id;
|
||||||
|
|
||||||
|
constexpr RedBlackKeyType(u32 object_id) : m_object_id(object_id) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr u32 GetObjectId() const {
|
||||||
|
return m_object_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> requires (std::same_as<T, PtpObject> || std::same_as<T, RedBlackKeyType>)
|
||||||
|
static constexpr int Compare(const T &lhs, const PtpObject &rhs) {
|
||||||
|
return lhs.GetObjectId() - rhs.GetObjectId();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class PtpObjectDatabase {
|
||||||
|
private:
|
||||||
|
using ObjectNameTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&PtpObject::m_name_node>;
|
||||||
|
using ObjectNameTree = ObjectNameTreeTraits::TreeType<PtpObject::NameComparator>;
|
||||||
|
|
||||||
|
using ObjectIdTreeTraits = util::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&PtpObject::m_object_id_node>;
|
||||||
|
using ObjectIdTree = ObjectIdTreeTraits::TreeType<PtpObject::ObjectIdComparator>;
|
||||||
|
|
||||||
|
PtpObjectHeap *m_object_heap;
|
||||||
|
ObjectNameTree m_name_tree;
|
||||||
|
ObjectIdTree m_object_id_tree;
|
||||||
|
u32 m_next_object_id;
|
||||||
|
public:
|
||||||
|
constexpr explicit PtpObjectDatabase() : m_object_heap(), m_name_tree(), m_object_id_tree(), m_next_object_id() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize(PtpObjectHeap *object_heap);
|
||||||
|
void Finalize();
|
||||||
|
public:
|
||||||
|
/* Object database API. */
|
||||||
|
Result CreateOrFindObject(const char *parent_name, const char *name, u32 parent_id, PtpObject **out_object);
|
||||||
|
void RegisterObject(PtpObject *object, u32 desired_id = 0);
|
||||||
|
void UnregisterObject(PtpObject *object);
|
||||||
|
void DeleteObject(PtpObject *obj);
|
||||||
|
|
||||||
|
Result CreateAndRegisterObjectId(const char *parent_name, const char *name, u32 parent_id, u32 *out_object_id);
|
||||||
|
public:
|
||||||
|
PtpObject *GetObjectById(u32 object_id);
|
||||||
|
PtpObject *GetObjectByName(const char *name);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
124
troposphere/haze/include/haze/ptp_object_heap.hpp
Normal file
124
troposphere/haze/include/haze/ptp_object_heap.hpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
/* This simple linear allocator implementation allows us to rapidly reclaim the entire object graph. */
|
||||||
|
/* This is critical for maintaining interactivity when a session is closed. */
|
||||||
|
class PtpObjectHeap {
|
||||||
|
private:
|
||||||
|
static constexpr size_t NumHeapBlocks = 2;
|
||||||
|
private:
|
||||||
|
void *m_heap_blocks[NumHeapBlocks];
|
||||||
|
void *m_next_address;
|
||||||
|
u32 m_heap_block_size;
|
||||||
|
u32 m_current_heap_block;
|
||||||
|
public:
|
||||||
|
constexpr explicit PtpObjectHeap() : m_heap_blocks(), m_next_address(), m_heap_block_size(), m_current_heap_block() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void Finalize();
|
||||||
|
public:
|
||||||
|
constexpr size_t GetTotalSize() const {
|
||||||
|
return m_heap_block_size * NumHeapBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetUsedSize() const {
|
||||||
|
return (m_heap_block_size * m_current_heap_block) + this->GetNextAddress() - this->GetFirstAddress();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
constexpr u8 *GetNextAddress() const { return static_cast<u8 *>(m_next_address); }
|
||||||
|
constexpr u8 *GetFirstAddress() const { return static_cast<u8 *>(m_heap_blocks[m_current_heap_block]); }
|
||||||
|
|
||||||
|
constexpr u8 *GetCurrentBlockEnd() const {
|
||||||
|
return this->GetFirstAddress() + m_heap_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool AllocationIsPossible(size_t n) const {
|
||||||
|
return n <= m_heap_block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool AllocationIsSatisfyable(size_t n) const {
|
||||||
|
/* Check for overflow. */
|
||||||
|
if (!util::CanAddWithoutOverflow(reinterpret_cast<uintptr_t>(this->GetNextAddress()), n)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we would exceed the size of the current block. */
|
||||||
|
if (this->GetNextAddress() + n > this->GetCurrentBlockEnd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool AdvanceToNextBlock() {
|
||||||
|
if (m_current_heap_block < NumHeapBlocks - 1) {
|
||||||
|
m_next_address = m_heap_blocks[++m_current_heap_block];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void *AllocateFromCurrentBlock(size_t n) {
|
||||||
|
void *result = this->GetNextAddress();
|
||||||
|
|
||||||
|
m_next_address = this->GetNextAddress() + n;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
template <typename T = void>
|
||||||
|
constexpr T *Allocate(size_t n) {
|
||||||
|
/* Check for overflow in alignment. */
|
||||||
|
if (!util::CanAddWithoutOverflow(n, alignof(u64) - 1)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Align the amount to satisfy allocation for u64. */
|
||||||
|
n = util::AlignUp(n, alignof(u64));
|
||||||
|
|
||||||
|
/* Check if the allocation is possible. */
|
||||||
|
if (!this->AllocationIsPossible(n)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the allocation is not satisfyable now, we might be able to satisfy it on the next block. */
|
||||||
|
/* However, if the next block would be empty, we won't be able to satisfy the request. */
|
||||||
|
if (!this->AllocationIsSatisfyable(n) && !this->AdvanceToNextBlock()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate the memory. */
|
||||||
|
return static_cast<T *>(this->AllocateFromCurrentBlock(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void Deallocate(void *p, size_t n) {
|
||||||
|
/* If the pointer was the last allocation, return the memory to the heap. */
|
||||||
|
if (static_cast<u8 *>(p) + n == this->GetNextAddress()) {
|
||||||
|
m_next_address = this->GetNextAddress() - n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, do nothing. */
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
75
troposphere/haze/include/haze/ptp_responder.hpp
Normal file
75
troposphere/haze/include/haze/ptp_responder.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
#include <haze/async_usb_server.hpp>
|
||||||
|
#include <haze/ptp_object_heap.hpp>
|
||||||
|
#include <haze/ptp_object_database.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
class PtpDataParser;
|
||||||
|
|
||||||
|
class PtpResponder final {
|
||||||
|
private:
|
||||||
|
AsyncUsbServer m_usb_server;
|
||||||
|
FileSystemProxy m_fs;
|
||||||
|
PtpUsbBulkContainer m_request_header;
|
||||||
|
PtpObjectHeap *m_object_heap;
|
||||||
|
u32 m_send_object_id;
|
||||||
|
bool m_session_open;
|
||||||
|
|
||||||
|
PtpObjectDatabase m_object_database;
|
||||||
|
public:
|
||||||
|
constexpr explicit PtpResponder() : m_usb_server(), m_fs(), m_request_header(), m_object_heap(), m_send_object_id(), m_session_open(), m_object_database() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(EventReactor *reactor, PtpObjectHeap *object_heap);
|
||||||
|
void Finalize();
|
||||||
|
public:
|
||||||
|
Result LoopProcess();
|
||||||
|
private:
|
||||||
|
/* Request handling. */
|
||||||
|
Result HandleRequest();
|
||||||
|
Result HandleRequestImpl();
|
||||||
|
Result HandleCommandRequest(PtpDataParser &dp);
|
||||||
|
void ForceCloseSession();
|
||||||
|
|
||||||
|
template <typename Data>
|
||||||
|
Result WriteResponse(PtpResponseCode code, Data &&data);
|
||||||
|
Result WriteResponse(PtpResponseCode code);
|
||||||
|
|
||||||
|
/* PTP operations. */
|
||||||
|
Result GetDeviceInfo(PtpDataParser &dp);
|
||||||
|
Result OpenSession(PtpDataParser &dp);
|
||||||
|
Result CloseSession(PtpDataParser &dp);
|
||||||
|
Result GetStorageIds(PtpDataParser &dp);
|
||||||
|
Result GetStorageInfo(PtpDataParser &dp);
|
||||||
|
Result GetObjectHandles(PtpDataParser &dp);
|
||||||
|
Result GetObjectInfo(PtpDataParser &dp);
|
||||||
|
Result GetObject(PtpDataParser &dp);
|
||||||
|
Result SendObjectInfo(PtpDataParser &dp);
|
||||||
|
Result SendObject(PtpDataParser &dp);
|
||||||
|
Result DeleteObject(PtpDataParser &dp);
|
||||||
|
|
||||||
|
/* MTP operations. */
|
||||||
|
Result GetObjectPropsSupported(PtpDataParser &dp);
|
||||||
|
Result GetObjectPropDesc(PtpDataParser &dp);
|
||||||
|
Result GetObjectPropValue(PtpDataParser &dp);
|
||||||
|
Result SetObjectPropValue(PtpDataParser &dp);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
41
troposphere/haze/include/haze/results.hpp
Normal file
41
troposphere/haze/include/haze/results.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <vapours/results.hpp>
|
||||||
|
|
||||||
|
/* NOTE: These results are all custom, and not official. */
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(haze, 420);
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(RegistrationFailed, 1);
|
||||||
|
R_DEFINE_ERROR_RESULT(NotConfigured, 2);
|
||||||
|
R_DEFINE_ERROR_RESULT(TransferFailed, 3);
|
||||||
|
R_DEFINE_ERROR_RESULT(StopRequested, 4);
|
||||||
|
R_DEFINE_ERROR_RESULT(FocusLost, 5);
|
||||||
|
R_DEFINE_ERROR_RESULT(EndOfTransmission, 6);
|
||||||
|
R_DEFINE_ERROR_RESULT(UnknownPacketType, 7);
|
||||||
|
R_DEFINE_ERROR_RESULT(SessionNotOpen, 8);
|
||||||
|
R_DEFINE_ERROR_RESULT(OutOfMemory, 9);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidObjectId, 10);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidStorageId, 11);
|
||||||
|
R_DEFINE_ERROR_RESULT(OperationNotSupported, 12);
|
||||||
|
R_DEFINE_ERROR_RESULT(UnknownRequestType, 13);
|
||||||
|
R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 14);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 15);
|
||||||
|
|
||||||
|
}
|
||||||
48
troposphere/haze/include/haze/usb_session.hpp
Normal file
48
troposphere/haze/include/haze/usb_session.hpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze/common.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
enum UsbSessionEndpoint {
|
||||||
|
UsbSessionEndpoint_Read = 0,
|
||||||
|
UsbSessionEndpoint_Write = 1,
|
||||||
|
UsbSessionEndpoint_Interrupt = 2,
|
||||||
|
UsbSessionEndpoint_Count = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
class UsbSession {
|
||||||
|
private:
|
||||||
|
UsbDsInterface *m_interface;
|
||||||
|
UsbDsEndpoint *m_endpoints[UsbSessionEndpoint_Count];
|
||||||
|
private:
|
||||||
|
Result Initialize1x(const UsbCommsInterfaceInfo *info);
|
||||||
|
Result Initialize5x(const UsbCommsInterfaceInfo *info);
|
||||||
|
public:
|
||||||
|
constexpr explicit UsbSession() : m_interface(), m_endpoints() { /* ... */ }
|
||||||
|
|
||||||
|
Result Initialize(const UsbCommsInterfaceInfo *info, u16 id_vendor, u16 id_product);
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
|
bool GetConfigured() const;
|
||||||
|
Event *GetCompletionEvent(UsbSessionEndpoint ep) const;
|
||||||
|
Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id);
|
||||||
|
Result GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_transferred_size);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
62
troposphere/haze/source/async_usb_server.cpp
Normal file
62
troposphere/haze/source/async_usb_server.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constinit UsbSession g_usb_session;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AsyncUsbServer::Initialize(const UsbCommsInterfaceInfo *interface_info, u16 id_vendor, u16 id_product, EventReactor *reactor) {
|
||||||
|
m_reactor = reactor;
|
||||||
|
|
||||||
|
/* Set up a new USB session. */
|
||||||
|
R_TRY(g_usb_session.Initialize(interface_info, id_vendor, id_product));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncUsbServer::Finalize() {
|
||||||
|
g_usb_session.Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AsyncUsbServer::TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred) const {
|
||||||
|
u32 urb_id;
|
||||||
|
s32 waiter_idx;
|
||||||
|
|
||||||
|
/* If we're not configured yet, wait to become configured first. */
|
||||||
|
if (!g_usb_session.GetConfigured()) {
|
||||||
|
R_TRY(m_reactor->WaitFor(std::addressof(waiter_idx), waiterForEvent(usbDsGetStateChangeEvent())));
|
||||||
|
R_TRY(eventClear(usbDsGetStateChangeEvent()));
|
||||||
|
|
||||||
|
R_THROW(haze::ResultNotConfigured());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the appropriate endpoint and begin a transfer. */
|
||||||
|
UsbSessionEndpoint ep = read ? UsbSessionEndpoint_Read : UsbSessionEndpoint_Write;
|
||||||
|
R_TRY(g_usb_session.TransferAsync(ep, page, size, std::addressof(urb_id)));
|
||||||
|
|
||||||
|
/* Try to wait for the event. */
|
||||||
|
R_TRY(m_reactor->WaitFor(std::addressof(waiter_idx), waiterForEvent(g_usb_session.GetCompletionEvent(ep))));
|
||||||
|
|
||||||
|
/* Return what we transferred. */
|
||||||
|
R_RETURN(g_usb_session.GetTransferResult(ep, urb_id, out_size_transferred));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
troposphere/haze/source/device_properties.cpp
Normal file
50
troposphere/haze/source/device_properties.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constinit SetSysSerialNumber g_serial_number = {};
|
||||||
|
constinit SetSysFirmwareVersion g_firmware_version = {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result LoadDeviceProperties() {
|
||||||
|
/* Initialize set:sys. */
|
||||||
|
R_TRY(setsysInitialize());
|
||||||
|
|
||||||
|
/* Ensure we maintain a clean state on exit. */
|
||||||
|
ON_SCOPE_EXIT { setsysExit(); };
|
||||||
|
|
||||||
|
/* Get the serial number and firmware version. */
|
||||||
|
R_TRY(setsysGetSerialNumber(std::addressof(g_serial_number)));
|
||||||
|
R_TRY(setsysGetFirmwareVersion(std::addressof(g_firmware_version)));
|
||||||
|
|
||||||
|
/* We succeeded. */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GetSerialNumber() {
|
||||||
|
return g_serial_number.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GetFirmwareVersion() {
|
||||||
|
return g_firmware_version.display_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
78
troposphere/haze/source/event_reactor.cpp
Normal file
78
troposphere/haze/source/event_reactor.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
bool EventReactor::AddConsumer(EventConsumer *consumer, Waiter waiter) {
|
||||||
|
HAZE_ASSERT(m_num_wait_objects + 1 <= svc::ArgumentHandleCountMax);
|
||||||
|
|
||||||
|
/* Add to the end of the list. */
|
||||||
|
m_consumers[m_num_wait_objects] = consumer;
|
||||||
|
m_waiters[m_num_wait_objects] = waiter;
|
||||||
|
m_num_wait_objects++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventReactor::RemoveConsumer(EventConsumer *consumer) {
|
||||||
|
s32 output_index = 0;
|
||||||
|
|
||||||
|
/* Remove the consumer. */
|
||||||
|
for (s32 input_index = 0; input_index < m_num_wait_objects; input_index++) {
|
||||||
|
if (m_consumers[input_index] == consumer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_index != input_index) {
|
||||||
|
m_consumers[output_index] = m_consumers[input_index];
|
||||||
|
m_waiters[output_index] = m_waiters[input_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
output_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_num_wait_objects = output_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EventReactor::WaitForImpl(s32 *out_arg_waiter, const Waiter *arg_waiters, s32 num_arg_waiters) {
|
||||||
|
HAZE_ASSERT(0 < num_arg_waiters && num_arg_waiters <= svc::ArgumentHandleCountMax);
|
||||||
|
HAZE_ASSERT(m_num_wait_objects + num_arg_waiters <= svc::ArgumentHandleCountMax);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* Check if we should wait for an event. */
|
||||||
|
R_TRY(m_result);
|
||||||
|
|
||||||
|
/* Insert waiters from argument list. */
|
||||||
|
for (s32 i = 0; i < num_arg_waiters; i++) {
|
||||||
|
m_waiters[i + m_num_wait_objects] = arg_waiters[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 idx;
|
||||||
|
HAZE_R_ABORT_UNLESS(waitObjects(std::addressof(idx), m_waiters, m_num_wait_objects + num_arg_waiters, svc::WaitInfinite));
|
||||||
|
|
||||||
|
/* If a waiter in the argument list was signaled, return it. */
|
||||||
|
if (idx >= m_num_wait_objects) {
|
||||||
|
*out_arg_waiter = idx - m_num_wait_objects;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, process the event as normal. */
|
||||||
|
m_consumers[idx]->ProcessEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
troposphere/haze/source/main.cpp
Normal file
28
troposphere/haze/source/main.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
#include <haze/console_main_loop.hpp>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
/* Load device firmware version and serial number. */
|
||||||
|
HAZE_R_ABORT_UNLESS(haze::LoadDeviceProperties());
|
||||||
|
|
||||||
|
/* Run the application. */
|
||||||
|
haze::ConsoleMainLoop::RunApplication();
|
||||||
|
|
||||||
|
/* Return to the loader. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
152
troposphere/haze/source/ptp_object_database.cpp
Normal file
152
troposphere/haze/source/ptp_object_database.cpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
void PtpObjectDatabase::Initialize(PtpObjectHeap *object_heap) {
|
||||||
|
m_object_heap = object_heap;
|
||||||
|
m_object_heap->Initialize();
|
||||||
|
|
||||||
|
std::construct_at(std::addressof(m_name_tree));
|
||||||
|
std::construct_at(std::addressof(m_object_id_tree));
|
||||||
|
|
||||||
|
m_next_object_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectDatabase::Finalize() {
|
||||||
|
std::destroy_at(std::addressof(m_object_id_tree));
|
||||||
|
std::destroy_at(std::addressof(m_name_tree));
|
||||||
|
|
||||||
|
m_next_object_id = 0;
|
||||||
|
|
||||||
|
m_object_heap->Finalize();
|
||||||
|
m_object_heap = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PtpObjectDatabase::CreateOrFindObject(const char *parent_name, const char *name, u32 parent_id, PtpObject **out_object) {
|
||||||
|
constexpr auto separator = "/";
|
||||||
|
|
||||||
|
/* Calculate length of the new name with null terminator. */
|
||||||
|
const size_t parent_name_len = util::Strlen(parent_name);
|
||||||
|
const size_t separator_len = util::Strlen(separator);
|
||||||
|
const size_t name_len = util::Strlen(name);
|
||||||
|
const size_t terminator_len = 1;
|
||||||
|
const size_t alloc_len = sizeof(PtpObject) + parent_name_len + separator_len + name_len + terminator_len;
|
||||||
|
|
||||||
|
/* Allocate memory for the object. */
|
||||||
|
PtpObject * const object = m_object_heap->Allocate<PtpObject>(alloc_len);
|
||||||
|
R_UNLESS(object != nullptr, haze::ResultOutOfMemory());
|
||||||
|
|
||||||
|
/* Build the object name. */
|
||||||
|
std::strncpy(object->m_name, parent_name, parent_name_len + terminator_len);
|
||||||
|
std::strncpy(object->m_name + parent_name_len, separator, separator_len + terminator_len);
|
||||||
|
std::strncpy(object->m_name + parent_name_len + separator_len, name, name_len + terminator_len);
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Ensure we maintain a clean state on failure. */
|
||||||
|
auto guard = SCOPE_GUARD { m_object_heap->Deallocate(object, alloc_len); };
|
||||||
|
|
||||||
|
/* Check if an object with this name already exists. If it does, we can just return it here. */
|
||||||
|
if (auto * const existing = this->GetObjectByName(object->GetName()); existing != nullptr) {
|
||||||
|
*out_object = existing;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Persist the reference to the object. */
|
||||||
|
guard.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set object properties. */
|
||||||
|
object->m_parent_id = parent_id;
|
||||||
|
object->m_object_id = 0;
|
||||||
|
|
||||||
|
/* Set output. */
|
||||||
|
*out_object = object;
|
||||||
|
|
||||||
|
/* We succeeded. */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectDatabase::RegisterObject(PtpObject *object, u32 desired_id) {
|
||||||
|
/* If the object is already registered, skip registration. */
|
||||||
|
if (object->GetIsRegistered()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set desired object ID. */
|
||||||
|
if (desired_id == 0) {
|
||||||
|
desired_id = m_next_object_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert object into trees. */
|
||||||
|
object->Register(desired_id);
|
||||||
|
m_object_id_tree.insert(*object);
|
||||||
|
m_name_tree.insert(*object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectDatabase::UnregisterObject(PtpObject *object) {
|
||||||
|
/* If the object is not registered, skip trying to unregister. */
|
||||||
|
if (!object->GetIsRegistered()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove object from trees. */
|
||||||
|
m_object_id_tree.erase(m_object_id_tree.iterator_to(*object));
|
||||||
|
m_name_tree.erase(m_name_tree.iterator_to(*object));
|
||||||
|
object->Unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectDatabase::DeleteObject(PtpObject *object) {
|
||||||
|
/* Unregister the object as required. */
|
||||||
|
this->UnregisterObject(object);
|
||||||
|
|
||||||
|
/* Free the object. */
|
||||||
|
m_object_heap->Deallocate(object, sizeof(PtpObject) + std::strlen(object->GetName()) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result PtpObjectDatabase::CreateAndRegisterObjectId(const char *parent_name, const char *name, u32 parent_id, u32 *out_object_id) {
|
||||||
|
/* Try to create the object. */
|
||||||
|
PtpObject *object;
|
||||||
|
R_TRY(this->CreateOrFindObject(parent_name, name, parent_id, std::addressof(object)));
|
||||||
|
|
||||||
|
/* We succeeded, so register it. */
|
||||||
|
this->RegisterObject(object);
|
||||||
|
|
||||||
|
/* Set the output ID. */
|
||||||
|
*out_object_id = object->GetObjectId();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
PtpObject *PtpObjectDatabase::GetObjectById(u32 object_id) {
|
||||||
|
/* Find in ID mapping. */
|
||||||
|
if (auto it = m_object_id_tree.find_key(object_id); it != m_object_id_tree.end()) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PtpObject *PtpObjectDatabase::GetObjectByName(const char *name) {
|
||||||
|
/* Find in name mapping. */
|
||||||
|
if (auto it = m_name_tree.find_key(name); it != m_name_tree.end()) {
|
||||||
|
return std::addressof(*it);
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
troposphere/haze/source/ptp_object_heap.cpp
Normal file
69
troposphere/haze/source/ptp_object_heap.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* Allow 20MiB for use by libnx. */
|
||||||
|
static constexpr size_t LibnxReservedMemorySize = 20_MB;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectHeap::Initialize() {
|
||||||
|
/* If we're already initialized, skip re-initialization. */
|
||||||
|
if (m_heap_block_size != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Estimate how much memory we can reserve. */
|
||||||
|
size_t mem_used = 0;
|
||||||
|
HAZE_R_ABORT_UNLESS(svcGetInfo(std::addressof(mem_used), InfoType_UsedMemorySize, svc::CurrentProcess, 0));
|
||||||
|
HAZE_ASSERT(mem_used > LibnxReservedMemorySize);
|
||||||
|
mem_used -= LibnxReservedMemorySize;
|
||||||
|
|
||||||
|
/* Calculate size of blocks. */
|
||||||
|
m_heap_block_size = mem_used / NumHeapBlocks;
|
||||||
|
HAZE_ASSERT(m_heap_block_size > 0);
|
||||||
|
|
||||||
|
/* Allocate the memory. */
|
||||||
|
for (size_t i = 0; i < NumHeapBlocks; i++) {
|
||||||
|
m_heap_blocks[i] = std::malloc(m_heap_block_size);
|
||||||
|
HAZE_ASSERT(m_heap_blocks[i] != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the address to allocate from. */
|
||||||
|
m_next_address = m_heap_blocks[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void PtpObjectHeap::Finalize() {
|
||||||
|
if (m_heap_block_size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tear down the heap, allowing a subsequent call to Initialize() if desired. */
|
||||||
|
for (size_t i = 0; i < NumHeapBlocks; i++) {
|
||||||
|
std::free(m_heap_blocks[i]);
|
||||||
|
m_heap_blocks[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_next_address = nullptr;
|
||||||
|
m_heap_block_size = 0;
|
||||||
|
m_current_heap_block = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1042
troposphere/haze/source/ptp_responder.cpp
Normal file
1042
troposphere/haze/source/ptp_responder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
262
troposphere/haze/source/usb_session.cpp
Normal file
262
troposphere/haze/source/usb_session.cpp
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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 <haze.hpp>
|
||||||
|
|
||||||
|
namespace haze {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const u32 DefaultInterfaceNumber = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbSession::Initialize1x(const UsbCommsInterfaceInfo *info) {
|
||||||
|
struct usb_interface_descriptor interface_descriptor = {
|
||||||
|
.bLength = USB_DT_INTERFACE_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
|
.bInterfaceNumber = DefaultInterfaceNumber,
|
||||||
|
.bInterfaceClass = info->bInterfaceClass,
|
||||||
|
.bInterfaceSubClass = info->bInterfaceSubClass,
|
||||||
|
.bInterfaceProtocol = info->bInterfaceProtocol,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = PtpUsbBulkHighSpeedMaxPacketLength,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = PtpUsbBulkHighSpeedMaxPacketLength,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_endpoint_descriptor endpoint_descriptor_interrupt = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||||
|
.bmAttributes = USB_TRANSFER_TYPE_INTERRUPT,
|
||||||
|
.wMaxPacketSize = 0x18,
|
||||||
|
.bInterval = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set up interface. */
|
||||||
|
R_TRY(usbDsGetDsInterface(std::addressof(m_interface), std::addressof(interface_descriptor), "usb"));
|
||||||
|
|
||||||
|
/* Set up endpoints. */
|
||||||
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Write]), std::addressof(endpoint_descriptor_in)));
|
||||||
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Read]), std::addressof(endpoint_descriptor_out)));
|
||||||
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Interrupt]), std::addressof(endpoint_descriptor_interrupt)));
|
||||||
|
|
||||||
|
R_RETURN(usbDsInterface_EnableInterface(m_interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbSession::Initialize5x(const UsbCommsInterfaceInfo *info) {
|
||||||
|
struct usb_interface_descriptor interface_descriptor = {
|
||||||
|
.bLength = USB_DT_INTERFACE_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
|
.bInterfaceNumber = DefaultInterfaceNumber,
|
||||||
|
.bNumEndpoints = 3,
|
||||||
|
.bInterfaceClass = info->bInterfaceClass,
|
||||||
|
.bInterfaceSubClass = info->bInterfaceSubClass,
|
||||||
|
.bInterfaceProtocol = info->bInterfaceProtocol,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = PtpUsbBulkHighSpeedMaxPacketLength,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = PtpUsbBulkHighSpeedMaxPacketLength,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_endpoint_descriptor endpoint_descriptor_interrupt = {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||||
|
.bmAttributes = USB_TRANSFER_TYPE_INTERRUPT,
|
||||||
|
.wMaxPacketSize = 0x18,
|
||||||
|
.bInterval = 0x6,
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_ss_endpoint_companion_descriptor endpoint_companion_interrupt = {
|
||||||
|
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
|
||||||
|
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
|
||||||
|
.bMaxBurst = 0x00,
|
||||||
|
.bmAttributes = 0x00,
|
||||||
|
.wBytesPerInterval = 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
R_TRY(usbDsRegisterInterface(std::addressof(m_interface)));
|
||||||
|
|
||||||
|
u8 iInterface;
|
||||||
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iInterface), "MTP"));
|
||||||
|
|
||||||
|
interface_descriptor.bInterfaceNumber = m_interface->interface_index;
|
||||||
|
interface_descriptor.iInterface = iInterface;
|
||||||
|
endpoint_descriptor_in.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||||
|
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||||
|
endpoint_descriptor_interrupt.bEndpointAddress += interface_descriptor.bInterfaceNumber + 2;
|
||||||
|
|
||||||
|
/* High speed config. */
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(interface_descriptor), USB_DT_INTERFACE_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_in), USB_DT_ENDPOINT_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_out), USB_DT_ENDPOINT_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_interrupt), USB_DT_ENDPOINT_SIZE));
|
||||||
|
|
||||||
|
/* Super speed config. */
|
||||||
|
endpoint_descriptor_in.wMaxPacketSize = PtpUsbBulkSuperSpeedMaxPacketLength;
|
||||||
|
endpoint_descriptor_out.wMaxPacketSize = PtpUsbBulkSuperSpeedMaxPacketLength;
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(interface_descriptor), USB_DT_INTERFACE_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_in), USB_DT_ENDPOINT_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_out), USB_DT_ENDPOINT_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_interrupt), USB_DT_ENDPOINT_SIZE));
|
||||||
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion_interrupt), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
||||||
|
|
||||||
|
/* Set up endpoints. */
|
||||||
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Write]), endpoint_descriptor_in.bEndpointAddress));
|
||||||
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Read]), endpoint_descriptor_out.bEndpointAddress));
|
||||||
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Interrupt]), endpoint_descriptor_interrupt.bEndpointAddress));
|
||||||
|
|
||||||
|
R_RETURN(usbDsInterface_EnableInterface(m_interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbSession::Initialize(const UsbCommsInterfaceInfo *info, u16 id_vendor, u16 id_product) {
|
||||||
|
R_TRY(usbDsInitialize());
|
||||||
|
|
||||||
|
if (hosversionAtLeast(5, 0, 0)) {
|
||||||
|
/* Report language as US English. */
|
||||||
|
static const u16 supported_langs[1] = { 0x0409 };
|
||||||
|
R_TRY(usbDsAddUsbLanguageStringDescriptor(nullptr, supported_langs, util::size(supported_langs)));
|
||||||
|
|
||||||
|
/* Report strings. */
|
||||||
|
u8 iManufacturer, iProduct, iSerialNumber;
|
||||||
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iManufacturer), "Nintendo"));
|
||||||
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iProduct), "Nintendo Switch"));
|
||||||
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iSerialNumber), GetSerialNumber()));
|
||||||
|
|
||||||
|
/* Send device descriptors */
|
||||||
|
struct usb_device_descriptor device_descriptor = {
|
||||||
|
.bLength = USB_DT_DEVICE_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_DEVICE,
|
||||||
|
.bcdUSB = 0x0200,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 0x40,
|
||||||
|
.idVendor = id_vendor,
|
||||||
|
.idProduct = id_product,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = iManufacturer,
|
||||||
|
.iProduct = iProduct,
|
||||||
|
.iSerialNumber = iSerialNumber,
|
||||||
|
.bNumConfigurations = 0x01
|
||||||
|
};
|
||||||
|
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, std::addressof(device_descriptor)));
|
||||||
|
|
||||||
|
device_descriptor.bcdUSB = 0x0300;
|
||||||
|
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, std::addressof(device_descriptor)));
|
||||||
|
|
||||||
|
/* 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, /* .bmAttributes */
|
||||||
|
|
||||||
|
/* USB 3.0 */
|
||||||
|
0x0a, /* .bLength */
|
||||||
|
USB_DT_DEVICE_CAPABILITY, /* .bDescriptorType */
|
||||||
|
0x03, /* .bDevCapabilityType */
|
||||||
|
0x00, /* .bmAttributes */
|
||||||
|
0x0c, 0x00, /* .wSpeedSupported */
|
||||||
|
0x03, /* .bFunctionalitySupport */
|
||||||
|
0x00, /* .bU1DevExitLat */
|
||||||
|
0x00, 0x00 /* .bU2DevExitLat */
|
||||||
|
};
|
||||||
|
R_TRY(usbDsSetBinaryObjectStore(bos, sizeof(bos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hosversionAtLeast(5, 0, 0)) {
|
||||||
|
R_TRY(this->Initialize5x(info));
|
||||||
|
R_TRY(usbDsEnable());
|
||||||
|
} else {
|
||||||
|
R_TRY(this->Initialize1x(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbSession::Finalize() {
|
||||||
|
usbDsExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsbSession::GetConfigured() const {
|
||||||
|
UsbState usb_state;
|
||||||
|
|
||||||
|
HAZE_R_ABORT_UNLESS(usbDsGetState(std::addressof(usb_state)));
|
||||||
|
|
||||||
|
return usb_state == UsbState_Configured;
|
||||||
|
}
|
||||||
|
|
||||||
|
Event *UsbSession::GetCompletionEvent(UsbSessionEndpoint ep) const {
|
||||||
|
return std::addressof(m_endpoints[ep]->CompletionEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbSession::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id) {
|
||||||
|
R_RETURN(usbDsEndpoint_PostBufferAsync(m_endpoints[ep], buffer, size, out_urb_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbSession::GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_transferred_size) {
|
||||||
|
UsbDsReportData report_data;
|
||||||
|
|
||||||
|
R_TRY(eventClear(std::addressof(m_endpoints[ep]->CompletionEvent)));
|
||||||
|
R_TRY(usbDsEndpoint_GetReportData(m_endpoints[ep], std::addressof(report_data)));
|
||||||
|
R_TRY(usbDsParseReportData(std::addressof(report_data), urb_id, nullptr, out_transferred_size));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user