feat(diag): host-side trigger-flow triage via 0xFD + mic_diag.sh bt-trace

Extends the firmware's 0xFD vendor feature report from 32 to 44 bytes
to carry the three host -> dongle -> BT trigger counters added in
5da1be4 (host_out02_total / host_out02_trig_allow / host_out02_to_bt).
mic_diag.sh's bt-trace decoder reads them and prints a one-line
verdict so the user can triage issue #3 in one terminal command
instead of switching to the OLED Diagnostics screen mid-game.

Verdicts encoded:
  host02 > 0, trig == 0       -> host driver never sets the trigger
                                 Allow bits; not a firmware problem.
  trig > 0, tx < trig         -> trigger Allow bits arrive but the
                                 speaker-active gate in main.cpp
                                 swallowed them.
  full chain populated        -> dongle delivered, controller didn't
                                 actuate; Sony BT-side limit.

The 0xFD report ID stays undeclared in the HID descriptor — Linux
hidraw ioctls don't enforce that and WebHID never sees these reports
(config goes through 0xF6). bt-trace's IOCTL_SIZE bumped to 45
(1 byte for the kernel-prepended report ID + 44 bytes payload).

Docs:
  README.md   - new "Diagnostics & debug tooling" section walks
                through the four mic_diag.sh subcommands; previously
                the script was only mentioned inside
                BLUETOOTH_AUDIO_NOTES.md.
  README.md   - Diagnostics-screen description updated for the
                scrolling row-list + cross-link to bt-trace.
  CLAUDE.md   - "Where features live" gets a host-side-diagnostics
                entry explaining how to extend the 0xFD payload and
                wire it into bt-trace's decoder.
  CHANGELOG   - records both the firmware extension and the README
                discoverability fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-05-19 19:21:00 -06:00
co-authored by Claude Opus 4.7
parent c6f490ad8e
commit 69a0cd4ddb
5 changed files with 112 additions and 44 deletions
+17 -6
View File
@@ -63,6 +63,9 @@ extern volatile uint8_t g_last_other_prefix[8];
extern volatile uint8_t g_last_any_prefix[16];
extern volatile uint16_t g_longest_len;
extern volatile uint8_t g_longest_frame[80];
extern volatile uint32_t g_host_out02_total;
extern volatile uint32_t g_host_out02_trig_allow;
extern volatile uint32_t g_host_out02_to_bt;
bool is_pico_cmd(uint8_t report_id) {
if (report_id == 0xf6 ||
@@ -180,8 +183,8 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) {
return want;
}
if (report_id == 0xfd) {
// Mic-debug feature report. 32-byte payload (under typical
// GET_REPORT control transfer cap; want=64 came back empty).
// Bridge-diagnostics feature report. 44-byte payload.
// Section 1: mic-investigation counters (original 0..31).
// [0..3] uint32 BT 0x31 input report count
// [4..7] uint32 BT non-0x31 input report count
// [8] uint8 last non-0x31 report ID seen
@@ -192,10 +195,11 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) {
// [14..15] uint16 max frame length seen
// [16..23] uint8[8] first 8 bytes of last non-0x31 frame
// [24..31] uint8[8] first 8 bytes of most recent ANY frame
constexpr uint16_t want = 32;
// Diagnostic: do NOT bail if reqlen < want — write what we can
// and set sentinel. If we still see 0x00 at byte[31] the handler
// isn't reached at all.
// Section 2: trigger-flow counters (issue #3 triage).
// [32..35] uint32 host 0x02 OUT reports received total
// [36..39] uint32 ...of those, with Allow*TriggerFFB set
// [40..43] uint32 ...forwarded as BT 0x31 sub-0x10
constexpr uint16_t want = 44;
for (uint16_t i = 0; i < want && i < reqlen; i++) buffer[i] = 0;
const uint32_t bt31 = g_bt_31_packets;
@@ -213,6 +217,13 @@ uint16_t pico_cmd_get(uint8_t report_id, uint8_t *buffer, uint16_t reqlen) {
memcpy(buffer + 14, &lmax, 2);
for (int i = 0; i < 8 && (16 + i) < reqlen; i++) buffer[16 + i] = g_last_other_prefix[i];
for (int i = 0; i < 8 && (24 + i) < reqlen; i++) buffer[24 + i] = g_last_any_prefix[i];
const uint32_t out02 = g_host_out02_total;
const uint32_t out02_t = g_host_out02_trig_allow;
const uint32_t out02_b = g_host_out02_to_bt;
if ((32 + 4) <= reqlen) memcpy(buffer + 32, &out02, 4);
if ((36 + 4) <= reqlen) memcpy(buffer + 36, &out02_t, 4);
if ((40 + 4) <= reqlen) memcpy(buffer + 40, &out02_b, 4);
return (reqlen < want) ? reqlen : want;
}
if (report_id == 0xfe) {