114 lines
3.3 KiB
C++
114 lines
3.3 KiB
C++
// The USB protocol was taken from Tinfoil, by Adubbz.
|
|
|
|
#include "usb/usb_uploader.hpp"
|
|
#include "usb/tinfoil.hpp"
|
|
#include "log.hpp"
|
|
#include "defines.hpp"
|
|
|
|
namespace sphaira::usb::upload {
|
|
namespace {
|
|
|
|
namespace tinfoil = usb::tinfoil;
|
|
|
|
const UsbHsInterfaceFilter FILTER{
|
|
.Flags = UsbHsInterfaceFilterFlags_idVendor |
|
|
UsbHsInterfaceFilterFlags_idProduct |
|
|
UsbHsInterfaceFilterFlags_bcdDevice_Min |
|
|
UsbHsInterfaceFilterFlags_bcdDevice_Max |
|
|
UsbHsInterfaceFilterFlags_bDeviceClass |
|
|
UsbHsInterfaceFilterFlags_bDeviceSubClass |
|
|
UsbHsInterfaceFilterFlags_bDeviceProtocol |
|
|
UsbHsInterfaceFilterFlags_bInterfaceClass |
|
|
UsbHsInterfaceFilterFlags_bInterfaceSubClass |
|
|
UsbHsInterfaceFilterFlags_bInterfaceProtocol,
|
|
.idVendor = 0x057e,
|
|
.idProduct = 0x3000,
|
|
.bcdDevice_Min = 0x0100,
|
|
.bcdDevice_Max = 0x0100,
|
|
.bDeviceClass = 0x00,
|
|
.bDeviceSubClass = 0x00,
|
|
.bDeviceProtocol = 0x00,
|
|
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
|
.bInterfaceSubClass = USB_CLASS_VENDOR_SPEC,
|
|
.bInterfaceProtocol = USB_CLASS_VENDOR_SPEC,
|
|
};
|
|
|
|
constexpr u8 INDEX = 0;
|
|
|
|
} // namespace
|
|
|
|
Usb::Usb(u64 transfer_timeout) {
|
|
m_usb = std::make_unique<usb::UsbHs>(INDEX, FILTER, transfer_timeout);
|
|
m_usb->Init();
|
|
}
|
|
|
|
Usb::~Usb() {
|
|
}
|
|
|
|
Result Usb::WaitForConnection(u64 timeout, u8 flags, std::span<const std::string> names) {
|
|
R_TRY(m_usb->IsUsbConnected(timeout));
|
|
|
|
std::string names_list;
|
|
for (auto& name : names) {
|
|
names_list += name + '\n';
|
|
}
|
|
|
|
tinfoil::TUSHeader header{};
|
|
header.magic = tinfoil::Magic_List0;
|
|
header.nspListSize = names_list.length();
|
|
header.flags = flags;
|
|
|
|
R_TRY(m_usb->TransferAll(false, &header, sizeof(header), timeout));
|
|
R_TRY(m_usb->TransferAll(false, names_list.data(), names_list.length(), timeout));
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
Result Usb::PollCommands() {
|
|
tinfoil::USBCmdHeader header;
|
|
R_TRY(m_usb->TransferAll(true, &header, sizeof(header)));
|
|
R_UNLESS(header.magic == tinfoil::Magic_Command0, Result_UsbUploadBadMagic);
|
|
|
|
if (header.cmdId == tinfoil::USBCmdId::EXIT) {
|
|
R_THROW(Result_UsbUploadExit);
|
|
} else if (header.cmdId == tinfoil::USBCmdId::FILE_RANGE) {
|
|
return FileRangeCmd(header.dataSize);
|
|
} else {
|
|
R_THROW(Result_UsbUploadBadCommand);
|
|
}
|
|
}
|
|
|
|
Result Usb::FileRangeCmd(u64 data_size) {
|
|
tinfoil::FileRangeCmdHeader header;
|
|
R_TRY(m_usb->TransferAll(true, &header, sizeof(header)));
|
|
|
|
std::string path(header.nspNameLen, '\0');
|
|
R_TRY(m_usb->TransferAll(true, path.data(), header.nspNameLen));
|
|
|
|
// send response header.
|
|
R_TRY(m_usb->TransferAll(false, &header, sizeof(header)));
|
|
|
|
s64 curr_off = 0x0;
|
|
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);
|
|
|
|
while (curr_off < end_off) {
|
|
if (curr_off + read_size >= end_off) {
|
|
read_size = end_off - curr_off;
|
|
}
|
|
|
|
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));
|
|
curr_off += bytes_read;
|
|
}
|
|
|
|
R_SUCCEED();
|
|
}
|
|
|
|
} // namespace sphaira::usb::upload
|