handle more usb errors, set max for notifications, usb transfer uses unique ptr over vector, show usb speed in usb menu.
This commit is contained in:
@@ -595,7 +595,10 @@ enum class SphairaResult : Result {
|
|||||||
UsbBadMagic,
|
UsbBadMagic,
|
||||||
UsbBadVersion,
|
UsbBadVersion,
|
||||||
UsbBadCount,
|
UsbBadCount,
|
||||||
|
UsbBadBufferAlign,
|
||||||
UsbBadTransferSize,
|
UsbBadTransferSize,
|
||||||
|
UsbEmptyTransferSize,
|
||||||
|
UsbOverflowTransferSize,
|
||||||
UsbBadTotalSize,
|
UsbBadTotalSize,
|
||||||
|
|
||||||
UsbUploadBadMagic,
|
UsbUploadBadMagic,
|
||||||
@@ -726,8 +729,10 @@ enum : Result {
|
|||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadVersion),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbBadVersion),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadCount),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbBadCount),
|
||||||
|
MAKE_SPHAIRA_RESULT_ENUM(UsbBadBufferAlign),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadTransferSize),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbBadTransferSize),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadTotalSize),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbEmptyTransferSize),
|
||||||
|
MAKE_SPHAIRA_RESULT_ENUM(UsbOverflowTransferSize),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadMagic),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadMagic),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadExit),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadExit),
|
||||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCount),
|
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCount),
|
||||||
|
|||||||
@@ -27,19 +27,16 @@ struct Menu final : MenuBase {
|
|||||||
auto GetShortTitle() const -> const char* override { return "USB"; };
|
auto GetShortTitle() const -> const char* override { return "USB"; };
|
||||||
void Update(Controller* controller, TouchInfo* touch) override;
|
void Update(Controller* controller, TouchInfo* touch) override;
|
||||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||||
void OnFocusGained() override;
|
|
||||||
|
|
||||||
// this should be private
|
void ThreadFunction();
|
||||||
// private:
|
|
||||||
|
private:
|
||||||
std::unique_ptr<yati::source::Usb> m_usb_source{};
|
std::unique_ptr<yati::source::Usb> m_usb_source{};
|
||||||
bool m_was_mtp_enabled{};
|
bool m_was_mtp_enabled{};
|
||||||
|
|
||||||
Thread m_thread{};
|
Thread m_thread{};
|
||||||
Mutex m_mutex{};
|
std::atomic<State> m_state{State::None};
|
||||||
// the below are shared across threads, lock with the above mutex!
|
|
||||||
State m_state{State::None};
|
|
||||||
std::vector<std::string> m_names{};
|
std::vector<std::string> m_names{};
|
||||||
bool m_usb_has_connection{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sphaira::ui::menu::usb
|
} // namespace sphaira::ui::menu::usb
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void Draw(NVGcontext* vg, Theme* theme, Entries& entries);
|
void Draw(NVGcontext* vg, Theme* theme, Entries& entries);
|
||||||
|
auto GetEntries(NotifEntry::Side side) -> Entries&;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Entries m_entries_left{};
|
Entries m_entries_left{};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <new>
|
#include <memory>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
namespace sphaira::usb {
|
namespace sphaira::usb {
|
||||||
@@ -39,43 +39,10 @@ struct Base {
|
|||||||
ueventSignal(GetCancelEvent());
|
ueventSignal(GetCancelEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& GetTransferBuffer() {
|
|
||||||
return m_aligned;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto GetTransferTimeout() const {
|
auto GetTransferTimeout() const {
|
||||||
return m_transfer_timeout;
|
return m_transfer_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
// custom allocator for std::vector that respects alignment.
|
|
||||||
// https://en.cppreference.com/w/cpp/named_req/Allocator
|
|
||||||
template <typename T, std::size_t Align>
|
|
||||||
struct CustomVectorAllocator {
|
|
||||||
public:
|
|
||||||
// https://en.cppreference.com/w/cpp/memory/new/operator_new
|
|
||||||
auto allocate(std::size_t n) -> T* {
|
|
||||||
n = (n + (Align - 1)) &~ (Align - 1);
|
|
||||||
return new(align) T[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.cppreference.com/w/cpp/memory/new/operator_delete
|
|
||||||
auto deallocate(T* p, std::size_t n) noexcept -> void {
|
|
||||||
// ::operator delete[] (p, n, align);
|
|
||||||
::operator delete[] (p, align);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr inline std::align_val_t align{Align};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct PageAllocator : CustomVectorAllocator<T, 0x1000> {
|
|
||||||
using value_type = T; // used by std::vector
|
|
||||||
};
|
|
||||||
|
|
||||||
using PageAlignedVector = std::vector<u8, PageAllocator<u8>>;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum UsbSessionEndpoint {
|
enum UsbSessionEndpoint {
|
||||||
UsbSessionEndpoint_In = 0,
|
UsbSessionEndpoint_In = 0,
|
||||||
@@ -90,7 +57,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
u64 m_transfer_timeout{};
|
u64 m_transfer_timeout{};
|
||||||
UEvent m_uevent{};
|
UEvent m_uevent{};
|
||||||
PageAlignedVector m_aligned{};
|
std::unique_ptr<u8*> m_aligned{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sphaira::usb
|
} // namespace sphaira::usb
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<usb::UsbHs> m_usb;
|
std::unique_ptr<usb::UsbHs> m_usb;
|
||||||
|
std::vector<u8> m_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sphaira::usb::upload
|
} // namespace sphaira::usb::upload
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ enum { UsbDeviceSpeed_None = 0x0 };
|
|||||||
enum { UsbDeviceSpeed_Low = 0x1 };
|
enum { UsbDeviceSpeed_Low = 0x1 };
|
||||||
Result usbDsGetSpeed(UsbDeviceSpeed *out);
|
Result usbDsGetSpeed(UsbDeviceSpeed *out);
|
||||||
|
|
||||||
|
auto GetUsbDsStateStr(UsbState state) -> const char*;
|
||||||
|
auto GetUsbDsSpeedStr(UsbDeviceSpeed speed) -> const char*;
|
||||||
|
|
||||||
namespace sphaira::usb {
|
namespace sphaira::usb {
|
||||||
|
|
||||||
// Device Host
|
// Device Host
|
||||||
|
|||||||
@@ -114,8 +114,10 @@ auto GetCodeMessage(Result rc) -> const char* {
|
|||||||
case Result_UsbBadMagic: return "SphairaError_UsbBadMagic";
|
case Result_UsbBadMagic: return "SphairaError_UsbBadMagic";
|
||||||
case Result_UsbBadVersion: return "SphairaError_UsbBadVersion";
|
case Result_UsbBadVersion: return "SphairaError_UsbBadVersion";
|
||||||
case Result_UsbBadCount: return "SphairaError_UsbBadCount";
|
case Result_UsbBadCount: return "SphairaError_UsbBadCount";
|
||||||
|
case Result_UsbBadBufferAlign: return "SphairaError_UsbBadBufferAlign";
|
||||||
case Result_UsbBadTransferSize: return "SphairaError_UsbBadTransferSize";
|
case Result_UsbBadTransferSize: return "SphairaError_UsbBadTransferSize";
|
||||||
case Result_UsbBadTotalSize: return "SphairaError_UsbBadTotalSize";
|
case Result_UsbEmptyTransferSize: return "SphairaError_UsbEmptyTransferSize";
|
||||||
|
case Result_UsbOverflowTransferSize: return "SphairaError_UsbOverflowTransferSize";
|
||||||
case Result_UsbUploadBadMagic: return "SphairaError_UsbUploadBadMagic";
|
case Result_UsbUploadBadMagic: return "SphairaError_UsbUploadBadMagic";
|
||||||
case Result_UsbUploadExit: return "SphairaError_UsbUploadExit";
|
case Result_UsbUploadExit: return "SphairaError_UsbUploadExit";
|
||||||
case Result_UsbUploadBadCount: return "SphairaError_UsbUploadBadCount";
|
case Result_UsbUploadBadCount: return "SphairaError_UsbUploadBadCount";
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) {
|
|||||||
while (!m_token.stop_requested()) {
|
while (!m_token.stop_requested()) {
|
||||||
SCOPED_MUTEX(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
if (m_active && m_buffer.empty()) {
|
if (m_active && m_buffer.empty()) {
|
||||||
condvarWait(&m_can_read, &m_mutex);
|
R_TRY(condvarWait(&m_can_read, &m_mutex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!m_active && m_buffer.empty()) || m_token.stop_requested()) {
|
if ((!m_active && m_buffer.empty()) || m_token.stop_requested()) {
|
||||||
|
|||||||
@@ -8,36 +8,6 @@
|
|||||||
#include "haze_helper.hpp"
|
#include "haze_helper.hpp"
|
||||||
|
|
||||||
namespace sphaira::ui::menu::mtp {
|
namespace sphaira::ui::menu::mtp {
|
||||||
namespace {
|
|
||||||
|
|
||||||
auto GetUsbStateStr(UsbState state) -> const char* {
|
|
||||||
switch (state) {
|
|
||||||
case UsbState_Detached: return "Detached";
|
|
||||||
case UsbState_Attached: return "Attached";
|
|
||||||
case UsbState_Powered: return "Powered";
|
|
||||||
case UsbState_Default: return "Default";
|
|
||||||
case UsbState_Address: return "Address";
|
|
||||||
case UsbState_Configured: return "Configured";
|
|
||||||
case UsbState_Suspended: return "Suspended";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto GetUsbSpeedStr(UsbDeviceSpeed speed) -> const char* {
|
|
||||||
// todo: remove this cast when libnx pr is merged.
|
|
||||||
switch ((u32)speed) {
|
|
||||||
case UsbDeviceSpeed_None: return "None";
|
|
||||||
case UsbDeviceSpeed_Low: return "USB 1.0 Low Speed";
|
|
||||||
case UsbDeviceSpeed_Full: return "USB 1.1 Full Speed";
|
|
||||||
case UsbDeviceSpeed_High: return "USB 2.0 High Speed";
|
|
||||||
case UsbDeviceSpeed_Super: return "USB 3.0 Super Speed";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Menu::Menu(u32 flags) : stream::Menu{"MTP Install"_i18n, flags} {
|
Menu::Menu(u32 flags) : stream::Menu{"MTP Install"_i18n, flags} {
|
||||||
m_was_mtp_enabled = App::GetMtpEnable();
|
m_was_mtp_enabled = App::GetMtpEnable();
|
||||||
@@ -77,7 +47,7 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
|||||||
usbDsGetSpeed(&speed);
|
usbDsGetSpeed(&speed);
|
||||||
|
|
||||||
char buf[128];
|
char buf[128];
|
||||||
std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbStateStr(state)).c_str(), i18n::get(GetUsbSpeedStr(speed)).c_str());
|
std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbDsStateStr(state)).c_str(), i18n::get(GetUsbDsSpeedStr(speed)).c_str());
|
||||||
SetSubHeading(buf);
|
SetSubHeading(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,37 +16,7 @@ constexpr u64 FINISHED_TIMEOUT = 1e+9 * 3; // 3 seconds.
|
|||||||
|
|
||||||
void thread_func(void* user) {
|
void thread_func(void* user) {
|
||||||
auto app = static_cast<Menu*>(user);
|
auto app = static_cast<Menu*>(user);
|
||||||
|
app->ThreadFunction();
|
||||||
for (;;) {
|
|
||||||
if (app->GetToken().stop_requested()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto rc = app->m_usb_source->IsUsbConnected(CONNECTION_TIMEOUT);
|
|
||||||
if (rc == Result_UsbCancelled) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set connected status
|
|
||||||
mutexLock(&app->m_mutex);
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
app->m_state = State::Connected_WaitForFileList;
|
|
||||||
} else {
|
|
||||||
app->m_state = State::None;
|
|
||||||
}
|
|
||||||
mutexUnlock(&app->m_mutex);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
std::vector<std::string> names;
|
|
||||||
if (R_SUCCEEDED(app->m_usb_source->WaitForConnection(CONNECTION_TIMEOUT, names))) {
|
|
||||||
mutexLock(&app->m_mutex);
|
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&app->m_mutex));
|
|
||||||
app->m_state = State::Connected_StartingTransfer;
|
|
||||||
app->m_names = names;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -74,8 +44,6 @@ Menu::Menu(u32 flags) : MenuBase{"USB"_i18n, flags} {
|
|||||||
m_state = State::Failed;
|
m_state = State::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutexInit(&m_mutex);
|
|
||||||
|
|
||||||
if (m_state != State::Failed) {
|
if (m_state != State::Failed) {
|
||||||
threadCreate(&m_thread, thread_func, this, nullptr, 1024*32, PRIO_PREEMPTIVE, 1);
|
threadCreate(&m_thread, thread_func, this, nullptr, 1024*32, PRIO_PREEMPTIVE, 1);
|
||||||
threadStart(&m_thread);
|
threadStart(&m_thread);
|
||||||
@@ -102,8 +70,20 @@ Menu::~Menu() {
|
|||||||
void Menu::Update(Controller* controller, TouchInfo* touch) {
|
void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||||
MenuBase::Update(controller, touch);
|
MenuBase::Update(controller, touch);
|
||||||
|
|
||||||
mutexLock(&m_mutex);
|
static TimeStamp poll_ts;
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
if (poll_ts.GetSeconds() >= 1) {
|
||||||
|
poll_ts.Update();
|
||||||
|
|
||||||
|
UsbState state{UsbState_Detached};
|
||||||
|
usbDsGetState(&state);
|
||||||
|
|
||||||
|
UsbDeviceSpeed speed{(UsbDeviceSpeed)UsbDeviceSpeed_None};
|
||||||
|
usbDsGetSpeed(&speed);
|
||||||
|
|
||||||
|
char buf[128];
|
||||||
|
std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbDsStateStr(state)).c_str(), i18n::get(GetUsbDsSpeedStr(speed)).c_str());
|
||||||
|
SetSubHeading(buf);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_state == State::Connected_StartingTransfer) {
|
if (m_state == State::Connected_StartingTransfer) {
|
||||||
log_write("set to progress\n");
|
log_write("set to progress\n");
|
||||||
@@ -152,9 +132,6 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
|||||||
void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||||
MenuBase::Draw(vg, theme);
|
MenuBase::Draw(vg, theme);
|
||||||
|
|
||||||
mutexLock(&m_mutex);
|
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
|
||||||
|
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case State::None:
|
case State::None:
|
||||||
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_INFO), "Waiting for connection..."_i18n.c_str());
|
gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_INFO), "Waiting for connection..."_i18n.c_str());
|
||||||
@@ -182,8 +159,33 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::OnFocusGained() {
|
void Menu::ThreadFunction() {
|
||||||
MenuBase::OnFocusGained();
|
for (;;) {
|
||||||
|
if (GetToken().stop_requested()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto rc = m_usb_source->IsUsbConnected(CONNECTION_TIMEOUT);
|
||||||
|
if (rc == Result_UsbCancelled) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set connected status
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
m_state = State::Connected_WaitForFileList;
|
||||||
|
} else {
|
||||||
|
m_state = State::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
if (R_SUCCEEDED(m_usb_source->WaitForConnection(CONNECTION_TIMEOUT, names))) {
|
||||||
|
m_names = names;
|
||||||
|
m_state = State::Connected_StartingTransfer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sphaira::ui::menu::usb
|
} // namespace sphaira::ui::menu::usb
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace sphaira::ui {
|
namespace sphaira::ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr u64 MAX_ENTRIES = 9;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
NotifEntry::NotifEntry(std::string text, Side side)
|
NotifEntry::NotifEntry(std::string text, Side side)
|
||||||
: m_text{std::move(text)}
|
: m_text{std::move(text)}
|
||||||
@@ -77,13 +82,12 @@ auto NotifMananger::Push(const NotifEntry& entry) -> void {
|
|||||||
mutexLock(&m_mutex);
|
mutexLock(&m_mutex);
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||||
|
|
||||||
switch (entry.GetSide()) {
|
auto& entries = GetEntries(entry.GetSide());
|
||||||
case NotifEntry::Side::LEFT:
|
entries.emplace_front(entry);
|
||||||
m_entries_left.emplace_front(entry);
|
|
||||||
break;
|
// check if we have too many entries, start removing old ones.
|
||||||
case NotifEntry::Side::RIGHT:
|
if (entries.size() >= MAX_ENTRIES) {
|
||||||
m_entries_right.emplace_front(entry);
|
entries.erase(entries.begin() + MAX_ENTRIES, entries.end());
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,32 +95,14 @@ auto NotifMananger::Pop(NotifEntry::Side side) -> void {
|
|||||||
mutexLock(&m_mutex);
|
mutexLock(&m_mutex);
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||||
|
|
||||||
switch (side) {
|
GetEntries(side).pop_back();
|
||||||
case NotifEntry::Side::LEFT:
|
|
||||||
if (!m_entries_left.empty()) {
|
|
||||||
m_entries_left.clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NotifEntry::Side::RIGHT:
|
|
||||||
if (!m_entries_right.empty()) {
|
|
||||||
m_entries_right.clear();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NotifMananger::Clear(NotifEntry::Side side) -> void {
|
auto NotifMananger::Clear(NotifEntry::Side side) -> void {
|
||||||
mutexLock(&m_mutex);
|
mutexLock(&m_mutex);
|
||||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||||
|
|
||||||
switch (side) {
|
GetEntries(side).clear();
|
||||||
case NotifEntry::Side::LEFT:
|
|
||||||
m_entries_left.clear();
|
|
||||||
break;
|
|
||||||
case NotifEntry::Side::RIGHT:
|
|
||||||
m_entries_right.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NotifMananger::Clear() -> void {
|
auto NotifMananger::Clear() -> void {
|
||||||
@@ -127,4 +113,12 @@ auto NotifMananger::Clear() -> void {
|
|||||||
m_entries_right.clear();
|
m_entries_right.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto NotifMananger::GetEntries(NotifEntry::Side side) -> Entries& {
|
||||||
|
if (side == NotifEntry::Side::LEFT) {
|
||||||
|
return m_entries_left;
|
||||||
|
} else {
|
||||||
|
return m_entries_right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sphaira::ui
|
} // namespace sphaira::ui
|
||||||
|
|||||||
@@ -24,14 +24,20 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace sphaira::usb {
|
namespace sphaira::usb {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr u64 TRANSFER_ALIGN = 0x1000;
|
||||||
|
constexpr u64 TRANSFER_MAX = 1024*1024*16;
|
||||||
|
static_assert(!(TRANSFER_MAX % TRANSFER_ALIGN));
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Base::Base(u64 transfer_timeout) {
|
Base::Base(u64 transfer_timeout) {
|
||||||
App::SetAutoSleepDisabled(true);
|
App::SetAutoSleepDisabled(true);
|
||||||
|
|
||||||
m_transfer_timeout = transfer_timeout;
|
m_transfer_timeout = transfer_timeout;
|
||||||
ueventCreate(GetCancelEvent(), true);
|
ueventCreate(GetCancelEvent(), true);
|
||||||
// this avoids allocations during transfers.
|
m_aligned = std::make_unique<u8*>(new(std::align_val_t{TRANSFER_ALIGN}) u8[TRANSFER_MAX]);
|
||||||
m_aligned.reserve(1024 * 1024 * 16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Base::~Base() {
|
Base::~Base() {
|
||||||
@@ -63,39 +69,28 @@ Result Base::TransferPacketImpl(bool read, void *page, u32 remaining, u32 size,
|
|||||||
// an changes are made.
|
// an changes are made.
|
||||||
// yati already goes to great lengths to be zero-copy during installing
|
// yati already goes to great lengths to be zero-copy during installing
|
||||||
// by swapping buffers and inflating in-place.
|
// by swapping buffers and inflating in-place.
|
||||||
|
|
||||||
// NOTE: it is now possible to request the transfer buffer using GetTransferBuffer(),
|
|
||||||
// which will always be aligned and have the size aligned.
|
|
||||||
// this allows for zero-copy transferrs to take place.
|
|
||||||
// this is used in usb_upload.cpp.
|
|
||||||
// do note that this relies of the host sending / receiving buffers of an aligned size.
|
|
||||||
Result Base::TransferAll(bool read, void *data, u32 size, u64 timeout) {
|
Result Base::TransferAll(bool read, void *data, u32 size, u64 timeout) {
|
||||||
auto buf = static_cast<u8*>(data);
|
auto buf = static_cast<u8*>(data);
|
||||||
auto transfer_buf = m_aligned.data();
|
auto transfer_buf = *m_aligned;
|
||||||
const auto alias = buf == transfer_buf;
|
|
||||||
|
|
||||||
if (!alias) {
|
R_UNLESS(!((u64)transfer_buf & 0xFFF), Result_UsbBadBufferAlign);
|
||||||
m_aligned.resize(size);
|
R_UNLESS(size <= TRANSFER_MAX, Result_UsbBadTransferSize);
|
||||||
}
|
|
||||||
|
|
||||||
while (size) {
|
while (size) {
|
||||||
if (!alias && !read) {
|
if (!read) {
|
||||||
std::memcpy(transfer_buf, buf, size);
|
std::memcpy(transfer_buf, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 out_size_transferred;
|
u32 out_size_transferred;
|
||||||
R_TRY(TransferPacketImpl(read, transfer_buf, size, size, &out_size_transferred, timeout));
|
R_TRY(TransferPacketImpl(read, transfer_buf, size, size, &out_size_transferred, timeout));
|
||||||
|
R_UNLESS(out_size_transferred > 0, Result_UsbEmptyTransferSize);
|
||||||
|
R_UNLESS(out_size_transferred <= size, Result_UsbOverflowTransferSize);
|
||||||
|
|
||||||
if (!alias && read) {
|
if (read) {
|
||||||
std::memcpy(buf, transfer_buf, out_size_transferred);
|
std::memcpy(buf, transfer_buf, out_size_transferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alias) {
|
|
||||||
transfer_buf += out_size_transferred;
|
|
||||||
} else {
|
|
||||||
buf += out_size_transferred;
|
buf += out_size_transferred;
|
||||||
}
|
|
||||||
|
|
||||||
size -= out_size_transferred;
|
size -= out_size_transferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ Result Usb::FileRangeCmd(u64 data_size) {
|
|||||||
s64 end_off = header.size;
|
s64 end_off = header.size;
|
||||||
s64 read_size = header.size;
|
s64 read_size = header.size;
|
||||||
|
|
||||||
// use transfer buffer directly to avoid copy overhead.
|
m_buf.resize(header.size);
|
||||||
auto& buf = m_usb->GetTransferBuffer();
|
|
||||||
buf.resize(header.size);
|
|
||||||
|
|
||||||
while (curr_off < end_off) {
|
while (curr_off < end_off) {
|
||||||
if (curr_off + read_size >= end_off) {
|
if (curr_off + read_size >= end_off) {
|
||||||
@@ -102,8 +100,8 @@ Result Usb::FileRangeCmd(u64 data_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 bytes_read;
|
u64 bytes_read;
|
||||||
R_TRY(Read(path, buf.data(), header.offset + curr_off, read_size, &bytes_read));
|
R_TRY(Read(path, m_buf.data(), header.offset + curr_off, read_size, &bytes_read));
|
||||||
R_TRY(m_usb->TransferAll(false, buf.data(), bytes_read));
|
R_TRY(m_usb->TransferAll(false, m_buf.data(), bytes_read));
|
||||||
curr_off += bytes_read;
|
curr_off += bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,33 @@ Result usbDsGetSpeed(UsbDeviceSpeed *out) {
|
|||||||
return serviceDispatchOut(usbDsGetServiceSession(), hosversionAtLeast(11,0,0) ? 11 : 12, *out);
|
return serviceDispatchOut(usbDsGetServiceSession(), hosversionAtLeast(11,0,0) ? 11 : 12, *out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto GetUsbDsStateStr(UsbState state) -> const char* {
|
||||||
|
switch (state) {
|
||||||
|
case UsbState_Detached: return "Detached";
|
||||||
|
case UsbState_Attached: return "Attached";
|
||||||
|
case UsbState_Powered: return "Powered";
|
||||||
|
case UsbState_Default: return "Default";
|
||||||
|
case UsbState_Address: return "Address";
|
||||||
|
case UsbState_Configured: return "Configured";
|
||||||
|
case UsbState_Suspended: return "Suspended";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GetUsbDsSpeedStr(UsbDeviceSpeed speed) -> const char* {
|
||||||
|
// todo: remove this cast when libnx pr is merged.
|
||||||
|
switch ((u32)speed) {
|
||||||
|
case UsbDeviceSpeed_None: return "None";
|
||||||
|
case UsbDeviceSpeed_Low: return "USB 1.0 Low Speed";
|
||||||
|
case UsbDeviceSpeed_Full: return "USB 1.1 Full Speed";
|
||||||
|
case UsbDeviceSpeed_High: return "USB 2.0 High Speed";
|
||||||
|
case UsbDeviceSpeed_Super: return "USB 3.0 Super Speed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
namespace sphaira::usb {
|
namespace sphaira::usb {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -210,10 +237,13 @@ Result UsbDs::WaitUntilConfigured(u64 timeout) {
|
|||||||
eventClear(usbDsGetStateChangeEvent());
|
eventClear(usbDsGetStateChangeEvent());
|
||||||
|
|
||||||
// check if we got one of the cancel events.
|
// check if we got one of the cancel events.
|
||||||
if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
if (waiters[idx].handle == waiterForUEvent(GetCancelEvent()).handle) {
|
||||||
|
log_write("got usb cancel event\n");
|
||||||
rc = Result_UsbCancelled;
|
rc = Result_UsbCancelled;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = usbDsGetState(&state);
|
rc = usbDsGetState(&state);
|
||||||
} while (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout > 0);
|
} while (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout > 0);
|
||||||
@@ -268,18 +298,19 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
|||||||
auto rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout);
|
auto rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout);
|
||||||
|
|
||||||
// check if we got one of the cancel events.
|
// check if we got one of the cancel events.
|
||||||
if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
log_write("got usb cancel event\n");
|
if (waiters[idx].handle == waiterForEvent(usbDsGetStateChangeEvent()).handle) {
|
||||||
rc = Result_UsbCancelled;
|
|
||||||
} else if (R_SUCCEEDED(rc) && idx == waiters.size() - 2) {
|
|
||||||
log_write("got usbDsGetStateChangeEvent() event\n");
|
log_write("got usbDsGetStateChangeEvent() event\n");
|
||||||
m_max_packet_size = 0;
|
m_max_packet_size = 0;
|
||||||
rc = KERNELRESULT(TimedOut);
|
rc = KERNELRESULT(TimedOut);
|
||||||
|
} else if (waiters[idx].handle == waiterForUEvent(GetCancelEvent()).handle) {
|
||||||
|
log_write("got usb cancel event\n");
|
||||||
|
rc = Result_UsbCancelled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
R_TRY(usbDsEndpoint_Cancel(m_endpoints[ep]));
|
usbDsEndpoint_Cancel(m_endpoints[ep]);
|
||||||
eventClear(GetCompletionEvent(ep));
|
eventClear(GetCompletionEvent(ep));
|
||||||
eventClear(usbDsGetStateChangeEvent());
|
eventClear(usbDsGetStateChangeEvent());
|
||||||
}
|
}
|
||||||
@@ -289,7 +320,7 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
|||||||
|
|
||||||
Result UsbDs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_urb_id) {
|
Result UsbDs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_urb_id) {
|
||||||
if (ep == UsbSessionEndpoint_In) {
|
if (ep == UsbSessionEndpoint_In) {
|
||||||
if (remaining == size && !(size % (u32)m_max_packet_size)) {
|
if (size && remaining == size && !(size % (u32)m_max_packet_size)) {
|
||||||
log_write("[USBDS] SetZlt(true)\n");
|
log_write("[USBDS] SetZlt(true)\n");
|
||||||
R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], true));
|
R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], true));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Result Usb::SendFileRangeCmd(u64 off, u64 size, u64 timeout) {
|
|||||||
|
|
||||||
R_TRY(SendCmdHeader(tinfoil::USBCmdId::FILE_RANGE, sizeof(fRangeHeader) + fRangeHeader.nspNameLen, timeout));
|
R_TRY(SendCmdHeader(tinfoil::USBCmdId::FILE_RANGE, sizeof(fRangeHeader) + fRangeHeader.nspNameLen, timeout));
|
||||||
R_TRY(m_usb->TransferAll(false, &fRangeHeader, sizeof(fRangeHeader), timeout));
|
R_TRY(m_usb->TransferAll(false, &fRangeHeader, sizeof(fRangeHeader), timeout));
|
||||||
R_TRY(m_usb->TransferAll(false, m_transfer_file_name.data(), m_transfer_file_name.size(), timeout));
|
R_TRY(m_usb->TransferAll(false, m_transfer_file_name.data(), fRangeHeader.nspNameLen, timeout));
|
||||||
|
|
||||||
tinfoil::USBCmdHeader responseHeader;
|
tinfoil::USBCmdHeader responseHeader;
|
||||||
R_TRY(m_usb->TransferAll(true, &responseHeader, sizeof(responseHeader), timeout));
|
R_TRY(m_usb->TransferAll(true, &responseHeader, sizeof(responseHeader), timeout));
|
||||||
|
|||||||
Reference in New Issue
Block a user