Repository navigation
Add support for Razer BlackShark V2 Pro for PlayStation (1532:1014) - #620
Conversation
Battery status for the 2.4 GHz dongle of the PlayStation edition. The dongle uses an Airoha chip and the HID framing of Razer's firmware updater: 64-byte reports with ID 0x02 on the vendor collection (usage page 0xFF00, usage 0x0001, interface 3), a CRC16-CCITT over the whole report, an 'O'/'F' acknowledgement fetched as an input report, and replies matched by command type and sequence number. The headset only answers while the dongle is in remote mode, so getBattery() checks that a headset is linked, switches the dongle to remote mode, reads the battery voltage in mV, and always switches back to local mode, its normal state. Only read commands and this mode switch are sent. The headset reports a voltage but no percentage and no charging state; the level comes from a generic Li-ion curve. Tested on Windows 11 with dongle firmware 1.0.8: 40 consecutive reads succeeded (about 0.9 s each) and the dongle was back in local mode after each one. The unit tests replay a captured session and simulate a switched-off headset, missing replies and rejected frames. Not tested on Linux or macOS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Tray frontends poll every second, and every voltage read switches the dongle to remote mode and back. The link status is still queried on each call, so switching the headset off shows up right away, but the voltage is cached for 60 s and dropped as soon as the headset is seen unlinked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e09355d9cc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Nice capture work - the mock recomputing the CRC on every write is what makes the byte-for-byte frame assertions worth having. Codex's finding is real, and it contradicts the PR description ("always switches back to local mode ... verifying that it took effect"). Both halves hold: for (int attempt = 0; attempt < ATTEMPTS && !isMode(device_handle, MODE_LOCAL); ++attempt) {
(void)setMode(device_handle, MODE_LOCAL);
}
return voltage;
This passes with your tests plus that case: // Local is the dongle's normal mode; restore it even if the read failed.
// Verify the last attempt too: leaving the dongle in remote mode outlives
// this call, so it is reported even when the voltage read succeeded.
bool local = isMode(device_handle, MODE_LOCAL);
for (int attempt = 0; attempt < ATTEMPTS && !local; ++attempt) {
(void)setMode(device_handle, MODE_LOCAL);
local = isMode(device_handle, MODE_LOCAL);
}
if (!local) {
return DeviceError::protocolError("Could not switch the dongle back to local mode");
}
return voltage;It reports the restore failure in preference to a failed read, which I think is right - the read failing is over, the dongle being left in remote mode is not. Worth a test for it either way; Second thing, from the caching commit: Everything else checks out, including CI on 0824418 - note the red-looking runs on e09355d are just the unapproved first-time-contributor ones, superseded. |
Verify the last attempt to switch the dongle back to local mode, and fail the call when it did not take, even if the voltage was read: remote mode outlives the call and takes the dongle off as the default audio device. Key the voltage cache by hid_device* and drop it in onConnectionClosed(), so two dongles no longer share it. A failed read is held for 10 seconds, so a headset that is linked but does not answer is not switched on every poll; only a failed restore is retried on the next call. Also leave the dongle alone when it reports a mode other than local or remote, and treat a non-zero status on SET_MODE as a failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for reproducing it. Both points are fixed in 8a2f514.
While at it I changed a few related things:
Unit tests pass on Windows (MinGW, GCC 16) and on Linux (GCC 15 and Clang 21 with ASan/UBSan, Ubuntu under WSL, without the dongle). On the hardware, 20 separate |
Changes made
Battery status for the PlayStation edition of the BlackShark V2 Pro through its 2.4 GHz dongle (
1532:1014, "BlackShark V2 Pro PS 2.4"). Razer Synapse does not support this edition, so until now there was no way to see the battery level on a PC.The protocol follows the HID framing used by Razer's firmware updater for this dongle (Airoha chip):
0x02on the vendor collection (usage page0xFF00, usage0x0001, interface 3)02 80 <len> <crc16 LE> <payload>; the CRC is CRC16-CCITT (poly0x1021, init 0) over the whole report with the CRC field zeroed'O'accepted,'F'rejectedThe headset only answers while the dongle is in "remote" mode.
getBattery()checks that a headset is linked (returnsBATTERY_UNAVAILABLEwhen it is off), switches the dongle to remote mode, reads the battery voltage in mV, and always switches back to local mode, the dongle's normal state, verifying that it took effect. Only read commands and this mode switch are sent.Tray frontends tend to poll every second, so the voltage is re-read (and the dongle switched) at most once a minute per device handle; a failed read is held for 10 seconds, except a failed switch back to local mode, which is retried on the next call. The cheap link-status query still runs on every call, which means switching the headset off is reported immediately and clears the cache. The cache lives in the process, so a frontend that starts
headsetcontrolfor every poll still switches the dongle each time.The headset reports a voltage, but no percentage and no charging state, so the level comes from a generic Li-ion curve and should be read as approximate.
Testing
-breads succeeded, about 0.9 s each, 3930-3951 mV; the dongle was back in local mode after every read and stayed the default audio device.tests/test_razer_blackshark_v2_pro_ps.cpp) replay a captured session and check the frames byte for byte, and simulate a switched-off headset, a dongle already in remote mode or in an unknown mode, one that will not go back to local mode, the voltage cache (per handle, and after a failure), missing and implausible battery replies, an unplug mid-read, rejected frames and unrelated replies.Checklist
🤖 Generated with Claude Code