ams.tma: restore tma code to debugger_dev branch
This commit is contained in:
93
stratosphere/tma/source/crc.h
Normal file
93
stratosphere/tma/source/crc.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Code taken from Yazen Ghannam <yazen.ghannam@linaro.org>, licensed GPLv2. */
|
||||
|
||||
#define CRC32X(crc, value) __asm__("crc32x %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32W(crc, value) __asm__("crc32w %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32H(crc, value) __asm__("crc32h %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32B(crc, value) __asm__("crc32b %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CX(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CW(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CH(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
#define CRC32CB(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]":[c]"+r"(crc):[v]"r"(value))
|
||||
|
||||
static inline uint16_t __get_unaligned_le16(const uint8_t *p)
|
||||
{
|
||||
return p[0] | p[1] << 8;
|
||||
}
|
||||
|
||||
static inline uint32_t __get_unaligned_le32(const uint8_t *p)
|
||||
{
|
||||
return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
|
||||
}
|
||||
|
||||
static inline uint64_t __get_unaligned_le64(const uint8_t *p)
|
||||
{
|
||||
return (uint64_t)__get_unaligned_le32(p + 4) << 32 |
|
||||
__get_unaligned_le32(p);
|
||||
}
|
||||
|
||||
static inline uint16_t get_unaligned_le16(const void *p)
|
||||
{
|
||||
return __get_unaligned_le16((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint32_t get_unaligned_le32(const void *p)
|
||||
{
|
||||
return __get_unaligned_le32((const uint8_t *)p);
|
||||
}
|
||||
|
||||
static inline uint64_t get_unaligned_le64(const void *p)
|
||||
{
|
||||
return __get_unaligned_le64((const uint8_t *)p);
|
||||
}
|
||||
|
||||
|
||||
static u32 crc32_arm64_le_hw(const u8 *p, unsigned int len) {
|
||||
u32 crc = 0xFFFFFFFF;
|
||||
|
||||
s64 length = len;
|
||||
|
||||
while ((length -= sizeof(u64)) >= 0) {
|
||||
CRC32X(crc, get_unaligned_le64(p));
|
||||
p += sizeof(u64);
|
||||
}
|
||||
|
||||
/* The following is more efficient than the straight loop */
|
||||
if (length & sizeof(u32)) {
|
||||
CRC32W(crc, get_unaligned_le32(p));
|
||||
p += sizeof(u32);
|
||||
}
|
||||
if (length & sizeof(u16)) {
|
||||
CRC32H(crc, get_unaligned_le16(p));
|
||||
p += sizeof(u16);
|
||||
}
|
||||
if (length & sizeof(u8))
|
||||
CRC32B(crc, *p);
|
||||
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
286
stratosphere/tma/source/dmnt.c
Normal file
286
stratosphere/tma/source/dmnt.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include "dmnt.h"
|
||||
|
||||
static Service g_dmntSrv;
|
||||
static u64 g_refCnt;
|
||||
|
||||
Result dmntInitialize(void) {
|
||||
atomicIncrement64(&g_refCnt);
|
||||
|
||||
if (serviceIsActive(&g_dmntSrv))
|
||||
return 0;
|
||||
|
||||
return smGetService(&g_dmntSrv, "dmnt:-");
|
||||
}
|
||||
|
||||
void dmntExit(void) {
|
||||
if (atomicDecrement64(&g_refCnt) == 0)
|
||||
serviceClose(&g_dmntSrv);
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, path, FS_MAX_PATH, BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, out, sizeof(*out), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
int flags;
|
||||
u32 create_option;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 29;
|
||||
raw->flags = flags;
|
||||
raw->create_option = create_option;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileClose(DmntFile *f) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 30;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t *out_read) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, buf, len, BufferType_Type1);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 offset;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 31;
|
||||
raw->offset = off;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 out_read;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_read) {
|
||||
*out_read = resp->out_read;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t *out_written) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, f, sizeof(*f), BufferType_Normal);
|
||||
ipcAddSendBuffer(&c, buf, len, BufferType_Type1);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 offset;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 32;
|
||||
raw->offset = off;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 out_written;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc) && out_written) {
|
||||
*out_written = resp->out_written;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileGetInformation(const char *path, bool *out_is_dir, DmntFileInformation *out_info) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, path, FS_MAX_PATH, BufferType_Normal);
|
||||
ipcAddRecvBuffer(&c, out_info, sizeof(*out_info), BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 34;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
int is_dir;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*out_is_dir = resp->is_dir != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileGetSize(const char *path, u64 *out_size) {
|
||||
DmntFileInformation info;
|
||||
bool is_dir;
|
||||
Result rc = dmntTargetIOFileGetInformation(path, &is_dir, &info);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (is_dir) {
|
||||
/* TODO: error code? */
|
||||
rc = 0x202;
|
||||
} else {
|
||||
*out_size = info.size;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _dmntTargetIOFileSetSize(const void *arg, size_t arg_size, u64 size) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcAddSendBuffer(&c, arg, arg_size, BufferType_Normal);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 size;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&g_dmntSrv, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 36;
|
||||
raw->size = size;
|
||||
|
||||
Result rc = serviceIpcDispatch(&g_dmntSrv);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&g_dmntSrv, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileSetSize(const char *path, u64 size) {
|
||||
return _dmntTargetIOFileSetSize(path, FS_MAX_PATH, size);
|
||||
}
|
||||
|
||||
Result dmntTargetIOFileSetOpenFileSize(DmntFile *f, u64 size) {
|
||||
/* Atmosphere extension */
|
||||
return _dmntTargetIOFileSetSize(f, sizeof(*f), size);
|
||||
}
|
||||
57
stratosphere/tma/source/dmnt.h
Normal file
57
stratosphere/tma/source/dmnt.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
u64 handle;
|
||||
} DmntFile;
|
||||
|
||||
typedef enum {
|
||||
DmntTIOCreateOption_CreateNew = 1,
|
||||
DmntTIOCreateOption_CreateAlways = 2,
|
||||
DmntTIOCreateOption_OpenExisting = 3,
|
||||
DmntTIOCreateOption_OpenAlways = 4,
|
||||
DmntTIOCreateOption_ResetSize = 5,
|
||||
} DmntTIOCreateOption;
|
||||
|
||||
typedef struct {
|
||||
u64 size;
|
||||
u64 create_time;
|
||||
u64 access_time;
|
||||
u64 modify_time;
|
||||
} DmntFileInformation;
|
||||
|
||||
Result dmntInitialize(void);
|
||||
void dmntExit(void);
|
||||
|
||||
Result dmntTargetIOFileOpen(DmntFile *out, const char *path, int flags, DmntTIOCreateOption create_option);
|
||||
Result dmntTargetIOFileClose(DmntFile *f);
|
||||
Result dmntTargetIOFileRead(DmntFile *f, u64 off, void* buf, size_t len, size_t* out_read);
|
||||
Result dmntTargetIOFileWrite(DmntFile *f, u64 off, const void* buf, size_t len, size_t* out_written);
|
||||
Result dmntTargetIOFileGetInformation(const char *path, bool *out_is_dir, DmntFileInformation *out_info);
|
||||
Result dmntTargetIOFileGetSize(const char *path, u64 *out_size);
|
||||
Result dmntTargetIOFileSetSize(const char *path, u64 size);
|
||||
Result dmntTargetIOFileSetOpenFileSize(DmntFile *f, u64 size); /* Atmosphere extension */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
43
stratosphere/tma/source/settings/settings_service.cpp
Normal file
43
stratosphere/tma/source/settings/settings_service.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "settings_service.hpp"
|
||||
#include "settings_task.hpp"
|
||||
|
||||
TmaTask *SettingsService::NewTask(TmaPacket *packet) {
|
||||
TmaTask *new_task = nullptr;
|
||||
switch (packet->GetCommand()) {
|
||||
case SettingsServiceCmd_GetSetting:
|
||||
{
|
||||
new_task = new GetSettingTask(this->manager);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
new_task = nullptr;
|
||||
break;
|
||||
}
|
||||
if (new_task != nullptr) {
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
new_task->SetNeedsPackets(true);
|
||||
}
|
||||
|
||||
return new_task;
|
||||
}
|
||||
34
stratosphere/tma/source/settings/settings_service.hpp
Normal file
34
stratosphere/tma/source/settings/settings_service.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
enum SettingsServiceCmd : u32 {
|
||||
SettingsServiceCmd_GetSetting = 0,
|
||||
};
|
||||
|
||||
class SettingsService : public TmaService {
|
||||
public:
|
||||
SettingsService(TmaServiceManager *m) : TmaService(m, "SettingsService") { }
|
||||
virtual ~SettingsService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
||||
48
stratosphere/tma/source/settings/settings_task.cpp
Normal file
48
stratosphere/tma/source/settings/settings_task.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "settings_task.hpp"
|
||||
|
||||
void GetSettingTask::OnStart(TmaPacket *packet) {
|
||||
size_t length;
|
||||
packet->ReadString(this->name, sizeof(this->name), &length);
|
||||
packet->ReadString(this->item_key, sizeof(this->item_key), &length);
|
||||
|
||||
if (R_SUCCEEDED(setsysGetSettingsItemValueSize(this->name, this->item_key, &this->value_size))) {
|
||||
if (this->value_size <= sizeof(this->value)) {
|
||||
if (R_SUCCEEDED(setsysGetSettingsItemValue(this->name, this->item_key, this->value, this->value_size))) {
|
||||
this->succeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetSettingTask::OnReceivePacket(TmaPacket *packet) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void GetSettingTask::OnSendPacket(TmaPacket *packet) {
|
||||
packet->Write<u8>((u8)this->succeeded);
|
||||
if (this->succeeded) {
|
||||
packet->Write<u32>((u32)this->value_size);
|
||||
packet->Write(this->value, this->value_size);
|
||||
}
|
||||
|
||||
this->Complete();
|
||||
}
|
||||
38
stratosphere/tma/source/settings/settings_task.hpp
Normal file
38
stratosphere/tma/source/settings/settings_task.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
|
||||
class GetSettingTask : public TmaTask {
|
||||
private:
|
||||
char name[0x40] = {0};
|
||||
char item_key[0x40] = {0};
|
||||
u8 value[0x40] = {0};
|
||||
u64 value_size = 0;
|
||||
bool succeeded = false;
|
||||
|
||||
public:
|
||||
GetSettingTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~GetSettingTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
};
|
||||
47
stratosphere/tma/source/target_io/tio_service.cpp
Normal file
47
stratosphere/tma/source/target_io/tio_service.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tio_service.hpp"
|
||||
#include "tio_task.hpp"
|
||||
|
||||
TmaTask *TIOService::NewTask(TmaPacket *packet) {
|
||||
TmaTask *new_task = nullptr;
|
||||
switch (packet->GetCommand()) {
|
||||
case TIOServiceCmd_FileRead:
|
||||
{
|
||||
new_task = new TIOFileReadTask(this->manager);
|
||||
}
|
||||
break;
|
||||
case TIOServiceCmd_FileWrite:
|
||||
{
|
||||
new_task = new TIOFileWriteTask(this->manager);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
new_task = nullptr;
|
||||
break;
|
||||
}
|
||||
if (new_task != nullptr) {
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
}
|
||||
|
||||
return new_task;
|
||||
}
|
||||
35
stratosphere/tma/source/target_io/tio_service.hpp
Normal file
35
stratosphere/tma/source/target_io/tio_service.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
enum TIOServiceCmd : u32 {
|
||||
TIOServiceCmd_FileRead = 2,
|
||||
TIOServiceCmd_FileWrite = 3,
|
||||
};
|
||||
|
||||
class TIOService : public TmaService {
|
||||
public:
|
||||
TIOService(TmaServiceManager *m) : TmaService(m, "TIOService") { }
|
||||
virtual ~TIOService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
||||
173
stratosphere/tma/source/target_io/tio_task.cpp
Normal file
173
stratosphere/tma/source/target_io/tio_task.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tio_task.hpp"
|
||||
|
||||
|
||||
void TIOFileReadTask::OnStart(TmaPacket *packet) {
|
||||
char path[FS_MAX_PATH];
|
||||
|
||||
packet->ReadString(path, sizeof(path), nullptr);
|
||||
packet->Read<u64>(this->size_remaining);
|
||||
packet->Read<u64>(this->cur_offset);
|
||||
|
||||
Result rc = 0;
|
||||
if (strlen(path) == 0) {
|
||||
rc = 0x202;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u64 file_size;
|
||||
rc = dmntTargetIOFileGetSize(path, &file_size);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (file_size < this->cur_offset + this->size_remaining) {
|
||||
this->size_remaining = file_size - this->cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
|
||||
if (R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
return;
|
||||
} else {
|
||||
auto packet = this->AllocateSendPacket();
|
||||
rc = this->ProcessPacket(packet);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
this->manager->SendPacket(packet);
|
||||
if (this->size_remaining) {
|
||||
this->SetNeedsPackets(true);
|
||||
} else {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
} else {
|
||||
this->manager->FreePacket(packet);
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileReadTask::OnSendPacket(TmaPacket *packet) {
|
||||
Result rc = this->ProcessPacket(packet);
|
||||
|
||||
if (this->size_remaining == 0 || R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileReadTask::SendResult(Result rc) {
|
||||
dmntTargetIOFileClose(&this->handle);
|
||||
this->SetNeedsPackets(false);
|
||||
|
||||
auto packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
Complete();
|
||||
}
|
||||
|
||||
Result TIOFileReadTask::ProcessPacket(TmaPacket *packet) {
|
||||
Result rc = 0x196002;
|
||||
|
||||
size_t cur_read = static_cast<u32>((this->size_remaining > MaxDataSize) ? MaxDataSize : this->size_remaining);
|
||||
|
||||
u8 *buf = new u8[cur_read];
|
||||
if (buf != nullptr) {
|
||||
size_t actual_read = 0;
|
||||
rc = dmntTargetIOFileRead(&this->handle, this->cur_offset, buf, cur_read, &actual_read);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
packet->Write<Result>(rc);
|
||||
packet->Write<u32>(actual_read);
|
||||
packet->Write(buf, actual_read);
|
||||
this->cur_offset += actual_read;
|
||||
this->size_remaining -= actual_read;
|
||||
}
|
||||
|
||||
delete buf;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::OnStart(TmaPacket *packet) {
|
||||
char path[FS_MAX_PATH];
|
||||
|
||||
packet->ReadString(path, sizeof(path), nullptr);
|
||||
packet->Read<u64>(this->size_remaining);
|
||||
packet->Read<u64>(this->cur_offset);
|
||||
|
||||
Result rc = 0;
|
||||
if (strlen(path) == 0) {
|
||||
rc = 0x202;
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
u64 file_size;
|
||||
rc = dmntTargetIOFileGetSize(path, &file_size);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (file_size < this->cur_offset + this->size_remaining) {
|
||||
this->size_remaining = file_size - this->cur_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = dmntTargetIOFileOpen(&this->handle, path, FS_OPEN_READ, DmntTIOCreateOption_OpenExisting);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::OnReceivePacket(TmaPacket *packet) {
|
||||
Result rc = this->ProcessPacket(packet);
|
||||
|
||||
if (this->size_remaining == 0 || R_FAILED(rc)) {
|
||||
this->SendResult(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void TIOFileWriteTask::SendResult(Result rc) {
|
||||
dmntTargetIOFileClose(&this->handle);
|
||||
|
||||
auto packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
Complete();
|
||||
}
|
||||
|
||||
Result TIOFileWriteTask::ProcessPacket(TmaPacket *packet) {
|
||||
Result rc = 0x196002;
|
||||
|
||||
/* Note: N does not bounds check this. We do. */
|
||||
u32 cur_write = 0;
|
||||
packet->Read<u32>(cur_write);
|
||||
|
||||
size_t actual_written = 0;
|
||||
if (cur_write < MaxDataSize) {
|
||||
if (cur_write > this->size_remaining) {
|
||||
cur_write = this->size_remaining;
|
||||
}
|
||||
rc = dmntTargetIOFileWrite(&this->handle, this->cur_offset, packet->GetCurrentBodyPtr(), cur_write, &actual_written);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
this->size_remaining -= actual_written;
|
||||
this->cur_offset += actual_written;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
85
stratosphere/tma/source/target_io/tio_task.hpp
Normal file
85
stratosphere/tma/source/target_io/tio_task.hpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
#include "../tma_service_manager.hpp"
|
||||
#include "../dmnt.h"
|
||||
|
||||
class TIOTask : public TmaTask {
|
||||
public:
|
||||
TIOTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~TIOTask() { }
|
||||
|
||||
virtual void SendResult(Result rc) {
|
||||
TmaPacket *packet = this->AllocateSendPacket();
|
||||
packet->Write<Result>(rc);
|
||||
this->manager->SendPacket(packet);
|
||||
this->Complete();
|
||||
}
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) = 0;
|
||||
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override {
|
||||
this->Complete();
|
||||
}
|
||||
|
||||
virtual void OnSendPacket(TmaPacket *packet) override {
|
||||
this->Complete();
|
||||
}
|
||||
};
|
||||
|
||||
class TIOFileReadTask : public TIOTask {
|
||||
private:
|
||||
static constexpr size_t HeaderSize = sizeof(Result) + sizeof(u32);
|
||||
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
|
||||
private:
|
||||
DmntFile handle = {0};
|
||||
u64 size_remaining = 0;
|
||||
u64 cur_offset = 0;
|
||||
public:
|
||||
TIOFileReadTask(TmaServiceManager *m) : TIOTask(m) { }
|
||||
virtual ~TIOFileReadTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
virtual void SendResult(Result rc) override;
|
||||
|
||||
Result ProcessPacket(TmaPacket *packet);
|
||||
};
|
||||
|
||||
class TIOFileWriteTask : public TIOTask {
|
||||
private:
|
||||
static constexpr size_t HeaderSize = sizeof(u32);
|
||||
static constexpr size_t MaxDataSize = TmaPacket::MaxBodySize - HeaderSize;
|
||||
private:
|
||||
DmntFile handle = {0};
|
||||
u64 size_remaining = 0;
|
||||
u64 cur_offset = 0;
|
||||
public:
|
||||
TIOFileWriteTask(TmaServiceManager *m) : TIOTask(m) { }
|
||||
virtual ~TIOFileWriteTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void SendResult(Result rc) override;
|
||||
|
||||
Result ProcessPacket(TmaPacket *packet);
|
||||
};
|
||||
|
||||
31
stratosphere/tma/source/test/atmosphere_test_service.cpp
Normal file
31
stratosphere/tma/source/test/atmosphere_test_service.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "atmosphere_test_service.hpp"
|
||||
#include "atmosphere_test_task.hpp"
|
||||
|
||||
TmaTask *AtmosphereTestService::NewTask(TmaPacket *packet) {
|
||||
auto new_task = new AtmosphereTestTask(this->manager);
|
||||
new_task->SetServiceId(this->GetServiceId());
|
||||
new_task->SetTaskId(packet->GetTaskId());
|
||||
new_task->OnStart(packet);
|
||||
new_task->SetNeedsPackets(true);
|
||||
|
||||
return new_task;
|
||||
}
|
||||
30
stratosphere/tma/source/test/atmosphere_test_service.hpp
Normal file
30
stratosphere/tma/source/test/atmosphere_test_service.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_conn_service_ids.hpp"
|
||||
#include "../tma_service.hpp"
|
||||
|
||||
class AtmosphereTestService : public TmaService {
|
||||
public:
|
||||
AtmosphereTestService(TmaServiceManager *m) : TmaService(m, "AtmosphereTestService") { }
|
||||
virtual ~AtmosphereTestService() { }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) override;
|
||||
};
|
||||
36
stratosphere/tma/source/test/atmosphere_test_task.cpp
Normal file
36
stratosphere/tma/source/test/atmosphere_test_task.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "atmosphere_test_task.hpp"
|
||||
|
||||
void AtmosphereTestTask::OnStart(TmaPacket *packet) {
|
||||
packet->Read<u32>(this->arg);
|
||||
}
|
||||
|
||||
void AtmosphereTestTask::OnReceivePacket(TmaPacket *packet) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void AtmosphereTestTask::OnSendPacket(TmaPacket *packet) {
|
||||
for (size_t i = 0; i < this->arg && i < 0x100; i++) {
|
||||
packet->Write<u8>('A');
|
||||
}
|
||||
|
||||
this->Complete();
|
||||
}
|
||||
33
stratosphere/tma/source/test/atmosphere_test_task.hpp
Normal file
33
stratosphere/tma/source/test/atmosphere_test_task.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../tma_task.hpp"
|
||||
|
||||
class AtmosphereTestTask : public TmaTask {
|
||||
private:
|
||||
u32 arg;
|
||||
public:
|
||||
AtmosphereTestTask(TmaServiceManager *m) : TmaTask(m) { }
|
||||
virtual ~AtmosphereTestTask() { }
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) override;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) override;
|
||||
virtual void OnSendPacket(TmaPacket *packet) override;
|
||||
};
|
||||
64
stratosphere/tma/source/tma_conn_connection.cpp
Normal file
64
stratosphere/tma/source/tma_conn_connection.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *TmaConnection::AllocateSendPacket() {
|
||||
return this->service_manager->AllocateSendPacket();
|
||||
}
|
||||
|
||||
TmaPacket *TmaConnection::AllocateRecvPacket() {
|
||||
return this->service_manager->AllocateRecvPacket();
|
||||
}
|
||||
|
||||
void TmaConnection::FreePacket(TmaPacket *packet) {
|
||||
this->service_manager->FreePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnReceivePacket(TmaPacket *packet) {
|
||||
this->service_manager->OnReceivePacket(packet);
|
||||
}
|
||||
|
||||
void TmaConnection::OnDisconnected() {
|
||||
if (!this->is_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (this->service_manager != nullptr) {
|
||||
this->service_manager->OnDisconnect();
|
||||
}
|
||||
|
||||
this->has_woken_up = false;
|
||||
this->OnConnectionEvent(ConnectionEvent::Disconnected);
|
||||
}
|
||||
|
||||
void TmaConnection::OnConnectionEvent(ConnectionEvent event) {
|
||||
if (this->connection_event_callback != nullptr) {
|
||||
this->connection_event_callback(this->connection_event_arg, event);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaConnection::CancelTasks() {
|
||||
this->service_manager->CancelTasks();
|
||||
}
|
||||
|
||||
void TmaConnection::Tick() {
|
||||
this->service_manager->Tick();
|
||||
}
|
||||
90
stratosphere/tma/source/tma_conn_connection.hpp
Normal file
90
stratosphere/tma/source/tma_conn_connection.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
enum class ConnectionEvent : u32 {
|
||||
Connected,
|
||||
Disconnected
|
||||
};
|
||||
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaConnection {
|
||||
protected:
|
||||
HosMutex lock;
|
||||
void (*connection_event_callback)(void *, ConnectionEvent) = nullptr;
|
||||
void *connection_event_arg = nullptr;
|
||||
bool has_woken_up = false;
|
||||
bool is_initialized = false;
|
||||
TmaServiceManager *service_manager = nullptr;
|
||||
protected:
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
void OnDisconnected();
|
||||
void OnConnectionEvent(ConnectionEvent event);
|
||||
void CancelTasks();
|
||||
void Tick();
|
||||
public:
|
||||
/* Setup */
|
||||
TmaConnection() { }
|
||||
virtual ~TmaConnection() { }
|
||||
|
||||
void Initialize() {
|
||||
if (this->is_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
this->is_initialized = true;
|
||||
}
|
||||
|
||||
void SetConnectionEventCallback(void (*callback)(void *, ConnectionEvent), void *arg) {
|
||||
this->connection_event_callback = callback;
|
||||
this->connection_event_arg = arg;
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
if (this->is_initialized) {
|
||||
this->StopListening();
|
||||
if (this->IsConnected()) {
|
||||
this->Disconnect();
|
||||
}
|
||||
this->is_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetServiceManager(TmaServiceManager *manager) { this->service_manager = manager; }
|
||||
|
||||
/* Packet management. */
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Sleep management. */
|
||||
bool HasWokenUp() const { return this->has_woken_up; }
|
||||
void SetWokenUp(bool woke) { this->has_woken_up = woke; }
|
||||
|
||||
/* For sub-interfaces to implement, connection management. */
|
||||
virtual void StartListening() { }
|
||||
virtual void StopListening() { }
|
||||
virtual bool IsConnected() = 0;
|
||||
virtual TmaConnResult Disconnect() = 0;
|
||||
virtual TmaConnResult SendPacket(TmaPacket *packet) = 0;
|
||||
};
|
||||
279
stratosphere/tma/source/tma_conn_packet.hpp
Normal file
279
stratosphere/tma/source/tma_conn_packet.hpp
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "crc.h"
|
||||
|
||||
class TmaPacket {
|
||||
public:
|
||||
struct Header {
|
||||
u32 service_id;
|
||||
u32 task_id;
|
||||
u16 command;
|
||||
u8 is_continuation;
|
||||
u8 version;
|
||||
u32 body_len;
|
||||
u32 reserved[4]; /* This is where N's header ends. */
|
||||
u32 body_checksum;
|
||||
u32 header_checksum;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x28, "Packet::Header definition!");
|
||||
|
||||
static constexpr u32 MaxBodySize = 0xE000;
|
||||
static constexpr u32 MaxPacketSize = MaxBodySize + sizeof(Header);
|
||||
|
||||
private:
|
||||
std::unique_ptr<u8[]> buffer = std::make_unique<u8[]>(MaxPacketSize);
|
||||
u32 offset = 0;
|
||||
HosMessageQueue *free_queue = nullptr;
|
||||
|
||||
Header *GetHeader() const {
|
||||
return reinterpret_cast<Header *>(buffer.get());
|
||||
}
|
||||
|
||||
u8 *GetBody(u32 ofs) const {
|
||||
return reinterpret_cast<u8 *>(buffer.get() + sizeof(Header) + ofs);
|
||||
}
|
||||
public:
|
||||
TmaPacket() {
|
||||
memset(buffer.get(), 0, MaxPacketSize);
|
||||
}
|
||||
|
||||
/* Implicit ~TmaPacket() */
|
||||
|
||||
/* These allow reading a packet in. */
|
||||
void CopyHeaderFrom(Header *hdr) {
|
||||
*GetHeader() = *hdr;
|
||||
}
|
||||
|
||||
TmaConnResult CopyBodyFrom(void *body, size_t size) {
|
||||
if (size >= MaxBodySize) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(GetBody(0), body, size);
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
void CopyHeaderTo(void *out) {
|
||||
memcpy(out, buffer.get(), sizeof(Header));
|
||||
}
|
||||
|
||||
void CopyBodyTo(void *out) const {
|
||||
memcpy(out, buffer.get() + sizeof(Header), GetBodyLength());
|
||||
}
|
||||
|
||||
bool IsHeaderValid() {
|
||||
Header *hdr = GetHeader();
|
||||
return crc32_arm64_le_hw(reinterpret_cast<const u8 *>(hdr), sizeof(*hdr) - sizeof(hdr->header_checksum)) == hdr->header_checksum;
|
||||
}
|
||||
|
||||
bool IsBodyValid() const {
|
||||
const u32 body_len = GetHeader()->body_len;
|
||||
if (body_len == 0) {
|
||||
return GetHeader()->body_checksum == 0;
|
||||
} else {
|
||||
return crc32_arm64_le_hw(GetBody(0), body_len) == GetHeader()->body_checksum;
|
||||
}
|
||||
}
|
||||
|
||||
HosMessageQueue *GetFreeQueue() const {
|
||||
return this->free_queue;
|
||||
}
|
||||
|
||||
void SetFreeQueue(HosMessageQueue *queue) {
|
||||
this->free_queue = queue;
|
||||
}
|
||||
|
||||
void SetChecksums() {
|
||||
Header *hdr = GetHeader();
|
||||
if (hdr->body_len) {
|
||||
hdr->body_checksum = crc32_arm64_le_hw(GetBody(0), hdr->body_len);
|
||||
} else {
|
||||
hdr->body_checksum = 0;
|
||||
}
|
||||
hdr->header_checksum = crc32_arm64_le_hw(reinterpret_cast<const u8 *>(hdr), sizeof(*hdr) - sizeof(hdr->header_checksum));
|
||||
}
|
||||
|
||||
u32 GetBodyLength() const {
|
||||
return GetHeader()->body_len;
|
||||
}
|
||||
|
||||
u32 GetLength() const {
|
||||
return GetBodyLength() + sizeof(Header);
|
||||
}
|
||||
|
||||
u32 GetBodyAvailableLength() const {
|
||||
return MaxPacketSize - this->offset;
|
||||
}
|
||||
|
||||
void SetServiceId(TmaServiceId srv) {
|
||||
GetHeader()->service_id = static_cast<u32>(srv);
|
||||
}
|
||||
|
||||
TmaServiceId GetServiceId() const {
|
||||
return static_cast<TmaServiceId>(GetHeader()->service_id);
|
||||
}
|
||||
|
||||
void SetTaskId(u32 id) {
|
||||
GetHeader()->task_id = id;
|
||||
}
|
||||
|
||||
u32 GetTaskId() const {
|
||||
return GetHeader()->task_id;
|
||||
}
|
||||
|
||||
void SetCommand(u16 cmd) {
|
||||
GetHeader()->command = cmd;
|
||||
}
|
||||
|
||||
u16 GetCommand() const {
|
||||
return GetHeader()->command;
|
||||
}
|
||||
|
||||
void SetContinuation(bool c) {
|
||||
GetHeader()->is_continuation = c ? 1 : 0;
|
||||
}
|
||||
|
||||
bool GetContinuation() const {
|
||||
return GetHeader()->is_continuation == 1;
|
||||
}
|
||||
|
||||
void SetVersion(u8 v) {
|
||||
GetHeader()->version = v;
|
||||
}
|
||||
|
||||
u8 GetVersion() const {
|
||||
return GetHeader()->version;
|
||||
}
|
||||
|
||||
u8 *GetCurrentBodyPtr() {
|
||||
return GetBody(this->offset);
|
||||
}
|
||||
|
||||
void ClearOffset() {
|
||||
this->offset = 0;
|
||||
}
|
||||
|
||||
void SetBodyLength() {
|
||||
GetHeader()->body_len = this->offset;
|
||||
}
|
||||
|
||||
TmaConnResult Write(const void *data, size_t size) {
|
||||
if (size > GetBodyAvailableLength()) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(GetBody(this->offset), data, size);
|
||||
this->offset += size;
|
||||
GetHeader()->body_len = this->offset;
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
TmaConnResult Read(void *data, size_t size) {
|
||||
if (size > GetBodyAvailableLength()) {
|
||||
return TmaConnResult::PacketOverflow;
|
||||
}
|
||||
|
||||
memcpy(data, GetBody(this->offset), size);
|
||||
this->offset += size;
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TmaConnResult Write(const T &t) {
|
||||
return Write(&t, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TmaConnResult Read(T &t) {
|
||||
return Read(&t, sizeof(T));
|
||||
}
|
||||
|
||||
TmaConnResult WriteString(const char *s) {
|
||||
return Write(s, strlen(s) + 1);
|
||||
}
|
||||
|
||||
size_t WriteFormat(const char *format, ...) {
|
||||
va_list va_arg;
|
||||
va_start(va_arg, format);
|
||||
const size_t available = GetBodyAvailableLength();
|
||||
const int written = vsnprintf(reinterpret_cast<char *>(GetBody(this->offset)), available, format, va_arg);
|
||||
|
||||
size_t total_written;
|
||||
if (static_cast<size_t>(written) < available) {
|
||||
this->offset += written;
|
||||
*GetBody(this->offset++) = 0;
|
||||
total_written = written + 1;
|
||||
} else {
|
||||
this->offset += available;
|
||||
total_written = available;
|
||||
}
|
||||
|
||||
GetHeader()->body_len = this->offset;
|
||||
return total_written;
|
||||
}
|
||||
|
||||
TmaConnResult ReadString(char *buf, size_t buf_size, size_t *out_size) {
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
size_t available = GetBodyAvailableLength();
|
||||
size_t ofs = 0;
|
||||
while (ofs < buf_size) {
|
||||
if (ofs >= available) {
|
||||
res = TmaConnResult::PacketOverflow;
|
||||
break;
|
||||
}
|
||||
if (ofs == buf_size) {
|
||||
res = TmaConnResult::BufferOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
buf[ofs] = static_cast<char>(*GetBody(this->offset++));
|
||||
|
||||
if (buf[ofs++] == '\x00') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish reading the string if the user buffer is too small. */
|
||||
if (res == TmaConnResult::BufferOverflow) {
|
||||
u8 cur = *GetBody(this->offset);
|
||||
while (cur != 0) {
|
||||
if (ofs >= available) {
|
||||
res = TmaConnResult::PacketOverflow;
|
||||
break;
|
||||
}
|
||||
cur = *GetBody(this->offset++);
|
||||
ofs++;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_size != nullptr) {
|
||||
*out_size = ofs;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
40
stratosphere/tma/source/tma_conn_result.hpp
Normal file
40
stratosphere/tma/source/tma_conn_result.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
enum class TmaConnResult : u32 {
|
||||
Success = 0,
|
||||
NotImplemented,
|
||||
GeneralFailure,
|
||||
ConnectionFailure,
|
||||
AlreadyConnected,
|
||||
WrongConnectionVersion,
|
||||
PacketOverflow,
|
||||
BufferOverflow,
|
||||
Disconnected,
|
||||
ServiceAlreadyRegistered,
|
||||
ServiceUnknown,
|
||||
Timeout,
|
||||
NotInitialized,
|
||||
};
|
||||
53
stratosphere/tma/source/tma_conn_service_ids.hpp
Normal file
53
stratosphere/tma/source/tma_conn_service_ids.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
|
||||
/* This is just python's hash function, but official TMA code uses it. */
|
||||
static constexpr u32 HashServiceName(const char *name) {
|
||||
u32 h = *name;
|
||||
u32 len = 0;
|
||||
|
||||
while (*name) {
|
||||
h = (1000003 * h) ^ *name;
|
||||
name++;
|
||||
len++;
|
||||
}
|
||||
|
||||
return h ^ len;
|
||||
}
|
||||
|
||||
enum class TmaServiceId : u32 {
|
||||
Invalid = 0,
|
||||
|
||||
/* Special nodes, for facilitating connection over USB. */
|
||||
UsbQueryTarget = HashServiceName("USBQueryTarget"),
|
||||
UsbSendHostInfo = HashServiceName("USBSendHostInfo"),
|
||||
UsbConnect = HashServiceName("USBConnect"),
|
||||
UsbDisconnect = HashServiceName("USBDisconnect"),
|
||||
|
||||
|
||||
TestService = HashServiceName("AtmosphereTestService"), /* Temporary service, will be used to debug communications. */
|
||||
};
|
||||
|
||||
static constexpr bool IsMetaService(TmaServiceId id) {
|
||||
return id == TmaServiceId::UsbQueryTarget ||
|
||||
id == TmaServiceId::UsbSendHostInfo ||
|
||||
id == TmaServiceId::UsbConnect ||
|
||||
id == TmaServiceId::UsbDisconnect;
|
||||
}
|
||||
204
stratosphere/tma/source/tma_conn_usb_connection.cpp
Normal file
204
stratosphere/tma/source/tma_conn_usb_connection.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_conn_usb_connection.hpp"
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
static HosThread g_SendThread, g_RecvThread;
|
||||
|
||||
TmaConnResult TmaUsbConnection::InitializeComms() {
|
||||
return TmaUsbComms::Initialize();
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::FinalizeComms() {
|
||||
return TmaUsbComms::Finalize();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::ClearSendQueue() {
|
||||
uintptr_t _packet;
|
||||
while (this->send_queue.TryReceive(&_packet)) {
|
||||
TmaPacket *packet = reinterpret_cast<TmaPacket *>(_packet);
|
||||
if (packet != nullptr) {
|
||||
this->FreePacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaUsbConnection::SendThreadFunc(void *arg) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
while (res == TmaConnResult::Success) {
|
||||
/* Receive a packet from the send queue. */
|
||||
{
|
||||
uintptr_t _packet;
|
||||
this_ptr->send_queue.Receive(&_packet);
|
||||
packet = reinterpret_cast<TmaPacket *>(_packet);
|
||||
}
|
||||
|
||||
if (packet != nullptr) {
|
||||
/* Send the packet if we're connected. */
|
||||
if (this_ptr->IsConnected()) {
|
||||
res = TmaUsbComms::SendPacket(packet);
|
||||
}
|
||||
|
||||
this_ptr->FreePacket(packet);
|
||||
this_ptr->Tick();
|
||||
} else {
|
||||
res = TmaConnResult::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->OnDisconnected();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::RecvThreadFunc(void *arg) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
this_ptr->SetConnected(true);
|
||||
|
||||
while (res == TmaConnResult::Success) {
|
||||
TmaPacket *packet = this_ptr->AllocateRecvPacket();
|
||||
if (packet == nullptr) { std::abort(); }
|
||||
|
||||
res = TmaUsbComms::ReceivePacket(packet);
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (!IsMetaService(packet->GetServiceId())) {
|
||||
this_ptr->OnReceivePacket(packet);
|
||||
} else {
|
||||
switch (packet->GetServiceId()) {
|
||||
case TmaServiceId::UsbQueryTarget: {
|
||||
this_ptr->SetConnected(false);
|
||||
|
||||
res = this_ptr->SendQueryReply(packet);
|
||||
|
||||
if (!this_ptr->has_woken_up) {
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbSendHostInfo: {
|
||||
struct {
|
||||
u32 version;
|
||||
u32 sleeping;
|
||||
} host_info;
|
||||
packet->Read<decltype(host_info)>(host_info);
|
||||
|
||||
if (!this_ptr->has_woken_up || !host_info.sleeping) {
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbConnect: {
|
||||
res = this_ptr->SendQueryReply(packet);
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
this_ptr->SetConnected(true);
|
||||
this_ptr->OnConnectionEvent(ConnectionEvent::Connected);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaServiceId::UsbDisconnect: {
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->OnDisconnected();
|
||||
|
||||
this_ptr->CancelTasks();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this_ptr->FreePacket(packet);
|
||||
}
|
||||
} else {
|
||||
this_ptr->FreePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
this_ptr->SetConnected(false);
|
||||
this_ptr->send_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
|
||||
}
|
||||
|
||||
void TmaUsbConnection::OnUsbStateChange(void *arg, u32 state) {
|
||||
TmaUsbConnection *this_ptr = reinterpret_cast<TmaUsbConnection *>(arg);
|
||||
switch (state) {
|
||||
case 0:
|
||||
case 6:
|
||||
this_ptr->StopThreads();
|
||||
break;
|
||||
case 5:
|
||||
this_ptr->StartThreads();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaUsbConnection::StartThreads() {
|
||||
g_SendThread.Join();
|
||||
g_RecvThread.Join();
|
||||
|
||||
g_SendThread.Initialize(&TmaUsbConnection::SendThreadFunc, this, 0x4000, 38);
|
||||
g_RecvThread.Initialize(&TmaUsbConnection::RecvThreadFunc, this, 0x4000, 38);
|
||||
|
||||
this->ClearSendQueue();
|
||||
g_SendThread.Start();
|
||||
g_RecvThread.Start();
|
||||
}
|
||||
|
||||
void TmaUsbConnection::StopThreads() {
|
||||
TmaUsbComms::CancelComms();
|
||||
g_SendThread.Join();
|
||||
g_RecvThread.Join();
|
||||
}
|
||||
|
||||
bool TmaUsbConnection::IsConnected() {
|
||||
return this->is_connected;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::Disconnect() {
|
||||
TmaUsbComms::SetStateChangeCallback(nullptr, nullptr);
|
||||
|
||||
this->StopThreads();
|
||||
|
||||
return TmaConnResult::Success;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::SendPacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
if (this->IsConnected()) {
|
||||
this->send_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
return TmaConnResult::Success;
|
||||
} else {
|
||||
this->FreePacket(packet);
|
||||
this->Tick();
|
||||
return TmaConnResult::Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbConnection::SendQueryReply(TmaPacket *packet) {
|
||||
packet->ClearOffset();
|
||||
struct {
|
||||
u32 version;
|
||||
} target_info;
|
||||
target_info.version = 0;
|
||||
packet->Write<decltype(target_info)>(target_info);
|
||||
return TmaUsbComms::SendPacket(packet);
|
||||
}
|
||||
52
stratosphere/tma/source/tma_conn_usb_connection.hpp
Normal file
52
stratosphere/tma/source/tma_conn_usb_connection.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
class TmaUsbConnection : public TmaConnection {
|
||||
private:
|
||||
HosMessageQueue send_queue = HosMessageQueue(64);
|
||||
std::atomic<bool> is_connected = false;
|
||||
private:
|
||||
static void SendThreadFunc(void *arg);
|
||||
static void RecvThreadFunc(void *arg);
|
||||
static void OnUsbStateChange(void *this_ptr, u32 state);
|
||||
TmaConnResult SendQueryReply(TmaPacket *packet);
|
||||
void ClearSendQueue();
|
||||
void StartThreads();
|
||||
void StopThreads();
|
||||
void SetConnected(bool c) { this->is_connected = c; }
|
||||
public:
|
||||
static TmaConnResult InitializeComms();
|
||||
static TmaConnResult FinalizeComms();
|
||||
|
||||
TmaUsbConnection() {
|
||||
TmaUsbComms::SetStateChangeCallback(&TmaUsbConnection::OnUsbStateChange, this);
|
||||
}
|
||||
|
||||
virtual ~TmaUsbConnection() {
|
||||
this->Disconnect();
|
||||
}
|
||||
|
||||
virtual bool IsConnected() override;
|
||||
virtual TmaConnResult Disconnect() override;
|
||||
virtual TmaConnResult SendPacket(TmaPacket *packet) override;
|
||||
};
|
||||
106
stratosphere/tma/source/tma_main.cpp
Normal file
106
stratosphere/tma/source/tma_main.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_target.hpp"
|
||||
|
||||
#include "dmnt.h"
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x400000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
void __libnx_initheap(void);
|
||||
void __appInit(void);
|
||||
void __appExit(void);
|
||||
}
|
||||
|
||||
|
||||
void __libnx_initheap(void) {
|
||||
void* addr = nx_inner_heap;
|
||||
size_t size = nx_inner_heap_size;
|
||||
|
||||
/* Newlib */
|
||||
extern char* fake_heap_start;
|
||||
extern char* fake_heap_end;
|
||||
|
||||
fake_heap_start = (char*)addr;
|
||||
fake_heap_end = (char*)addr + size;
|
||||
}
|
||||
|
||||
void __appInit(void) {
|
||||
Result rc;
|
||||
|
||||
rc = smInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM));
|
||||
}
|
||||
|
||||
rc = pscInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
rc = setsysInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
rc = dmntInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
|
||||
}
|
||||
|
||||
void __appExit(void) {
|
||||
/* Cleanup services. */
|
||||
dmntExit();
|
||||
setsysExit();
|
||||
pscExit();
|
||||
smExit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
consoleDebugInit(debugDevice_SVC);
|
||||
|
||||
/* This will initialize the target. */
|
||||
TmaTarget::Initialize();
|
||||
|
||||
while (true) {
|
||||
svcSleepThread(10000000UL);
|
||||
}
|
||||
|
||||
TmaTarget::Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
68
stratosphere/tma/source/tma_power_manager.cpp
Normal file
68
stratosphere/tma/source/tma_power_manager.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_power_manager.hpp"
|
||||
|
||||
static constexpr u16 PscPmModuleId_Usb = 0x04;
|
||||
static constexpr u16 PscPmModuleId_Pcie = 0x13;
|
||||
static constexpr u16 PscPmModuleId_Tma = 0x1E;
|
||||
|
||||
static const u16 g_tma_pm_dependencies[] = {
|
||||
PscPmModuleId_Usb,
|
||||
};
|
||||
|
||||
static void (*g_pm_callback)(PscPmState, u32) = nullptr;
|
||||
static HosThread g_pm_thread;
|
||||
|
||||
static void PowerManagerThread(void *arg) {
|
||||
/* Setup psc module. */
|
||||
Result rc;
|
||||
PscPmModule tma_module = {0};
|
||||
if (R_FAILED((rc = pscGetPmModule(&tma_module, PscPmModuleId_Tma, g_tma_pm_dependencies, sizeof(g_tma_pm_dependencies)/sizeof(u16), true)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
/* For now, just do what dummy tma does -- loop forever, acknowledging everything. */
|
||||
while (true) {
|
||||
if (R_FAILED((rc = eventWait(&tma_module.event, U64_MAX)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
PscPmState state;
|
||||
u32 flags;
|
||||
if (R_FAILED((rc = pscPmModuleGetRequest(&tma_module, &state, &flags)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
g_pm_callback(state, flags);
|
||||
|
||||
if (R_FAILED((rc = pscPmModuleAcknowledge(&tma_module, state)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaPowerManager::Initialize(void (*callback)(PscPmState, u32)) {
|
||||
g_pm_callback = callback;
|
||||
g_pm_thread.Initialize(PowerManagerThread, nullptr, 0x4000, 0x26);
|
||||
g_pm_thread.Start();
|
||||
}
|
||||
|
||||
void TmaPowerManager::Finalize() {
|
||||
/* TODO */
|
||||
}
|
||||
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
25
stratosphere/tma/source/tma_power_manager.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class TmaPowerManager {
|
||||
public:
|
||||
static void Initialize(void (*callback)(PscPmState, u32));
|
||||
static void Finalize();
|
||||
};
|
||||
32
stratosphere/tma/source/tma_service.cpp
Normal file
32
stratosphere/tma/source/tma_service.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
u32 TmaService::GetNextTaskId() {
|
||||
return this->manager->GetNextTaskId();
|
||||
}
|
||||
|
||||
void TmaService::OnSleep() {
|
||||
/* Default service does nothing here. */
|
||||
}
|
||||
|
||||
void TmaService::OnWake() {
|
||||
/* Default service does nothing here. */
|
||||
}
|
||||
43
stratosphere/tma/source/tma_service.hpp
Normal file
43
stratosphere/tma/source/tma_service.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
#include "tma_task.hpp"
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaService {
|
||||
protected:
|
||||
TmaServiceManager *manager;
|
||||
const char *service_name;
|
||||
const TmaServiceId id;
|
||||
protected:
|
||||
u32 GetNextTaskId();
|
||||
public:
|
||||
TmaService(TmaServiceManager *m, const char *n) : manager(m), service_name(n), id(static_cast<TmaServiceId>(HashServiceName(this->service_name))) { }
|
||||
virtual ~TmaService() { }
|
||||
|
||||
TmaServiceId GetServiceId() const { return this->id; }
|
||||
|
||||
virtual TmaTask *NewTask(TmaPacket *packet) = 0;
|
||||
virtual void OnSleep();
|
||||
virtual void OnWake();
|
||||
};
|
||||
406
stratosphere/tma/source/tma_service_manager.cpp
Normal file
406
stratosphere/tma/source/tma_service_manager.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
TmaServiceManager::TmaServiceManager() {
|
||||
/* Set up queues */
|
||||
for (size_t i = 0; i < TmaServiceManager::PacketQueueDepth; i++) {
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_send_packet_queue);
|
||||
this->free_send_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
|
||||
packet = new TmaPacket();
|
||||
packet->SetFreeQueue(&this->free_recv_packet_queue);
|
||||
this->free_recv_packet_queue.Send(reinterpret_cast<uintptr_t>(packet));
|
||||
packet = nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < TmaServiceManager::WorkQueueDepth; i++) {
|
||||
this->free_work_queue.Send(reinterpret_cast<uintptr_t>(new TmaWorkItem()));
|
||||
}
|
||||
}
|
||||
|
||||
TmaServiceManager::~TmaServiceManager() {
|
||||
/* Destroy queues. */
|
||||
TmaPacket *packet = nullptr;
|
||||
while (this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
while (this->free_recv_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
delete packet;
|
||||
packet = nullptr;
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
while (this->free_work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
while (this->work_queue.TryReceive(reinterpret_cast<uintptr_t *>(&work_item))) {
|
||||
delete work_item;
|
||||
work_item = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Initialize() {
|
||||
this->initialized = true;
|
||||
this->work_thread.Initialize(TmaServiceManager::WorkThread, this, 0x4000, 0x26);
|
||||
this->work_thread.Start();
|
||||
}
|
||||
|
||||
void TmaServiceManager::Finalize() {
|
||||
if (this->initialized) {
|
||||
this->initialized = false;
|
||||
if (this->connection && this->connection->IsConnected()) {
|
||||
this->connection->Disconnect();
|
||||
}
|
||||
|
||||
/* Signal to work thread to end. */
|
||||
this->work_queue.Send(reinterpret_cast<uintptr_t>(nullptr));
|
||||
this->work_thread.Join();
|
||||
|
||||
/* TODO: N tells services that they have no manager here. Do we want to do that? */
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this->free_work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
work_item->task = task;
|
||||
work_item->packet = packet;
|
||||
work_item->work_type = type;
|
||||
this->work_queue.Send(reinterpret_cast<uintptr_t>(work_item));
|
||||
}
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult TmaServiceManager::SendPacket(TmaPacket *packet) {
|
||||
TmaConnResult res = TmaConnResult::Disconnected;
|
||||
|
||||
if (this->connection != nullptr) {
|
||||
res = this->connection->SendPacket(packet);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void TmaServiceManager::OnReceivePacket(TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::ReceivePacket, nullptr, packet);
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateSendPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_send_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
packet->SetBodyLength();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
TmaPacket *TmaServiceManager::AllocateRecvPacket() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
this->free_recv_packet_queue.Receive(reinterpret_cast<uintptr_t *>(&packet));
|
||||
|
||||
packet->ClearOffset();
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreePacket(TmaPacket *packet) {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (packet != nullptr) {
|
||||
packet->GetFreeQueue()->Send(reinterpret_cast<uintptr_t>(packet));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *TmaServiceManager::GetServiceById(TmaServiceId id) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (auto srv : this->services) {
|
||||
if (srv->GetServiceId() == id) {
|
||||
return srv;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddService(TmaService *service) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
this->services.push_back(service);
|
||||
}
|
||||
|
||||
void TmaServiceManager::AddTask(TmaTask *task, TmaPacket *packet) {
|
||||
this->AddWork(TmaWorkType::NewTask, task, packet);
|
||||
}
|
||||
|
||||
void TmaServiceManager::FreeTask(TmaTask *task) {
|
||||
this->AddWork(TmaWorkType::FreeTask, task, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTask(u32 task_id) {
|
||||
if (this->initialized) {
|
||||
this->task_list.Cancel(task_id);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::CancelTasks() {
|
||||
if (this->initialized) {
|
||||
this->task_list.CancelAll();
|
||||
}
|
||||
}
|
||||
|
||||
u32 TmaServiceManager::GetNextTaskId() {
|
||||
while (true) {
|
||||
u32 id;
|
||||
{
|
||||
/* N only uses 16 bits for the task id. We'll use 24. */
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
id = (this->next_task_id++) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
if (this->task_list.IsIdFree(id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connection management. */
|
||||
void TmaServiceManager::Tick() {
|
||||
this->AddWork(TmaWorkType::Tick, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void TmaServiceManager::SetConnection(TmaConnection *conn) {
|
||||
this->connection = conn;
|
||||
}
|
||||
|
||||
void TmaServiceManager::OnDisconnect() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->disconnect_signal.Reset();
|
||||
|
||||
this->AddWork(TmaWorkType::Disconnect, nullptr, nullptr);
|
||||
|
||||
/* TODO: why does N wait with a timeout of zero here? */
|
||||
this->disconnect_signal.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Sleep() {
|
||||
if (!this->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->GetAsleep()) {
|
||||
this->wake_signal.Reset();
|
||||
this->sleep_signal.Reset();
|
||||
|
||||
/* Tell the work thread to stall, wait for ACK. */
|
||||
this->AddWork(TmaWorkType::Sleep, nullptr, nullptr);
|
||||
this->sleep_signal.Wait();
|
||||
|
||||
this->SetAsleep(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::Wake(TmaConnection *conn) {
|
||||
if (this->connection != nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
if (this->GetAsleep()) {
|
||||
this->connection = conn;
|
||||
this->connection->SetWokenUp(true);
|
||||
this->connection->SetServiceManager(this);
|
||||
/* Tell the work thread to resume. */
|
||||
this->wake_signal.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool TmaServiceManager::GetConnected() const {
|
||||
return this->connection != nullptr && this->connection->IsConnected();
|
||||
}
|
||||
|
||||
/* Work thread. */
|
||||
void TmaServiceManager::WorkThread(void *_this) {
|
||||
TmaServiceManager *this_ptr = reinterpret_cast<TmaServiceManager *>(_this);
|
||||
if (!this_ptr->initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
/* Receive a work item. */
|
||||
TmaWorkItem *work_item = nullptr;
|
||||
this_ptr->work_queue.Receive(reinterpret_cast<uintptr_t *>(&work_item));
|
||||
|
||||
if (work_item == nullptr) {
|
||||
/* We're done. */
|
||||
this_ptr->task_list.CancelAll();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (work_item->work_type) {
|
||||
case TmaWorkType::Tick:
|
||||
/* HandleTickWork called unconditionally. */
|
||||
break;
|
||||
case TmaWorkType::NewTask:
|
||||
this_ptr->HandleNewTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::FreeTask:
|
||||
this_ptr->HandleFreeTaskWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::ReceivePacket:
|
||||
this_ptr->HandleReceivePacketWork(work_item);
|
||||
break;
|
||||
case TmaWorkType::Disconnect:
|
||||
this_ptr->HandleDisconnectWork();
|
||||
break;
|
||||
case TmaWorkType::Sleep:
|
||||
this_ptr->HandleSleepWork();
|
||||
break;
|
||||
case TmaWorkType::None:
|
||||
default:
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
|
||||
this_ptr->free_work_queue.Send(reinterpret_cast<uintptr_t>(work_item));
|
||||
this_ptr->HandleTickWork();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleNewTaskWork(TmaWorkItem *work_item) {
|
||||
this->task_list.Add(work_item->task);
|
||||
if (this->GetConnected()) {
|
||||
if (work_item->packet != nullptr) {
|
||||
this->SendPacket(work_item->packet);
|
||||
}
|
||||
} else {
|
||||
work_item->task->Cancel();
|
||||
this->FreePacket(work_item->packet);
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleFreeTaskWork(TmaWorkItem *work_item) {
|
||||
delete work_item->task;
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleReceivePacketWork(TmaWorkItem *work_item) {
|
||||
ON_SCOPE_EXIT { this->FreePacket(work_item->packet); };
|
||||
|
||||
/* Handle continuation packets. */
|
||||
if (work_item->packet->GetContinuation()) {
|
||||
this->task_list.ReceivePacket(work_item->packet);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make a new task for the packet. */
|
||||
TmaService *srv = this->GetServiceById(work_item->packet->GetServiceId());
|
||||
if (srv != nullptr) {
|
||||
TmaTask *task = srv->NewTask(work_item->packet);
|
||||
if (task != nullptr) {
|
||||
this->task_list.Add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleTickWork() {
|
||||
if (this->connection == nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* N does this kind of manual cleanup if send isn't called. */
|
||||
/* It's pretty gross, but in lieu of a better idea... */
|
||||
bool needs_manual_cleanup = true;
|
||||
|
||||
TmaPacket *packet = nullptr;
|
||||
|
||||
while (this->connection != nullptr && this->free_send_packet_queue.TryReceive(reinterpret_cast<uintptr_t *>(&packet))) {
|
||||
needs_manual_cleanup = false;
|
||||
|
||||
if (this->task_list.SendPacket(this->GetConnected(), packet)) {
|
||||
if (this->SendPacket(packet) != TmaConnResult::Success) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->FreePacket(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_manual_cleanup) {
|
||||
this->task_list.CleanupDoneTasks();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleDisconnectWork() {
|
||||
this->task_list.CancelAll();
|
||||
this->disconnect_signal.Signal();
|
||||
}
|
||||
|
||||
void TmaServiceManager::HandleSleepWork() {
|
||||
/* Put the task list to sleep. */
|
||||
this->task_list.Sleep();
|
||||
|
||||
/* Put services to sleep. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnSleep();
|
||||
}
|
||||
|
||||
/* Signal to main thread that we're sleeping. */
|
||||
this->sleep_signal.Signal();
|
||||
/* Wait for us to wake up. */
|
||||
this->wake_signal.Wait();
|
||||
|
||||
/* We're awake now... */
|
||||
this->SetAsleep(false);
|
||||
|
||||
/* Wake up services. */
|
||||
for (auto srv : this->services) {
|
||||
srv->OnWake();
|
||||
}
|
||||
|
||||
/* Wake up the task list. */
|
||||
this->task_list.Wake();
|
||||
}
|
||||
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
109
stratosphere/tma/source/tma_service_manager.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service.hpp"
|
||||
#include "tma_task_list.hpp"
|
||||
#include "tma_conn_connection.hpp"
|
||||
|
||||
enum class TmaWorkType : u32 {
|
||||
None,
|
||||
NewTask,
|
||||
FreeTask,
|
||||
ReceivePacket,
|
||||
Tick,
|
||||
Disconnect,
|
||||
Sleep,
|
||||
};
|
||||
|
||||
struct TmaWorkItem {
|
||||
TmaTask *task;
|
||||
TmaPacket *packet;
|
||||
TmaWorkType work_type;
|
||||
};
|
||||
|
||||
class TmaServiceManager {
|
||||
public:
|
||||
static constexpr size_t PacketQueueDepth = 0x8;
|
||||
static constexpr size_t WorkQueueDepth = 0x80;
|
||||
private:
|
||||
HosMutex lock;
|
||||
bool initialized = false;
|
||||
TmaTaskList task_list;
|
||||
HosThread work_thread;
|
||||
std::vector<TmaService *> services;
|
||||
TmaConnection *connection = nullptr;
|
||||
u32 next_task_id = 0;
|
||||
|
||||
/* Work queues. */
|
||||
HosMessageQueue free_send_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue free_recv_packet_queue = HosMessageQueue(PacketQueueDepth);
|
||||
HosMessageQueue work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
HosMessageQueue free_work_queue = HosMessageQueue(WorkQueueDepth);
|
||||
|
||||
/* Sleep management. */
|
||||
HosSignal disconnect_signal;
|
||||
HosSignal wake_signal;
|
||||
HosSignal sleep_signal;
|
||||
bool asleep = false;
|
||||
private:
|
||||
static void WorkThread(void *arg);
|
||||
void AddWork(TmaWorkType type, TmaTask *task, TmaPacket *packet);
|
||||
void HandleNewTaskWork(TmaWorkItem *work_item);
|
||||
void HandleFreeTaskWork(TmaWorkItem *work_item);
|
||||
void HandleReceivePacketWork(TmaWorkItem *work_item);
|
||||
void HandleTickWork();
|
||||
void HandleDisconnectWork();
|
||||
void HandleSleepWork();
|
||||
|
||||
void SetAsleep(bool s) { this->asleep = s; }
|
||||
public:
|
||||
TmaServiceManager();
|
||||
virtual ~TmaServiceManager();
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
/* Packet management. */
|
||||
TmaConnResult SendPacket(TmaPacket *packet);
|
||||
void OnReceivePacket(TmaPacket *packet);
|
||||
TmaPacket *AllocateSendPacket();
|
||||
TmaPacket *AllocateRecvPacket();
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
/* Service/task management. */
|
||||
TmaService *GetServiceById(TmaServiceId id);
|
||||
void AddService(TmaService *service);
|
||||
void AddTask(TmaTask *task, TmaPacket *packet);
|
||||
void FreeTask(TmaTask *task);
|
||||
void CancelTask(u32 task_id);
|
||||
void CancelTasks();
|
||||
u32 GetNextTaskId();
|
||||
|
||||
/* Connection management. */
|
||||
void Tick();
|
||||
void SetConnection(TmaConnection *conn);
|
||||
void OnDisconnect();
|
||||
void Sleep();
|
||||
void Wake(TmaConnection *conn);
|
||||
bool GetAsleep() const { return this->asleep; }
|
||||
bool GetConnected() const;
|
||||
};
|
||||
234
stratosphere/tma/source/tma_target.cpp
Normal file
234
stratosphere/tma/source/tma_target.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_connection.hpp"
|
||||
#include "tma_conn_usb_connection.hpp"
|
||||
|
||||
#include "tma_service_manager.hpp"
|
||||
#include "tma_power_manager.hpp"
|
||||
|
||||
#include "tma_target.hpp"
|
||||
|
||||
#include "test/atmosphere_test_service.hpp"
|
||||
#include "settings/settings_service.hpp"
|
||||
#include "target_io/tio_service.hpp"
|
||||
|
||||
struct TmaTargetConfig {
|
||||
char configuration_id1[0x80];
|
||||
char serial_number[0x80];
|
||||
};
|
||||
|
||||
static TmaConnection *g_active_connection = nullptr;
|
||||
static TmaServiceManager *g_service_manager = nullptr;
|
||||
static HosMutex g_connection_event_mutex;
|
||||
static bool g_has_woken_up = false;
|
||||
static bool g_connected_before_sleep = false;
|
||||
static bool g_signal_on_disconnect = false;
|
||||
|
||||
static TmaUsbConnection *g_usb_connection = nullptr;
|
||||
|
||||
static TmaTargetConfig g_target_config = {
|
||||
"Unknown",
|
||||
"SerialNumber",
|
||||
};
|
||||
|
||||
static void RefreshTargetConfig() {
|
||||
setsysInitialize();
|
||||
|
||||
/* TODO: setsysGetConfigurationId1(&g_target_config.configuration_id1); */
|
||||
|
||||
g_target_config.serial_number[0] = 0;
|
||||
setsysGetSerialNumber(g_target_config.serial_number);
|
||||
|
||||
setsysExit();
|
||||
}
|
||||
|
||||
static void InitializeServices() {
|
||||
g_service_manager->Initialize();
|
||||
}
|
||||
|
||||
static void FinalizeServices() {
|
||||
g_service_manager->Finalize();
|
||||
}
|
||||
|
||||
static void SetActiveConnection(TmaConnection *connection) {
|
||||
if (g_active_connection != connection) {
|
||||
if (g_active_connection != nullptr) {
|
||||
FinalizeServices();
|
||||
g_service_manager->SetConnection(nullptr);
|
||||
g_active_connection->Disconnect();
|
||||
g_active_connection = nullptr;
|
||||
}
|
||||
|
||||
if (connection != nullptr) {
|
||||
g_active_connection = connection;
|
||||
InitializeServices();
|
||||
g_service_manager->SetConnection(g_active_connection);
|
||||
g_active_connection->SetServiceManager(g_service_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnConnectionEvent(void *arg, ConnectionEvent evt) {
|
||||
std::scoped_lock<HosMutex> lk(g_connection_event_mutex);
|
||||
|
||||
switch (evt) {
|
||||
case ConnectionEvent::Connected:
|
||||
{
|
||||
bool has_active_connection = false;
|
||||
g_has_woken_up = false;
|
||||
|
||||
if (arg == g_usb_connection) {
|
||||
SetActiveConnection(g_usb_connection);
|
||||
has_active_connection = true;
|
||||
}
|
||||
|
||||
if (has_active_connection) {
|
||||
/* TODO: Signal connected */
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ConnectionEvent::Disconnected:
|
||||
if (g_signal_on_disconnect) {
|
||||
/* TODO: Signal disconnected */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void Wake() {
|
||||
if (g_service_manager->GetAsleep()) {
|
||||
g_has_woken_up = true;
|
||||
|
||||
/* N checks what kind of connection to use here. For now, we only use USB. */
|
||||
TmaUsbConnection::InitializeComms();
|
||||
g_usb_connection = new TmaUsbConnection();
|
||||
g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection);
|
||||
g_usb_connection->Initialize();
|
||||
g_active_connection = g_usb_connection;
|
||||
|
||||
g_service_manager->Wake(g_active_connection);
|
||||
}
|
||||
}
|
||||
|
||||
static void Sleep() {
|
||||
if (!g_service_manager->GetAsleep()) {
|
||||
if (g_active_connection->IsConnected()) {
|
||||
g_connected_before_sleep = true;
|
||||
|
||||
/* TODO: Send a packet saying we're going to sleep. */
|
||||
} else {
|
||||
g_connected_before_sleep = false;
|
||||
}
|
||||
|
||||
g_service_manager->Sleep();
|
||||
g_service_manager->SetConnection(nullptr);
|
||||
g_active_connection->Disconnect();
|
||||
g_active_connection = nullptr;
|
||||
g_service_manager->CancelTasks();
|
||||
|
||||
if (g_usb_connection != nullptr) {
|
||||
g_usb_connection->Finalize();
|
||||
delete g_usb_connection;
|
||||
g_usb_connection = nullptr;
|
||||
TmaUsbConnection::FinalizeComms();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnPowerManagementEvent(PscPmState state, u32 flags) {
|
||||
switch (state) {
|
||||
case PscPmState_Awake:
|
||||
{
|
||||
Wake();
|
||||
}
|
||||
break;
|
||||
case PscPmState_ReadyAwaken:
|
||||
{
|
||||
if (g_service_manager->GetAsleep()) {
|
||||
Wake();
|
||||
if (g_connected_before_sleep)
|
||||
{
|
||||
/* Try to restore a connection. */
|
||||
bool connected = g_service_manager->GetConnected();
|
||||
|
||||
/* N uses a seven-second timeout, here. */
|
||||
TimeoutHelper timeout_helper(7000000000ULL);
|
||||
while (!connected && !timeout_helper.TimedOut()) {
|
||||
connected = g_service_manager->GetConnected();
|
||||
if (!connected) {
|
||||
/* Sleep for 1ms. */
|
||||
svcSleepThread(1000000ULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
/* TODO: Signal disconnected */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PscPmState_ReadySleep:
|
||||
{
|
||||
Sleep();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Don't handle ReadySleepCritical/ReadyAwakenCritical/ReadyShutdown */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTarget::Initialize() {
|
||||
/* Get current thread priority. */
|
||||
u32 cur_prio;
|
||||
if (R_FAILED(svcGetThreadPriority(&cur_prio, CUR_THREAD_HANDLE))) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
g_active_connection = nullptr;
|
||||
g_service_manager = new TmaServiceManager();
|
||||
/* TODO: Make this better. */
|
||||
g_service_manager->AddService(new AtmosphereTestService(g_service_manager));
|
||||
g_service_manager->AddService(new SettingsService(g_service_manager));
|
||||
g_service_manager->AddService(new TIOService(g_service_manager));
|
||||
|
||||
RefreshTargetConfig();
|
||||
|
||||
/* N checks what kind of connection to use here. For now, we only use USB. */
|
||||
TmaUsbConnection::InitializeComms();
|
||||
g_usb_connection = new TmaUsbConnection();
|
||||
g_usb_connection->SetConnectionEventCallback(OnConnectionEvent, g_usb_connection);
|
||||
g_usb_connection->Initialize();
|
||||
SetActiveConnection(g_usb_connection);
|
||||
|
||||
/* TODO: Initialize connection events */
|
||||
|
||||
/* TODO: Initialize IPC services */
|
||||
|
||||
TmaPowerManager::Initialize(OnPowerManagementEvent);
|
||||
}
|
||||
|
||||
void TmaTarget::Finalize() {
|
||||
/* TODO: Is implementing this actually worthwhile? It will never be called in practice... */
|
||||
}
|
||||
25
stratosphere/tma/source/tma_target.hpp
Normal file
25
stratosphere/tma/source/tma_target.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
class TmaTarget {
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
};
|
||||
52
stratosphere/tma/source/tma_task.cpp
Normal file
52
stratosphere/tma/source/tma_task.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_task.hpp"
|
||||
#include "tma_service_manager.hpp"
|
||||
|
||||
void TmaTask::SetNeedsPackets(bool n) {
|
||||
this->needs_packets = n;
|
||||
this->manager->Tick();
|
||||
}
|
||||
|
||||
TmaPacket *TmaTask::AllocateSendPacket(bool continuation) {
|
||||
auto packet = this->manager->AllocateSendPacket();
|
||||
packet->SetServiceId(this->service_id);
|
||||
packet->SetTaskId(this->task_id);
|
||||
packet->SetCommand(this->command);
|
||||
packet->SetContinuation(continuation);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
|
||||
void TmaTask::FreePacket(TmaPacket *packet) {
|
||||
this->manager->FreePacket(packet);
|
||||
}
|
||||
|
||||
void TmaTask::Complete() {
|
||||
SetNeedsPackets(false);
|
||||
this->state = TmaTaskState::Complete;
|
||||
this->manager->Tick();
|
||||
}
|
||||
|
||||
void TmaTask::Cancel() {
|
||||
SetNeedsPackets(false);
|
||||
this->state = TmaTaskState::Canceled;
|
||||
this->manager->Tick();
|
||||
}
|
||||
80
stratosphere/tma/source/tma_task.hpp
Normal file
80
stratosphere/tma/source/tma_task.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
enum class TmaTaskState : u32 {
|
||||
InProgress,
|
||||
Complete,
|
||||
Canceled,
|
||||
};
|
||||
|
||||
class TmaServiceManager;
|
||||
|
||||
class TmaTask {
|
||||
public:
|
||||
static constexpr u32 MaxPriority = 15;
|
||||
static constexpr u32 NumPriorities = MaxPriority + 1;
|
||||
protected:
|
||||
TmaServiceManager *manager;
|
||||
u32 priority = 0;
|
||||
TmaServiceId service_id = TmaServiceId::Invalid;
|
||||
u32 task_id = 0;
|
||||
u32 command = 0;
|
||||
TmaTaskState state = TmaTaskState::InProgress;
|
||||
HosSignal signal;
|
||||
bool owned_by_task_list = true;
|
||||
bool sleep_allowed = true;
|
||||
bool needs_packets = false;
|
||||
public:
|
||||
TmaTask(TmaServiceManager *m) : manager(m) { }
|
||||
virtual ~TmaTask() { }
|
||||
|
||||
u32 GetPriority() const { return this->priority; }
|
||||
TmaServiceId GetServiceId() const { return this->service_id; }
|
||||
u32 GetTaskId() const { return this->task_id; }
|
||||
u32 GetCommand() const { return this->command; }
|
||||
TmaTaskState GetState() const { return this->state; }
|
||||
bool GetOwnedByTaskList() const { return this->owned_by_task_list; }
|
||||
bool GetSleepAllowed() const { return this->sleep_allowed; }
|
||||
bool GetNeedsPackets() const { return this->needs_packets; }
|
||||
|
||||
void SetPriority(u32 p) { this->priority = p; }
|
||||
void SetServiceId(TmaServiceId s) { this->service_id = s; }
|
||||
void SetTaskId(u32 i) { this->task_id = i; }
|
||||
void SetCommand(u32 c) { this->command = c; }
|
||||
void SetOwnedByTaskList(bool o) { this->owned_by_task_list = o; }
|
||||
void SetSleepAllowed(bool a) { this->sleep_allowed = a; }
|
||||
void SetNeedsPackets(bool n);
|
||||
|
||||
void Signal() { this->signal.Signal(); }
|
||||
void ResetSignal() { this->signal.Reset(); }
|
||||
|
||||
TmaPacket *AllocateSendPacket(bool continuation = true);
|
||||
void FreePacket(TmaPacket *packet);
|
||||
|
||||
void Complete();
|
||||
void Cancel();
|
||||
|
||||
virtual void OnStart(TmaPacket *packet) = 0;
|
||||
virtual void OnReceivePacket(TmaPacket *packet) = 0;
|
||||
virtual void OnSendPacket(TmaPacket *packet) = 0;
|
||||
};
|
||||
218
stratosphere/tma/source/tma_task_list.cpp
Normal file
218
stratosphere/tma/source/tma_task_list.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <algorithm>
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "tma_task_list.hpp"
|
||||
|
||||
TmaTask *TmaTaskList::GetById(u32 task_id) const {
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
for (auto task : this->tasks[i]) {
|
||||
if (task->GetTaskId() == task_id) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 TmaTaskList::GetNumTasks() const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
u32 count = 0;
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
count += this->tasks[i].size();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 TmaTaskList::GetNumSleepingTasks() const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
u32 count = 0;
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
count += this->sleeping_tasks[i].size();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool TmaTaskList::IsIdFree(u32 task_id) const {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
return GetById(task_id) == nullptr;
|
||||
}
|
||||
|
||||
bool TmaTaskList::SendPacket(bool connected, TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
TmaTask *target_task = nullptr;
|
||||
|
||||
/* This loop both finds a target task, and cleans up finished tasks. */
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
switch (task->GetState()) {
|
||||
case TmaTaskState::InProgress:
|
||||
it++;
|
||||
if (target_task == nullptr && task->GetNeedsPackets()) {
|
||||
if (connected || IsMetaService(task->GetServiceId())) {
|
||||
target_task = task;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TmaTaskState::Complete:
|
||||
case TmaTaskState::Canceled:
|
||||
it = this->tasks[i].erase(it);
|
||||
if (task->GetOwnedByTaskList()) {
|
||||
delete task;
|
||||
} else {
|
||||
task->Signal();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target_task) {
|
||||
/* Setup packet. */
|
||||
packet->SetContinuation(true);
|
||||
packet->SetServiceId(target_task->GetServiceId());
|
||||
packet->SetTaskId(target_task->GetTaskId());
|
||||
packet->SetCommand(target_task->GetCommand());
|
||||
packet->ClearOffset();
|
||||
|
||||
/* Actually handle packet send. */
|
||||
target_task->OnSendPacket(packet);
|
||||
}
|
||||
|
||||
return target_task != nullptr;
|
||||
}
|
||||
|
||||
bool TmaTaskList::ReceivePacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
auto task = this->GetById(packet->GetTaskId());
|
||||
if (task != nullptr) {
|
||||
task->OnReceivePacket(packet);
|
||||
}
|
||||
return task != nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TmaTaskList::CleanupDoneTasks() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
/* Clean up all tasks in Complete/Canceled state. */
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
switch (task->GetState()) {
|
||||
case TmaTaskState::InProgress:
|
||||
it++;
|
||||
break;
|
||||
case TmaTaskState::Complete:
|
||||
case TmaTaskState::Canceled:
|
||||
it = this->tasks[i].erase(it);
|
||||
if (task->GetOwnedByTaskList()) {
|
||||
delete task;
|
||||
} else {
|
||||
task->Signal();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Add(TmaTask *task) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
this->tasks[task->GetPriority()].push_back(task);
|
||||
}
|
||||
|
||||
void TmaTaskList::Remove(TmaTask *task) {
|
||||
const auto priority = task->GetPriority();
|
||||
|
||||
/* Nintendo iterates over all lists instead of just the correct one. */
|
||||
/* TODO: Is there actually any reason to do that? */
|
||||
auto ind = std::find(this->tasks[priority].begin(), this->tasks[priority].end(), task);
|
||||
if (ind != this->tasks[priority].end()) {
|
||||
this->tasks[priority].erase(ind);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Panic to fatal? */
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void TmaTaskList::Cancel(u32 task_id) {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
auto task = this->GetById(task_id);
|
||||
if (task != nullptr) {
|
||||
task->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::CancelAll() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0 ; i < TmaTask::NumPriorities; i++) {
|
||||
for (auto task : this->tasks[i]) {
|
||||
task->Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Sleep() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->tasks[i].begin();
|
||||
while (it != this->tasks[i].end()) {
|
||||
auto task = *it;
|
||||
if (task->GetSleepAllowed()) {
|
||||
it = this->tasks[i].erase(it);
|
||||
this->sleeping_tasks[i].push_back(task);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TmaTaskList::Wake() {
|
||||
std::scoped_lock<HosMutex> lk(this->lock);
|
||||
|
||||
for (u32 i = 0; i < TmaTask::NumPriorities; i++) {
|
||||
auto it = this->sleeping_tasks[i].begin();
|
||||
while (it != this->sleeping_tasks[i].end()) {
|
||||
auto task = *it;
|
||||
it = this->sleeping_tasks[i].erase(it);
|
||||
this->tasks[i].push_back(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
stratosphere/tma/source/tma_task_list.hpp
Normal file
50
stratosphere/tma/source/tma_task_list.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "tma_conn_service_ids.hpp"
|
||||
#include "tma_task.hpp"
|
||||
|
||||
class TmaTaskList {
|
||||
private:
|
||||
mutable HosMutex lock;
|
||||
std::vector<TmaTask *> tasks[TmaTask::NumPriorities];
|
||||
std::vector<TmaTask *> sleeping_tasks[TmaTask::NumPriorities];
|
||||
private:
|
||||
void Remove(TmaTask *task);
|
||||
TmaTask *GetById(u32 task_id) const;
|
||||
public:
|
||||
TmaTaskList() { }
|
||||
virtual ~TmaTaskList() { }
|
||||
|
||||
u32 GetNumTasks() const;
|
||||
u32 GetNumSleepingTasks() const;
|
||||
bool IsIdFree(u32 task_id) const;
|
||||
|
||||
bool SendPacket(bool connected, TmaPacket *packet);
|
||||
bool ReceivePacket(TmaPacket *packet);
|
||||
void CleanupDoneTasks();
|
||||
void Add(TmaTask *task);
|
||||
void Cancel(u32 task_id);
|
||||
void CancelAll();
|
||||
|
||||
void Sleep();
|
||||
void Wake();
|
||||
};
|
||||
484
stratosphere/tma/source/tma_usb_comms.cpp
Normal file
484
stratosphere/tma/source/tma_usb_comms.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "tma_usb_comms.hpp"
|
||||
|
||||
/* TODO: Is this actually allowed? */
|
||||
#define ATMOSPHERE_INTERFACE_PROTOCOL 0xFC
|
||||
|
||||
static std::atomic<bool> g_initialized = false;
|
||||
static UsbDsInterface *g_interface;
|
||||
static UsbDsEndpoint *g_endpoint_in, *g_endpoint_out;
|
||||
|
||||
/* USB State Change Tracking. */
|
||||
static HosThread g_state_change_thread;
|
||||
static WaitableManagerBase *g_state_change_manager = nullptr;
|
||||
static void (*g_state_change_callback)(void *arg, u32 state);
|
||||
static void *g_state_change_arg;
|
||||
|
||||
/* USB Send/Receive mutexes. */
|
||||
static HosMutex g_send_mutex;
|
||||
static HosMutex g_recv_mutex;
|
||||
|
||||
/* Static arrays to do USB DMA into. */
|
||||
static constexpr size_t DmaBufferAlign = 0x1000;
|
||||
static constexpr size_t HeaderBufferSize = DmaBufferAlign;
|
||||
static constexpr size_t DataBufferSize = 0x18000;
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_header_buffer[HeaderBufferSize];
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_recv_data_buf[DataBufferSize];
|
||||
static __attribute__((aligned(DmaBufferAlign))) u8 g_send_data_buf[DataBufferSize];
|
||||
|
||||
/* Taken from libnx usb comms. */
|
||||
static Result _usbCommsInterfaceInit1x()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
struct usb_interface_descriptor interface_descriptor = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 4,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceProtocol = ATMOSPHERE_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x200,
|
||||
};
|
||||
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup interface.
|
||||
rc = usbDsGetDsInterface(&g_interface, &interface_descriptor, "usb");
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup endpoints.
|
||||
rc = usbDsInterface_GetDsEndpoint(g_interface, &g_endpoint_in, &endpoint_descriptor_in);//device->host
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsInterface_GetDsEndpoint(g_interface, &g_endpoint_out, &endpoint_descriptor_out);//host->device
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _usbCommsInterfaceInit5x() {
|
||||
Result rc = 0;
|
||||
|
||||
u8 iManufacturer, iProduct, iSerialNumber;
|
||||
static const u16 supported_langs[1] = {0x0409};
|
||||
// Send language descriptor
|
||||
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs)/sizeof(u16));
|
||||
// Send manufacturer
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iManufacturer, "Nintendo");
|
||||
// Send product
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iProduct, "Nintendo Switch");
|
||||
// Send serial number
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iSerialNumber, "SerialNumber");
|
||||
|
||||
// Send device descriptors
|
||||
struct usb_device_descriptor device_descriptor = {
|
||||
.bLength = USB_DT_DEVICE_SIZE,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.idVendor = 0x057e,
|
||||
.idProduct = 0x3000,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = iManufacturer,
|
||||
.iProduct = iProduct,
|
||||
.iSerialNumber = iSerialNumber,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
// Full Speed is USB 1.1
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor);
|
||||
|
||||
// High Speed is USB 2.0
|
||||
device_descriptor.bcdUSB = 0x0200;
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
|
||||
|
||||
// Super Speed is USB 3.0
|
||||
device_descriptor.bcdUSB = 0x0300;
|
||||
// Upgrade packet size to 512
|
||||
device_descriptor.bMaxPacketSize0 = 0x09;
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor);
|
||||
|
||||
// Define Binary Object Store
|
||||
u8 bos[0x16] = {
|
||||
0x05, // .bLength
|
||||
USB_DT_BOS, // .bDescriptorType
|
||||
0x16, 0x00, // .wTotalLength
|
||||
0x02, // .bNumDeviceCaps
|
||||
|
||||
// USB 2.0
|
||||
0x07, // .bLength
|
||||
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
|
||||
0x02, // .bDevCapabilityType
|
||||
0x02, 0x00, 0x00, 0x00, // dev_capability_data
|
||||
|
||||
// USB 3.0
|
||||
0x0A, // .bLength
|
||||
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
|
||||
0x03, // .bDevCapabilityType
|
||||
0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00
|
||||
};
|
||||
if (R_SUCCEEDED(rc)) rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
|
||||
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
struct usb_interface_descriptor interface_descriptor = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bInterfaceNumber = 4,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceProtocol = ATMOSPHERE_INTERFACE_PROTOCOL,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_IN,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
};
|
||||
|
||||
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_ENDPOINT_OUT,
|
||||
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
};
|
||||
|
||||
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
|
||||
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
|
||||
.bMaxBurst = 0x0F,
|
||||
.bmAttributes = 0x00,
|
||||
.wBytesPerInterval = 0x00,
|
||||
};
|
||||
|
||||
rc = usbDsRegisterInterface(&g_interface);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
interface_descriptor.bInterfaceNumber = g_interface->interface_index;
|
||||
endpoint_descriptor_in.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||
|
||||
// Full Speed Config
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
// High Speed Config
|
||||
endpoint_descriptor_in.wMaxPacketSize = 0x200;
|
||||
endpoint_descriptor_out.wMaxPacketSize = 0x200;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
// Super Speed Config
|
||||
endpoint_descriptor_in.wMaxPacketSize = 0x400;
|
||||
endpoint_descriptor_out.wMaxPacketSize = 0x400;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
rc = usbDsInterface_AppendConfigurationData(g_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
//Setup endpoints.
|
||||
rc = usbDsInterface_RegisterEndpoint(g_interface, &g_endpoint_in, endpoint_descriptor_in.bEndpointAddress);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsInterface_RegisterEndpoint(g_interface, &g_endpoint_out, endpoint_descriptor_out.bEndpointAddress);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Actual function implementations. */
|
||||
TmaConnResult TmaUsbComms::Initialize() {
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (g_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Result rc = usbDsInitialize();
|
||||
|
||||
/* Perform interface setup. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
|
||||
rc = _usbCommsInterfaceInit5x();
|
||||
} else {
|
||||
rc = _usbCommsInterfaceInit1x();
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the state change thread. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = g_state_change_thread.Initialize(&TmaUsbComms::UsbStateChangeThreadFunc, nullptr, 0x4000, 38);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = g_state_change_thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable USB communication. */
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = usbDsInterface_EnableInterface(g_interface);
|
||||
}
|
||||
if (R_SUCCEEDED(rc) && GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
|
||||
rc = usbDsEnable();
|
||||
}
|
||||
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
/* TODO: Should I not abort here? */
|
||||
std::abort();
|
||||
}
|
||||
|
||||
g_initialized = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::Finalize() {
|
||||
Result rc = 0;
|
||||
/* We must have initialized before calling finalize. */
|
||||
if (!g_initialized) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* Kill the state change thread. */
|
||||
g_state_change_manager->RequestStop();
|
||||
if (R_FAILED(g_state_change_thread.Join())) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
CancelComms();
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
usbDsExit();
|
||||
}
|
||||
|
||||
g_state_change_callback = nullptr;
|
||||
g_interface = nullptr;
|
||||
g_endpoint_in = nullptr;
|
||||
g_endpoint_out = nullptr;
|
||||
g_initialized = false;
|
||||
|
||||
return R_SUCCEEDED(rc) ? TmaConnResult::Success : TmaConnResult::ConnectionFailure;
|
||||
}
|
||||
|
||||
void TmaUsbComms::CancelComms() {
|
||||
if (!g_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
usbDsEndpoint_Cancel(g_endpoint_in);
|
||||
usbDsEndpoint_Cancel(g_endpoint_out);
|
||||
}
|
||||
|
||||
void TmaUsbComms::SetStateChangeCallback(void (*callback)(void *, u32), void *arg) {
|
||||
g_state_change_callback = callback;
|
||||
g_state_change_arg = arg;
|
||||
}
|
||||
|
||||
Result TmaUsbComms::UsbXfer(UsbDsEndpoint *ep, size_t *out_xferd, void *buf, size_t size) {
|
||||
Result rc = 0;
|
||||
u32 urbId = 0;
|
||||
u32 total_xferd = 0;
|
||||
UsbDsReportData reportdata;
|
||||
|
||||
if (size) {
|
||||
/* Start transfer. */
|
||||
rc = usbDsEndpoint_PostBufferAsync(ep, buf, size, &urbId);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
/* Wait for transfer to complete. */
|
||||
eventWait(&ep->CompletionEvent, U64_MAX);
|
||||
eventClear(&ep->CompletionEvent);
|
||||
|
||||
rc = usbDsEndpoint_GetReportData(ep, &reportdata);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
|
||||
rc = usbDsParseReportData(&reportdata, urbId, NULL, &total_xferd);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
}
|
||||
|
||||
if (out_xferd) *out_xferd = total_xferd;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::ReceivePacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk{g_recv_mutex};
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (!g_initialized || packet == nullptr) {
|
||||
return TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Read the header. */
|
||||
size_t read = 0;
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_out, &read, g_header_buffer, sizeof(TmaPacket::Header)))) {
|
||||
packet->CopyHeaderFrom(reinterpret_cast<TmaPacket::Header *>(g_header_buffer));
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Validate the read header data. */
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (read != sizeof(TmaPacket::Header) || !packet->IsHeaderValid()) {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the body! */
|
||||
if (res == TmaConnResult::Success) {
|
||||
const u32 body_len = packet->GetBodyLength();
|
||||
if (0 < body_len) {
|
||||
if (body_len <= sizeof(g_recv_data_buf)) {
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_out, &read, g_recv_data_buf, body_len))) {
|
||||
if (read == body_len) {
|
||||
res = packet->CopyBodyFrom(g_recv_data_buf, body_len);
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate the body. */
|
||||
if (res == TmaConnResult::Success) {
|
||||
if (!packet->IsBodyValid()) {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == TmaConnResult::Success) {
|
||||
packet->ClearOffset();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
TmaConnResult TmaUsbComms::SendPacket(TmaPacket *packet) {
|
||||
std::scoped_lock<HosMutex> lk{g_send_mutex};
|
||||
TmaConnResult res = TmaConnResult::Success;
|
||||
|
||||
if (!g_initialized || packet == nullptr) {
|
||||
return TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
/* Ensure our packets have the correct checksums. */
|
||||
packet->SetChecksums();
|
||||
|
||||
/* Send the packet. */
|
||||
size_t written = 0;
|
||||
const u32 body_len = packet->GetBodyLength();
|
||||
if (body_len <= sizeof(g_send_data_buf)) {
|
||||
/* Copy header to send buffer. */
|
||||
packet->CopyHeaderTo(g_send_data_buf);
|
||||
|
||||
/* Send the packet header. */
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_in, &written, g_send_data_buf, sizeof(TmaPacket::Header)))) {
|
||||
if (written == sizeof(TmaPacket::Header)) {
|
||||
res = TmaConnResult::Success;
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
if (res == TmaConnResult::Success && 0 < body_len) {
|
||||
/* Copy body to send buffer. */
|
||||
packet->CopyBodyTo(g_send_data_buf);
|
||||
|
||||
|
||||
/* Send the packet body. */
|
||||
if (R_SUCCEEDED(UsbXfer(g_endpoint_in, &written, g_send_data_buf, body_len))) {
|
||||
if (written == body_len) {
|
||||
res = TmaConnResult::Success;
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = TmaConnResult::GeneralFailure;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void TmaUsbComms::UsbStateChangeThreadFunc(void *arg) {
|
||||
u32 state;
|
||||
g_state_change_manager = new WaitableManager(1);
|
||||
|
||||
auto state_change_event = LoadReadOnlySystemEvent(usbDsGetStateChangeEvent()->revent, [&](u64 timeout) {
|
||||
if (R_SUCCEEDED(usbDsGetState(&state))) {
|
||||
if (g_state_change_callback != nullptr) {
|
||||
g_state_change_callback(g_state_change_arg, state);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}, true);
|
||||
|
||||
g_state_change_manager->AddWaitable(state_change_event);
|
||||
g_state_change_manager->Process();
|
||||
|
||||
/* If we get here, we're exiting. */
|
||||
state_change_event->r_h = 0;
|
||||
delete g_state_change_manager;
|
||||
g_state_change_manager = nullptr;
|
||||
|
||||
svcExitThread();
|
||||
}
|
||||
36
stratosphere/tma/source/tma_usb_comms.hpp
Normal file
36
stratosphere/tma/source/tma_usb_comms.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "tma_conn_result.hpp"
|
||||
#include "tma_conn_packet.hpp"
|
||||
|
||||
class TmaUsbComms {
|
||||
private:
|
||||
static void UsbStateChangeThreadFunc(void *arg);
|
||||
static Result UsbXfer(UsbDsEndpoint *ep, size_t *out_xferd, void *buf, size_t size);
|
||||
public:
|
||||
static TmaConnResult Initialize();
|
||||
static TmaConnResult Finalize();
|
||||
static void CancelComms();
|
||||
static TmaConnResult ReceivePacket(TmaPacket *packet);
|
||||
static TmaConnResult SendPacket(TmaPacket *packet);
|
||||
|
||||
static void SetStateChangeCallback(void (*callback)(void *, u32), void *arg);
|
||||
};
|
||||
Reference in New Issue
Block a user