htc: implement socket driver (socket api not really impl'd yet)
This commit is contained in:
@@ -17,35 +17,99 @@
|
||||
#include "socket_api.hpp"
|
||||
#include "socket_allocator.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <switch/services/bsd.h>
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
/* TODO: expheap, heap generation. */
|
||||
return ams::Malloc(size);
|
||||
std::scoped_lock lk(g_heap_mutex);
|
||||
|
||||
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) {
|
||||
if (void *ptr = Alloc(size * num); ptr != nullptr) {
|
||||
std::memset(ptr, 0, size * num);
|
||||
return ptr;
|
||||
std::scoped_lock lk(g_heap_mutex);
|
||||
|
||||
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 {
|
||||
return nullptr;
|
||||
std::memset(ptr, 0, size * num);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Free(void *ptr) {
|
||||
/* TODO: expheap, heap generation. */
|
||||
return ams::Free(ptr);
|
||||
std::scoped_lock lk(g_heap_mutex);
|
||||
|
||||
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() {
|
||||
/* TODO: check that client library is initialized. */
|
||||
return static_cast<Errno>(errno);
|
||||
if (AMS_LIKELY(IsInitialized())) {
|
||||
return static_cast<Errno>(errno);
|
||||
} else {
|
||||
return Errno::EInval;
|
||||
}
|
||||
}
|
||||
|
||||
void SetLastError(Errno err) {
|
||||
/* TODO: check that client library is initialized. */
|
||||
errno = static_cast<int>(err);
|
||||
if (AMS_LIKELY(IsInitialized())) {
|
||||
errno = static_cast<int>(err);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user