diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index bd4b16e..b41159c 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13) -set(sphaira_VERSION 0.9.0) +set(sphaira_VERSION 0.9.1) project(sphaira VERSION ${sphaira_VERSION} diff --git a/sphaira/include/yati/source/usb.hpp b/sphaira/include/yati/source/usb.hpp index 06ce513..820bb76 100644 --- a/sphaira/include/yati/source/usb.hpp +++ b/sphaira/include/yati/source/usb.hpp @@ -19,6 +19,7 @@ struct Usb final : Base { Result_BadCount = MAKERESULT(USBModule, 2), Result_BadTransferSize = MAKERESULT(USBModule, 3), Result_BadTotalSize = MAKERESULT(USBModule, 4), + Result_Cancelled = MAKERESULT(USBModule, 11), }; Usb(u64 transfer_timeout); @@ -28,7 +29,7 @@ struct Usb final : Base { Result Finished(); Result Init(); - Result IsUsbConnected(u64 timeout) const; + Result IsUsbConnected(u64 timeout); Result WaitForConnection(u64 timeout, std::vector& out_names); void SetFileNameForTranfser(const std::string& name); diff --git a/sphaira/source/ui/menus/usb_menu.cpp b/sphaira/source/ui/menus/usb_menu.cpp index ba875ac..142a5a6 100644 --- a/sphaira/source/ui/menus/usb_menu.cpp +++ b/sphaira/source/ui/menus/usb_menu.cpp @@ -22,6 +22,9 @@ void thread_func(void* user) { } const auto rc = app->m_usb_source->IsUsbConnected(CONNECTION_TIMEOUT); + if (rc == app->m_usb_source->Result_Cancelled) { + break; + } // set connected status mutexLock(&app->m_mutex); diff --git a/sphaira/source/yati/source/usb.cpp b/sphaira/source/yati/source/usb.cpp index 5de92e1..6e8516c 100644 --- a/sphaira/source/yati/source/usb.cpp +++ b/sphaira/source/yati/source/usb.cpp @@ -224,8 +224,49 @@ Result Usb::Init() { R_SUCCEED(); } -Result Usb::IsUsbConnected(u64 timeout) const { - return usbDsWaitReady(timeout); +// the blow code is taken from libnx, with the addition of a uevent to cancel. +Result Usb::IsUsbConnected(u64 timeout) { + Result rc; + UsbState state = UsbState_Detached; + + rc = usbDsGetState(&state); + if (R_FAILED(rc)) return rc; + if (state == UsbState_Configured) return 0; + + bool has_timeout = timeout != UINT64_MAX; + u64 deadline = 0; + + const std::array waiters{ + waiterForEvent(usbDsGetStateChangeEvent()), + waiterForUEvent(GetCancelEvent()), + }; + + if (has_timeout) + deadline = armGetSystemTick() + armNsToTicks(timeout); + + do { + if (has_timeout) { + s64 remaining = deadline - armGetSystemTick(); + timeout = remaining > 0 ? armTicksToNs(remaining) : 0; + } + + s32 idx; + rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout); + eventClear(usbDsGetStateChangeEvent()); + + // check if we got one of the cancel events. + if (R_SUCCEEDED(rc) && idx != 0) { + rc = Result_Cancelled; // cancelled. + break; + } + + rc = usbDsGetState(&state); + } while (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout > 0); + + if (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout == 0) + return KERNELRESULT(TimedOut); + + return rc; } Result Usb::WaitForConnection(u64 timeout, std::vector& out_names) { @@ -276,7 +317,7 @@ Result Usb::WaitTransferCompletion(UsbSessionEndpoint ep, u64 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. + rc = Result_Cancelled; // cancelled. } if (R_FAILED(rc)) {