Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_driver_manager.hpp"
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
Result DriverManager::OpenDriver(impl::DriverType driver_type) {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that we're not already open. */
|
||||
R_UNLESS(m_open_driver == nullptr, htclow::ResultDriverOpened());
|
||||
|
||||
/* Open the driver. */
|
||||
switch (driver_type) {
|
||||
case impl::DriverType::Debug:
|
||||
R_TRY(m_debug_driver->Open());
|
||||
m_open_driver = m_debug_driver;
|
||||
break;
|
||||
case impl::DriverType::Socket:
|
||||
m_socket_driver.Open();
|
||||
m_open_driver = std::addressof(m_socket_driver);
|
||||
break;
|
||||
case impl::DriverType::Usb:
|
||||
m_usb_driver.Open();
|
||||
m_open_driver = std::addressof(m_usb_driver);
|
||||
break;
|
||||
case impl::DriverType::PlainChannel:
|
||||
//m_plain_channel_driver.Open();
|
||||
//m_open_driver = std::addressof(m_plain_channel_driver);
|
||||
//break;
|
||||
R_THROW(htclow::ResultUnknownDriverType());
|
||||
default:
|
||||
R_THROW(htclow::ResultUnknownDriverType());
|
||||
}
|
||||
|
||||
/* Set the driver type. */
|
||||
m_driver_type = driver_type;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void DriverManager::CloseDriver() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Clear our driver type. */
|
||||
m_driver_type = util::nullopt;
|
||||
|
||||
/* Close our driver. */
|
||||
if (m_open_driver != nullptr) {
|
||||
m_open_driver->Close();
|
||||
m_open_driver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
impl::DriverType DriverManager::GetDriverType() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return m_driver_type.value_or(impl::DriverType::Unknown);
|
||||
}
|
||||
|
||||
IDriver *DriverManager::GetCurrentDriver() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return m_open_driver;
|
||||
}
|
||||
|
||||
void DriverManager::Cancel() {
|
||||
m_open_driver->CancelSendReceive();
|
||||
}
|
||||
|
||||
void DriverManager::SetDebugDriver(IDriver *driver) {
|
||||
m_debug_driver = driver;
|
||||
m_driver_type = impl::DriverType::Debug;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_i_driver.hpp"
|
||||
#include "htclow_socket_driver.hpp"
|
||||
#include "htclow_usb_driver.hpp"
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
class DriverManager {
|
||||
private:
|
||||
util::optional<htclow::impl::DriverType> m_driver_type{};
|
||||
IDriver *m_debug_driver{};
|
||||
SocketDriver m_socket_driver;
|
||||
UsbDriver m_usb_driver{};
|
||||
/* TODO: PlainChannelDriver m_plain_channel_driver; */
|
||||
os::SdkMutex m_mutex{};
|
||||
IDriver *m_open_driver{};
|
||||
public:
|
||||
DriverManager(mem::StandardAllocator *allocator) : m_socket_driver(allocator) { /* ... */ }
|
||||
|
||||
Result OpenDriver(impl::DriverType driver_type);
|
||||
void CloseDriver();
|
||||
|
||||
impl::DriverType GetDriverType();
|
||||
|
||||
IDriver *GetCurrentDriver();
|
||||
|
||||
void Cancel();
|
||||
|
||||
void SetDebugDriver(IDriver *driver);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "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, 2);
|
||||
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
constexpr inline size_t UsbDmaBufferSize = 0x80000;
|
||||
constexpr inline size_t UsbIndicationThreadStackSize = 16_KB;
|
||||
|
||||
void *GetUsbReceiveBuffer();
|
||||
void *GetUsbSendBuffer();
|
||||
void *GetUsbIndicationThreadStack();
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
class IDriver {
|
||||
public:
|
||||
virtual Result Open() = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual Result Connect(os::EventType *event) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual Result Send(const void *src, int src_size) = 0;
|
||||
virtual Result Receive(void *dst, int dst_size) = 0;
|
||||
virtual void CancelSendReceive() = 0;
|
||||
virtual void Suspend() = 0;
|
||||
virtual void Resume() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "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) {
|
||||
AMS_UNUSED(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::Msg_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::Msg_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::Msg_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();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::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);
|
||||
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "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));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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::Msg_None, reinterpret_cast<const socket::SockAddr *>(std::addressof(sockaddr)), sizeof(sockaddr));
|
||||
}
|
||||
|
||||
Result SocketDriver::Open() {
|
||||
m_discovery_manager.OnDriverOpen();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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. */
|
||||
R_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::Msg_None);
|
||||
R_UNLESS(cur_sent > 0, htclow::ResultSocketSendError());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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::Msg_None);
|
||||
R_UNLESS(cur_recv > 0, htclow::ResultSocketReceiveError());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SocketDriver::CancelSendReceive() {
|
||||
this->Shutdown();
|
||||
}
|
||||
|
||||
void SocketDriver::Suspend() {
|
||||
this->ReserveAutoConnect();
|
||||
}
|
||||
|
||||
void SocketDriver::Resume() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_usb_driver.hpp"
|
||||
#include "htclow_usb_impl.hpp"
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
void UsbDriver::OnUsbAvailabilityChange(UsbAvailability availability, void *param) {
|
||||
/* Convert the argument to a driver. */
|
||||
UsbDriver *driver = static_cast<UsbDriver *>(param);
|
||||
|
||||
/* Handle the change. */
|
||||
switch (availability) {
|
||||
case UsbAvailability_Unavailable:
|
||||
CancelUsbSendReceive();
|
||||
break;
|
||||
case UsbAvailability_Available:
|
||||
driver->m_event.Signal();
|
||||
break;
|
||||
case UsbAvailability_Unknown:
|
||||
driver->CancelSendReceive();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result UsbDriver::Open() {
|
||||
/* Clear our event. */
|
||||
m_event.Clear();
|
||||
|
||||
/* Set the availability change callback. */
|
||||
SetUsbAvailabilityChangeCallback(OnUsbAvailabilityChange, this);
|
||||
|
||||
/* Initialize the interface. */
|
||||
R_RETURN(InitializeUsbInterface());
|
||||
}
|
||||
|
||||
void UsbDriver::Close() {
|
||||
/* Finalize the interface. */
|
||||
FinalizeUsbInterface();
|
||||
|
||||
/* Clear the availability callback. */
|
||||
ClearUsbAvailabilityChangeCallback();
|
||||
}
|
||||
|
||||
Result UsbDriver::Connect(os::EventType *event) {
|
||||
/* We must not already be connected. */
|
||||
AMS_ABORT_UNLESS(!m_connected);
|
||||
|
||||
/* Perform a wait on our event. */
|
||||
const int idx = os::WaitAny(m_event.GetBase(), event);
|
||||
R_UNLESS(idx == 0, htclow::ResultCancelled());
|
||||
|
||||
/* Clear our event. */
|
||||
m_event.Clear();
|
||||
|
||||
/* We're connected. */
|
||||
m_connected = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void UsbDriver::Shutdown() {
|
||||
/* If we're connected, cancel anything we're doing. */
|
||||
if (m_connected) {
|
||||
this->CancelSendReceive();
|
||||
m_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result UsbDriver::Send(const void *src, int src_size) {
|
||||
/* Check size. */
|
||||
R_UNLESS(src_size >= 0, htclow::ResultInvalidArgument());
|
||||
|
||||
/* Send until we've sent everything. */
|
||||
for (auto transferred = 0; transferred < src_size; /* ... */) {
|
||||
int cur;
|
||||
R_TRY(SendUsb(std::addressof(cur), reinterpret_cast<const void *>(reinterpret_cast<uintptr_t>(src) + transferred), src_size - transferred));
|
||||
|
||||
transferred += cur;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UsbDriver::Receive(void *dst, int dst_size) {
|
||||
/* Check size. */
|
||||
R_UNLESS(dst_size >= 0, htclow::ResultInvalidArgument());
|
||||
|
||||
/* Send until we've sent everything. */
|
||||
for (auto transferred = 0; transferred < dst_size; /* ... */) {
|
||||
int cur;
|
||||
R_TRY(ReceiveUsb(std::addressof(cur), reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(dst) + transferred), dst_size - transferred));
|
||||
|
||||
transferred += cur;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void UsbDriver::CancelSendReceive() {
|
||||
CancelUsbSendReceive();
|
||||
}
|
||||
|
||||
void UsbDriver::Suspend() {
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void UsbDriver::Resume() {
|
||||
R_ABORT_UNLESS(this->Open());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_i_driver.hpp"
|
||||
#include "htclow_usb_impl.hpp"
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
class UsbDriver final : public IDriver {
|
||||
private:
|
||||
bool m_connected;
|
||||
os::Event m_event;
|
||||
public:
|
||||
UsbDriver() : m_connected(false), m_event(os::EventClearMode_ManualClear) { /* ... */ }
|
||||
public:
|
||||
static void OnUsbAvailabilityChange(UsbAvailability availability, void *param);
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,462 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "htclow_usb_impl.hpp"
|
||||
#include "htclow_driver_memory_management.hpp"
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: Should we identify differently than Nintendo does? */
|
||||
/* It's kind of silly to identify as "NintendoSDK DevKit", but it's also kind of amusing. */
|
||||
/* TBD */
|
||||
|
||||
constinit usb::UsbStringDescriptor LanguageStringDescriptor = { 4, usb::UsbDescriptorType_String, {0x0409}};
|
||||
constinit usb::UsbStringDescriptor ManufacturerStringDescriptor = {18, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o'}};
|
||||
constinit usb::UsbStringDescriptor ProductStringFullSpeedDescriptor = {38, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o', 'S', 'D', 'K', ' ', 'D', 'e', 'v', 'K', 'i', 't'}};
|
||||
constinit usb::UsbStringDescriptor SerialNumberStringDescriptor = { 0, usb::UsbDescriptorType_String, {}};
|
||||
constinit usb::UsbStringDescriptor InterfaceStringDescriptor = {16, usb::UsbDescriptorType_String, {'h', 't', 'c', ' ', 'u', 's', 'b'}};
|
||||
|
||||
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorFullSpeed = {
|
||||
.bLength = usb::UsbDescriptorSize_Device,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.idVendor = 0x057E,
|
||||
.idProduct = 0x3005,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01,
|
||||
};
|
||||
|
||||
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorHighSpeed = {
|
||||
.bLength = usb::UsbDescriptorSize_Device,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.idVendor = 0x057E,
|
||||
.idProduct = 0x3005,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01,
|
||||
};
|
||||
|
||||
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorSuperSpeed = {
|
||||
.bLength = usb::UsbDescriptorSize_Device,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||
.bcdUSB = 0x0300,
|
||||
.bDeviceClass = 0x00,
|
||||
.bDeviceSubClass = 0x00,
|
||||
.bDeviceProtocol = 0x00,
|
||||
.bMaxPacketSize0 = 0x09,
|
||||
.idVendor = 0x057E,
|
||||
.idProduct = 0x3005,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01,
|
||||
};
|
||||
|
||||
constinit u8 BinaryObjectStore[] = {
|
||||
0x05,
|
||||
usb::UsbDescriptorType_Bos,
|
||||
0x16, 0x00,
|
||||
0x02,
|
||||
|
||||
0x07,
|
||||
usb::UsbDescriptorType_DeviceCapability,
|
||||
0x02,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
0x0A,
|
||||
usb::UsbDescriptorType_DeviceCapability,
|
||||
0x03,
|
||||
0x00,
|
||||
0x0E, 0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
constinit usb::UsbInterfaceDescriptor UsbInterfaceDescriptor = {
|
||||
.bLength = usb::UsbDescriptorSize_Interface,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Interface,
|
||||
.bInterfaceNumber = 0x00,
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = 0xFF,
|
||||
.bInterfaceSubClass = 0xFF,
|
||||
.bInterfaceProtocol = 0xFF,
|
||||
.iInterface = 0x04,
|
||||
};
|
||||
|
||||
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsFullSpeed[2] = {
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x81,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x40,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x40,
|
||||
.bInterval = 0x00,
|
||||
}
|
||||
};
|
||||
|
||||
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsHighSpeed[2] = {
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x81,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x200,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x200,
|
||||
.bInterval = 0x00,
|
||||
}
|
||||
};
|
||||
|
||||
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsSuperSpeed[2] = {
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x81,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x400,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
{
|
||||
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||
.bEndpointAddress = 0x01,
|
||||
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||
.wMaxPacketSize = 0x400,
|
||||
.bInterval = 0x00,
|
||||
}
|
||||
};
|
||||
|
||||
constinit usb::UsbEndpointCompanionDescriptor UsbEndpointCompanionDescriptor = {
|
||||
.bLength = usb::UsbDescriptorSize_EndpointCompanion,
|
||||
.bDescriptorType = usb::UsbDescriptorType_EndpointCompanion,
|
||||
.bMaxBurst = 0x0F,
|
||||
.bmAttributes = 0x00,
|
||||
.wBytesPerInterval = 0x0000,
|
||||
};
|
||||
|
||||
constinit void *g_usb_receive_buffer = nullptr;
|
||||
constinit void *g_usb_send_buffer = nullptr;
|
||||
|
||||
constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr;
|
||||
constinit void *g_availability_change_param = nullptr;
|
||||
|
||||
constinit bool g_usb_interface_initialized = false;
|
||||
|
||||
os::Event g_usb_break_event(os::EventClearMode_ManualClear);
|
||||
|
||||
constinit os::ThreadType g_usb_indication_thread = {};
|
||||
|
||||
constinit os::SdkMutex g_usb_driver_mutex;
|
||||
|
||||
usb::DsClient g_ds_client;
|
||||
usb::DsInterface g_ds_interface;
|
||||
usb::DsEndpoint g_ds_endpoints[2];
|
||||
|
||||
void InvokeAvailabilityChangeCallback(UsbAvailability availability) {
|
||||
if (g_availability_change_callback) {
|
||||
g_availability_change_callback(availability, g_availability_change_param);
|
||||
}
|
||||
}
|
||||
|
||||
Result ConvertUsbDriverResult(Result result) {
|
||||
if (result.GetModule() == R_NAMESPACE_MODULE_ID(usb)) {
|
||||
if (usb::ResultResourceBusy::Includes(result)) {
|
||||
R_THROW(htclow::ResultUsbDriverBusyError());
|
||||
} else if (usb::ResultMemAllocFailure::Includes(result)) {
|
||||
R_THROW(htclow::ResultOutOfMemory());
|
||||
} else {
|
||||
R_THROW(htclow::ResultUsbDriverUnknownError());
|
||||
}
|
||||
} else {
|
||||
R_RETURN(result);
|
||||
}
|
||||
}
|
||||
|
||||
Result InitializeDsClient() {
|
||||
/* Initialize the client. */
|
||||
R_TRY(ConvertUsbDriverResult(g_ds_client.Initialize(usb::ComplexId_Tegra21x)));
|
||||
|
||||
/* Clear device data. */
|
||||
R_ABORT_UNLESS(g_ds_client.ClearDeviceData());
|
||||
|
||||
/* Add string descriptors. */
|
||||
u8 index;
|
||||
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(LanguageStringDescriptor)));
|
||||
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ManufacturerStringDescriptor)));
|
||||
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ProductStringFullSpeedDescriptor)));
|
||||
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(SerialNumberStringDescriptor)));
|
||||
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(InterfaceStringDescriptor)));
|
||||
|
||||
/* Add device descriptors. */
|
||||
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorFullSpeed), usb::UsbDeviceSpeed_Full));
|
||||
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorHighSpeed), usb::UsbDeviceSpeed_High));
|
||||
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorSuperSpeed), usb::UsbDeviceSpeed_Super));
|
||||
|
||||
/* Set binary object store. */
|
||||
R_TRY(g_ds_client.SetBinaryObjectStore(BinaryObjectStore, sizeof(BinaryObjectStore)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InitializeDsInterface() {
|
||||
/* Initialize the interface. */
|
||||
R_TRY(ConvertUsbDriverResult(g_ds_interface.Initialize(std::addressof(g_ds_client), 0)));
|
||||
|
||||
/* Append the interface descriptors for all speeds. */
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InitializeDsEndpoints() {
|
||||
R_TRY(g_ds_endpoints[0].Initialize(std::addressof(g_ds_interface), 0x81));
|
||||
R_TRY(g_ds_endpoints[1].Initialize(std::addressof(g_ds_interface), 0x01));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void UsbIndicationThreadFunction(void *) {
|
||||
/* Get the state change event. */
|
||||
os::SystemEventType *state_change_event = g_ds_client.GetStateChangeEvent();
|
||||
|
||||
/* Setup multi wait. */
|
||||
os::MultiWaitType multi_wait;
|
||||
os::InitializeMultiWait(std::addressof(multi_wait));
|
||||
|
||||
/* Link multi wait holders. */
|
||||
os::MultiWaitHolderType state_change_holder;
|
||||
os::MultiWaitHolderType break_holder;
|
||||
os::InitializeMultiWaitHolder(std::addressof(state_change_holder), state_change_event);
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(state_change_holder));
|
||||
os::InitializeMultiWaitHolder(std::addressof(break_holder), g_usb_break_event.GetBase());
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(break_holder));
|
||||
|
||||
/* Loop forever. */
|
||||
while (true) {
|
||||
/* If we should break, do so. */
|
||||
if (os::WaitAny(std::addressof(multi_wait)) == std::addressof(break_holder)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clear the state change event. */
|
||||
os::ClearSystemEvent(state_change_event);
|
||||
|
||||
/* Get the new state. */
|
||||
usb::UsbState usb_state;
|
||||
R_ABORT_UNLESS(g_ds_client.GetState(std::addressof(usb_state)));
|
||||
|
||||
switch (usb_state) {
|
||||
case usb::UsbState_Detached:
|
||||
case usb::UsbState_Suspended:
|
||||
InvokeAvailabilityChangeCallback(UsbAvailability_Unavailable);
|
||||
break;
|
||||
case usb::UsbState_Configured:
|
||||
InvokeAvailabilityChangeCallback(UsbAvailability_Available);
|
||||
break;
|
||||
default:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the break event. */
|
||||
g_usb_break_event.Clear();
|
||||
|
||||
/* Unlink all holders. */
|
||||
os::UnlinkAllMultiWaitHolder(std::addressof(multi_wait));
|
||||
|
||||
/* Finalize the multi wait/holders. */
|
||||
os::FinalizeMultiWaitHolder(std::addressof(break_holder));
|
||||
os::FinalizeMultiWaitHolder(std::addressof(state_change_holder));
|
||||
os::FinalizeMultiWait(std::addressof(multi_wait));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param) {
|
||||
g_availability_change_callback = callback;
|
||||
g_availability_change_param = param;
|
||||
}
|
||||
|
||||
void ClearUsbAvailabilityChangeCallback() {
|
||||
g_availability_change_callback = nullptr;
|
||||
g_availability_change_param = nullptr;
|
||||
}
|
||||
|
||||
Result InitializeUsbInterface() {
|
||||
/* Set the interface as initialized. */
|
||||
g_usb_interface_initialized = true;
|
||||
|
||||
/* Get the dma buffers. */
|
||||
g_usb_receive_buffer = GetUsbReceiveBuffer();
|
||||
g_usb_send_buffer = GetUsbSendBuffer();
|
||||
|
||||
/* If we fail somewhere, finalize. */
|
||||
auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); };
|
||||
|
||||
/* Get the serial number. */
|
||||
{
|
||||
settings::factory::SerialNumber serial_number;
|
||||
serial_number.str[0] = '\x00';
|
||||
|
||||
if (R_FAILED(settings::factory::GetSerialNumber(std::addressof(serial_number))) || serial_number.str[0] == '\x00') {
|
||||
std::strcpy(serial_number.str, "Corrupted S/N");
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
|
||||
|
||||
u16 *dst = SerialNumberStringDescriptor.wData;
|
||||
u8 *src = reinterpret_cast<u8 *>(serial_number.str);
|
||||
u8 count = 0;
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
while (*src) {
|
||||
*dst++ = static_cast<u16>(*src++);
|
||||
++count;
|
||||
}
|
||||
|
||||
SerialNumberStringDescriptor.bLength = 2 + (2 * count);
|
||||
}
|
||||
|
||||
/* Initialize the client. */
|
||||
R_TRY(InitializeDsClient());
|
||||
|
||||
/* Initialize the interface. */
|
||||
R_TRY(InitializeDsInterface());
|
||||
|
||||
/* Initialize the endpoints. */
|
||||
R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints()));
|
||||
|
||||
/* Create the indication thread. */
|
||||
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. */
|
||||
os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication));
|
||||
|
||||
/* Start the indication thread. */
|
||||
os::StartThread(std::addressof(g_usb_indication_thread));
|
||||
|
||||
/* Enable the usb device. */
|
||||
R_TRY(g_ds_client.EnableDevice());
|
||||
|
||||
/* We succeeded! */
|
||||
init_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FinalizeUsbInterface() {
|
||||
g_usb_break_event.Signal();
|
||||
os::WaitThread(std::addressof(g_usb_indication_thread));
|
||||
os::DestroyThread(std::addressof(g_usb_indication_thread));
|
||||
g_ds_client.DisableDevice();
|
||||
g_ds_endpoints[1].Finalize();
|
||||
g_ds_endpoints[0].Finalize();
|
||||
g_ds_interface.Finalize();
|
||||
g_ds_client.Finalize();
|
||||
g_usb_interface_initialized = false;
|
||||
}
|
||||
|
||||
Result SendUsb(int *out_transferred, const void *src, int src_size) {
|
||||
/* Acquire exclusive access to the driver. */
|
||||
std::scoped_lock lk(g_usb_driver_mutex);
|
||||
|
||||
/* Check that we can send the data. */
|
||||
R_UNLESS(src_size <= static_cast<int>(UsbDmaBufferSize), htclow::ResultInvalidArgument());
|
||||
|
||||
/* Copy the data to the dma buffer. */
|
||||
std::memcpy(g_usb_send_buffer, src, src_size);
|
||||
|
||||
/* Transfer data. */
|
||||
u32 transferred;
|
||||
R_UNLESS(R_SUCCEEDED(g_ds_endpoints[0].PostBuffer(std::addressof(transferred), g_usb_send_buffer, src_size)), htclow::ResultUsbDriverSendError());
|
||||
R_UNLESS(transferred == static_cast<u32>(src_size), htclow::ResultUsbDriverSendError());
|
||||
|
||||
/* Set output transferred size. */
|
||||
*out_transferred = src_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ReceiveUsb(int *out_transferred, void *dst, int dst_size) {
|
||||
/* Check that we can send the data. */
|
||||
R_UNLESS(dst_size <= static_cast<int>(UsbDmaBufferSize), htclow::ResultInvalidArgument());
|
||||
|
||||
/* Transfer data. */
|
||||
u32 transferred;
|
||||
R_UNLESS(R_SUCCEEDED(g_ds_endpoints[1].PostBuffer(std::addressof(transferred), g_usb_receive_buffer, dst_size)), htclow::ResultUsbDriverReceiveError());
|
||||
R_UNLESS(transferred == static_cast<u32>(dst_size), htclow::ResultUsbDriverReceiveError());
|
||||
|
||||
/* Copy the data. */
|
||||
std::memcpy(dst, g_usb_receive_buffer, dst_size);
|
||||
|
||||
/* Set output transferred size. */
|
||||
*out_transferred = dst_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void CancelUsbSendReceive() {
|
||||
if (g_usb_interface_initialized) {
|
||||
g_ds_endpoints[0].Cancel();
|
||||
g_ds_endpoints[1].Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::htclow::driver {
|
||||
|
||||
enum UsbAvailability {
|
||||
UsbAvailability_Unavailable = 1,
|
||||
UsbAvailability_Available = 2,
|
||||
UsbAvailability_Unknown = 3,
|
||||
};
|
||||
|
||||
using UsbAvailabilityChangeCallback = void (*)(UsbAvailability availability, void *param);
|
||||
|
||||
void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param);
|
||||
void ClearUsbAvailabilityChangeCallback();
|
||||
|
||||
Result InitializeUsbInterface();
|
||||
void FinalizeUsbInterface();
|
||||
|
||||
Result SendUsb(int *out_transferred, const void *src, int src_size);
|
||||
Result ReceiveUsb(int *out_transferred, void *dst, int dst_size);
|
||||
|
||||
void CancelUsbSendReceive();
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user