usb: change api so that every packet sent is crc32c, update python usb api, add automated tests for usb.

This commit is contained in:
ITotalJustice
2025-08-31 06:12:02 +01:00
parent b6b1af5959
commit 22e965521a
13 changed files with 469 additions and 71 deletions

View File

@@ -7,7 +7,7 @@ namespace sphaira::usb::api {
enum : u32 {
MAGIC = 0x53504830,
PACKET_SIZE = 16,
PACKET_SIZE = 24,
};
enum : u32 {
@@ -26,39 +26,87 @@ enum : u32 {
FLAG_STREAM = 1 << 0,
};
struct SendHeader {
u32 magic;
u32 cmd;
u32 arg3;
u32 arg4;
struct UsbPacket {
u32 magic{};
u32 arg2{};
u32 arg3{};
u32 arg4{};
u32 arg5{};
u32 crc32c{}; // crc32 over the above 16 bytes.
protected:
u32 CalculateCrc32c() const {
return crc32cCalculate(this, 20);
}
void GenerateCrc32c() {
crc32c = CalculateCrc32c();
}
Result Verify() const {
R_UNLESS(crc32c == CalculateCrc32c(), 1); // todo: add error code.
R_UNLESS(magic == MAGIC, Result_UsbBadMagic);
R_SUCCEED();
}
};
struct ResultHeader {
u32 magic;
u32 result;
u32 arg3;
u32 arg4;
struct SendPacket : UsbPacket {
static SendPacket Build(u32 cmd, u32 arg3 = 0, u32 arg4 = 0) {
SendPacket packet{MAGIC, cmd, arg3, arg4};
packet.GenerateCrc32c();
return packet;
}
Result Verify() const {
R_UNLESS(magic == MAGIC, Result_UsbBadMagic);
R_UNLESS(result == RESULT_OK, 1); // todo: create error code.
return UsbPacket::Verify();
}
u32 GetCmd() const {
return arg2;
}
};
struct ResultPacket : UsbPacket {
static ResultPacket Build(u32 result, u32 arg3 = 0, u32 arg4 = 0) {
ResultPacket packet{MAGIC, result, arg3, arg4};
packet.GenerateCrc32c();
return packet;
}
Result Verify() const {
R_TRY(UsbPacket::Verify());
R_UNLESS(arg2 == RESULT_OK, 1); // todo: create error code.
R_SUCCEED();
}
};
struct SendDataHeader {
u64 offset;
u32 size;
u32 crc32c;
struct SendDataPacket : UsbPacket {
static SendDataPacket Build(u64 off, u32 size, u32 crc32c) {
SendDataPacket packet{MAGIC, u32(off >> 32), u32(off), size, crc32c};
packet.GenerateCrc32c();
return packet;
}
Result Verify() const {
return UsbPacket::Verify();
}
u64 GetOffset() const {
return (u64(arg2) << 32) | arg3;
}
u32 GetSize() const {
return arg4;
}
u32 GetCrc32c() const {
return arg5;
}
};
static_assert(sizeof(SendHeader) == PACKET_SIZE);
static_assert(sizeof(ResultHeader) == PACKET_SIZE);
static_assert(sizeof(SendDataHeader) == PACKET_SIZE);
static_assert(sizeof(UsbPacket) == PACKET_SIZE);
static_assert(sizeof(SendPacket) == PACKET_SIZE);
static_assert(sizeof(ResultPacket) == PACKET_SIZE);
static_assert(sizeof(SendDataPacket) == PACKET_SIZE);
} // namespace sphaira::usb::api

View File

@@ -25,8 +25,8 @@ struct Usb {
Result CloseFile();
private:
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultHeader* out = nullptr);
Result SendAndVerify(const void* data, u32 size, api::ResultHeader* out = nullptr);
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultPacket* out = nullptr);
Result SendAndVerify(const void* data, u32 size, api::ResultPacket* out = nullptr);
private:
std::unique_ptr<usb::UsbDs> m_usb{};

View File

@@ -26,8 +26,8 @@ struct Usb {
Result CloseFile();
private:
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultHeader* out = nullptr);
Result SendAndVerify(const void* data, u32 size, api::ResultHeader* out = nullptr);
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultPacket* out = nullptr);
Result SendAndVerify(const void* data, u32 size, api::ResultPacket* out = nullptr);
private:
std::unique_ptr<usb::UsbDs> m_usb{};

View File

@@ -19,7 +19,7 @@ Usb::Usb(u64 transfer_timeout) {
Usb::~Usb() {
if (m_was_connected && R_SUCCEEDED(m_usb->IsUsbConnected(0))) {
SendHeader send_header{MAGIC, CMD_QUIT};
const auto send_header = SendPacket::Build(CMD_QUIT);
SendAndVerify(&send_header, sizeof(send_header));
}
}
@@ -35,7 +35,7 @@ Result Usb::WaitForConnection(std::string_view path, u64 timeout) {
R_TRY(m_open_result);
R_TRY(m_usb->IsUsbConnected(timeout));
SendHeader send_header{MAGIC, CMD_EXPORT, (u32)path.length()};
const auto send_header = SendPacket::Build(CMD_EXPORT, path.length());
R_TRY(SendAndVerify(&send_header, sizeof(send_header), timeout));
R_TRY(SendAndVerify(path.data(), path.length(), timeout));
@@ -44,7 +44,7 @@ Result Usb::WaitForConnection(std::string_view path, u64 timeout) {
}
Result Usb::CloseFile() {
SendDataHeader send_header{0, 0};
const auto send_header = SendDataPacket::Build(0, 0, 0);
return SendAndVerify(&send_header, sizeof(send_header));
}
@@ -54,17 +54,18 @@ void Usb::SignalCancel() {
}
Result Usb::Write(const void* buf, u64 off, u32 size) {
SendDataHeader send_header{off, size, crc32cCalculate(buf, size)};
const auto send_header = SendDataPacket::Build(off, size, crc32cCalculate(buf, size));
R_TRY(SendAndVerify(&send_header, sizeof(send_header)));
return SendAndVerify(buf, size);
}
// casts away const, but it does not modify the buffer!
Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultHeader* out) {
Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultPacket* out) {
R_TRY(m_usb->TransferAll(false, const_cast<void*>(data), size, timeout));
ResultHeader recv_header;
ResultPacket recv_header;
R_TRY(m_usb->TransferAll(true, &recv_header, sizeof(recv_header), timeout));
R_TRY(recv_header.Verify());
@@ -72,7 +73,7 @@ Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultHeader*
R_SUCCEED();
}
Result Usb::SendAndVerify(const void* data, u32 size, ResultHeader* out) {
Result Usb::SendAndVerify(const void* data, u32 size, ResultPacket* out) {
return SendAndVerify(data, size, m_usb->GetTransferTimeout(), out);
}

View File

@@ -19,7 +19,7 @@ Usb::Usb(u64 transfer_timeout) {
Usb::~Usb() {
if (m_was_connected && R_SUCCEEDED(m_usb->IsUsbConnected(0))) {
SendHeader send_header{MAGIC, CMD_QUIT};
const auto send_header = SendPacket::Build(CMD_QUIT);
SendAndVerify(&send_header, sizeof(send_header));
}
}
@@ -35,8 +35,8 @@ Result Usb::WaitForConnection(u64 timeout, std::vector<std::string>& out_names)
R_TRY(m_open_result);
R_TRY(m_usb->IsUsbConnected(timeout));
SendHeader send_header{MAGIC, RESULT_OK};
ResultHeader recv_header;
const auto send_header = SendPacket::Build(RESULT_OK);
ResultPacket recv_header;
R_TRY(SendAndVerify(&send_header, sizeof(send_header), timeout, &recv_header))
std::vector<char> names(recv_header.arg3);
@@ -50,15 +50,14 @@ Result Usb::WaitForConnection(u64 timeout, std::vector<std::string>& out_names)
}
}
m_flags = recv_header.arg4;
m_was_connected = true;
R_SUCCEED();
}
Result Usb::OpenFile(u32 index, s64& file_size) {
log_write("doing open file\n");
SendHeader send_header{MAGIC, CMD_OPEN, index};
ResultHeader recv_header;
const auto send_header = SendPacket::Build(CMD_OPEN, index);
ResultPacket recv_header;
R_TRY(SendAndVerify(&send_header, sizeof(send_header), &recv_header))
log_write("did open file\n");
@@ -72,7 +71,7 @@ Result Usb::OpenFile(u32 index, s64& file_size) {
}
Result Usb::CloseFile() {
SendDataHeader send_header{0, 0};
const auto send_header = SendDataPacket::Build(0, 0, 0);
return SendAndVerify(&send_header, sizeof(send_header));
}
@@ -86,8 +85,8 @@ u32 Usb::GetFlags() const {
}
Result Usb::Read(void* buf, u64 off, u32 size, u64* bytes_read) {
SendDataHeader send_header{off, size};
ResultHeader recv_header;
const auto send_header = SendDataPacket::Build(off, size, 0);
ResultPacket recv_header;
R_TRY(SendAndVerify(&send_header, sizeof(send_header), &recv_header))
// adjust the size and read the data.
@@ -102,10 +101,10 @@ Result Usb::Read(void* buf, u64 off, u32 size, u64* bytes_read) {
}
// casts away const, but it does not modify the buffer!
Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultHeader* out) {
Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultPacket* out) {
R_TRY(m_usb->TransferAll(false, const_cast<void*>(data), size, timeout));
ResultHeader recv_header;
ResultPacket recv_header;
R_TRY(m_usb->TransferAll(true, &recv_header, sizeof(recv_header), timeout));
R_TRY(recv_header.Verify());
@@ -113,7 +112,7 @@ Result Usb::SendAndVerify(const void* data, u32 size, u64 timeout, ResultHeader*
R_SUCCEED();
}
Result Usb::SendAndVerify(const void* data, u32 size, ResultHeader* out) {
Result Usb::SendAndVerify(const void* data, u32 size, ResultPacket* out) {
return SendAndVerify(data, size, m_usb->GetTransferTimeout(), out);
}

View File

@@ -53,7 +53,7 @@ Result Usb::WaitForConnection(u64 timeout, std::span<const std::string> names) {
}
// send.
SendHeader send_header;
SendPacket send_header;
R_TRY(m_usb->TransferAll(true, &send_header, sizeof(send_header), timeout));
R_TRY(send_header.Verify());
@@ -67,14 +67,14 @@ Result Usb::WaitForConnection(u64 timeout, std::span<const std::string> names) {
}
Result Usb::PollCommands() {
SendHeader send_header;
SendPacket send_header;
R_TRY(m_usb->TransferAll(true, &send_header, sizeof(send_header)));
R_TRY(send_header.Verify());
if (send_header.cmd == CMD_QUIT) {
if (send_header.GetCmd() == CMD_QUIT) {
R_TRY(SendResult(RESULT_OK));
R_THROW(Result_UsbUploadExit);
} else if (send_header.cmd == CMD_OPEN) {
} else if (send_header.GetCmd() == CMD_OPEN) {
s64 file_size;
u16 flags;
R_TRY(Open(send_header.arg3, file_size, flags));
@@ -92,11 +92,11 @@ Result Usb::file_transfer_loop() {
log_write("doing file transfer\n");
// get offset + size.
SendDataHeader send_header;
SendDataPacket send_header;
R_TRY(m_usb->TransferAll(true, &send_header, sizeof(send_header)));
// check if we should finish now.
if (send_header.offset == 0 && send_header.size == 0) {
if (send_header.GetOffset() == 0 && send_header.GetSize() == 0) {
log_write("finished\n");
R_TRY(SendResult(RESULT_OK));
return Result_UsbUploadExit;
@@ -104,10 +104,10 @@ Result Usb::file_transfer_loop() {
// read file and calculate the hash.
u64 bytes_read;
m_buf.resize(send_header.size);
m_buf.resize(send_header.GetSize());
log_write("reading buffer: %zu\n", m_buf.size());
R_TRY(Read(m_buf.data(), send_header.offset, m_buf.size(), &bytes_read));
R_TRY(Read(m_buf.data(), send_header.GetOffset(), m_buf.size(), &bytes_read));
const auto crc32 = crc32Calculate(m_buf.data(), m_buf.size());
log_write("read the buffer: %zu\n", bytes_read);
@@ -125,7 +125,7 @@ Result Usb::file_transfer_loop() {
}
Result Usb::SendResult(u32 result, u32 arg3, u32 arg4) {
ResultHeader recv_header{MAGIC, result, arg3, arg4};
auto recv_header = api::ResultPacket::Build(result, arg3, arg4);
return m_usb->TransferAll(false, &recv_header, sizeof(recv_header));
}