mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
htc: implement socket driver (socket api not really impl'd yet)
This commit is contained in:
parent
b5ab491603
commit
1c974a387c
31 changed files with 1389 additions and 35 deletions
|
@ -18,5 +18,9 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stratosphere/socket/socket_types.hpp>
|
#include <stratosphere/socket/socket_types.hpp>
|
||||||
|
#include <stratosphere/socket/socket_options.hpp>
|
||||||
#include <stratosphere/socket/socket_errno.hpp>
|
#include <stratosphere/socket/socket_errno.hpp>
|
||||||
|
#include <stratosphere/socket/socket_constants.hpp>
|
||||||
|
#include <stratosphere/socket/socket_config.hpp>
|
||||||
|
#include <stratosphere/socket/socket_system_config.hpp>
|
||||||
#include <stratosphere/socket/socket_api.hpp>
|
#include <stratosphere/socket/socket_api.hpp>
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/socket/socket_types.hpp>
|
#include <stratosphere/socket/socket_types.hpp>
|
||||||
|
#include <stratosphere/socket/socket_options.hpp>
|
||||||
#include <stratosphere/socket/socket_errno.hpp>
|
#include <stratosphere/socket/socket_errno.hpp>
|
||||||
|
#include <stratosphere/socket/socket_config.hpp>
|
||||||
|
|
||||||
namespace ams::socket {
|
namespace ams::socket {
|
||||||
|
|
||||||
|
@ -28,4 +30,30 @@ namespace ams::socket {
|
||||||
u32 InetNtohl(u32 net);
|
u32 InetNtohl(u32 net);
|
||||||
u16 InetNtohs(u16 net);
|
u16 InetNtohs(u16 net);
|
||||||
|
|
||||||
|
Result Initialize(const Config &config);
|
||||||
|
Result Finalize();
|
||||||
|
|
||||||
|
Result InitializeAllocatorForInternal(void *buffer, size_t size);
|
||||||
|
|
||||||
|
ssize_t RecvFrom(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags, SockAddr *out_address, SockLenT *out_addr_len);
|
||||||
|
ssize_t Recv(s32 desc, void *buffer, size_t buffer_size, MsgFlag flags);
|
||||||
|
|
||||||
|
ssize_t SendTo(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags, const SockAddr *address, SockLenT len);
|
||||||
|
ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, MsgFlag flags);
|
||||||
|
|
||||||
|
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||||
|
|
||||||
|
s32 SocketExempt(Family domain, Type type, Protocol protocol);
|
||||||
|
|
||||||
|
s32 Accept(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||||
|
s32 Bind(s32 desc, const SockAddr *address, SockLenT len);
|
||||||
|
|
||||||
|
s32 GetSockName(s32 desc, SockAddr *out_address, SockLenT *out_addr_len);
|
||||||
|
s32 SetSockOpt(s32 desc, Level level, Option option_name, const void *option_value, SockLenT option_size);
|
||||||
|
|
||||||
|
s32 Listen(s32 desc, s32 backlog);
|
||||||
|
|
||||||
|
s32 Close(s32 desc);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os.hpp>
|
||||||
|
#include <stratosphere/socket/socket_constants.hpp>
|
||||||
|
|
||||||
|
namespace ams::socket {
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE size_t AlignMss(size_t size) {
|
||||||
|
return util::DivideUp(size, static_cast<size_t>(1500));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
private:
|
||||||
|
u32 m_version;
|
||||||
|
protected:
|
||||||
|
bool m_system;
|
||||||
|
bool m_smbp;
|
||||||
|
void *m_memory_pool;
|
||||||
|
size_t m_memory_pool_size;
|
||||||
|
size_t m_allocator_pool_size;
|
||||||
|
size_t m_tcp_initial_send_buffer_size;
|
||||||
|
size_t m_tcp_initial_receive_buffer_size;
|
||||||
|
size_t m_tcp_auto_send_buffer_size_max;
|
||||||
|
size_t m_tcp_auto_receive_buffer_size_max;
|
||||||
|
size_t m_udp_send_buffer_size;
|
||||||
|
size_t m_udp_receive_buffer_size;
|
||||||
|
int m_sb_efficiency;
|
||||||
|
int m_concurrency_count_max;
|
||||||
|
public:
|
||||||
|
constexpr Config(void *mp, size_t mp_sz, size_t ap, size_t is, size_t ir, size_t as, size_t ar, size_t us, size_t ur, int sbe, int c)
|
||||||
|
: m_version(LibraryVersion),
|
||||||
|
m_system(false),
|
||||||
|
m_smbp(false),
|
||||||
|
m_memory_pool(mp),
|
||||||
|
m_memory_pool_size(mp_sz),
|
||||||
|
m_allocator_pool_size(ap),
|
||||||
|
m_tcp_initial_send_buffer_size(is),
|
||||||
|
m_tcp_initial_receive_buffer_size(ir),
|
||||||
|
m_tcp_auto_send_buffer_size_max(as),
|
||||||
|
m_tcp_auto_receive_buffer_size_max(ar),
|
||||||
|
m_udp_send_buffer_size(us),
|
||||||
|
m_udp_receive_buffer_size(ur),
|
||||||
|
m_sb_efficiency(sbe),
|
||||||
|
m_concurrency_count_max(c)
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 GetVersion() const { return m_version; }
|
||||||
|
constexpr bool IsSystemClient() const { return m_system; }
|
||||||
|
constexpr bool IsSmbpClient() const { return m_smbp; }
|
||||||
|
constexpr void *GetMemoryPool() const { return m_memory_pool; }
|
||||||
|
constexpr size_t GetMemoryPoolSize() const { return m_memory_pool_size; }
|
||||||
|
constexpr size_t GetAllocatorPoolSize() const { return m_allocator_pool_size; }
|
||||||
|
constexpr size_t GetTcpInitialSendBufferSize() const { return m_tcp_initial_send_buffer_size; }
|
||||||
|
constexpr size_t GetTcpInitialReceiveBufferSize() const { return m_tcp_initial_receive_buffer_size; }
|
||||||
|
constexpr size_t GetTcpAutoSendBufferSizeMax() const { return m_tcp_auto_send_buffer_size_max; }
|
||||||
|
constexpr size_t GetTcpAutoReceiveBufferSizeMax() const { return m_tcp_auto_receive_buffer_size_max; }
|
||||||
|
constexpr size_t GetUdpSendBufferSize() const { return m_udp_send_buffer_size; }
|
||||||
|
constexpr size_t GetUdpReceiveBufferSize() const { return m_udp_receive_buffer_size; }
|
||||||
|
constexpr int GetSocketBufferEfficiency() const { return m_sb_efficiency; }
|
||||||
|
constexpr int GetConcurrencyCountMax() const { return m_concurrency_count_max; }
|
||||||
|
|
||||||
|
constexpr void SetTcpInitialSendBufferSize(size_t size) { m_tcp_initial_send_buffer_size = size; }
|
||||||
|
constexpr void SetTcpInitialReceiveBufferSize(size_t size) { m_tcp_initial_receive_buffer_size = size; }
|
||||||
|
constexpr void SetTcpAutoSendBufferSizeMax(size_t size) { m_tcp_auto_send_buffer_size_max = size; }
|
||||||
|
constexpr void SetTcpAutoReceiveBufferSizeMax(size_t size) { m_tcp_auto_receive_buffer_size_max = size; }
|
||||||
|
constexpr void SetUdpSendBufferSize(size_t size) { m_udp_send_buffer_size = size; }
|
||||||
|
constexpr void SetUdpReceiveBufferSize(size_t size) { m_udp_receive_buffer_size = size; }
|
||||||
|
constexpr void SetSocketBufferEfficiency(int sb) { AMS_ABORT_UNLESS(1 <= sb && sb <= 8); m_sb_efficiency = sb; }
|
||||||
|
constexpr void SetConcurrencyCountMax(int c) { m_concurrency_count_max = c; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::socket {
|
||||||
|
|
||||||
|
constexpr inline s32 InvalidSocket = -1;
|
||||||
|
constexpr inline s32 SocketError = -1;
|
||||||
|
|
||||||
|
constexpr inline auto DefaultTcpAutoBufferSizeMax = 192_KB;
|
||||||
|
constexpr inline auto MinTransferMemorySize = (2 * DefaultTcpAutoBufferSizeMax + 128_KB);
|
||||||
|
constexpr inline auto MinSocketAllocatorSize = 128_KB;
|
||||||
|
constexpr inline auto MinSocketMemoryPoolSize = MinSocketAllocatorSize + MinTransferMemorySize;
|
||||||
|
constexpr inline auto MinMemHeapAllocatorSize = 16_KB;
|
||||||
|
constexpr inline auto MinimumSharedMbufPoolReservation = 4_KB;
|
||||||
|
|
||||||
|
constexpr inline size_t MemoryPoolAlignment = 4_KB;
|
||||||
|
|
||||||
|
constexpr inline auto ConcurrencyLimitMax = 14;
|
||||||
|
|
||||||
|
/* TODO: Does this need to be 1 for sockets to work on lower firmware versions? */
|
||||||
|
/* Is this value actually used/checked by bsdsockets sysmodule? */
|
||||||
|
constexpr inline auto LibraryVersion = 7;
|
||||||
|
|
||||||
|
}
|
|
@ -19,10 +19,21 @@
|
||||||
namespace ams::socket {
|
namespace ams::socket {
|
||||||
|
|
||||||
enum class Errno : u32 {
|
enum class Errno : u32 {
|
||||||
ESuccess = 0,
|
ESuccess = 0,
|
||||||
/* ... */
|
/* ... */
|
||||||
ENoSpc = 28,
|
EAgain = 11,
|
||||||
|
ENoMem = 12,
|
||||||
/* ... */
|
/* ... */
|
||||||
|
EFault = 14,
|
||||||
|
/* ... */
|
||||||
|
EInval = 22,
|
||||||
|
/* ... */
|
||||||
|
ENoSpc = 28,
|
||||||
|
/* ... */
|
||||||
|
EL3Hlt = 46,
|
||||||
|
/* ... */
|
||||||
|
EOpNotSupp = 95,
|
||||||
|
ENotSup = EOpNotSupp,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class HErrno : s32 {
|
enum class HErrno : s32 {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
namespace ams::socket {
|
||||||
|
|
||||||
|
enum class Level : s32 {
|
||||||
|
Sol_Ip = 0,
|
||||||
|
Sol_Icmp = 1,
|
||||||
|
Sol_Tcp = 6,
|
||||||
|
Sol_Udp = 17,
|
||||||
|
Sol_UdpLite = 136,
|
||||||
|
|
||||||
|
Sol_Socket = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Option : u32 {
|
||||||
|
So_Debug = (1 << 0),
|
||||||
|
/* ... */
|
||||||
|
So_ReuseAddr = (1 << 2),
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/socket/socket_config.hpp>
|
||||||
|
|
||||||
|
namespace ams::socket {
|
||||||
|
|
||||||
|
class SystemConfigDefault : public Config {
|
||||||
|
public:
|
||||||
|
static constexpr size_t DefaultTcpInitialSendBufferSize = 32_KB;
|
||||||
|
static constexpr size_t DefaultTcpInitialReceiveBufferSize = 64_KB;
|
||||||
|
static constexpr size_t DefaultTcpAutoSendBufferSizeMax = 256_KB;
|
||||||
|
static constexpr size_t DefaultTcpAutoReceiveBufferSizeMax = 256_KB;
|
||||||
|
static constexpr size_t DefaultUdpSendBufferSize = 9_KB;
|
||||||
|
static constexpr size_t DefaultUdpReceiveBufferSize = 42240;
|
||||||
|
static constexpr auto DefaultSocketBufferEfficiency = 2;
|
||||||
|
static constexpr auto DefaultConcurrency = 8;
|
||||||
|
static constexpr size_t DefaultAllocatorPoolSize = 128_KB;
|
||||||
|
|
||||||
|
static constexpr size_t PerTcpSocketWorstCaseMemoryPoolSize = [] {
|
||||||
|
constexpr size_t WorstCaseTcpSendBufferSize = AlignMss(std::max(DefaultTcpInitialSendBufferSize, DefaultTcpAutoSendBufferSizeMax));
|
||||||
|
constexpr size_t WorstCaseTcpReceiveBufferSize = AlignMss(std::max(DefaultTcpInitialReceiveBufferSize, DefaultTcpAutoReceiveBufferSizeMax));
|
||||||
|
|
||||||
|
return util::AlignUp(WorstCaseTcpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseTcpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||||
|
}();
|
||||||
|
|
||||||
|
static constexpr size_t PerUdpSocketWorstCaseMemoryPoolSize = [] {
|
||||||
|
constexpr size_t WorstCaseUdpSendBufferSize = AlignMss(DefaultUdpSendBufferSize);
|
||||||
|
constexpr size_t WorstCaseUdpReceiveBufferSize = AlignMss(DefaultUdpReceiveBufferSize);
|
||||||
|
|
||||||
|
return util::AlignUp(WorstCaseUdpSendBufferSize * DefaultSocketBufferEfficiency + WorstCaseUdpReceiveBufferSize * DefaultSocketBufferEfficiency, os::MemoryPageSize);
|
||||||
|
}();
|
||||||
|
public:
|
||||||
|
constexpr SystemConfigDefault(void *mp, size_t mp_sz, size_t ap, int c=DefaultConcurrency)
|
||||||
|
: Config(mp, mp_sz, ap,
|
||||||
|
DefaultTcpInitialSendBufferSize, DefaultTcpInitialReceiveBufferSize,
|
||||||
|
DefaultTcpAutoSendBufferSizeMax, DefaultTcpAutoReceiveBufferSizeMax,
|
||||||
|
DefaultUdpSendBufferSize, DefaultUdpReceiveBufferSize,
|
||||||
|
DefaultSocketBufferEfficiency, c)
|
||||||
|
{
|
||||||
|
/* Mark as system. */
|
||||||
|
m_system = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -79,6 +79,19 @@ namespace ams::socket {
|
||||||
Pf_Max = Af_Max
|
Pf_Max = Af_Max
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MsgFlag : s32 {
|
||||||
|
MsgFlag_None = (0 << 0),
|
||||||
|
/* ... */
|
||||||
|
MsgFlag_WaitAll = (1 << 6),
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShutdownMethod : u32 {
|
||||||
|
Shut_Rd = 0,
|
||||||
|
Shut_Wr = 1,
|
||||||
|
Shut_RdWr = 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct HostEnt {
|
struct HostEnt {
|
||||||
char *h_name;
|
char *h_name;
|
||||||
char **h_aliases;
|
char **h_aliases;
|
||||||
|
@ -98,7 +111,7 @@ namespace ams::socket {
|
||||||
Ai_NumericHost = (1 << 2),
|
Ai_NumericHost = (1 << 2),
|
||||||
Ai_NumericServ = (1 << 3),
|
Ai_NumericServ = (1 << 3),
|
||||||
|
|
||||||
Ai_AddrConfig = (1 << 10),
|
Ai_AddrConfig = (1 << 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SockAddr {
|
struct SockAddr {
|
||||||
|
|
|
@ -20,12 +20,12 @@ namespace ams::htclow::ctrl {
|
||||||
|
|
||||||
class SettingsHolder {
|
class SettingsHolder {
|
||||||
private:
|
private:
|
||||||
char m_hardware_type[0x40];
|
char m_hardware_type[0x40]{};
|
||||||
char m_target_name[0x40];
|
char m_target_name[0x40]{};
|
||||||
char m_serial_number[0x40];
|
char m_serial_number[0x40]{};
|
||||||
char m_firmware_version[0x40];
|
char m_firmware_version[0x40]{};
|
||||||
public:
|
public:
|
||||||
SettingsHolder() { /* ... */ }
|
constexpr SettingsHolder() = default;
|
||||||
|
|
||||||
void LoadSettings();
|
void LoadSettings();
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,9 @@ namespace ams::htclow::driver {
|
||||||
m_open_driver = m_debug_driver;
|
m_open_driver = m_debug_driver;
|
||||||
break;
|
break;
|
||||||
case impl::DriverType::Socket:
|
case impl::DriverType::Socket:
|
||||||
//m_socket_driver.Open();
|
m_socket_driver.Open();
|
||||||
//m_open_driver = std::addressof(m_socket_driver);
|
m_open_driver = std::addressof(m_socket_driver);
|
||||||
//break;
|
break;
|
||||||
return htclow::ResultUnknownDriverType();
|
|
||||||
case impl::DriverType::Usb:
|
case impl::DriverType::Usb:
|
||||||
m_usb_driver.Open();
|
m_usb_driver.Open();
|
||||||
m_open_driver = std::addressof(m_usb_driver);
|
m_open_driver = std::addressof(m_usb_driver);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_i_driver.hpp"
|
#include "htclow_i_driver.hpp"
|
||||||
|
#include "htclow_socket_driver.hpp"
|
||||||
#include "htclow_usb_driver.hpp"
|
#include "htclow_usb_driver.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::driver {
|
namespace ams::htclow::driver {
|
||||||
|
@ -23,14 +24,14 @@ namespace ams::htclow::driver {
|
||||||
class DriverManager {
|
class DriverManager {
|
||||||
private:
|
private:
|
||||||
std::optional<htclow::impl::DriverType> m_driver_type{};
|
std::optional<htclow::impl::DriverType> m_driver_type{};
|
||||||
IDriver *m_debug_driver;
|
IDriver *m_debug_driver{};
|
||||||
/* TODO: SocketDriver m_socket_driver; */
|
SocketDriver m_socket_driver;
|
||||||
UsbDriver m_usb_driver{};
|
UsbDriver m_usb_driver{};
|
||||||
/* TODO: PlainChannelDriver m_plain_channel_driver; */
|
/* TODO: PlainChannelDriver m_plain_channel_driver; */
|
||||||
os::SdkMutex m_mutex{};
|
os::SdkMutex m_mutex{};
|
||||||
IDriver *m_open_driver{};
|
IDriver *m_open_driver{};
|
||||||
public:
|
public:
|
||||||
DriverManager() = default;
|
DriverManager(mem::StandardAllocator *allocator) : m_socket_driver(allocator) { /* ... */ }
|
||||||
|
|
||||||
Result OpenDriver(impl::DriverType driver_type);
|
Result OpenDriver(impl::DriverType driver_type);
|
||||||
void CloseDriver();
|
void CloseDriver();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_driver_memory_management.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t RequiredAlignment = std::max(os::ThreadStackAlignment, os::MemoryPageSize);
|
||||||
|
|
||||||
|
using SocketConfigType = socket::SystemConfigDefault;
|
||||||
|
|
||||||
|
/* TODO: If we ever use resolvers, increase this. */
|
||||||
|
constexpr inline size_t SocketAllocatorSize = 4_KB;
|
||||||
|
constexpr inline size_t SocketMemoryPoolSize = util::AlignUp(SocketConfigType::PerTcpSocketWorstCaseMemoryPoolSize + SocketConfigType::PerUdpSocketWorstCaseMemoryPoolSize, os::MemoryPageSize);
|
||||||
|
|
||||||
|
constexpr inline size_t SocketRequiredSize = util::AlignUp(SocketMemoryPoolSize + SocketAllocatorSize, os::MemoryPageSize);
|
||||||
|
constexpr inline size_t UsbRequiredSize = 2 * UsbDmaBufferSize + UsbIndicationThreadStackSize;
|
||||||
|
static_assert(util::IsAligned(UsbDmaBufferSize, RequiredAlignment));
|
||||||
|
|
||||||
|
constexpr inline size_t RequiredSize = std::max(SocketRequiredSize, UsbRequiredSize);
|
||||||
|
static_assert(util::IsAligned(RequiredSize, os::MemoryPageSize));
|
||||||
|
|
||||||
|
/* Declare the memory pool. */
|
||||||
|
alignas(RequiredAlignment) constinit u8 g_driver_memory[RequiredSize];
|
||||||
|
|
||||||
|
constexpr inline const socket::SystemConfigDefault SocketConfig(g_driver_memory, RequiredSize, SocketAllocatorSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetUsbReceiveBuffer() {
|
||||||
|
return g_driver_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetUsbSendBuffer() {
|
||||||
|
return g_driver_memory + UsbDmaBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *GetUsbIndicationThreadStack() {
|
||||||
|
return g_driver_memory + 2 * UsbDmaBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeSocketApiForSocketDriver() {
|
||||||
|
R_ABORT_UNLESS(socket::Initialize(SocketConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
constexpr inline size_t UsbDmaBufferSize = 0x80000;
|
||||||
|
constexpr inline size_t UsbIndicationThreadStackSize = 16_KB;
|
||||||
|
|
||||||
|
void *GetUsbReceiveBuffer();
|
||||||
|
void *GetUsbSendBuffer();
|
||||||
|
void *GetUsbIndicationThreadStack();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_socket_discovery_manager.hpp"
|
||||||
|
#include "htclow_socket_discovery_util.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline u32 BeaconQueryServiceId = 0xB48F5C51;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDiscoveryManager::OnDriverOpen() {
|
||||||
|
/* Create our socket. */
|
||||||
|
m_socket = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Dgram, socket::Protocol::IpProto_Udp);
|
||||||
|
AMS_ABORT_UNLESS(m_socket != -1);
|
||||||
|
|
||||||
|
/* Mark driver open. */
|
||||||
|
m_driver_closed = false;
|
||||||
|
|
||||||
|
/* Create our thread. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_discovery_thread), ThreadEntry, this, m_thread_stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowDiscovery)));
|
||||||
|
|
||||||
|
/* Set our thread name. */
|
||||||
|
os::SetThreadNamePointer(std::addressof(m_discovery_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowDiscovery));
|
||||||
|
|
||||||
|
/* Start our thread. */
|
||||||
|
os::StartThread(std::addressof(m_discovery_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDiscoveryManager::OnDriverClose() {
|
||||||
|
/* Mark driver closed. */
|
||||||
|
m_driver_closed = true;
|
||||||
|
|
||||||
|
/* Shutdown our socket. */
|
||||||
|
socket::Shutdown(m_socket, socket::ShutdownMethod::Shut_RdWr);
|
||||||
|
|
||||||
|
/* Close our socket. */
|
||||||
|
socket::Close(m_socket);
|
||||||
|
|
||||||
|
/* Destroy our thread. */
|
||||||
|
os::WaitThread(std::addressof(m_discovery_thread));
|
||||||
|
os::DestroyThread(std::addressof(m_discovery_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDiscoveryManager::OnSocketAcceptBegin(u16 port) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDiscoveryManager::OnSocketAcceptEnd() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDiscoveryManager::ThreadFunc() {
|
||||||
|
for (this->DoDiscovery(); !m_driver_closed; this->DoDiscovery()) {
|
||||||
|
/* Check if the driver is closed five times. */
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||||
|
if (m_driver_closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDiscoveryManager::DoDiscovery() {
|
||||||
|
/* Ensure we close our socket if we fail. */
|
||||||
|
auto socket_guard = SCOPE_GUARD { socket::Close(m_socket); };
|
||||||
|
|
||||||
|
/* Create sockaddr for our socket. */
|
||||||
|
const socket::SockAddrIn sockaddr = {
|
||||||
|
.sin_len = 0,
|
||||||
|
.sin_family = socket::Family::Af_Inet,
|
||||||
|
.sin_port = socket::InetHtons(20181),
|
||||||
|
.sin_addr = { socket::InetHtonl(0) },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bind our socket. */
|
||||||
|
const auto bind_res = socket::Bind(m_socket, reinterpret_cast<const socket::SockAddr *>(std::addressof(sockaddr)), sizeof(sockaddr));
|
||||||
|
R_UNLESS(bind_res != 0, htclow::ResultSocketBindError());
|
||||||
|
|
||||||
|
/* Loop processing beacon queries. */
|
||||||
|
while (true) {
|
||||||
|
/* Receive a tmipc query header. */
|
||||||
|
TmipcHeader header;
|
||||||
|
socket::SockAddr recv_sockaddr;
|
||||||
|
socket::SockLenT recv_sockaddr_len = sizeof(recv_sockaddr);
|
||||||
|
const auto recv_res = socket::RecvFrom(m_socket, std::addressof(header), sizeof(header), socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len));
|
||||||
|
|
||||||
|
/* Check that our receive was valid. */
|
||||||
|
R_UNLESS(recv_res >= 0, htclow::ResultSocketReceiveFromError());
|
||||||
|
R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError());
|
||||||
|
|
||||||
|
/* Check we received a packet header. */
|
||||||
|
if (recv_res != sizeof(header)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we received a correctly versioned BeaconQuery packet. */
|
||||||
|
/* NOTE: Nintendo checks this *after* the following receive, but this seems saner. */
|
||||||
|
if (header.version != TmipcVersion || header.service_id != BeaconQueryServiceId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive the packet body, if there is one. */
|
||||||
|
char packet_data[0x120];
|
||||||
|
|
||||||
|
/* NOTE: Nintendo does not check this... */
|
||||||
|
if (header.data_len > sizeof(packet_data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.data_len > 0) {
|
||||||
|
const auto body_res = socket::RecvFrom(m_socket, packet_data, header.data_len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len));
|
||||||
|
R_UNLESS(body_res >= 0, htclow::ResultSocketReceiveFromError());
|
||||||
|
R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError());
|
||||||
|
|
||||||
|
if (body_res != header.data_len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make our beacon response packet. */
|
||||||
|
const auto len = MakeBeaconResponsePacket(packet_data, sizeof(packet_data));
|
||||||
|
|
||||||
|
/* Send the beacon response data. */
|
||||||
|
const auto send_res = socket::SendTo(m_socket, packet_data, len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), sizeof(recv_sockaddr));
|
||||||
|
R_UNLESS(send_res >= 0, htclow::ResultSocketSendToError());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This can never happen, as the above loop should be infinite, but completion logic is here for posterity. */
|
||||||
|
socket_guard.Cancel();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
class SocketDiscoveryManager {
|
||||||
|
private:
|
||||||
|
bool m_driver_closed;
|
||||||
|
mem::StandardAllocator *m_allocator;
|
||||||
|
void *m_thread_stack;
|
||||||
|
os::ThreadType m_discovery_thread;
|
||||||
|
s32 m_socket;
|
||||||
|
public:
|
||||||
|
SocketDiscoveryManager(mem::StandardAllocator *allocator)
|
||||||
|
: m_driver_closed(false), m_allocator(allocator), m_thread_stack(allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment))
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static void ThreadEntry(void *arg) {
|
||||||
|
static_cast<SocketDiscoveryManager *>(arg)->ThreadFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadFunc();
|
||||||
|
|
||||||
|
Result DoDiscovery();
|
||||||
|
public:
|
||||||
|
void OnDriverOpen();
|
||||||
|
void OnDriverClose();
|
||||||
|
void OnSocketAcceptBegin(u16 port);
|
||||||
|
void OnSocketAcceptEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_socket_discovery_util.hpp"
|
||||||
|
#include "../ctrl/htclow_ctrl_settings_holder.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline u32 AutoConnectIpv4RequestServiceId = 0x834C775A;
|
||||||
|
constexpr inline u32 BeaconResponseServiceId = 0xA6F7FA96;
|
||||||
|
|
||||||
|
constexpr const char MakeAutoConnectIpv4RequestPacketFormat[] =
|
||||||
|
"{\r\n"
|
||||||
|
" \"Address\" : \"%u.%u.%u.%u\",\r\n"
|
||||||
|
" \"Port\" : %u,\r\n"
|
||||||
|
" \"HW\" : \"%s\",\r\n"
|
||||||
|
" \"SN\" : \"%s\"\r\n"
|
||||||
|
"}\r\n";
|
||||||
|
|
||||||
|
constexpr const char BeaconResponsePacketFormat[] =
|
||||||
|
"{\r\n"
|
||||||
|
" \"Gen\" : 2,\r\n"
|
||||||
|
" \"Spec \": \"%s\",\r\n"
|
||||||
|
" \"MAC\" : \"00:00:00:00:00:00\",\r\n"
|
||||||
|
" \"Conn\" : \"TCP\",\r\n"
|
||||||
|
" \"HW\" : \"%s\",\r\n"
|
||||||
|
" \"Name\" : \"%s\",\r\n"
|
||||||
|
" \"SN\" : \"%s\",\r\n"
|
||||||
|
" \"FW\" : \"%s\"\r\n"
|
||||||
|
"}\r\n";
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_settings_holder_mutex;
|
||||||
|
constinit bool g_settings_holder_initialized = false;
|
||||||
|
constinit htclow::ctrl::SettingsHolder g_settings_holder;
|
||||||
|
|
||||||
|
void InitializeSettingsHolder() {
|
||||||
|
std::scoped_lock lk(g_settings_holder_mutex);
|
||||||
|
|
||||||
|
if (!g_settings_holder_initialized) {
|
||||||
|
g_settings_holder.LoadSettings();
|
||||||
|
g_settings_holder_initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 MakeAutoConnectIpv4RequestPacket(char *dst, size_t dst_size, const socket::SockAddrIn &sockaddr) {
|
||||||
|
/* Initialize the settings holder. */
|
||||||
|
InitializeSettingsHolder();
|
||||||
|
|
||||||
|
/* Create the packet header. */
|
||||||
|
TmipcHeader header = {
|
||||||
|
.service_id = AutoConnectIpv4RequestServiceId,
|
||||||
|
.version = TmipcVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create the packet body. */
|
||||||
|
std::scoped_lock lk(g_settings_holder_mutex);
|
||||||
|
const auto addr = sockaddr.sin_addr.s_addr;
|
||||||
|
|
||||||
|
char packet_body[0x100];
|
||||||
|
const auto ideal_len = util::SNPrintf(packet_body, sizeof(packet_body), MakeAutoConnectIpv4RequestPacketFormat,
|
||||||
|
(addr >> 0) & 0xFF, (addr >> 8) & 0xFF, (addr >> 16) & 0xFF, (addr >> 24) & 0xFF,
|
||||||
|
socket::InetNtohs(sockaddr.sin_port),
|
||||||
|
g_settings_holder.GetHardwareType(),
|
||||||
|
"" /* Nintendo passes empty string as serial number here. */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Determine actual usable body length. */
|
||||||
|
header.data_len = std::max<u32>(ideal_len, sizeof(packet_body));
|
||||||
|
|
||||||
|
/* Check that the packet will fit. */
|
||||||
|
AMS_ABORT_UNLESS(sizeof(header) + header.data_len <= dst_size);
|
||||||
|
|
||||||
|
/* Copy the formatted header. */
|
||||||
|
std::memcpy(dst, std::addressof(header), sizeof(header));
|
||||||
|
std::memcpy(dst + sizeof(header), packet_body, header.data_len);
|
||||||
|
|
||||||
|
return header.data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 MakeBeaconResponsePacket(char *dst, size_t dst_size) {
|
||||||
|
/* Initialize the settings holder. */
|
||||||
|
InitializeSettingsHolder();
|
||||||
|
|
||||||
|
/* Create the packet header. */
|
||||||
|
TmipcHeader header = {
|
||||||
|
.service_id = BeaconResponseServiceId,
|
||||||
|
.version = TmipcVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create the packet body. */
|
||||||
|
std::scoped_lock lk(g_settings_holder_mutex);
|
||||||
|
|
||||||
|
char packet_body[0x100];
|
||||||
|
const auto ideal_len = util::SNPrintf(packet_body, sizeof(packet_body), BeaconResponsePacketFormat,
|
||||||
|
g_settings_holder.GetSpec(),
|
||||||
|
g_settings_holder.GetHardwareType(),
|
||||||
|
g_settings_holder.GetTargetName(),
|
||||||
|
g_settings_holder.GetSerialNumber(),
|
||||||
|
g_settings_holder.GetFirmwareVersion()
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Determine actual usable body length. */
|
||||||
|
header.data_len = std::max<u32>(ideal_len, sizeof(packet_body));
|
||||||
|
|
||||||
|
/* Check that the packet will fit. */
|
||||||
|
AMS_ABORT_UNLESS(sizeof(header) + header.data_len <= dst_size);
|
||||||
|
|
||||||
|
/* Copy the formatted header. */
|
||||||
|
std::memcpy(dst, std::addressof(header), sizeof(header));
|
||||||
|
std::memcpy(dst + sizeof(header), packet_body, header.data_len);
|
||||||
|
|
||||||
|
return header.data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
constexpr inline u8 TmipcVersion = 5;
|
||||||
|
|
||||||
|
struct TmipcHeader {
|
||||||
|
u32 service_id;
|
||||||
|
u32 reserved_00;
|
||||||
|
u16 reserved_01;
|
||||||
|
u8 reserved_02;
|
||||||
|
u8 version;
|
||||||
|
u32 data_len;
|
||||||
|
u32 reserved[4];
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<TmipcHeader>::value);
|
||||||
|
static_assert(sizeof(TmipcHeader) == 0x20);
|
||||||
|
|
||||||
|
s32 MakeAutoConnectIpv4RequestPacket(char *dst, size_t dst_size, const socket::SockAddrIn &sockaddr);
|
||||||
|
s32 MakeBeaconResponsePacket(char *dst, size_t dst_size);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,283 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_socket_driver.hpp"
|
||||||
|
#include "htclow_socket_discovery_util.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
Result SocketDriver::ConnectThread() {
|
||||||
|
/* Do auto connect, if we should. */
|
||||||
|
if (m_auto_connect_reserved) {
|
||||||
|
this->DoAutoConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the socket's name. */
|
||||||
|
socket::SockAddrIn sockaddr;
|
||||||
|
socket::SockLenT sockaddr_len = sizeof(sockaddr);
|
||||||
|
R_UNLESS(socket::GetSockName(m_server_socket, reinterpret_cast<socket::SockAddr *>(std::addressof(sockaddr)), std::addressof(sockaddr_len)) == 0, htclow::ResultSocketGetSockNameError());
|
||||||
|
|
||||||
|
/* Accept. */
|
||||||
|
m_discovery_manager.OnSocketAcceptBegin(sockaddr.sin_port);
|
||||||
|
|
||||||
|
sockaddr_len = sizeof(m_server_sockaddr);
|
||||||
|
const auto client_desc = socket::Accept(m_server_socket, reinterpret_cast<socket::SockAddr *>(std::addressof(m_server_sockaddr)), std::addressof(sockaddr_len));
|
||||||
|
|
||||||
|
m_discovery_manager.OnSocketAcceptEnd();
|
||||||
|
|
||||||
|
/* Check accept result. */
|
||||||
|
R_UNLESS(client_desc >= 0, htclow::ResultSocketAcceptError());
|
||||||
|
|
||||||
|
/* Setup client socket. */
|
||||||
|
R_TRY(this->SetupClientSocket(client_desc));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::CreateServerSocket() {
|
||||||
|
/* Check that we don't have a server socket. */
|
||||||
|
AMS_ASSERT(!m_server_socket_valid);
|
||||||
|
|
||||||
|
/* Create the socket. */
|
||||||
|
const auto desc = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Tcp);
|
||||||
|
R_UNLESS(desc != -1, htclow::ResultSocketSocketExemptError());
|
||||||
|
|
||||||
|
/* Be sure that we close the socket if we don't succeed. */
|
||||||
|
auto socket_guard = SCOPE_GUARD { socket::Close(desc); };
|
||||||
|
|
||||||
|
/* Create sockaddr for our socket. */
|
||||||
|
const socket::SockAddrIn sockaddr = {
|
||||||
|
.sin_len = 0,
|
||||||
|
.sin_family = socket::Family::Af_Inet,
|
||||||
|
.sin_port = socket::InetHtons(20180),
|
||||||
|
.sin_addr = { socket::InetHtonl(0) },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Enable local address reuse. */
|
||||||
|
{
|
||||||
|
u32 enable = 1;
|
||||||
|
const auto res = socket::SetSockOpt(desc, socket::Level::Sol_Socket, socket::Option::So_ReuseAddr, std::addressof(enable), sizeof(enable));
|
||||||
|
AMS_ABORT_UNLESS(res == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind the socket. */
|
||||||
|
const auto bind_res = socket::Bind(desc, reinterpret_cast<const socket::SockAddr *>(std::addressof(sockaddr)), sizeof(sockaddr));
|
||||||
|
R_UNLESS(bind_res == 0, htclow::ResultSocketBindError());
|
||||||
|
|
||||||
|
/* Listen on the socket. */
|
||||||
|
const auto listen_res = socket::Listen(desc, 1);
|
||||||
|
R_UNLESS(listen_res == 0, htclow::ResultSocketListenError());
|
||||||
|
|
||||||
|
/* We succeeded. */
|
||||||
|
socket_guard.Cancel();
|
||||||
|
m_server_socket = desc;
|
||||||
|
m_server_socket_valid = true;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::DestroyServerSocket() {
|
||||||
|
if (m_server_socket_valid) {
|
||||||
|
socket::Shutdown(m_server_socket, socket::ShutdownMethod::Shut_RdWr);
|
||||||
|
socket::Close(m_server_socket);
|
||||||
|
m_server_socket_valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::SetupClientSocket(s32 desc) {
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Check that we don't have a client socket. */
|
||||||
|
AMS_ASSERT(!m_client_socket_valid);
|
||||||
|
|
||||||
|
/* Be sure that we close the socket if we don't succeed. */
|
||||||
|
auto socket_guard = SCOPE_GUARD { socket::Close(desc); };
|
||||||
|
|
||||||
|
/* Enable debug logging for the socket. */
|
||||||
|
u32 debug = 1;
|
||||||
|
const auto res = socket::SetSockOpt(desc, socket::Level::Sol_Tcp, socket::Option::So_Debug, std::addressof(debug), sizeof(debug));
|
||||||
|
R_UNLESS(res >= 0, htclow::ResultSocketSetSockOptError());
|
||||||
|
|
||||||
|
/* We succeeded. */
|
||||||
|
socket_guard.Cancel();
|
||||||
|
m_client_socket = desc;
|
||||||
|
m_client_socket_valid = true;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SocketDriver::IsAutoConnectReserved() {
|
||||||
|
return m_auto_connect_reserved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::ReserveAutoConnect() {
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
if (m_client_socket_valid) {
|
||||||
|
/* Save our client sockaddr. */
|
||||||
|
socket::SockLenT sockaddr_len = sizeof(m_saved_client_sockaddr);
|
||||||
|
if (socket::GetSockName(m_server_socket, reinterpret_cast<socket::SockAddr *>(std::addressof(m_saved_client_sockaddr)), std::addressof(sockaddr_len)) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save our server sockaddr. */
|
||||||
|
m_saved_server_sockaddr = m_server_sockaddr;
|
||||||
|
|
||||||
|
/* Mark auto-connect reserved. */
|
||||||
|
m_auto_connect_reserved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::DoAutoConnect() {
|
||||||
|
/* Clear auto-connect reserved. */
|
||||||
|
m_auto_connect_reserved = false;
|
||||||
|
|
||||||
|
/* Create udb socket. */
|
||||||
|
const auto desc = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Dgram, socket::Protocol::IpProto_Udp);
|
||||||
|
if (desc == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up the desc when we're done. */
|
||||||
|
ON_SCOPE_EXIT { socket::Close(desc); };
|
||||||
|
|
||||||
|
/* Create auto-connect packet. */
|
||||||
|
char auto_connect_packet[0x120];
|
||||||
|
s32 len;
|
||||||
|
{
|
||||||
|
const socket::SockAddrIn sockaddr = {
|
||||||
|
.sin_family = socket::Family::Af_Inet,
|
||||||
|
.sin_port = m_saved_client_sockaddr.sin_port,
|
||||||
|
.sin_addr = m_saved_client_sockaddr.sin_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
len = htclow::driver::MakeAutoConnectIpv4RequestPacket(auto_connect_packet, sizeof(auto_connect_packet), sockaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the auto-connect packet to the host on port 20181. */
|
||||||
|
const socket::SockAddrIn sockaddr = {
|
||||||
|
.sin_family = socket::Family::Af_Inet,
|
||||||
|
.sin_port = socket::InetHtons(20181),
|
||||||
|
.sin_addr = m_saved_server_sockaddr.sin_addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Send the auto-connect packet. */
|
||||||
|
socket::SendTo(desc, auto_connect_packet, len, socket::MsgFlag::MsgFlag_None, reinterpret_cast<const socket::SockAddr *>(std::addressof(sockaddr)), sizeof(sockaddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::Open() {
|
||||||
|
m_discovery_manager.OnDriverOpen();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::Close() {
|
||||||
|
m_discovery_manager.OnDriverClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::Connect(os::EventType *event) {
|
||||||
|
/* Allocate a temporary thread stack. */
|
||||||
|
void *stack = m_allocator->Allocate(os::MemoryPageSize, os::ThreadStackAlignment);
|
||||||
|
ON_SCOPE_EXIT { m_allocator->Free(stack); };
|
||||||
|
|
||||||
|
/* Try to create a server socket. */
|
||||||
|
R_TRY(this->CreateServerSocket());
|
||||||
|
|
||||||
|
/* Prepare to run our connect thread. */
|
||||||
|
m_event.Clear();
|
||||||
|
|
||||||
|
/* Run our connect thread. */
|
||||||
|
{
|
||||||
|
/* Create the thread. */
|
||||||
|
os::ThreadType connect_thread;
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(connect_thread), ConnectThreadEntry, this, stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowTcpServer)));
|
||||||
|
|
||||||
|
/* Set the thread's name. */
|
||||||
|
os::SetThreadNamePointer(std::addressof(connect_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowTcpServer));
|
||||||
|
|
||||||
|
/* Start the thread. */
|
||||||
|
os::StartThread(std::addressof(connect_thread));
|
||||||
|
|
||||||
|
/* Check if we should cancel the connection. */
|
||||||
|
if (os::WaitAny(event, m_event.GetBase()) == 0) {
|
||||||
|
this->DestroyServerSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for the connect thread to finish. */
|
||||||
|
os::WaitThread(std::addressof(connect_thread));
|
||||||
|
|
||||||
|
/* Destroy the connection thread. */
|
||||||
|
os::DestroyThread(std::addressof(connect_thread));
|
||||||
|
|
||||||
|
/* Destroy the server socket. */
|
||||||
|
this->DestroyServerSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return our connection result. */
|
||||||
|
return m_connect_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::Shutdown() {
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Shut down our client socket, if we need to. */
|
||||||
|
if (m_client_socket_valid) {
|
||||||
|
socket::Shutdown(m_client_socket, socket::ShutdownMethod::Shut_RdWr);
|
||||||
|
socket::Close(m_client_socket);
|
||||||
|
m_client_socket_valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::Send(const void *src, int src_size) {
|
||||||
|
/* Check the input size. */
|
||||||
|
R_UNLESS(src_size >= 0, htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Repeatedly send data until it's all sent. */
|
||||||
|
ssize_t cur_sent;
|
||||||
|
for (ssize_t sent = 0; sent < src_size; sent += cur_sent) {
|
||||||
|
cur_sent = socket::Send(m_client_socket, static_cast<const u8 *>(src) + sent, src_size - sent, socket::MsgFlag::MsgFlag_None);
|
||||||
|
R_UNLESS(cur_sent > 0, htclow::ResultSocketSendError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocketDriver::Receive(void *dst, int dst_size) {
|
||||||
|
/* Check the input size. */
|
||||||
|
R_UNLESS(dst_size >= 0, htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Repeatedly receive data until it's all sent. */
|
||||||
|
ssize_t cur_recv;
|
||||||
|
for (ssize_t received = 0; received < dst_size; received += cur_recv) {
|
||||||
|
cur_recv = socket::Recv(m_client_socket, static_cast<u8 *>(dst) + received, dst_size - received, socket::MsgFlag::MsgFlag_None);
|
||||||
|
R_UNLESS(cur_recv > 0, htclow::ResultSocketReceiveError());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::CancelSendReceive() {
|
||||||
|
this->Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::Suspend() {
|
||||||
|
this->ReserveAutoConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketDriver::Resume() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "htclow_i_driver.hpp"
|
||||||
|
#include "htclow_socket_discovery_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
class SocketDriver final : public IDriver {
|
||||||
|
private:
|
||||||
|
mem::StandardAllocator *m_allocator;
|
||||||
|
SocketDiscoveryManager m_discovery_manager;
|
||||||
|
socket::SockAddrIn m_server_sockaddr;
|
||||||
|
socket::SockAddrIn m_saved_server_sockaddr;
|
||||||
|
socket::SockAddrIn m_saved_client_sockaddr;
|
||||||
|
os::Event m_event;
|
||||||
|
Result m_connect_result;
|
||||||
|
os::SdkMutex m_mutex;
|
||||||
|
s32 m_server_socket;
|
||||||
|
s32 m_client_socket;
|
||||||
|
bool m_server_socket_valid;
|
||||||
|
bool m_client_socket_valid;
|
||||||
|
bool m_auto_connect_reserved;
|
||||||
|
public:
|
||||||
|
SocketDriver(mem::StandardAllocator *allocator)
|
||||||
|
: m_allocator(allocator), m_discovery_manager(m_allocator), m_event(os::EventClearMode_ManualClear),
|
||||||
|
m_connect_result(), m_mutex(), m_server_socket(), m_client_socket(), m_server_socket_valid(false),
|
||||||
|
m_client_socket_valid(false), m_auto_connect_reserved(false)
|
||||||
|
{
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static void ConnectThreadEntry(void *arg) {
|
||||||
|
auto * const driver = static_cast<SocketDriver *>(arg);
|
||||||
|
|
||||||
|
driver->m_connect_result = driver->ConnectThread();
|
||||||
|
driver->m_event.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ConnectThread();
|
||||||
|
private:
|
||||||
|
Result CreateServerSocket();
|
||||||
|
void DestroyServerSocket();
|
||||||
|
|
||||||
|
Result SetupClientSocket(s32 desc);
|
||||||
|
|
||||||
|
bool IsAutoConnectReserved();
|
||||||
|
void ReserveAutoConnect();
|
||||||
|
void DoAutoConnect();
|
||||||
|
public:
|
||||||
|
virtual Result Open() override;
|
||||||
|
virtual void Close() override;
|
||||||
|
virtual Result Connect(os::EventType *event) override;
|
||||||
|
virtual void Shutdown() override;
|
||||||
|
virtual Result Send(const void *src, int src_size) override;
|
||||||
|
virtual Result Receive(void *dst, int dst_size) override;
|
||||||
|
virtual void CancelSendReceive() override;
|
||||||
|
virtual void Suspend() override;
|
||||||
|
virtual void Resume() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_usb_impl.hpp"
|
#include "htclow_usb_impl.hpp"
|
||||||
|
#include "htclow_driver_memory_management.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::driver {
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
@ -179,11 +180,8 @@ namespace ams::htclow::driver {
|
||||||
.wBytesPerInterval = 0x0000,
|
.wBytesPerInterval = 0x0000,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr size_t UsbDmaBufferSize = 0x60000;
|
constinit void *g_usb_receive_buffer = nullptr;
|
||||||
|
constinit void *g_usb_send_buffer = nullptr;
|
||||||
alignas(os::MemoryPageSize) constinit u8 g_usb_receive_buffer[UsbDmaBufferSize];
|
|
||||||
alignas(os::MemoryPageSize) constinit u8 g_usb_send_buffer[UsbDmaBufferSize];
|
|
||||||
alignas(os::ThreadStackAlignment) constinit u8 g_usb_indication_thread_stack[16_KB];
|
|
||||||
|
|
||||||
constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr;
|
constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr;
|
||||||
constinit void *g_availability_change_param = nullptr;
|
constinit void *g_availability_change_param = nullptr;
|
||||||
|
@ -346,6 +344,10 @@ namespace ams::htclow::driver {
|
||||||
/* Set the interface as initialized. */
|
/* Set the interface as initialized. */
|
||||||
g_usb_interface_initialized = true;
|
g_usb_interface_initialized = true;
|
||||||
|
|
||||||
|
/* Get the dma buffers. */
|
||||||
|
g_usb_receive_buffer = GetUsbReceiveBuffer();
|
||||||
|
g_usb_send_buffer = GetUsbSendBuffer();
|
||||||
|
|
||||||
/* If we fail somewhere, finalize. */
|
/* If we fail somewhere, finalize. */
|
||||||
auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); };
|
auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); };
|
||||||
|
|
||||||
|
@ -385,7 +387,7 @@ namespace ams::htclow::driver {
|
||||||
R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints()));
|
R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints()));
|
||||||
|
|
||||||
/* Create the indication thread. */
|
/* Create the indication thread. */
|
||||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, g_usb_indication_thread_stack, sizeof(g_usb_indication_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication)));
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, GetUsbIndicationThreadStack(), UsbIndicationThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication)));
|
||||||
|
|
||||||
/* Set the thread name. */
|
/* Set the thread name. */
|
||||||
os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication));
|
os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication));
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
HtclowManagerImpl::HtclowManagerImpl(mem::StandardAllocator *allocator)
|
HtclowManagerImpl::HtclowManagerImpl(mem::StandardAllocator *allocator)
|
||||||
: m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)),
|
: m_packet_factory(allocator), m_driver_manager(allocator), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)),
|
||||||
m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)),
|
m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)),
|
||||||
m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)),
|
m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)),
|
||||||
m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)),
|
m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)),
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
namespace ams::socket::impl {
|
namespace ams::socket::impl {
|
||||||
|
|
||||||
|
constexpr inline auto MinimumHeapAlignment = 0x10;
|
||||||
|
|
||||||
void *Alloc(size_t size);
|
void *Alloc(size_t size);
|
||||||
void *Calloc(size_t num, size_t size);
|
void *Calloc(size_t num, size_t size);
|
||||||
void Free(void *ptr);
|
void Free(void *ptr);
|
||||||
|
|
|
@ -20,9 +20,19 @@ namespace ams::socket::impl {
|
||||||
Errno GetLastError();
|
Errno GetLastError();
|
||||||
void SetLastError(Errno err);
|
void SetLastError(Errno err);
|
||||||
|
|
||||||
|
bool HeapIsAvailable(int generation);
|
||||||
|
int GetHeapGeneration();
|
||||||
|
|
||||||
u32 InetHtonl(u32 host);
|
u32 InetHtonl(u32 host);
|
||||||
u16 InetHtons(u16 host);
|
u16 InetHtons(u16 host);
|
||||||
u32 InetNtohl(u32 net);
|
u32 InetNtohl(u32 net);
|
||||||
u16 InetNtohs(u16 net);
|
u16 InetNtohs(u16 net);
|
||||||
|
|
||||||
|
Result Initialize(const Config &config);
|
||||||
|
Result Finalize();
|
||||||
|
|
||||||
|
Result InitializeAllocatorForInternal(void *buffer, size_t size);
|
||||||
|
|
||||||
|
s32 Shutdown(s32 desc, ShutdownMethod how);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,35 +17,99 @@
|
||||||
#include "socket_api.hpp"
|
#include "socket_api.hpp"
|
||||||
#include "socket_allocator.hpp"
|
#include "socket_allocator.hpp"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
#include <switch/services/bsd.h>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace ams::socket::impl {
|
namespace ams::socket::impl {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constinit bool g_initialized = false;
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_heap_mutex;
|
||||||
|
|
||||||
|
constinit lmem::HeapHandle g_heap_handle = nullptr;
|
||||||
|
constinit int g_heap_generation = -1;
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool IsInitialized() {
|
||||||
|
return g_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void *Alloc(size_t size) {
|
void *Alloc(size_t size) {
|
||||||
/* TODO: expheap, heap generation. */
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
return ams::Malloc(size);
|
|
||||||
|
AMS_ASSERT(g_heap_generation > 0);
|
||||||
|
|
||||||
|
void *ptr = nullptr;
|
||||||
|
|
||||||
|
if (!g_heap_handle) {
|
||||||
|
socket::impl::SetLastError(Errno::EOpNotSupp);
|
||||||
|
} else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size)) == nullptr) {
|
||||||
|
socket::impl::SetLastError(Errno::EOpNotSupp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *Calloc(size_t num, size_t size) {
|
void *Calloc(size_t num, size_t size) {
|
||||||
if (void *ptr = Alloc(size * num); ptr != nullptr) {
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
std::memset(ptr, 0, size * num);
|
|
||||||
return ptr;
|
AMS_ASSERT(g_heap_generation > 0);
|
||||||
|
|
||||||
|
void *ptr = nullptr;
|
||||||
|
|
||||||
|
if (!g_heap_handle) {
|
||||||
|
socket::impl::SetLastError(Errno::EOpNotSupp);
|
||||||
|
} else if ((ptr = lmem::AllocateFromExpHeap(g_heap_handle, size * num)) == nullptr) {
|
||||||
|
socket::impl::SetLastError(Errno::EOpNotSupp);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
std::memset(ptr, 0, size * num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Free(void *ptr) {
|
void Free(void *ptr) {
|
||||||
/* TODO: expheap, heap generation. */
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
return ams::Free(ptr);
|
|
||||||
|
AMS_ASSERT(g_heap_generation > 0);
|
||||||
|
|
||||||
|
if (!g_heap_handle) {
|
||||||
|
socket::impl::SetLastError(Errno::EOpNotSupp);
|
||||||
|
} else if (ptr != nullptr) {
|
||||||
|
lmem::FreeToExpHeap(g_heap_handle, ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeapIsAvailable(int generation) {
|
||||||
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
|
|
||||||
|
return g_heap_handle && g_heap_generation == generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetHeapGeneration() {
|
||||||
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
|
|
||||||
|
return g_heap_generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno GetLastError() {
|
Errno GetLastError() {
|
||||||
/* TODO: check that client library is initialized. */
|
if (AMS_LIKELY(IsInitialized())) {
|
||||||
return static_cast<Errno>(errno);
|
return static_cast<Errno>(errno);
|
||||||
|
} else {
|
||||||
|
return Errno::EInval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetLastError(Errno err) {
|
void SetLastError(Errno err) {
|
||||||
/* TODO: check that client library is initialized. */
|
if (AMS_LIKELY(IsInitialized())) {
|
||||||
errno = static_cast<int>(err);
|
errno = static_cast<int>(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 InetHtonl(u32 host) {
|
u32 InetHtonl(u32 host) {
|
||||||
|
@ -80,4 +144,109 @@ namespace ams::socket::impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void InitializeHeapImpl(void *buffer, size_t size) {
|
||||||
|
/* NOTE: Nintendo uses both CreateOption_ThreadSafe *and* a global heap mutex. */
|
||||||
|
/* This is unnecessary, and using a single SdkMutex is more performant, since we're not recursive. */
|
||||||
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
|
|
||||||
|
g_heap_handle = lmem::CreateExpHeap(buffer, size, lmem::CreateOption_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeCommon(const Config &config) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(!IsInitialized());
|
||||||
|
AMS_ABORT_UNLESS(config.GetMemoryPool() != nullptr);
|
||||||
|
AMS_ABORT_UNLESS(1 <= config.GetConcurrencyCountMax() && config.GetConcurrencyCountMax() <= ConcurrencyLimitMax);
|
||||||
|
if (!config.IsSmbpClient()) { AMS_ABORT_UNLESS(config.GetAllocatorPoolSize() < config.GetMemoryPoolSize()); }
|
||||||
|
AMS_ABORT_UNLESS(util::IsAligned(config.GetMemoryPoolSize(), os::MemoryPageSize));
|
||||||
|
AMS_ABORT_UNLESS(util::IsAligned(config.GetAllocatorPoolSize(), os::MemoryPageSize));
|
||||||
|
AMS_ABORT_UNLESS(config.GetAllocatorPoolSize() >= 4_KB);
|
||||||
|
if (!config.IsSystemClient()) {
|
||||||
|
R_UNLESS(config.GetMemoryPoolSize() >= socket::MinSocketMemoryPoolSize, socket::ResultInsufficientProvidedMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t transfer_memory_size = config.GetMemoryPoolSize() - config.GetAllocatorPoolSize();
|
||||||
|
if (!config.IsSmbpClient()) {
|
||||||
|
R_UNLESS(transfer_memory_size >= socket::MinMemHeapAllocatorSize, socket::ResultInsufficientProvidedMemory());
|
||||||
|
} else {
|
||||||
|
R_UNLESS(config.GetMemoryPoolSize() >= socket::MinimumSharedMbufPoolReservation, socket::ResultInsufficientProvidedMemory());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the allocator heap. */
|
||||||
|
InitializeHeapImpl(static_cast<u8 *>(config.GetMemoryPool()) + transfer_memory_size, config.GetAllocatorPoolSize());
|
||||||
|
|
||||||
|
/* Initialize libnx. */
|
||||||
|
{
|
||||||
|
const ::BsdInitConfig libnx_config = {
|
||||||
|
.version = config.GetVersion(),
|
||||||
|
.tmem_buffer = config.GetMemoryPool(),
|
||||||
|
.tmem_buffer_size = transfer_memory_size,
|
||||||
|
|
||||||
|
.tcp_tx_buf_size = static_cast<u32>(config.GetTcpInitialSendBufferSize()),
|
||||||
|
.tcp_rx_buf_size = static_cast<u32>(config.GetTcpInitialReceiveBufferSize()),
|
||||||
|
.tcp_tx_buf_max_size = static_cast<u32>(config.GetTcpAutoSendBufferSizeMax()),
|
||||||
|
.tcp_rx_buf_max_size = static_cast<u32>(config.GetTcpAutoReceiveBufferSizeMax()),
|
||||||
|
|
||||||
|
.udp_tx_buf_size = static_cast<u32>(config.GetUdpSendBufferSize()),
|
||||||
|
.udp_rx_buf_size = static_cast<u32>(config.GetUdpReceiveBufferSize()),
|
||||||
|
|
||||||
|
.sb_efficiency = static_cast<u32>(config.GetSocketBufferEfficiency()),
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto service_type = config.IsSystemClient() ? (1 << 1) : (1 << 0);
|
||||||
|
|
||||||
|
sm::DoWithSession([&] {
|
||||||
|
R_ABORT_UNLESS(::bsdInitialize(std::addressof(libnx_config), static_cast<u32>(config.GetConcurrencyCountMax()), service_type));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the heap generation. */
|
||||||
|
g_heap_generation = (g_heap_generation + 1) % MinimumHeapAlignment;
|
||||||
|
|
||||||
|
/* TODO: socket::resolver::EnableResolverCalls()? Not necessary in our case (htc), but consider calling it. */
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Initialize(const Config &config) {
|
||||||
|
return InitializeCommon(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Finalize() {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(IsInitialized());
|
||||||
|
|
||||||
|
/* TODO: If we support statistics, kill the statistics thread. */
|
||||||
|
|
||||||
|
/* TODO: socket::resolver::DisableResolverCalls()? */
|
||||||
|
|
||||||
|
/* Finalize libnx. */
|
||||||
|
::bsdExit();
|
||||||
|
|
||||||
|
/* Finalize the heap. */
|
||||||
|
lmem::HeapHandle heap_handle;
|
||||||
|
{
|
||||||
|
std::scoped_lock lk(g_heap_mutex);
|
||||||
|
|
||||||
|
heap_handle = g_heap_handle;
|
||||||
|
g_heap_handle = nullptr;
|
||||||
|
}
|
||||||
|
lmem::DestroyExpHeap(heap_handle);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeAllocatorForInternal(void *buffer, size_t size) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ABORT_UNLESS(util::IsAligned(size, os::MemoryPageSize));
|
||||||
|
AMS_ABORT_UNLESS(size >= 4_KB);
|
||||||
|
|
||||||
|
InitializeHeapImpl(buffer, size);
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,4 +42,16 @@ namespace ams::socket {
|
||||||
return impl::InetNtohs(net);
|
return impl::InetNtohs(net);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result Initialize(const Config &config) {
|
||||||
|
return impl::Initialize(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Finalize() {
|
||||||
|
return impl::Finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeAllocatorForInternal(void *buffer, size_t size) {
|
||||||
|
return impl::InitializeAllocatorForInternal(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
#include <vapours/results/settings_results.hpp>
|
#include <vapours/results/settings_results.hpp>
|
||||||
#include <vapours/results/sf_results.hpp>
|
#include <vapours/results/sf_results.hpp>
|
||||||
#include <vapours/results/sm_results.hpp>
|
#include <vapours/results/sm_results.hpp>
|
||||||
|
#include <vapours/results/socket_results.hpp>
|
||||||
#include <vapours/results/spl_results.hpp>
|
#include <vapours/results/spl_results.hpp>
|
||||||
#include <vapours/results/svc_results.hpp>
|
#include <vapours/results/svc_results.hpp>
|
||||||
#include <vapours/results/time_results.hpp>
|
#include <vapours/results/time_results.hpp>
|
||||||
|
|
|
@ -52,6 +52,18 @@ namespace ams::htclow {
|
||||||
R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999);
|
R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999);
|
||||||
R_DEFINE_ERROR_RESULT(DriverOpened, 1201);
|
R_DEFINE_ERROR_RESULT(DriverOpened, 1201);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(SocketDriverError, 1300, 1399);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketSocketExemptError, 1301);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketBindError, 1302);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketListenError, 1304);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketAcceptError, 1305);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketReceiveError, 1306);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketSendError, 1307);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketReceiveFromError, 1308);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketSendToError, 1309);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketSetSockOptError, 1310);
|
||||||
|
R_DEFINE_ERROR_RESULT(SocketGetSockNameError, 1311);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499);
|
R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499);
|
||||||
R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401);
|
R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401);
|
||||||
R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402);
|
R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402);
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::socket {
|
||||||
|
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(27);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(InsufficientProvidedMemory, 1);
|
||||||
|
|
||||||
|
}
|
|
@ -39,6 +39,8 @@ namespace ams::mitm::socket::resolver {
|
||||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
alignas(os::MemoryPageSize) constinit u8 g_resolver_allocator_buffer[16_KB];
|
||||||
|
|
||||||
ServerManager g_server_manager;
|
ServerManager g_server_manager;
|
||||||
|
|
||||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||||
|
@ -121,6 +123,9 @@ namespace ams::mitm::socket::resolver {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Initialize the socket allocator. */
|
||||||
|
ams::socket::InitializeAllocatorForInternal(g_resolver_allocator_buffer, sizeof(g_resolver_allocator_buffer));
|
||||||
|
|
||||||
/* Initialize debug. */
|
/* Initialize debug. */
|
||||||
resolver::InitializeDebug(ShouldEnableDebugLog());
|
resolver::InitializeDebug(ShouldEnableDebugLog());
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"filesystem_access": {
|
"filesystem_access": {
|
||||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||||
},
|
},
|
||||||
"service_access": ["pcie", "psc:m", "set:cal", "set:fd", "set:sys", "usb:ds", "fsp-srv"],
|
"service_access": ["pcie", "psc:m", "set:cal", "set:fd", "set:sys", "usb:ds", "fsp-srv", "bsd:s"],
|
||||||
"service_host": ["file_io", "htc", "htcs"],
|
"service_host": ["file_io", "htc", "htcs"],
|
||||||
"kernel_capabilities": [{
|
"kernel_capabilities": [{
|
||||||
"type": "kernel_flags",
|
"type": "kernel_flags",
|
||||||
|
|
|
@ -200,6 +200,10 @@ namespace ams::htc {
|
||||||
return htclow::impl::DriverType::HostBridge;
|
return htclow::impl::DriverType::HostBridge;
|
||||||
} else if (std::strstr(transport, "plainchannel")) {
|
} else if (std::strstr(transport, "plainchannel")) {
|
||||||
return htclow::impl::DriverType::PlainChannel;
|
return htclow::impl::DriverType::PlainChannel;
|
||||||
|
} else if (std::strstr(transport, "socket")) {
|
||||||
|
/* NOTE: Nintendo does not actually allow socket driver to be selected. */
|
||||||
|
/* Should we disallow this? Undesirable, because people will want to use docked tma. */
|
||||||
|
return htclow::impl::DriverType::Socket;
|
||||||
} else {
|
} else {
|
||||||
return DefaultHtclowDriverType;
|
return DefaultHtclowDriverType;
|
||||||
}
|
}
|
||||||
|
@ -230,6 +234,12 @@ namespace ams::htc {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
void InitializeSocketApiForSocketDriver();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
/* Set thread name. */
|
/* Set thread name. */
|
||||||
|
@ -240,6 +250,11 @@ int main(int argc, char **argv)
|
||||||
const auto driver_type = htc::GetHtclowDriverType();
|
const auto driver_type = htc::GetHtclowDriverType();
|
||||||
htclow::HtclowManagerHolder::SetDefaultDriver(driver_type);
|
htclow::HtclowManagerHolder::SetDefaultDriver(driver_type);
|
||||||
|
|
||||||
|
/* If necessary, initialize the socket driver. */
|
||||||
|
if (driver_type == htclow::impl::DriverType::Socket) {
|
||||||
|
htclow::driver::InitializeSocketApiForSocketDriver();
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the htclow manager. */
|
/* Initialize the htclow manager. */
|
||||||
htclow::HtclowManagerHolder::AddReference();
|
htclow::HtclowManagerHolder::AddReference();
|
||||||
ON_SCOPE_EXIT { htclow::HtclowManagerHolder::Release(); };
|
ON_SCOPE_EXIT { htclow::HtclowManagerHolder::Release(); };
|
||||||
|
|
Loading…
Reference in a new issue