Merge pull request #93 from awalol/refactor/state_mgr

Refactor: unified Controller SetStateData
This commit is contained in:
awalol
2026-05-17 14:23:02 +08:00
committed by GitHub
9 changed files with 434 additions and 118 deletions
+1
View File
@@ -87,6 +87,7 @@ add_executable(ds5-bridge
src/bt.cpp src/bt.cpp
src/config.cpp src/config.cpp
src/cmd.cpp src/cmd.cpp
src/state_mgr.cpp
) )
if (ENABLE_BATT_LED) if (ENABLE_BATT_LED)
+15 -7
View File
@@ -14,6 +14,7 @@
#include "pico/multicore.h" #include "pico/multicore.h"
#include "pico/util/queue.h" #include "pico/util/queue.h"
#include "config.h" #include "config.h"
#include "state_mgr.h"
#include "usb.h" #include "usb.h"
#define INPUT_CHANNELS 4 #define INPUT_CHANNELS 4
@@ -116,20 +117,27 @@ void audio_loop() {
pkt[8] = buf_len; // 这 4 个字节的作用未知,调整没有效果 pkt[8] = buf_len; // 这 4 个字节的作用未知,调整没有效果
pkt[9] = buf_len; // audio buffer length 只有调整这个字节生效。 pkt[9] = buf_len; // audio buffer length 只有调整这个字节生效。
pkt[10] = packetCounter++; pkt[10] = packetCounter++;
pkt[11] = 0x12 | 0 << 6 | 1 << 7; // SetStateData
pkt[12] = SAMPLE_SIZE; pkt[11] = 0x10 | 0 << 6 | 1 << 7;
memcpy(pkt + 13, haptic_buf, SAMPLE_SIZE); pkt[12] = 63;
state_set(pkt + 13,63);
// Haptics Audio Data
pkt[76] = 0x12 | 0 << 6 | 1 << 7;
pkt[77] = SAMPLE_SIZE;
memcpy(pkt + 78, haptic_buf, SAMPLE_SIZE);
#if !DISABLE_SPEAKER_PROC #if !DISABLE_SPEAKER_PROC
pkt[77] = (plug_headset ? 0x16 : 0x13) | 0 << 6 | 1 << 7; // Speaker: 0x13 // Speaker Audio Data
pkt[142] = (plug_headset ? 0x16 : 0x13) | 0 << 6 | 1 << 7; // Speaker: 0x13
// L Headset Mono: 0x14 // L Headset Mono: 0x14
// L Headset R Speaker: 0x15 // L Headset R Speaker: 0x15
// Headset: 0x16 // Headset: 0x16
pkt[78] = 200; pkt[143] = 200;
critical_section_enter_blocking(&opus_cs); critical_section_enter_blocking(&opus_cs);
memcpy(pkt + 79, opus_buf, 200); memcpy(pkt + 144, opus_buf, 200);
critical_section_exit(&opus_cs); critical_section_exit(&opus_cs);
#endif #endif
bt_write(pkt, sizeof(pkt), true);
bt_write(pkt, sizeof(pkt));
haptic_buf_pos = 0; haptic_buf_pos = 0;
} }
} }
+14 -39
View File
@@ -16,6 +16,7 @@
#include "bsp/board_api.h" #include "bsp/board_api.h"
#include "classic/sdp_server.h" #include "classic/sdp_server.h"
#include "config.h" #include "config.h"
#include "state_mgr.h"
#include "pico/util/queue.h" #include "pico/util/queue.h"
#define MTU_CONTROL 672 #define MTU_CONTROL 672
@@ -41,7 +42,6 @@ static bool check_dse = false;
static int8_t bt_rssi = 0; static int8_t bt_rssi = 0;
unordered_map<uint8_t, vector<uint8_t> > feature_data; unordered_map<uint8_t, vector<uint8_t> > feature_data;
queue_t send_fifo; queue_t send_fifo;
queue_t priority_send_fifo;
struct send_element { struct send_element {
uint8_t data[512]; uint8_t data[512];
@@ -101,7 +101,6 @@ void bt_l2cap_init() {
int bt_init() { int bt_init() {
queue_init(&send_fifo, sizeof(send_element), 10); queue_init(&send_fifo, sizeof(send_element), 10);
queue_init(&priority_send_fifo, sizeof(send_element), 10);
bt_l2cap_init(); bt_l2cap_init();
@@ -448,30 +447,19 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
init_feature(); init_feature();
// 初始化手柄状态 // 初始化手柄状态
uint8_t report32[142]; uint8_t report32[142]{};
report32[0] = 0x32; report32[0] = 0x32;
report32[1] = 0x10; // reportSeqCounter report32[1] = 0x10; // reportSeqCounter
uint8_t packet_0x10[] = report32[2] = 0x10 | 0 << 6 | 1 << 7;
{ report32[3] = 0x3f; // 63 bytes
0x90, // Packet: 0x10 state_set(report32 + 4,sizeof(SetStateData));
0x3f, // 63
// SetStateData
0xfd, 0xf7, 0x0, 0x0,
0x7f, 0x7f, // Headphones, Speaker
0xff, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
0x7, 0x0, 0x0, 0x2, 0x1,
0x00,
0xff, 0xd7, 0x00 // RGB LED: R, G, B (Nijika Color!)✨
};
memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10));
bt_write(report32, sizeof(report32)); bt_write(report32, sizeof(report32));
const auto mtu = l2cap_get_remote_mtu_for_local_cid(hid_interrupt_cid); const auto mtu = l2cap_get_remote_mtu_for_local_cid(hid_interrupt_cid);
printf("[L2CAP] Remote Interrupt MTU: %d\n",mtu); printf("[L2CAP] Remote Interrupt MTU: %d\n",mtu);
gap_connectable_control(false);
gap_discoverable_control(false);
// tud_connect(); // tud_connect();
} else { } else {
printf("[L2CAP] Unknown Channel psm: 0x%02X", psm); printf("[L2CAP] Unknown Channel psm: 0x%02X", psm);
@@ -521,19 +509,13 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
// printf("[L2CAP] L2CAP_EVENT_CAN_SEND_NOW\n"); // printf("[L2CAP] L2CAP_EVENT_CAN_SEND_NOW\n");
send_element send_packet{}; send_element send_packet{};
bool get_data = false; if (queue_try_remove(&send_fifo, &send_packet)) {
if (!queue_is_empty(&priority_send_fifo)) {
get_data = queue_try_remove(&priority_send_fifo, &send_packet);
}else {
get_data = queue_try_remove(&send_fifo, &send_packet);
}
if (get_data) {
const uint8_t status = l2cap_send(hid_interrupt_cid, send_packet.data, send_packet.len); const uint8_t status = l2cap_send(hid_interrupt_cid, send_packet.data, send_packet.len);
if (status != 0) { if (status != 0) {
printf("[L2CAP] L2CAP Send Error, Status: 0x%02X\n", status); printf("[L2CAP] L2CAP Send Error, Status: 0x%02X\n", status);
} }
} }
if (!queue_is_empty(&priority_send_fifo) || !queue_is_empty(&send_fifo)) { if (!queue_is_empty(&send_fifo)) {
l2cap_request_can_send_now_event(hid_interrupt_cid); l2cap_request_can_send_now_event(hid_interrupt_cid);
} }
break; break;
@@ -541,7 +523,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
} }
} }
void bt_write(const uint8_t *data, const uint16_t len, const bool priority) { void bt_write(const uint8_t *data, const uint16_t len) {
if (hid_interrupt_cid == 0) return; if (hid_interrupt_cid == 0) return;
static send_element packet{}; static send_element packet{};
memset(packet.data, 0, 512); memset(packet.data, 0, 512);
@@ -550,18 +532,11 @@ void bt_write(const uint8_t *data, const uint16_t len, const bool priority) {
memcpy(packet.data + 1, data, len); memcpy(packet.data + 1, data, len);
fill_output_report_checksum(packet.data + 1, len); fill_output_report_checksum(packet.data + 1, len);
if (priority) { if (!queue_try_add(&send_fifo, &packet)) {
if (!queue_try_add(&priority_send_fifo, &packet)) { printf("[L2CAP bt_write] Error: Failed to add packet to send FIFO\n");
printf("[L2CAP bt_write] Error: Failed to add packet to priority send FIFO\n"); return;
return;
}
}else {
if (!queue_try_add(&send_fifo, &packet)) {
printf("[L2CAP bt_write] Error: Failed to add packet to send FIFO\n");
return;
}
} }
if (queue_get_level(&send_fifo) + queue_get_level(&priority_send_fifo) == 1) { if (queue_get_level(&send_fifo) == 1) {
l2cap_request_can_send_now_event(hid_interrupt_cid); l2cap_request_can_send_now_event(hid_interrupt_cid);
} }
} }
+1 -1
View File
@@ -19,7 +19,7 @@ int bt_init();
void bt_register_data_callback(bt_data_callback_t callback); void bt_register_data_callback(bt_data_callback_t callback);
void bt_send_packet(uint8_t *data, uint16_t len); void bt_send_packet(uint8_t *data, uint16_t len);
void bt_send_control(uint8_t *data, uint16_t len); void bt_send_control(uint8_t *data, uint16_t len);
void bt_write(const uint8_t *data, uint16_t len, bool priority = false); void bt_write(const uint8_t *data, uint16_t len);
void bt_get_signal_strength(int8_t *rssi); void bt_get_signal_strength(int8_t *rssi);
std::vector<uint8_t> get_feature_data(uint8_t reportId,uint16_t len); std::vector<uint8_t> get_feature_data(uint8_t reportId,uint16_t len);
void init_feature(); void init_feature();
+1 -1
View File
@@ -11,7 +11,7 @@ struct __attribute__((packed)) Config_body {
uint8_t config_version; // Config Version uint8_t config_version; // Config Version
float haptics_gain; // [1.0,2.0] float haptics_gain; // [1.0,2.0]
float speaker_volume; // [-100,0] float speaker_volume; // [-100,0]
uint8_t inactive_time; // [10,60] min uint8_t inactive_time; // [5,60] min
uint8_t disable_inactive_disconnect; // bool: 0 disable,1 enable uint8_t disable_inactive_disconnect; // bool: 0 disable,1 enable
uint8_t disable_pico_led; // bool uint8_t disable_pico_led; // bool
uint8_t polling_rate_mode; // 0: 250Hz, 1: 500Hz, 2: real-time uint8_t polling_rate_mode; // 0: 250Hz, 1: 500Hz, 2: real-time
+11 -2
View File
@@ -12,6 +12,7 @@
#include "hardware/vreg.h" #include "hardware/vreg.h"
#include "hardware/watchdog.h" #include "hardware/watchdog.h"
#include "pico/cyw43_arch.h" #include "pico/cyw43_arch.h"
#include "state_mgr.h"
#if ENABLE_SERIAL #if ENABLE_SERIAL
#include "pico/stdio_usb.h" #include "pico/stdio_usb.h"
#endif #endif
@@ -26,6 +27,7 @@
int reportSeqCounter = 0; int reportSeqCounter = 0;
uint8_t packetCounter = 0; uint8_t packetCounter = 0;
bool spk_active = false;
uint8_t interrupt_in_data[63] = { uint8_t interrupt_in_data[63] = {
0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7, 0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7,
@@ -140,6 +142,7 @@ bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_reques
if (itf == 1) { if (itf == 1) {
printf("[AUDIO] Set interface Speaker to alternate setting %d\n", alt); printf("[AUDIO] Set interface Speaker to alternate setting %d\n", alt);
spk_active = alt;
} }
return true; return true;
@@ -165,14 +168,19 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep
if (report_id == 0) { if (report_id == 0) {
switch (buffer[0]) { switch (buffer[0]) {
case 0x02: { case 0x02: {
uint8_t outputData[78]; state_update(buffer + 1, bufsize - 1);
if (spk_active) {
break;
}
uint8_t outputData[78]{};
outputData[0] = 0x31; outputData[0] = 0x31;
outputData[1] = reportSeqCounter << 4; outputData[1] = reportSeqCounter << 4;
if (++reportSeqCounter == 256) { if (++reportSeqCounter == 256) {
reportSeqCounter = 0; reportSeqCounter = 0;
} }
outputData[2] = 0x10; outputData[2] = 0x10;
memcpy(outputData + 3, buffer + 1, bufsize - 1); // memcpy(outputData + 3, buffer + 1, bufsize - 1);
state_set(outputData + 3,sizeof(SetStateData));
bt_write(outputData, sizeof(outputData)); bt_write(outputData, sizeof(outputData));
break; break;
} }
@@ -243,6 +251,7 @@ int main() {
bt_register_data_callback(on_bt_data); bt_register_data_callback(on_bt_data);
audio_init(); audio_init();
state_init();
#if !ENABLE_SERIAL #if !ENABLE_SERIAL
watchdog_enable(1000, true); watchdog_enable(1000, true);
+153
View File
@@ -0,0 +1,153 @@
//
// Created by awalol on 2026/5/15.
//
#include <cstddef>
#include <cstring>
#include "utils.h"
namespace {
constexpr size_t kAudioControlOffset = offsetof(SetStateData, MuteLightMode) - sizeof(uint8_t);
constexpr size_t kMuteControlOffset = offsetof(SetStateData, RightTriggerFFB) - sizeof(uint8_t);
constexpr size_t kMotorPowerLevelOffset = offsetof(SetStateData, HostTimestamp) + sizeof(uint32_t);
constexpr size_t kAudioControl2Offset = kMotorPowerLevelOffset + sizeof(uint8_t);
constexpr size_t kHapticLowPassFilterOffset = offsetof(SetStateData, LightFadeAnimation) - 2 * sizeof(uint8_t);
constexpr size_t kPlayerIndicatorsOffset = offsetof(SetStateData, LedRed) - sizeof(uint8_t);
}
static constexpr uint8_t state_init_data[63] = {
0xfd, 0xf7, 0x0, 0x0,
0x7f, 0x64, // Headphones, Speaker
0xff, 0x9, 0x0, 0x0F, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
0x7, 0x0, 0x0, 0x2, 0x1,
0x00,
0xff, 0xd7, 0x00 // RGB LED: R, G, B (Nijika Color!)✨
};
uint8_t state[63]{};
void state_init() {
memcpy(state, state_init_data, sizeof(state));
}
void state_set(uint8_t *data, const uint8_t size) {
if (size > 63) {
printf("[StateMgr] Warning: State Set over 63 bytes\n");
}
memcpy(data, state, size);
}
void state_update(const uint8_t *data, const uint8_t size) {
if (size < sizeof(SetStateData)) {
printf(
"[StateMgr] Error: SetStateData at least %u bytes\n",
static_cast<unsigned>(sizeof(SetStateData))
);
return;
}
SetStateData update{};
memcpy(&update, data, sizeof(update));
const auto copy_if_allowed = [&](const bool allowed, const size_t offset, const size_t length) {
if (allowed) {
memcpy(state + offset, data + offset, length);
}
};
auto set_bit = [](uint8_t &byte, const int bit, const bool value) {
byte = (byte & ~(1 << bit)) | (value << bit);
};
set_bit(state[0], 1, update.UseRumbleNotHaptics);
set_bit(state[38], 2, update.EnableImprovedRumbleEmulation);
copy_if_allowed(
update.EnableRumbleEmulation,
offsetof(SetStateData, RumbleEmulationRight),
2
);
/*copy_if_allowed(
update.AllowHeadphoneVolume,
offsetof(SetStateData, VolumeHeadphones),
sizeof(update.VolumeHeadphones)
);*/
/*copy_if_allowed(
update.AllowSpeakerVolume,
offsetof(SetStateData, VolumeSpeaker),
sizeof(update.VolumeSpeaker)
);*/
/*copy_if_allowed(
update.AllowMicVolume,
offsetof(SetStateData, VolumeMic),
sizeof(update.VolumeMic)
);*/
/*copy_if_allowed(
update.AllowAudioControl,
kAudioControlOffset,
sizeof(uint8_t)
);*/
copy_if_allowed(
update.AllowMuteLight,
offsetof(SetStateData, MuteLightMode),
sizeof(update.MuteLightMode)
);
/*copy_if_allowed(
update.AllowAudioMute,
kMuteControlOffset,
sizeof(uint8_t)
);*/
copy_if_allowed(
update.AllowRightTriggerFFB,
offsetof(SetStateData, RightTriggerFFB),
sizeof(update.RightTriggerFFB)
);
copy_if_allowed(
update.AllowLeftTriggerFFB,
offsetof(SetStateData, LeftTriggerFFB),
sizeof(update.LeftTriggerFFB)
);
/*copy_if_allowed(
update.AllowMotorPowerLevel,
kMotorPowerLevelOffset,
sizeof(uint8_t)
);*/
/*copy_if_allowed(
update.AllowAudioControl2,
kAudioControl2Offset,
sizeof(uint8_t)
);*/
/*copy_if_allowed(
update.AllowHapticLowPassFilter,
kHapticLowPassFilterOffset,
sizeof(uint8_t)
);*/
copy_if_allowed(
update.AllowColorLightFadeAnimation,
offsetof(SetStateData, LightFadeAnimation),
sizeof(update.LightFadeAnimation)
);
copy_if_allowed(
update.AllowLightBrightnessChange,
offsetof(SetStateData, LightBrightness),
sizeof(update.LightBrightness)
);
copy_if_allowed(
update.AllowPlayerIndicators,
kPlayerIndicatorsOffset,
sizeof(uint8_t)
);
copy_if_allowed(
update.AllowLedColor,
offsetof(SetStateData, LedRed),
sizeof(update.LedRed) * 3
);
}
+12
View File
@@ -0,0 +1,12 @@
//
// Created by awalol on 2026/5/15.
//
#ifndef DS5_BRIDGE_STATE_MGR_H
#define DS5_BRIDGE_STATE_MGR_H
void state_init();
void state_set(uint8_t *data, const uint8_t size);
void state_update(const uint8_t *data, const uint8_t size);
#endif //DS5_BRIDGE_STATE_MGR_H
+226 -68
View File
@@ -145,7 +145,7 @@ inline void fill_feature_report_checksum(uint8_t *data, const size_t len) {
data[len - 1] = (crc >> 24) & 0xFF; data[len - 1] = (crc >> 24) & 0xFF;
} }
enum PowerState : uint8_t { /*enum PowerState : uint8_t {
Discharging = 0x00, // Use PowerPercent Discharging = 0x00, // Use PowerPercent
Charging = 0x01, // Use PowerPercent Charging = 0x01, // Use PowerPercent
Complete = 0x02, // PowerPercent not valid? assume 100%? Complete = 0x02, // PowerPercent not valid? assume 100%?
@@ -167,88 +167,246 @@ enum Direction : uint8_t {
}; };
struct __attribute__((packed)) TouchFingerData { // 4 struct __attribute__((packed)) TouchFingerData { // 4
/*0.0*/ uint32_t Index : 7; /*0.0#1# uint32_t Index : 7;
/*0.7*/ uint32_t NotTouching : 1; /*0.7#1# uint32_t NotTouching : 1;
/*1.0*/ uint32_t FingerX : 12; /*1.0#1# uint32_t FingerX : 12;
/*2.4*/ uint32_t FingerY : 12; /*2.4#1# uint32_t FingerY : 12;
}; };
struct __attribute__((packed)) TouchData { // 9 struct __attribute__((packed)) TouchData { // 9
/*0*/ TouchFingerData Finger[2]; /*0#1# TouchFingerData Finger[2];
/*8*/ uint8_t Timestamp; /*8#1# uint8_t Timestamp;
}; };
struct __attribute__((packed)) USBGetStateData { // 63 struct __attribute__((packed)) USBGetStateData { // 63
/* 0 */ uint8_t LeftStickX; /* 0 #1# uint8_t LeftStickX;
/* 1 */ uint8_t LeftStickY; /* 1 #1# uint8_t LeftStickY;
/* 2 */ uint8_t RightStickX; /* 2 #1# uint8_t RightStickX;
/* 3 */ uint8_t RightStickY; /* 3 #1# uint8_t RightStickY;
/* 4 */ uint8_t TriggerLeft; /* 4 #1# uint8_t TriggerLeft;
/* 5 */ uint8_t TriggerRight; /* 5 #1# uint8_t TriggerRight;
/* 6 */ uint8_t SeqNo; // always 0x01 on BT /* 6 #1# uint8_t SeqNo; // always 0x01 on BT
/* 7.0*/ Direction DPad : 4; /* 7.0#1# Direction DPad : 4;
/* 7.4*/ uint8_t ButtonSquare : 1; /* 7.4#1# uint8_t ButtonSquare : 1;
/* 7.5*/ uint8_t ButtonCross : 1; /* 7.5#1# uint8_t ButtonCross : 1;
/* 7.6*/ uint8_t ButtonCircle : 1; /* 7.6#1# uint8_t ButtonCircle : 1;
/* 7.7*/ uint8_t ButtonTriangle : 1; /* 7.7#1# uint8_t ButtonTriangle : 1;
/* 8.0*/ uint8_t ButtonL1 : 1; /* 8.0#1# uint8_t ButtonL1 : 1;
/* 8.1*/ uint8_t ButtonR1 : 1; /* 8.1#1# uint8_t ButtonR1 : 1;
/* 8.2*/ uint8_t ButtonL2 : 1; /* 8.2#1# uint8_t ButtonL2 : 1;
/* 8.3*/ uint8_t ButtonR2 : 1; /* 8.3#1# uint8_t ButtonR2 : 1;
/* 8.4*/ uint8_t ButtonCreate : 1; /* 8.4#1# uint8_t ButtonCreate : 1;
/* 8.5*/ uint8_t ButtonOptions : 1; /* 8.5#1# uint8_t ButtonOptions : 1;
/* 8.6*/ uint8_t ButtonL3 : 1; /* 8.6#1# uint8_t ButtonL3 : 1;
/* 8.7*/ uint8_t ButtonR3 : 1; /* 8.7#1# uint8_t ButtonR3 : 1;
/* 9.0*/ uint8_t ButtonHome : 1; /* 9.0#1# uint8_t ButtonHome : 1;
/* 9.1*/ uint8_t ButtonPad : 1; /* 9.1#1# uint8_t ButtonPad : 1;
/* 9.2*/ uint8_t ButtonMute : 1; /* 9.2#1# uint8_t ButtonMute : 1;
/* 9.3*/ uint8_t UNK1 : 1; // appears unused /* 9.3#1# uint8_t UNK1 : 1; // appears unused
/* 9.4*/ uint8_t ButtonLeftFunction : 1; // DualSense Edge /* 9.4#1# uint8_t ButtonLeftFunction : 1; // DualSense Edge
/* 9.5*/ uint8_t ButtonRightFunction : 1; // DualSense Edge /* 9.5#1# uint8_t ButtonRightFunction : 1; // DualSense Edge
/* 9.6*/ uint8_t ButtonLeftPaddle : 1; // DualSense Edge /* 9.6#1# uint8_t ButtonLeftPaddle : 1; // DualSense Edge
/* 9.7*/ uint8_t ButtonRightPaddle : 1; // DualSense Edge /* 9.7#1# uint8_t ButtonRightPaddle : 1; // DualSense Edge
/*10 */ uint8_t UNK2; // appears unused /*10 #1# uint8_t UNK2; // appears unused
/*11 */ uint32_t UNK_COUNTER; // Linux driver calls this reserved, tools leak calls the 2 high bytes "random" /*11 #1# uint32_t UNK_COUNTER; // Linux driver calls this reserved, tools leak calls the 2 high bytes "random"
/*15 */ int16_t AngularVelocityX; /*15 #1# int16_t AngularVelocityX;
/*17 */ int16_t AngularVelocityZ; /*17 #1# int16_t AngularVelocityZ;
/*19 */ int16_t AngularVelocityY; /*19 #1# int16_t AngularVelocityY;
/*21 */ int16_t AccelerometerX; /*21 #1# int16_t AccelerometerX;
/*23 */ int16_t AccelerometerY; /*23 #1# int16_t AccelerometerY;
/*25 */ int16_t AccelerometerZ; /*25 #1# int16_t AccelerometerZ;
/*27 */ uint32_t SensorTimestamp; /*27 #1# uint32_t SensorTimestamp;
/*31 */ int8_t Temperature; // reserved2 in Linux driver /*31 #1# int8_t Temperature; // reserved2 in Linux driver
/*32 */ TouchData Touch; /*32 #1# TouchData Touch;
/*41.0*/ uint8_t TriggerRightStopLocation: 4; // trigger stop can be a range from 0 to 9 (F/9.0 for Apple interface) /*41.0#1# uint8_t TriggerRightStopLocation: 4; // trigger stop can be a range from 0 to 9 (F/9.0 for Apple interface)
/*41.4*/ uint8_t TriggerRightStatus: 4; /*41.4#1# uint8_t TriggerRightStatus: 4;
/*42.0*/ uint8_t TriggerLeftStopLocation: 4; /*42.0#1# uint8_t TriggerLeftStopLocation: 4;
/*42.4*/ uint8_t TriggerLeftStatus: 4; // 0 feedbackNoLoad /*42.4#1# uint8_t TriggerLeftStatus: 4; // 0 feedbackNoLoad
// 1 feedbackLoadApplied // 1 feedbackLoadApplied
// 0 weaponReady // 0 weaponReady
// 1 weaponFiring // 1 weaponFiring
// 2 weaponFired // 2 weaponFired
// 0 vibrationNotVibrating // 0 vibrationNotVibrating
// 1 vibrationIsVibrating // 1 vibrationIsVibrating
/*43 */ uint32_t HostTimestamp; // mirrors data from report write /*43 #1# uint32_t HostTimestamp; // mirrors data from report write
/*47.0*/ uint8_t TriggerRightEffect: 4; // Active trigger effect, previously we thought this was status max /*47.0#1# uint8_t TriggerRightEffect: 4; // Active trigger effect, previously we thought this was status max
/*47.4*/ uint8_t TriggerLeftEffect: 4; // 0 for reset and all other effects /*47.4#1# uint8_t TriggerLeftEffect: 4; // 0 for reset and all other effects
// 1 for feedback effect // 1 for feedback effect
// 2 for weapon effect // 2 for weapon effect
// 3 for vibration // 3 for vibration
/*48 */ uint32_t DeviceTimeStamp; /*48 #1# uint32_t DeviceTimeStamp;
/*52.0*/ uint8_t PowerPercent : 4; // 0x00-0x0A /*52.0#1# uint8_t PowerPercent : 4; // 0x00-0x0A
/*52.4*/ PowerState Power : 4; /*52.4#1# PowerState Power : 4;
/*53.0*/ uint8_t PluggedHeadphones : 1; /*53.0#1# uint8_t PluggedHeadphones : 1;
/*53.1*/ uint8_t PluggedMic : 1; /*53.1#1# uint8_t PluggedMic : 1;
/*53.2*/ uint8_t MicMuted: 1; // Mic muted by powersave/mute command /*53.2#1# uint8_t MicMuted: 1; // Mic muted by powersave/mute command
/*53.3*/ uint8_t PluggedUsbData : 1; /*53.3#1# uint8_t PluggedUsbData : 1;
/*53.4*/ uint8_t PluggedUsbPower : 1; // appears that this cannot be 1 if PluggedUsbData is 1 /*53.4#1# uint8_t PluggedUsbPower : 1; // appears that this cannot be 1 if PluggedUsbData is 1
/*53.5*/ uint8_t UsbPowerOnBT : 1; // appears this is only 1 if BT connected and USB powered /*53.5#1# uint8_t UsbPowerOnBT : 1; // appears this is only 1 if BT connected and USB powered
/*53.5*/ uint8_t DockDetect : 1; /*53.5#1# uint8_t DockDetect : 1;
/*53.5*/ uint8_t PluggedUnk : 1; /*53.5#1# uint8_t PluggedUnk : 1;
/*54.0*/ uint8_t PluggedExternalMic : 1; // Is external mic active (automatic in mic auto mode) /*54.0#1# uint8_t PluggedExternalMic : 1; // Is external mic active (automatic in mic auto mode)
/*54.1*/ uint8_t HapticLowPassFilter : 1; // Is the Haptic Low-Pass-Filter active? /*54.1#1# uint8_t HapticLowPassFilter : 1; // Is the Haptic Low-Pass-Filter active?
/*54.2*/ uint8_t PluggedUnk3 : 6; /*54.2#1# uint8_t PluggedUnk3 : 6;
/*55 */ uint8_t AesCmac[8]; /*55 #1# uint8_t AesCmac[8];
};*/
namespace MuteLight{
enum MuteLight : uint8_t {
Off = 0,
On,
Breathing,
DoNothing, // literally nothing, this input is ignored,
// though it might be a faster blink in other versions
NoAction4,
NoAction5,
NoAction6,
NoAction7= 7
};
}
namespace LightBrightness{
enum LightBrightness : uint8_t {
Bright = 0,
Mid,
Dim,
NoAction3,
NoAction4,
NoAction5,
NoAction6,
NoAction7= 7
};
}
namespace LightFadeAnimation {
enum LightFadeAnimation : uint8_t {
Nothing = 0,
FadeIn, // from black to blue
FadeOut // from blue to black
};
}
struct __attribute__((packed)) SetStateData { // 47
/* */ // Report Set Flags
/* */ // These flags are used to indicate what contents from this report should be processed
/* 0.0*/ uint8_t EnableRumbleEmulation: 1; // Suggest halving rumble strength
/* 0.1*/ uint8_t UseRumbleNotHaptics: 1; //
/* */
/* 0.2*/ uint8_t AllowRightTriggerFFB: 1; // Enable setting RightTriggerFFB
/* 0.3*/ uint8_t AllowLeftTriggerFFB: 1; // Enable setting LeftTriggerFFB
/* */
/* 0.4*/ uint8_t AllowHeadphoneVolume: 1; // Enable setting VolumeHeadphones
/* 0.5*/ uint8_t AllowSpeakerVolume: 1; // Enable setting VolumeSpeaker
/* 0.6*/ uint8_t AllowMicVolume: 1; // Enable setting VolumeMic
/* */
/* 0.7*/ uint8_t AllowAudioControl: 1; // Enable setting AudioControl section
/* 1.0*/ uint8_t AllowMuteLight: 1; // Enable setting MuteLightMode
/* 1.1*/ uint8_t AllowAudioMute: 1; // Enable setting MuteControl section
/* */
/* 1.2*/ uint8_t AllowLedColor: 1; // Enable RGB LED section
/* */
/* 1.3*/ uint8_t ResetLights: 1; // Release the LEDs from Wireless firmware control
/* */ // When in wireless mode this must be signaled to control LEDs
/* */ // This cannot be applied during the BT pair animation.
/* */ // SDL2 waits until the SensorTimestamp value is >= 10200000
/* */ // before pulsing this bit once.
/* */
/* 1.4*/ uint8_t AllowPlayerIndicators: 1; // Enable setting PlayerIndicators section
/* 1.5*/ uint8_t AllowHapticLowPassFilter: 1; // Enable HapticLowPassFilter
/* 1.6*/ uint8_t AllowMotorPowerLevel: 1; // MotorPowerLevel reductions for trigger/haptic
/* 1.7*/ uint8_t AllowAudioControl2: 1; // Enable setting AudioControl2 section
/* */
/* 2 */ uint8_t RumbleEmulationRight; // emulates the light weight
/* 3 */ uint8_t RumbleEmulationLeft; // emulated the heavy weight
/* */
/* 4 */ uint8_t VolumeHeadphones; // max 0x7f
/* 5 */ uint8_t VolumeSpeaker; // PS5 appears to only use the range 0x3d-0x64
/* 6 */ uint8_t VolumeMic; // not linear, seems to max at 64, 0 is fully muted only in chat mode
/* */
/* */ // AudioControl
/* 7.0*/ uint8_t MicSelect: 2; // 0 Auto
/* */ // 1 Internal Only
/* */ // 2 External Only
/* */ // 3 Unclear, sets external mic flag but might use internal mic, do test
/* 7.2*/ uint8_t EchoCancelEnable: 1;
/* 7.3*/ uint8_t NoiseCancelEnable: 1;
/* 7.4*/ uint8_t OutputPathSelect: 2; // 0 L_R_X
/* */ // 1 L_L_X
/* */ // 2 L_L_R
/* */ // 3 X_X_R
/* 7.6*/ uint8_t InputPathSelect: 2; // 0 CHAT_ASR
/* */ // 1 CHAT_CHAT
/* */ // 2 ASR_ASR
/* */ // 3 Does Nothing, invalid
/* */
/* 8 */ MuteLight::MuteLight MuteLightMode;
/* */
/* */ // MuteControl
/* 9.0*/ uint8_t TouchPowerSave: 1;
/* 9.1*/ uint8_t MotionPowerSave: 1;
/* 9.2*/ uint8_t HapticPowerSave: 1; // AKA BulletPowerSave
/* 9.3*/ uint8_t AudioPowerSave: 1;
/* 9.4*/ uint8_t MicMute: 1;
/* 9.5*/ uint8_t SpeakerMute: 1;
/* 9.6*/ uint8_t HeadphoneMute: 1;
/* 9.7*/ uint8_t HapticMute: 1; // AKA BulletMute
/* */
/*10 */ uint8_t RightTriggerFFB[11];
/*21 */ uint8_t LeftTriggerFFB[11];
/*32 */ uint32_t HostTimestamp; // mirrored into report read
/* */
/* */ // MotorPowerLevel
/*36.0*/ uint8_t TriggerMotorPowerReduction : 4; // 0x0-0x7 (no 0x8?) Applied in 12.5% reductions
/*36.4*/ uint8_t RumbleMotorPowerReduction : 4; // 0x0-0x7 (no 0x8?) Applied in 12.5% reductions
/* */
/* */ // AudioControl2
/*37.0*/ uint8_t SpeakerCompPreGain: 3; // additional speaker volume boost
/*37.3*/ uint8_t BeamformingEnable: 1; // Probably for MIC given there's 2, might be more bits, can't find what it does
/*37.4*/ uint8_t UnkAudioControl2: 4; // some of these bits might apply to the above
/* */
/*38.0*/ uint8_t AllowLightBrightnessChange: 1; // LED_BRIHTNESS_CONTROL
/*38.1*/ uint8_t AllowColorLightFadeAnimation: 1; // LIGHTBAR_SETUP_CONTROL
/*38.2*/ uint8_t EnableImprovedRumbleEmulation: 1; // Use instead of EnableRumbleEmulation
// requires FW >= 0x0224
// No need to halve rumble strength
/*38.3*/ uint8_t UNKBITC: 5; // unused
/* */
/*39.0*/ uint8_t HapticLowPassFilter: 1;
/*39.1*/ uint8_t UNKBIT: 7;
/* */
/*40 */ uint8_t UNKBYTE; // previous notes suggested this was HLPF, was probably off by 1
/* */
/*41 */ LightFadeAnimation::LightFadeAnimation LightFadeAnimation;
/*42 */ LightBrightness::LightBrightness LightBrightness;
/* */
/* */ // PlayerIndicators
/* */ // These bits control the white LEDs under the touch pad.
/* */ // Note the reduction in functionality for later revisions.
/* */ // Generation 0x03 - Full Functionality
/* */ // Generation 0x04 - Mirrored Only
/* */ // Suggested detection: (HardwareInfo & 0x00FFFF00) == 0X00000400
/* */ //
/* */ // Layout used by PS5:
/* */ // 0x04 - -x- - Player 1
/* */ // 0x06 - x-x - Player 2
/* */ // 0x15 x -x- x Player 3
/* */ // 0x1B x x-x x Player 4
/* */ // 0x1F x xxx x Player 5* (Unconfirmed)
/* */ //
/* */ // // HW 0x03 // HW 0x04
/*43.0*/ uint8_t PlayerLight1 : 1; // x --- - // x --- x
/*43.1*/ uint8_t PlayerLight2 : 1; // - x-- - // - x-x -
/*43.2*/ uint8_t PlayerLight3 : 1; // - -x- - // - -x- -
/*43.3*/ uint8_t PlayerLight4 : 1; // - --x - // - x-x -
/*43.4*/ uint8_t PlayerLight5 : 1; // - --- x // x --- x
/*43.5*/ uint8_t PlayerLightFade: 1; // if low player lights fade in, if high player lights instantly change
/*43.6*/ uint8_t PlayerLightUNK : 2;
/* */
/* */ // RGB LED
/*44 */ uint8_t LedRed;
/*45 */ uint8_t LedGreen;
/*46 */ uint8_t LedBlue;
// Structure ends here though on BT there is padding and a CRC, see ReportOut31
}; };
inline void print_hex(const uint8_t* data,size_t size) { inline void print_hex(const uint8_t* data,size_t size) {