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,
|
||||
UsbBadVersion,
|
||||
UsbBadCount,
|
||||
UsbBadBufferAlign,
|
||||
UsbBadTransferSize,
|
||||
UsbEmptyTransferSize,
|
||||
UsbOverflowTransferSize,
|
||||
UsbBadTotalSize,
|
||||
|
||||
UsbUploadBadMagic,
|
||||
@@ -726,8 +729,10 @@ enum : Result {
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadVersion),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadCount),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbBadBufferAlign),
|
||||
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(UsbUploadExit),
|
||||
MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCount),
|
||||
|
||||
@@ -27,19 +27,16 @@ struct Menu final : MenuBase {
|
||||
auto GetShortTitle() const -> const char* override { return "USB"; };
|
||||
void Update(Controller* controller, TouchInfo* touch) override;
|
||||
void Draw(NVGcontext* vg, Theme* theme) override;
|
||||
void OnFocusGained() override;
|
||||
|
||||
// this should be private
|
||||
// private:
|
||||
void ThreadFunction();
|
||||
|
||||
private:
|
||||
std::unique_ptr<yati::source::Usb> m_usb_source{};
|
||||
bool m_was_mtp_enabled{};
|
||||
|
||||
Thread m_thread{};
|
||||
Mutex m_mutex{};
|
||||
// the below are shared across threads, lock with the above mutex!
|
||||
State m_state{State::None};
|
||||
std::atomic<State> m_state{State::None};
|
||||
std::vector<std::string> m_names{};
|
||||
bool m_usb_has_connection{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::ui::menu::usb
|
||||
|
||||
@@ -45,6 +45,7 @@ private:
|
||||
|
||||
private:
|
||||
void Draw(NVGcontext* vg, Theme* theme, Entries& entries);
|
||||
auto GetEntries(NotifEntry::Side side) -> Entries&;
|
||||
|
||||
private:
|
||||
Entries m_entries_left{};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <new>
|
||||
#include <memory>
|
||||
#include <switch.h>
|
||||
|
||||
namespace sphaira::usb {
|
||||
@@ -39,43 +39,10 @@ struct Base {
|
||||
ueventSignal(GetCancelEvent());
|
||||
}
|
||||
|
||||
auto& GetTransferBuffer() {
|
||||
return m_aligned;
|
||||
}
|
||||
|
||||
auto GetTransferTimeout() const {
|
||||
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:
|
||||
enum UsbSessionEndpoint {
|
||||
UsbSessionEndpoint_In = 0,
|
||||
@@ -90,7 +57,7 @@ protected:
|
||||
private:
|
||||
u64 m_transfer_timeout{};
|
||||
UEvent m_uevent{};
|
||||
PageAlignedVector m_aligned{};
|
||||
std::unique_ptr<u8*> m_aligned{};
|
||||
};
|
||||
|
||||
} // namespace sphaira::usb
|
||||
|
||||
@@ -31,6 +31,7 @@ private:
|
||||
|
||||
private:
|
||||
std::unique_ptr<usb::UsbHs> m_usb;
|
||||
std::vector<u8> m_buf;
|
||||
};
|
||||
|
||||
} // namespace sphaira::usb::upload
|
||||
|
||||
@@ -7,6 +7,9 @@ enum { UsbDeviceSpeed_None = 0x0 };
|
||||
enum { UsbDeviceSpeed_Low = 0x1 };
|
||||
Result usbDsGetSpeed(UsbDeviceSpeed *out);
|
||||
|
||||
auto GetUsbDsStateStr(UsbState state) -> const char*;
|
||||
auto GetUsbDsSpeedStr(UsbDeviceSpeed speed) -> const char*;
|
||||
|
||||
namespace sphaira::usb {
|
||||
|
||||
// Device Host
|
||||
|
||||
@@ -114,8 +114,10 @@ auto GetCodeMessage(Result rc) -> const char* {
|
||||
case Result_UsbBadMagic: return "SphairaError_UsbBadMagic";
|
||||
case Result_UsbBadVersion: return "SphairaError_UsbBadVersion";
|
||||
case Result_UsbBadCount: return "SphairaError_UsbBadCount";
|
||||
case Result_UsbBadBufferAlign: return "SphairaError_UsbBadBufferAlign";
|
||||
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_UsbUploadExit: return "SphairaError_UsbUploadExit";
|
||||
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()) {
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
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()) {
|
||||
|
||||
@@ -8,36 +8,6 @@
|
||||
#include "haze_helper.hpp"
|
||||
|
||||
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} {
|
||||
m_was_mtp_enabled = App::GetMtpEnable();
|
||||
@@ -77,7 +47,7 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
usbDsGetSpeed(&speed);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,37 +16,7 @@ constexpr u64 FINISHED_TIMEOUT = 1e+9 * 3; // 3 seconds.
|
||||
|
||||
void thread_func(void* user) {
|
||||
auto app = static_cast<Menu*>(user);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
app->ThreadFunction();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -74,8 +44,6 @@ Menu::Menu(u32 flags) : MenuBase{"USB"_i18n, flags} {
|
||||
m_state = State::Failed;
|
||||
}
|
||||
|
||||
mutexInit(&m_mutex);
|
||||
|
||||
if (m_state != State::Failed) {
|
||||
threadCreate(&m_thread, thread_func, this, nullptr, 1024*32, PRIO_PREEMPTIVE, 1);
|
||||
threadStart(&m_thread);
|
||||
@@ -102,8 +70,20 @@ Menu::~Menu() {
|
||||
void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
MenuBase::Update(controller, touch);
|
||||
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
static TimeStamp poll_ts;
|
||||
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) {
|
||||
log_write("set to progress\n");
|
||||
@@ -152,9 +132,6 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
MenuBase::Draw(vg, theme);
|
||||
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
switch (m_state) {
|
||||
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());
|
||||
@@ -182,8 +159,33 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::OnFocusGained() {
|
||||
MenuBase::OnFocusGained();
|
||||
void Menu::ThreadFunction() {
|
||||
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
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
#include <optional>
|
||||
|
||||
namespace sphaira::ui {
|
||||
namespace {
|
||||
|
||||
constexpr u64 MAX_ENTRIES = 9;
|
||||
|
||||
} // namespace
|
||||
|
||||
NotifEntry::NotifEntry(std::string text, Side side)
|
||||
: m_text{std::move(text)}
|
||||
@@ -77,13 +82,12 @@ auto NotifMananger::Push(const NotifEntry& entry) -> void {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
switch (entry.GetSide()) {
|
||||
case NotifEntry::Side::LEFT:
|
||||
m_entries_left.emplace_front(entry);
|
||||
break;
|
||||
case NotifEntry::Side::RIGHT:
|
||||
m_entries_right.emplace_front(entry);
|
||||
break;
|
||||
auto& entries = GetEntries(entry.GetSide());
|
||||
entries.emplace_front(entry);
|
||||
|
||||
// check if we have too many entries, start removing old ones.
|
||||
if (entries.size() >= MAX_ENTRIES) {
|
||||
entries.erase(entries.begin() + MAX_ENTRIES, entries.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,32 +95,14 @@ auto NotifMananger::Pop(NotifEntry::Side side) -> void {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
switch (side) {
|
||||
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;
|
||||
}
|
||||
GetEntries(side).pop_back();
|
||||
}
|
||||
|
||||
auto NotifMananger::Clear(NotifEntry::Side side) -> void {
|
||||
mutexLock(&m_mutex);
|
||||
ON_SCOPE_EXIT(mutexUnlock(&m_mutex));
|
||||
|
||||
switch (side) {
|
||||
case NotifEntry::Side::LEFT:
|
||||
m_entries_left.clear();
|
||||
break;
|
||||
case NotifEntry::Side::RIGHT:
|
||||
m_entries_right.clear();
|
||||
break;
|
||||
}
|
||||
GetEntries(side).clear();
|
||||
}
|
||||
|
||||
auto NotifMananger::Clear() -> void {
|
||||
@@ -127,4 +113,12 @@ auto NotifMananger::Clear() -> void {
|
||||
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
|
||||
|
||||
@@ -24,14 +24,20 @@
|
||||
#include <cstring>
|
||||
|
||||
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) {
|
||||
App::SetAutoSleepDisabled(true);
|
||||
|
||||
m_transfer_timeout = transfer_timeout;
|
||||
ueventCreate(GetCancelEvent(), true);
|
||||
// this avoids allocations during transfers.
|
||||
m_aligned.reserve(1024 * 1024 * 16);
|
||||
m_aligned = std::make_unique<u8*>(new(std::align_val_t{TRANSFER_ALIGN}) u8[TRANSFER_MAX]);
|
||||
}
|
||||
|
||||
Base::~Base() {
|
||||
@@ -63,39 +69,28 @@ Result Base::TransferPacketImpl(bool read, void *page, u32 remaining, u32 size,
|
||||
// an changes are made.
|
||||
// yati already goes to great lengths to be zero-copy during installing
|
||||
// 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) {
|
||||
auto buf = static_cast<u8*>(data);
|
||||
auto transfer_buf = m_aligned.data();
|
||||
const auto alias = buf == transfer_buf;
|
||||
auto transfer_buf = *m_aligned;
|
||||
|
||||
if (!alias) {
|
||||
m_aligned.resize(size);
|
||||
}
|
||||
R_UNLESS(!((u64)transfer_buf & 0xFFF), Result_UsbBadBufferAlign);
|
||||
R_UNLESS(size <= TRANSFER_MAX, Result_UsbBadTransferSize);
|
||||
|
||||
while (size) {
|
||||
if (!alias && !read) {
|
||||
if (!read) {
|
||||
std::memcpy(transfer_buf, buf, size);
|
||||
}
|
||||
|
||||
u32 out_size_transferred;
|
||||
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);
|
||||
}
|
||||
|
||||
if (alias) {
|
||||
transfer_buf += out_size_transferred;
|
||||
} else {
|
||||
buf += out_size_transferred;
|
||||
}
|
||||
|
||||
buf += out_size_transferred;
|
||||
size -= out_size_transferred;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,9 +92,7 @@ Result Usb::FileRangeCmd(u64 data_size) {
|
||||
s64 end_off = header.size;
|
||||
s64 read_size = header.size;
|
||||
|
||||
// use transfer buffer directly to avoid copy overhead.
|
||||
auto& buf = m_usb->GetTransferBuffer();
|
||||
buf.resize(header.size);
|
||||
m_buf.resize(header.size);
|
||||
|
||||
while (curr_off < end_off) {
|
||||
if (curr_off + read_size >= end_off) {
|
||||
@@ -102,8 +100,8 @@ Result Usb::FileRangeCmd(u64 data_size) {
|
||||
}
|
||||
|
||||
u64 bytes_read;
|
||||
R_TRY(Read(path, buf.data(), header.offset + curr_off, read_size, &bytes_read));
|
||||
R_TRY(m_usb->TransferAll(false, buf.data(), bytes_read));
|
||||
R_TRY(Read(path, m_buf.data(), header.offset + curr_off, read_size, &bytes_read));
|
||||
R_TRY(m_usb->TransferAll(false, m_buf.data(), bytes_read));
|
||||
curr_off += bytes_read;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,33 @@ Result usbDsGetSpeed(UsbDeviceSpeed *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 {
|
||||
|
||||
@@ -210,9 +237,12 @@ Result UsbDs::WaitUntilConfigured(u64 timeout) {
|
||||
eventClear(usbDsGetStateChangeEvent());
|
||||
|
||||
// check if we got one of the cancel events.
|
||||
if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) {
|
||||
rc = Result_UsbCancelled;
|
||||
break;
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (waiters[idx].handle == waiterForUEvent(GetCancelEvent()).handle) {
|
||||
log_write("got usb cancel event\n");
|
||||
rc = Result_UsbCancelled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = usbDsGetState(&state);
|
||||
@@ -268,18 +298,19 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
||||
auto rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout);
|
||||
|
||||
// check if we got one of the cancel events.
|
||||
if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) {
|
||||
log_write("got usb cancel event\n");
|
||||
rc = Result_UsbCancelled;
|
||||
} else if (R_SUCCEEDED(rc) && idx == waiters.size() - 2) {
|
||||
log_write("got usbDsGetStateChangeEvent() event\n");
|
||||
m_max_packet_size = 0;
|
||||
rc = KERNELRESULT(TimedOut);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (waiters[idx].handle == waiterForEvent(usbDsGetStateChangeEvent()).handle) {
|
||||
log_write("got usbDsGetStateChangeEvent() event\n");
|
||||
m_max_packet_size = 0;
|
||||
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)) {
|
||||
R_TRY(usbDsEndpoint_Cancel(m_endpoints[ep]));
|
||||
usbDsEndpoint_Cancel(m_endpoints[ep]);
|
||||
eventClear(GetCompletionEvent(ep));
|
||||
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) {
|
||||
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");
|
||||
R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], true));
|
||||
} 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(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;
|
||||
R_TRY(m_usb->TransferAll(true, &responseHeader, sizeof(responseHeader), timeout));
|
||||
|
||||
Reference in New Issue
Block a user