add usbds speed / max packet detection, add zlt support for usbds, fix game usb transfer bug (see below).
due to the previous commit, i broke dumping multiple games via usb as the stream offset wasn't reset. because of this, the first transfer would complete, but the 2nd one would fail.
This commit is contained in:
@@ -24,9 +24,9 @@ struct Base {
|
|||||||
virtual Result IsUsbConnected(u64 timeout) = 0;
|
virtual Result IsUsbConnected(u64 timeout) = 0;
|
||||||
|
|
||||||
// transfers a chunk of data, check out_size_transferred for how much was transferred.
|
// transfers a chunk of data, check out_size_transferred for how much was transferred.
|
||||||
Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout);
|
Result TransferPacketImpl(bool read, void *page, u32 remaining, u32 size, u32 *out_size_transferred, u64 timeout);
|
||||||
Result TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred) {
|
Result TransferPacketImpl(bool read, void *page, u32 remaining, u32 size, u32 *out_size_transferred) {
|
||||||
return TransferPacketImpl(read, page, size, out_size_transferred, m_transfer_timeout);
|
return TransferPacketImpl(read, page, remaining, size, out_size_transferred, m_transfer_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// transfers all data.
|
// transfers all data.
|
||||||
@@ -90,7 +90,7 @@ protected:
|
|||||||
|
|
||||||
virtual Event *GetCompletionEvent(UsbSessionEndpoint ep) = 0;
|
virtual Event *GetCompletionEvent(UsbSessionEndpoint ep) = 0;
|
||||||
virtual Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) = 0;
|
virtual Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) = 0;
|
||||||
virtual Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_xfer_id) = 0;
|
virtual Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_xfer_id) = 0;
|
||||||
virtual Result GetTransferResult(UsbSessionEndpoint ep, u32 xfer_id, u32 *out_requested_size, u32 *out_transferred_size) = 0;
|
virtual Result GetTransferResult(UsbSessionEndpoint ep, u32 xfer_id, u32 *out_requested_size, u32 *out_transferred_size) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -11,17 +11,19 @@ struct UsbDs final : Base {
|
|||||||
|
|
||||||
Result Init() override;
|
Result Init() override;
|
||||||
Result IsUsbConnected(u64 timeout) override;
|
Result IsUsbConnected(u64 timeout) override;
|
||||||
Result GetSpeed(UsbDeviceSpeed* out);
|
Result GetSpeed(UsbDeviceSpeed* out, u16* max_packet_size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Result WaitUntilConfigured(u64 timeout);
|
||||||
Event *GetCompletionEvent(UsbSessionEndpoint ep) override;
|
Event *GetCompletionEvent(UsbSessionEndpoint ep) override;
|
||||||
Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) override;
|
Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) override;
|
||||||
Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id) override;
|
Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_urb_id) override;
|
||||||
Result GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_requested_size, u32 *out_transferred_size) override;
|
Result GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_requested_size, u32 *out_transferred_size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UsbDsInterface* m_interface{};
|
UsbDsInterface* m_interface{};
|
||||||
UsbDsEndpoint* m_endpoints[2]{};
|
UsbDsEndpoint* m_endpoints[2]{};
|
||||||
|
u16 m_max_packet_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sphaira::usb
|
} // namespace sphaira::usb
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct UsbHs final : Base {
|
|||||||
private:
|
private:
|
||||||
Event *GetCompletionEvent(UsbSessionEndpoint ep) override;
|
Event *GetCompletionEvent(UsbSessionEndpoint ep) override;
|
||||||
Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) override;
|
Result WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) override;
|
||||||
Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_xfer_id) override;
|
Result TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_xfer_id) override;
|
||||||
Result GetTransferResult(UsbSessionEndpoint ep, u32 xfer_id, u32 *out_requested_size, u32 *out_transferred_size) override;
|
Result GetTransferResult(UsbSessionEndpoint ep, u32 xfer_id, u32 *out_requested_size, u32 *out_transferred_size) override;
|
||||||
|
|
||||||
Result Connect();
|
Result Connect();
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ struct Stream : Base {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
m_offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Result m_open_result{};
|
Result m_open_result{};
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ struct UsbTest final : usb::upload::Usb, yati::source::Stream {
|
|||||||
m_path = path;
|
m_path = path;
|
||||||
m_progress = 0;
|
m_progress = 0;
|
||||||
m_pull_offset = 0;
|
m_pull_offset = 0;
|
||||||
|
Stream::Reset();
|
||||||
m_size = m_source->GetSize(path);
|
m_size = m_source->GetSize(path);
|
||||||
m_pbox->SetTitle(m_source->GetName(path));
|
m_pbox->SetTitle(m_source->GetName(path));
|
||||||
m_pbox->NewTransfer(m_path);
|
m_pbox->NewTransfer(m_path);
|
||||||
@@ -422,7 +423,11 @@ Result DumpNspToUsbS2S(ProgressBox* pbox, std::span<NspEntry> entries) {
|
|||||||
|
|
||||||
// wait for exit command.
|
// wait for exit command.
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
log_write("waiting for exit command\n");
|
||||||
rc = usb->PollCommands();
|
rc = usb->PollCommands();
|
||||||
|
log_write("finished polling for exit command\n");
|
||||||
|
} else {
|
||||||
|
log_write("skipped polling for exit command\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc == usb->Result_Exit) {
|
if (rc == usb->Result_Exit) {
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ struct UsbTest final : usb::upload::Usb, yati::source::Stream {
|
|||||||
m_path = path;
|
m_path = path;
|
||||||
m_progress = 0;
|
m_progress = 0;
|
||||||
m_pull_offset = 0;
|
m_pull_offset = 0;
|
||||||
|
Stream::Reset();
|
||||||
m_size = m_entry.GetSize(path);
|
m_size = m_entry.GetSize(path);
|
||||||
m_pbox->SetTitle(m_entry.GetName(path));
|
m_pbox->SetTitle(m_entry.GetName(path));
|
||||||
m_pbox->NewTransfer(m_path);
|
m_pbox->NewTransfer(m_path);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Base::~Base() {
|
|||||||
App::SetAutoSleepDisabled(false);
|
App::SetAutoSleepDisabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Base::TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_transferred, u64 timeout) {
|
Result Base::TransferPacketImpl(bool read, void *page, u32 remaining, u32 size, u32 *out_size_transferred, u64 timeout) {
|
||||||
u32 xfer_id;
|
u32 xfer_id;
|
||||||
|
|
||||||
/* If we're not configured yet, wait to become configured first. */
|
/* If we're not configured yet, wait to become configured first. */
|
||||||
@@ -46,7 +46,7 @@ Result Base::TransferPacketImpl(bool read, void *page, u32 size, u32 *out_size_t
|
|||||||
|
|
||||||
/* Select the appropriate endpoint and begin a transfer. */
|
/* Select the appropriate endpoint and begin a transfer. */
|
||||||
const auto ep = read ? UsbSessionEndpoint_Out : UsbSessionEndpoint_In;
|
const auto ep = read ? UsbSessionEndpoint_Out : UsbSessionEndpoint_In;
|
||||||
R_TRY(TransferAsync(ep, page, size, std::addressof(xfer_id)));
|
R_TRY(TransferAsync(ep, page, remaining, size, std::addressof(xfer_id)));
|
||||||
|
|
||||||
/* Try to wait for the event. */
|
/* Try to wait for the event. */
|
||||||
R_TRY(WaitTransferCompletion(ep, timeout));
|
R_TRY(WaitTransferCompletion(ep, timeout));
|
||||||
@@ -84,7 +84,7 @@ Result Base::TransferAll(bool read, void *data, u32 size, u64 timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u32 out_size_transferred;
|
u32 out_size_transferred;
|
||||||
R_TRY(TransferPacketImpl(read, transfer_buf, size, &out_size_transferred, timeout));
|
R_TRY(TransferPacketImpl(read, transfer_buf, size, size, &out_size_transferred, timeout));
|
||||||
|
|
||||||
if (!alias && read) {
|
if (!alias && read) {
|
||||||
std::memcpy(buf, transfer_buf, out_size_transferred);
|
std::memcpy(buf, transfer_buf, out_size_transferred);
|
||||||
|
|||||||
@@ -7,8 +7,19 @@
|
|||||||
namespace sphaira::usb {
|
namespace sphaira::usb {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// untested, should work tho.
|
|
||||||
// TODO: pr missing speed fields to libnx.
|
// TODO: pr missing speed fields to libnx.
|
||||||
|
enum { UsbDeviceSpeed_None = 0x0 };
|
||||||
|
enum { UsbDeviceSpeed_Low = 0x1 };
|
||||||
|
|
||||||
|
constexpr u16 DEVICE_SPEED[] = {
|
||||||
|
[UsbDeviceSpeed_None] = 0x0,
|
||||||
|
[UsbDeviceSpeed_Low] = 0x0,
|
||||||
|
[UsbDeviceSpeed_Full] = 0x40,
|
||||||
|
[UsbDeviceSpeed_High] = 0x200,
|
||||||
|
[UsbDeviceSpeed_Super] = 0x400,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: pr this to libnx.
|
||||||
Result usbDsGetSpeed(u32 *out) {
|
Result usbDsGetSpeed(u32 *out) {
|
||||||
if (hosversionBefore(8,0,0)) {
|
if (hosversionBefore(8,0,0)) {
|
||||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||||
@@ -140,22 +151,22 @@ Result UsbDs::Init() {
|
|||||||
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
||||||
|
|
||||||
// Full Speed Config
|
// Full Speed Config
|
||||||
endpoint_descriptor_in.wMaxPacketSize = 0x40;
|
endpoint_descriptor_in.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_Full];
|
||||||
endpoint_descriptor_out.wMaxPacketSize = 0x40;
|
endpoint_descriptor_out.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_Full];
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE));
|
||||||
|
|
||||||
// High Speed Config
|
// High Speed Config
|
||||||
endpoint_descriptor_in.wMaxPacketSize = 0x200;
|
endpoint_descriptor_in.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_High];
|
||||||
endpoint_descriptor_out.wMaxPacketSize = 0x200;
|
endpoint_descriptor_out.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_High];
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE));
|
||||||
|
|
||||||
// Super Speed Config
|
// Super Speed Config
|
||||||
endpoint_descriptor_in.wMaxPacketSize = 0x400;
|
endpoint_descriptor_in.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_Super];
|
||||||
endpoint_descriptor_out.wMaxPacketSize = 0x400;
|
endpoint_descriptor_out.wMaxPacketSize = DEVICE_SPEED[UsbDeviceSpeed_Super];
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE));
|
||||||
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
||||||
@@ -174,7 +185,7 @@ Result UsbDs::Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the below code is taken from libnx, with the addition of a uevent to cancel.
|
// the below code is taken from libnx, with the addition of a uevent to cancel.
|
||||||
Result UsbDs::IsUsbConnected(u64 timeout) {
|
Result UsbDs::WaitUntilConfigured(u64 timeout) {
|
||||||
Result rc;
|
Result rc;
|
||||||
UsbState state = UsbState_Detached;
|
UsbState state = UsbState_Detached;
|
||||||
|
|
||||||
@@ -218,8 +229,33 @@ Result UsbDs::IsUsbConnected(u64 timeout) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UsbDs::GetSpeed(UsbDeviceSpeed* out) {
|
Result UsbDs::IsUsbConnected(u64 timeout) {
|
||||||
return usbDsGetSpeed((u32*)out);
|
const auto rc = WaitUntilConfigured(timeout);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
m_max_packet_size = 0;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_max_packet_size) {
|
||||||
|
UsbDeviceSpeed speed;
|
||||||
|
R_TRY(GetSpeed(&speed, &m_max_packet_size));
|
||||||
|
log_write("[USBDS] speed: %u max_packet: 0x%X\n", speed, m_max_packet_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbDs::GetSpeed(UsbDeviceSpeed* out, u16* max_packet_size) {
|
||||||
|
if (hosversionAtLeast(8,0,0)) {
|
||||||
|
R_TRY(usbDsGetSpeed((u32*)out));
|
||||||
|
} else {
|
||||||
|
// assume USB 2.0 speed (likely the case anyway).
|
||||||
|
*out = UsbDeviceSpeed_High;
|
||||||
|
}
|
||||||
|
|
||||||
|
*max_packet_size = DEVICE_SPEED[*out];
|
||||||
|
R_UNLESS(*max_packet_size > 0, 0x1);
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Event *UsbDs::GetCompletionEvent(UsbSessionEndpoint ep) {
|
Event *UsbDs::GetCompletionEvent(UsbSessionEndpoint ep) {
|
||||||
@@ -242,6 +278,7 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
|||||||
rc = Result_Cancelled;
|
rc = Result_Cancelled;
|
||||||
} else if (R_SUCCEEDED(rc) && idx == waiters.size() - 2) {
|
} 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;
|
||||||
rc = KERNELRESULT(TimedOut);
|
rc = KERNELRESULT(TimedOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +292,14 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UsbDs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id) {
|
Result UsbDs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_urb_id) {
|
||||||
|
if (remaining == size && size == m_max_packet_size) {
|
||||||
|
log_write("[USBDS] SetZlt(true)\n");
|
||||||
|
R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], true));
|
||||||
|
} else {
|
||||||
|
R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], false));
|
||||||
|
}
|
||||||
|
|
||||||
return usbDsEndpoint_PostBufferAsync(m_endpoints[ep], buffer, size, out_urb_id);
|
return usbDsEndpoint_PostBufferAsync(m_endpoints[ep], buffer, size, out_urb_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ Result UsbHs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UsbHs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_xfer_id) {
|
Result UsbHs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_xfer_id) {
|
||||||
return usbHsEpPostBufferAsync(&m_endpoints[ep], buffer, size, 0, out_xfer_id);
|
return usbHsEpPostBufferAsync(&m_endpoints[ep], buffer, size, 0, out_xfer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Result Usb::SendFileRangeCmd(u64 off, u64 size, u64 timeout) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result Usb::Finished(u64 timeout) {
|
Result Usb::Finished(u64 timeout) {
|
||||||
|
log_write("[USB] sending finished command\n");
|
||||||
return SendCmdHeader(tinfoil::USBCmdId::EXIT, 0, timeout);
|
return SendCmdHeader(tinfoil::USBCmdId::EXIT, 0, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user