Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion core/libs/prx/libSceBluetoothHid/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ int APS5_VABI sceBluetoothHidSetFeatureReport(std::uint64_t deviceHandle, std::u
}

int APS5_VABI sceBluetoothHidThreadParamInitialize(BluetoothHidThreadParam* param, std::int32_t priority, std::uint64_t affinityMask) {
NotImplemented_nid_no_patch(__func__);
if (param == nullptr) return InvalidArgument;
param->priority = priority;
param->affinityMask = affinityMask;
param->reserved = 0;
return 0;
}

Expand Down
12 changes: 12 additions & 0 deletions core/libs/tests/GuestBluetoothHid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ int main() {
Require(sceBluetoothHidParamInitialize(&filled, nullptr) == 0 && filled.threadParam == nullptr && filled.reserved == 0 && filled.padding == 0x5a5a5a5au, "a null thread parameter was not stored as given");
Require(sceBluetoothHidParamInitialize(nullptr, &thread) == invalidArgument, "a null parameter block was not refused");
Require(sceBluetoothHidParamInitialize(nullptr, nullptr) == invalidArgument, "null parameter and null thread parameter were not refused");
BluetoothHidThreadParam built;
std::memset(&built, 0x5a, sizeof(built));
Require(sceBluetoothHidThreadParamInitialize(&built, 700, 0x3f) == 0, "thread parameter initialization did not return 0");
unsigned char threadBytes[sizeof(BluetoothHidThreadParam)];
std::memcpy(threadBytes, &built, sizeof(threadBytes));
constexpr unsigned char expected[20] = {0xbc, 0x02, 0x00, 0x00, 0x5a, 0x5a, 0x5a, 0x5a, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Require(std::memcmp(threadBytes, expected, 4) == 0, "the priority was not stored as 32 bits at offset 0");
Require(std::memcmp(threadBytes + 4, expected + 4, 4) == 0, "bytes 4 to 7 of the thread parameter block changed");
Require(std::memcmp(threadBytes + 8, expected + 8, 8) == 0, "the affinity mask was not stored as 64 bits at offset 8");
Require(std::memcmp(threadBytes + 16, expected + 16, 4) == 0, "the word at offset 16 was not cleared");
Require(sceBluetoothHidThreadParamInitialize(nullptr, 700, 0x3f) == invalidArgument, "a null thread parameter block was not refused");
Require(sceBluetoothHidThreadParamInitialize(nullptr, 0, 0) == invalidArgument, "a null thread parameter block with zero values was not refused");
std::puts("BluetoothHid tests passed");
return 0;
}
3 changes: 2 additions & 1 deletion docs/dev/debt/libSceBluetoothHid.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
### Unknown function info

- [sceBluetoothHidGetDeviceName and sceBluetoothHidGetDeviceInfo](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): output pointers are forwarded to device ioctls without accessing the buffers. Their output sizes and layouts remain unresolved. Report APIs use 64-bit device handles; device-info and disconnect APIs use 32-bit device IDs.
- [sceBluetoothHidParamInitialize](../../../core/libs/prx/libSceBluetoothHid/Export.cpp) follows firmware 13.42's module as BunnyFl ran it natively ([comment](https://github.com/boykopovar/AnyPS5/pull/11124#issuecomment-6416053242); not a console, and neither the kernel side of `/dev/bt` nor the event thread's work is covered): it writes its second argument at offset 0 of the 16-byte block, clears the 32-bit word at offset 8, leaves bytes 12 to 15 and returns 0, and a null block returns `0x81540001`. [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Bluetooth/SceBluetoothHid.cs#L21-L49) types that argument as a 64-bit value; it is the address of a `BluetoothHidThreadParam`, since `sceBluetoothHidRegisterCallback` reads the event thread's priority and affinity through it. The same run covers `sceBluetoothHidThreadParamInitialize`, still unimplemented here
- [sceBluetoothHidParamInitialize](../../../core/libs/prx/libSceBluetoothHid/Export.cpp) follows firmware 13.42's module as BunnyFl ran it natively ([comment](https://github.com/boykopovar/AnyPS5/pull/11124#issuecomment-6416053242); not a console, and neither the kernel side of `/dev/bt` nor the event thread's work is covered): it writes its second argument at offset 0 of the 16-byte block, clears the 32-bit word at offset 8, leaves bytes 12 to 15 and returns 0, and a null block returns `0x81540001`. [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Bluetooth/SceBluetoothHid.cs#L21-L49) types that argument as a 64-bit value; it is the address of a `BluetoothHidThreadParam`, since `sceBluetoothHidRegisterCallback` reads the event thread's priority and affinity through it.
- [sceBluetoothHidThreadParamInitialize](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): the success value, range checks of the priority or affinity mask, bytes 20 to 23 and calls before `sceBluetoothHidInit` are not covered by the run it follows (BunnyFl's native x86-64 run of firmware 13.42's module, not a console; [comment](https://github.com/boykopovar/AnyPS5/pull/11124#issuecomment-6416053242)). The run covers (700, 0x3f), stored as the 32-bit priority at offset 0 and the 64-bit mask at 8 with a 32-bit 0 at 16 and bytes 4 to 7 kept, and a null block, refused with `0x81540001`. Here any values are stored unchecked, bytes 20 to 23 are kept, `sceBluetoothHidInit` is not needed, and 0 is returned, as `sceBluetoothHidParamInitialize` returns in that run and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Bluetooth/SceBluetoothHid.cs#L11-L12) gives for every call on success.
- [sceBluetoothHidDebugGetVersion](../../../core/libs/prx/libSceBluetoothHid/Export.cpp): returns a constant without reading arguments, so the supplied implementation does not establish whether the public prototype has unused parameters.
Loading