From c59388caf1a20a657e853e9c92c235e031cfb31f Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Feb 2021 03:37:30 -0800 Subject: [PATCH] htc: implement complete usb driver --- .../stratosphere/os/os_waitable_utils.hpp | 81 +++ .../htclow/ctrl/htclow_ctrl_service.cpp | 7 + .../htclow/ctrl/htclow_ctrl_service.hpp | 2 + .../htclow/driver/htclow_driver_manager.cpp | 63 +++ .../htclow/driver/htclow_driver_manager.hpp | 8 +- .../htclow/driver/htclow_usb_driver.cpp | 125 +++++ .../htclow/driver/htclow_usb_driver.hpp | 43 ++ .../source/htclow/driver/htclow_usb_impl.cpp | 460 ++++++++++++++++++ .../source/htclow/driver/htclow_usb_impl.hpp | 41 ++ .../source/htclow/htclow_listener.cpp | 37 +- .../source/htclow/htclow_listener.hpp | 8 + .../source/htclow/htclow_manager_impl.cpp | 21 +- .../source/htclow/mux/htclow_mux.hpp | 2 +- .../libvapours/include/vapours/results.hpp | 1 + .../vapours/results/htclow_results.hpp | 39 ++ 15 files changed, 933 insertions(+), 5 deletions(-) create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp create mode 100644 libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp create mode 100644 libraries/libvapours/include/vapours/results/htclow_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp index 7119d4ec7..5a25dd429 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/os/os_waitable_utils.hpp @@ -16,7 +16,88 @@ #pragma once #include #include +#include +#include namespace ams::os { + namespace impl { + + class AutoWaitableHolder { + private: + WaitableHolderType m_holder; + public: + template + ALWAYS_INLINE explicit AutoWaitableHolder(WaitableManagerType *manager, T &&arg) { + InitializeWaitableHolder(std::addressof(m_holder), std::forward(arg)); + LinkWaitableHolder(manager, std::addressof(m_holder)); + } + + ALWAYS_INLINE ~AutoWaitableHolder() { + UnlinkWaitableHolder(std::addressof(m_holder)); + FinalizeWaitableHolder(std::addressof(m_holder)); + } + + ALWAYS_INLINE std::pair ConvertResult(const std::pair result, int index) { + if (result.first == std::addressof(m_holder)) { + return std::make_pair(static_cast(nullptr), index); + } else { + return result; + } + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int) { + return std::pair(func(manager), -1); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, int index, T &&x, Args &&... args) { + AutoWaitableHolder holder(manager, std::forward(x)); + return holder.ConvertResult(WaitAnyImpl(std::forward(func), manager, index + 1, std::forward(args)...), index); + } + + template + inline std::pair WaitAnyImpl(F &&func, WaitableManagerType *manager, Args &&... args) { + return WaitAnyImpl(std::forward(func), manager, 0, std::forward(args)...); + } + + class TempWaitableManager { + private: + WaitableManagerType m_manager; + public: + ALWAYS_INLINE TempWaitableManager() { + os::InitializeWaitableManager(std::addressof(m_manager)); + } + + ALWAYS_INLINE ~TempWaitableManager() { + os::FinalizeWaitableManager(std::addressof(m_manager)); + } + + WaitableManagerType *Get() { + return std::addressof(m_manager); + } + }; + + template + inline std::pair WaitAnyImpl(F &&func, Args &&... args) { + TempWaitableManager temp_manager; + return WaitAnyImpl(std::forward(func), temp_manager.Get(), 0, std::forward(args)...); + } + + using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *); + + } + + template requires (sizeof...(Args) > 0) + inline std::pair WaitAny(WaitableManagerType *manager, Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), manager, std::forward(args)...); + } + + template requires (sizeof...(Args) > 0) + inline int WaitAny(Args &&... args) { + return impl::WaitAnyImpl(static_cast(&::ams::os::WaitAny), std::forward(args)...).second; + } + } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp index df719e155..546f9fb1b 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.cpp @@ -73,5 +73,12 @@ namespace ams::htclow::ctrl { ); } + void HtcctrlService::SetDriverType(impl::DriverType driver_type) { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + /* Update our beacon response. */ + this->UpdateBeaconResponse(this->GetConnectionType(driver_type)); + } } diff --git a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp index 39b0a0274..c6d208325 100644 --- a/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp +++ b/libraries/libstratosphere/source/htclow/ctrl/htclow_ctrl_service.hpp @@ -57,6 +57,8 @@ namespace ams::htclow::ctrl { void UpdateBeaconResponse(const char *connection); public: HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux); + + void SetDriverType(impl::DriverType driver_type); }; } diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp new file mode 100644 index 000000000..ea16c765d --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.cpp @@ -0,0 +1,63 @@ +/* + * 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 . + */ +#include +#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; + return htclow::ResultUnknownDriverType(); + case impl::DriverType::Usb: + //m_usb_driver.Open(); + //m_open_driver = std::addressof(m_usb_driver); + //break; + return htclow::ResultUnknownDriverType(); + case impl::DriverType::PlainChannel: + //m_plain_channel_driver.Open(); + //m_open_driver = std::addressof(m_plain_channel_driver); + //break; + return htclow::ResultUnknownDriverType(); + default: + return htclow::ResultUnknownDriverType(); + } + + return ResultSuccess(); + } + + IDriver *DriverManager::GetCurrentDriver() { + /* Lock ourselves. */ + std::scoped_lock lk(m_mutex); + + return m_open_driver; + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp index 5eb99553e..6a2e80aff 100644 --- a/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp +++ b/libraries/libstratosphere/source/htclow/driver/htclow_driver_manager.hpp @@ -16,19 +16,25 @@ #pragma once #include #include "htclow_i_driver.hpp" +#include "htclow_usb_driver.hpp" namespace ams::htclow::driver { class DriverManager { private: std::optional m_driver_type{}; + IDriver *m_debug_driver; /* TODO: SocketDriver m_socket_driver; */ - /* TODO: UsbDriver m_usb_driver; */ + UsbDriver m_usb_driver{}; /* TODO: PlainChannelDriver m_plain_channel_driver; */ os::SdkMutex m_mutex{}; IDriver *m_open_driver{}; public: DriverManager() = default; + + Result OpenDriver(impl::DriverType driver_type); + + IDriver *GetCurrentDriver(); }; } diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp new file mode 100644 index 000000000..d52c07ea0 --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.cpp @@ -0,0 +1,125 @@ +/* + * 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 . + */ +#include +#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(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. */ + 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; + return ResultSuccess(); + } + + 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(reinterpret_cast(src) + transferred), src_size - transferred)); + + transferred += cur; + } + + return ResultSuccess(); + } + + 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(SendUsb(std::addressof(cur), reinterpret_cast(reinterpret_cast(dst) + transferred), dst_size - transferred)); + + transferred += cur; + } + + return ResultSuccess(); + } + + void UsbDriver::CancelSendReceive() { + CancelUsbSendReceive(); + } + + void UsbDriver::Suspend() { + this->Close(); + } + + void UsbDriver::Resume() { + R_ABORT_UNLESS(this->Open()); + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp new file mode 100644 index 000000000..ba4be55ab --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_driver.hpp @@ -0,0 +1,43 @@ +/* + * 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 . + */ +#pragma once +#include +#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; + }; + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp new file mode 100644 index 000000000..76b55f1ce --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.cpp @@ -0,0 +1,460 @@ +/* + * 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 . + */ +#include +#include "htclow_usb_impl.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, + }; + + constexpr size_t UsbDmaBufferSize = 0x60000; + + 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 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)) { + return htclow::ResultUsbDriverBusyError(); + } else if (usb::ResultMemAllocFailure::Includes(result)) { + return htclow::ResultOutOfMemory(); + } else { + return htclow::ResultUsbDriverUnknownError(); + } + } else { + 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))); + + return ResultSuccess(); + } + + 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))); + + return ResultSuccess(); + } + + 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)); + return ResultSuccess(); + } + + void UsbIndicationThreadFunction(void *arg) { + /* Get the state change event. */ + os::SystemEventType *state_change_event = g_ds_client.GetStateChangeEvent(); + + /* Setup waitable manager. */ + os::WaitableManagerType manager; + os::InitializeWaitableManager(std::addressof(manager)); + + /* Link waitable holders. */ + os::WaitableHolderType state_change_holder; + os::WaitableHolderType break_holder; + os::InitializeWaitableHolder(std::addressof(state_change_holder), state_change_event); + os::LinkWaitableHolder(std::addressof(manager), std::addressof(state_change_holder)); + os::InitializeWaitableHolder(std::addressof(break_holder), g_usb_break_event.GetBase()); + os::LinkWaitableHolder(std::addressof(manager), std::addressof(break_holder)); + + /* Loop forever. */ + while (true) { + /* If we should break, do so. */ + if (os::WaitAny(std::addressof(manager)) == 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 UsbState_Detached: + case UsbState_Suspended: + InvokeAvailabilityChangeCallback(UsbAvailability_Unavailable); + break; + case UsbState_Configured: + InvokeAvailabilityChangeCallback(UsbAvailability_Available); + break; + default: + /* Nothing to do. */ + break; + } + } + + /* Clear the break event. */ + g_usb_break_event.Clear(); + + /* Unlink all holders. */ + os::UnlinkAllWaitableHolder(std::addressof(manager)); + + /* Finalize the waitable holders and manager. */ + os::FinalizeWaitableHolder(std::addressof(break_holder)); + os::FinalizeWaitableHolder(std::addressof(state_change_holder)); + os::FinalizeWaitableManager(std::addressof(manager)); + } + + } + + 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; + + /* 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(serial_number.str); + u8 count = 0; + +#pragma GCC diagnostic pop + + while (*src) { + *dst++ = static_cast(*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, g_usb_indication_thread_stack, sizeof(g_usb_indication_thread_stack), 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(); + return ResultSuccess(); + } + + 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(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(src_size), htclow::ResultUsbDriverSendError()); + + /* Set output transferred size. */ + *out_transferred = src_size; + return ResultSuccess(); + } + + Result ReceiveUsb(int *out_transferred, void *dst, int dst_size) { + /* Check that we can send the data. */ + R_UNLESS(dst_size <= static_cast(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(dst_size), htclow::ResultUsbDriverReceiveError()); + + /* Copy the data. */ + std::memcpy(dst, g_usb_receive_buffer, dst_size); + + /* Set output transferred size. */ + *out_transferred = dst_size; + return ResultSuccess(); + } + + void CancelUsbSendReceive() { + if (g_usb_interface_initialized) { + g_ds_endpoints[0].Cancel(); + g_ds_endpoints[1].Cancel(); + } + } + +} diff --git a/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp new file mode 100644 index 000000000..507dffeff --- /dev/null +++ b/libraries/libstratosphere/source/htclow/driver/htclow_usb_impl.hpp @@ -0,0 +1,41 @@ +/* + * 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 . + */ +#pragma once +#include + +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(); + + +} diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.cpp b/libraries/libstratosphere/source/htclow/htclow_listener.cpp index cb2a2d7bc..ac2d0a9b4 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.cpp @@ -18,11 +18,46 @@ namespace ams::htclow { + namespace { + + constexpr inline size_t ThreadStackSize = 4_KB; + + } + Listener::Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker) - : m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false) + : m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false) { /* Allocate stack. */ m_listen_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment); } + void Listener::Start(driver::IDriver *driver) { + /* Check pre-conditions. */ + AMS_ASSERT(!m_thread_running); + + /* Create the thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(m_listen_thread), ListenThreadEntry, this, m_listen_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowListen))); + + /* Set the thread name. */ + os::SetThreadNamePointer(std::addressof(m_listen_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowListen)); + + /* Set our driver. */ + m_driver = driver; + + /* Set state. */ + m_thread_running = true; + m_cancelled = false; + + /* Clear our event. */ + m_event.Clear(); + + /* Start the thread. */ + os::StartThread(std::addressof(m_listen_thread)); + } + + void Listener::ListenThread() { + /* TODO */ + AMS_ABORT("Listener::ListenThread"); + } + } diff --git a/libraries/libstratosphere/source/htclow/htclow_listener.hpp b/libraries/libstratosphere/source/htclow/htclow_listener.hpp index 0567a176f..17e4aa698 100644 --- a/libraries/libstratosphere/source/htclow/htclow_listener.hpp +++ b/libraries/libstratosphere/source/htclow/htclow_listener.hpp @@ -32,8 +32,16 @@ namespace ams::htclow { driver::IDriver *m_driver; bool m_thread_running; bool m_cancelled; + private: + static void ListenThreadEntry(void *arg) { + static_cast(arg)->ListenThread(); + } + + void ListenThread(); public: Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker); + + void Start(driver::IDriver *driver); }; } diff --git a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp index e7154f25c..865c82dcc 100644 --- a/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp +++ b/libraries/libstratosphere/source/htclow/htclow_manager_impl.cpp @@ -22,7 +22,8 @@ namespace ams::htclow { : m_packet_factory(allocator), m_driver_manager(), 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_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)), + m_is_driver_open(false) { /* ... */ } @@ -32,7 +33,23 @@ namespace ams::htclow { } Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type) { - AMS_ABORT("TODO"); + /* Set the driver type. */ + m_ctrl_service.SetDriverType(driver_type); + + /* Ensure that we don't end up in an invalid state. */ + auto drv_guard = SCOPE_GUARD { m_ctrl_service.SetDriverType(impl::DriverType::Unknown); }; + + /* Try to open the driver. */ + R_TRY(m_driver_manager.OpenDriver(driver_type)); + + /* Start the listener. */ + m_listener.Start(m_driver_manager.GetCurrentDriver()); + + /* Note the driver as open. */ + m_is_driver_open = true; + + drv_guard.Cancel(); + return ResultSuccess(); } //void HtclowManagerImpl::CloseDriver(); diff --git a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp index af8c27ae5..1afb8c2c9 100644 --- a/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp +++ b/libraries/libstratosphere/source/htclow/mux/htclow_mux.hpp @@ -31,7 +31,7 @@ namespace ams::htclow::mux { GlobalSendBuffer m_global_send_buffer; os::SdkMutex m_mutex; bool m_is_sleeping; - u16 m_version; + s16 m_version; public: Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm); diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 0781b5d9e..95c7f83e4 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/htclow_results.hpp b/libraries/libvapours/include/vapours/results/htclow_results.hpp new file mode 100644 index 000000000..e78bf0b2b --- /dev/null +++ b/libraries/libvapours/include/vapours/results/htclow_results.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::htclow { + + R_DEFINE_NAMESPACE_RESULT_MODULE(29); + + R_DEFINE_ERROR_RESULT(UnknownDriverType, 3); + + R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999); + R_DEFINE_ERROR_RESULT(OutOfMemory, 1002); + R_DEFINE_ERROR_RESULT(InvalidArgument, 1003); + R_DEFINE_ERROR_RESULT(Cancelled, 1005); + + R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999); + R_DEFINE_ERROR_RESULT(DriverOpened, 1201); + + R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499); + R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401); + R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402); + R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403); + R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404); + +}