diff --git a/sphaira/include/yati/source/base.hpp b/sphaira/include/yati/source/base.hpp index 612ca84..dff7286 100644 --- a/sphaira/include/yati/source/base.hpp +++ b/sphaira/include/yati/source/base.hpp @@ -14,6 +14,10 @@ struct Base { return false; } + virtual void SignalCancel() { + + } + Result GetOpenResult() const { return m_open_result; } diff --git a/sphaira/include/yati/source/usb.hpp b/sphaira/include/yati/source/usb.hpp index 200b0d1..06ce513 100644 --- a/sphaira/include/yati/source/usb.hpp +++ b/sphaira/include/yati/source/usb.hpp @@ -32,6 +32,14 @@ struct Usb final : Base { Result WaitForConnection(u64 timeout, std::vector& out_names); void SetFileNameForTranfser(const std::string& name); + auto GetCancelEvent() { + return &m_uevent; + } + + void SignalCancel() override { + ueventSignal(GetCancelEvent()); + } + public: // custom allocator for std::vector that respects alignment. // https://en.cppreference.com/w/cpp/named_req/Allocator @@ -69,16 +77,18 @@ private: Result SendFileRangeCmd(u64 offset, u64 size); Event *GetCompletionEvent(UsbSessionEndpoint ep) const; - Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) const; + Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout); Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id) const; Result GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_requested_size, u32 *out_transferred_size) const; - Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout) const; + Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout); Result TransferAll(bool read, void *data, u32 size, u64 timeout); private: UsbDsInterface* m_interface{}; UsbDsEndpoint* m_endpoints[2]{}; u64 m_transfer_timeout{}; + UEvent m_uevent{}; + // std::vector m_cancel_events{}; // aligned buffer that transfer data is copied to and from. // a vector is used to avoid multiple alloc within the transfer loop. PageAlignedVector m_aligned{}; diff --git a/sphaira/source/ui/menus/usb_menu.cpp b/sphaira/source/ui/menus/usb_menu.cpp index f1732d7..ba875ac 100644 --- a/sphaira/source/ui/menus/usb_menu.cpp +++ b/sphaira/source/ui/menus/usb_menu.cpp @@ -10,8 +10,8 @@ namespace sphaira::ui::menu::usb { namespace { -constexpr u64 CONNECTION_TIMEOUT = 1e+9 * 1; // 1 second -constexpr u64 TRANSFER_TIMEOUT = 1e+9 * 5; // 5 seconds +constexpr u64 CONNECTION_TIMEOUT = UINT64_MAX; +constexpr u64 TRANSFER_TIMEOUT = UINT64_MAX; void thread_func(void* user) { auto app = static_cast(user); @@ -76,6 +76,7 @@ Menu::Menu() : MenuBase{"USB"_i18n} { } mutexInit(&m_mutex); + if (m_state != State::Failed) { threadCreate(&m_thread, thread_func, this, nullptr, 1024*32, 0x2C, 1); threadStart(&m_thread); @@ -85,6 +86,7 @@ Menu::Menu() : MenuBase{"USB"_i18n} { Menu::~Menu() { // signal for thread to exit and wait. m_stop_source.request_stop(); + m_usb_source->SignalCancel(); threadWaitForExit(&m_thread); threadClose(&m_thread); @@ -117,6 +119,8 @@ void Menu::Update(Controller* controller, TouchInfo* touch) { const auto rc = yati::InstallFromSource(pbox, m_usb_source, file_name); if (R_FAILED(rc)) { + m_usb_source->SignalCancel(); + log_write("exiting usb install\n"); return false; } diff --git a/sphaira/source/yati/source/usb.cpp b/sphaira/source/yati/source/usb.cpp index ccfcd0f..5de92e1 100644 --- a/sphaira/source/yati/source/usb.cpp +++ b/sphaira/source/yati/source/usb.cpp @@ -64,6 +64,7 @@ static_assert(sizeof(USBCmdHeader) == 0x20, "USBCmdHeader must be 0x20!"); Usb::Usb(u64 transfer_timeout) { m_open_result = usbDsInitialize(); m_transfer_timeout = transfer_timeout; + ueventCreate(GetCancelEvent(), true); // this avoids allocations during transfers. m_aligned.reserve(1024 * 1024 * 16); } @@ -261,9 +262,22 @@ Event *Usb::GetCompletionEvent(UsbSessionEndpoint ep) const { return std::addressof(m_endpoints[ep]->CompletionEvent); } -Result Usb::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) const { +Result Usb::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) { auto event = GetCompletionEvent(ep); - const auto rc = eventWait(event, timeout); + + const std::array waiters{ + waiterForEvent(event), + waiterForUEvent(GetCancelEvent()), + }; + + s32 idx; + auto rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout); + + // check if we got one of the cancel events. + if (R_SUCCEEDED(rc) && idx != 0) { + log_write("got usb cancel event\n"); + rc = 0xEC01; // cancelled. + } if (R_FAILED(rc)) { R_TRY(usbDsEndpoint_Cancel(m_endpoints[ep])); @@ -287,7 +301,7 @@ Result Usb::GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_reques R_SUCCEED(); } -Result Usb::TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout) const { +Result Usb::TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout) { u32 urb_id; /* If we're not configured yet, wait to become configured first. */