diff --git a/CMakeLists.txt b/CMakeLists.txt index e660178..cb7d8a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ add_executable(ds5-bridge src/bt.cpp src/config.cpp src/cmd.cpp + src/state_mgr.cpp ) if (ENABLE_BATT_LED) diff --git a/src/audio.cpp b/src/audio.cpp index 735db5f..eee41d0 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -14,6 +14,7 @@ #include "pico/multicore.h" #include "pico/util/queue.h" #include "config.h" +#include "state_mgr.h" #include "usb.h" #define INPUT_CHANNELS 4 @@ -116,20 +117,23 @@ void audio_loop() { pkt[8] = buf_len; // 这 4 个字节的作用未知,调整没有效果 pkt[9] = buf_len; // audio buffer length 只有调整这个字节生效。 pkt[10] = packetCounter++; - pkt[11] = 0x12 | 0 << 6 | 1 << 7; - pkt[12] = SAMPLE_SIZE; - memcpy(pkt + 13, haptic_buf, SAMPLE_SIZE); -#if !DISABLE_SPEAKER_PROC - pkt[77] = (plug_headset ? 0x16 : 0x13) | 0 << 6 | 1 << 7; // Speaker: 0x13 + pkt[11] = 0x10 | 0 << 6 | 1 << 7; + pkt[12] = 63; + state_set(pkt + 13,63); + // memcpy(pkt + 13, state_data, sizeof(state_data)); + pkt[76] = 0x12 | 0 << 6 | 1 << 7; + pkt[77] = SAMPLE_SIZE; + memcpy(pkt + 78, haptic_buf, SAMPLE_SIZE); + pkt[142] = (plug_headset ? 0x16 : 0x13) | 0 << 6 | 1 << 7; // Speaker: 0x13 // L Headset Mono: 0x14 // L Headset R Speaker: 0x15 // Headset: 0x16 - pkt[78] = 200; + pkt[143] = 200; critical_section_enter_blocking(&opus_cs); - memcpy(pkt + 79, opus_buf, 200); + memcpy(pkt + 144, opus_buf, 200); critical_section_exit(&opus_cs); -#endif - bt_write(pkt, sizeof(pkt), true); + + bt_write(pkt, sizeof(pkt)); haptic_buf_pos = 0; } } diff --git a/src/bt.cpp b/src/bt.cpp index d79f772..8f2568b 100644 --- a/src/bt.cpp +++ b/src/bt.cpp @@ -15,6 +15,7 @@ #include "bsp/board_api.h" #include "classic/sdp_server.h" #include "config.h" +#include "state_mgr.h" #include "pico/util/queue.h" #define MTU_CONTROL 672 @@ -39,7 +40,6 @@ static bt_data_callback_t bt_data_callback = nullptr; static bool check_dse = false; unordered_map > feature_data; queue_t send_fifo; -queue_t priority_send_fifo; struct send_element { uint8_t data[512]; @@ -87,7 +87,6 @@ void bt_l2cap_init() { int bt_init() { queue_init(&send_fifo, sizeof(send_element), 10); - queue_init(&priority_send_fifo, sizeof(send_element), 10); bt_l2cap_init(); @@ -417,25 +416,10 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t init_feature(); // 初始化手柄状态 - uint8_t report32[142]; + uint8_t report32[142]{}; report32[0] = 0x32; report32[1] = 0x10; // reportSeqCounter - uint8_t packet_0x10[] = - { - 0x90, // Packet: 0x10 - 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)); + state_set(report32 + 2,sizeof(SetStateData)); bt_write(report32, sizeof(report32)); const auto mtu = l2cap_get_remote_mtu_for_local_cid(hid_interrupt_cid); @@ -492,19 +476,13 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t // printf("[L2CAP] L2CAP_EVENT_CAN_SEND_NOW\n"); send_element send_packet{}; - bool get_data = false; - 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) { + if (queue_try_remove(&send_fifo, &send_packet)) { const uint8_t status = l2cap_send(hid_interrupt_cid, send_packet.data, send_packet.len); if (status != 0) { 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); } break; @@ -512,7 +490,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; static send_element packet{}; memset(packet.data, 0, 512); @@ -521,18 +499,11 @@ void bt_write(const uint8_t *data, const uint16_t len, const bool priority) { memcpy(packet.data + 1, data, len); fill_output_report_checksum(packet.data + 1, len); - if (priority) { - if (!queue_try_add(&priority_send_fifo, &packet)) { - printf("[L2CAP bt_write] Error: Failed to add packet to priority send FIFO\n"); - 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_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); } } diff --git a/src/bt.h b/src/bt.h index 6404f9b..bbde89f 100644 --- a/src/bt.h +++ b/src/bt.h @@ -19,7 +19,7 @@ int bt_init(); void bt_register_data_callback(bt_data_callback_t callback); void bt_send_packet(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); std::vector get_feature_data(uint8_t reportId,uint16_t len); void init_feature(); void set_feature_data(uint8_t reportId, uint8_t* data,uint16_t len); diff --git a/src/main.cpp b/src/main.cpp index 7f58e79..e384e6c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include "hardware/vreg.h" #include "hardware/watchdog.h" #include "pico/cyw43_arch.h" +#include "state_mgr.h" #if ENABLE_SERIAL #include "pico/stdio_usb.h" #endif @@ -26,6 +27,7 @@ int reportSeqCounter = 0; uint8_t packetCounter = 0; +bool spk_active = false; uint8_t interrupt_in_data[63] = { 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) { printf("[AUDIO] Set interface Speaker to alternate setting %d\n", alt); + spk_active = alt; } 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) { switch (buffer[0]) { case 0x02: { - uint8_t outputData[78]; + state_update(buffer + 1, bufsize - 1); + if (spk_active) { + break; + } + uint8_t outputData[78]{}; outputData[0] = 0x31; outputData[1] = reportSeqCounter << 4; if (++reportSeqCounter == 256) { reportSeqCounter = 0; } 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)); break; } @@ -243,6 +251,7 @@ int main() { bt_register_data_callback(on_bt_data); audio_init(); + state_init(); #if !ENABLE_SERIAL watchdog_enable(1000, true); diff --git a/src/state_mgr.cpp b/src/state_mgr.cpp new file mode 100644 index 0000000..c265ac6 --- /dev/null +++ b/src/state_mgr.cpp @@ -0,0 +1,86 @@ +// +// Created by awalol on 2026/5/15. +// + +#include +#include + +#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, 0x7f, // 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(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); + } + }; + + const auto copy_field = [&](const bool allowed, const size_t offset, const auto &field) { + copy_if_allowed(allowed, offset, sizeof(field)); + }; + + /*copy_if_allowed( + update.EnableRumbleEmulation || update.UseRumbleNotHaptics, + offsetof(SetStateData, RumbleEmulationRight), + 2 + );*/ + // copy_field(update.AllowHeadphoneVolume, offsetof(SetStateData, VolumeHeadphones), update.VolumeHeadphones); + // copy_field(update.AllowSpeakerVolume, offsetof(SetStateData, VolumeSpeaker), update.VolumeSpeaker); + // copy_field(update.AllowMicVolume, offsetof(SetStateData, VolumeMic), update.VolumeMic); + // copy_if_allowed(update.AllowAudioControl, kAudioControlOffset, sizeof(uint8_t)); + copy_field(update.AllowMuteLight, offsetof(SetStateData, MuteLightMode), update.MuteLightMode); + // copy_if_allowed(update.AllowAudioMute, kMuteControlOffset, sizeof(uint8_t)); + copy_field(update.AllowRightTriggerFFB, offsetof(SetStateData, RightTriggerFFB), update.RightTriggerFFB); + copy_field(update.AllowLeftTriggerFFB, offsetof(SetStateData, LeftTriggerFFB), 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_field(update.AllowColorLightFadeAnimation, offsetof(SetStateData, LightFadeAnimation), update.LightFadeAnimation); + copy_field(update.AllowLightBrightnessChange, offsetof(SetStateData, LightBrightness), update.LightBrightness); + copy_if_allowed(update.AllowPlayerIndicators, kPlayerIndicatorsOffset, sizeof(uint8_t)); + copy_if_allowed(update.AllowLedColor, offsetof(SetStateData, LedRed), sizeof(update.LedRed) * 3); +} diff --git a/src/state_mgr.h b/src/state_mgr.h new file mode 100644 index 0000000..85f0d23 --- /dev/null +++ b/src/state_mgr.h @@ -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 diff --git a/src/utils.h b/src/utils.h index 29db588..57b1886 100644 --- a/src/utils.h +++ b/src/utils.h @@ -143,7 +143,7 @@ inline void fill_feature_report_checksum(uint8_t *data, const size_t len) { data[len - 1] = (crc >> 24) & 0xFF; } -enum PowerState : uint8_t { +/*enum PowerState : uint8_t { Discharging = 0x00, // Use PowerPercent Charging = 0x01, // Use PowerPercent Complete = 0x02, // PowerPercent not valid? assume 100%? @@ -165,88 +165,246 @@ enum Direction : uint8_t { }; struct __attribute__((packed)) TouchFingerData { // 4 - /*0.0*/ uint32_t Index : 7; - /*0.7*/ uint32_t NotTouching : 1; - /*1.0*/ uint32_t FingerX : 12; - /*2.4*/ uint32_t FingerY : 12; + /*0.0#1# uint32_t Index : 7; + /*0.7#1# uint32_t NotTouching : 1; + /*1.0#1# uint32_t FingerX : 12; + /*2.4#1# uint32_t FingerY : 12; }; struct __attribute__((packed)) TouchData { // 9 - /*0*/ TouchFingerData Finger[2]; - /*8*/ uint8_t Timestamp; + /*0#1# TouchFingerData Finger[2]; + /*8#1# uint8_t Timestamp; }; struct __attribute__((packed)) USBGetStateData { // 63 -/* 0 */ uint8_t LeftStickX; -/* 1 */ uint8_t LeftStickY; -/* 2 */ uint8_t RightStickX; -/* 3 */ uint8_t RightStickY; -/* 4 */ uint8_t TriggerLeft; -/* 5 */ uint8_t TriggerRight; -/* 6 */ uint8_t SeqNo; // always 0x01 on BT -/* 7.0*/ Direction DPad : 4; -/* 7.4*/ uint8_t ButtonSquare : 1; -/* 7.5*/ uint8_t ButtonCross : 1; -/* 7.6*/ uint8_t ButtonCircle : 1; -/* 7.7*/ uint8_t ButtonTriangle : 1; -/* 8.0*/ uint8_t ButtonL1 : 1; -/* 8.1*/ uint8_t ButtonR1 : 1; -/* 8.2*/ uint8_t ButtonL2 : 1; -/* 8.3*/ uint8_t ButtonR2 : 1; -/* 8.4*/ uint8_t ButtonCreate : 1; -/* 8.5*/ uint8_t ButtonOptions : 1; -/* 8.6*/ uint8_t ButtonL3 : 1; -/* 8.7*/ uint8_t ButtonR3 : 1; -/* 9.0*/ uint8_t ButtonHome : 1; -/* 9.1*/ uint8_t ButtonPad : 1; -/* 9.2*/ uint8_t ButtonMute : 1; -/* 9.3*/ uint8_t UNK1 : 1; // appears unused -/* 9.4*/ uint8_t ButtonLeftFunction : 1; // DualSense Edge -/* 9.5*/ uint8_t ButtonRightFunction : 1; // DualSense Edge -/* 9.6*/ uint8_t ButtonLeftPaddle : 1; // DualSense Edge -/* 9.7*/ uint8_t ButtonRightPaddle : 1; // DualSense Edge -/*10 */ uint8_t UNK2; // appears unused -/*11 */ uint32_t UNK_COUNTER; // Linux driver calls this reserved, tools leak calls the 2 high bytes "random" -/*15 */ int16_t AngularVelocityX; -/*17 */ int16_t AngularVelocityZ; -/*19 */ int16_t AngularVelocityY; -/*21 */ int16_t AccelerometerX; -/*23 */ int16_t AccelerometerY; -/*25 */ int16_t AccelerometerZ; -/*27 */ uint32_t SensorTimestamp; -/*31 */ int8_t Temperature; // reserved2 in Linux driver -/*32 */ 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.4*/ uint8_t TriggerRightStatus: 4; -/*42.0*/ uint8_t TriggerLeftStopLocation: 4; -/*42.4*/ uint8_t TriggerLeftStatus: 4; // 0 feedbackNoLoad +/* 0 #1# uint8_t LeftStickX; +/* 1 #1# uint8_t LeftStickY; +/* 2 #1# uint8_t RightStickX; +/* 3 #1# uint8_t RightStickY; +/* 4 #1# uint8_t TriggerLeft; +/* 5 #1# uint8_t TriggerRight; +/* 6 #1# uint8_t SeqNo; // always 0x01 on BT +/* 7.0#1# Direction DPad : 4; +/* 7.4#1# uint8_t ButtonSquare : 1; +/* 7.5#1# uint8_t ButtonCross : 1; +/* 7.6#1# uint8_t ButtonCircle : 1; +/* 7.7#1# uint8_t ButtonTriangle : 1; +/* 8.0#1# uint8_t ButtonL1 : 1; +/* 8.1#1# uint8_t ButtonR1 : 1; +/* 8.2#1# uint8_t ButtonL2 : 1; +/* 8.3#1# uint8_t ButtonR2 : 1; +/* 8.4#1# uint8_t ButtonCreate : 1; +/* 8.5#1# uint8_t ButtonOptions : 1; +/* 8.6#1# uint8_t ButtonL3 : 1; +/* 8.7#1# uint8_t ButtonR3 : 1; +/* 9.0#1# uint8_t ButtonHome : 1; +/* 9.1#1# uint8_t ButtonPad : 1; +/* 9.2#1# uint8_t ButtonMute : 1; +/* 9.3#1# uint8_t UNK1 : 1; // appears unused +/* 9.4#1# uint8_t ButtonLeftFunction : 1; // DualSense Edge +/* 9.5#1# uint8_t ButtonRightFunction : 1; // DualSense Edge +/* 9.6#1# uint8_t ButtonLeftPaddle : 1; // DualSense Edge +/* 9.7#1# uint8_t ButtonRightPaddle : 1; // DualSense Edge +/*10 #1# uint8_t UNK2; // appears unused +/*11 #1# uint32_t UNK_COUNTER; // Linux driver calls this reserved, tools leak calls the 2 high bytes "random" +/*15 #1# int16_t AngularVelocityX; +/*17 #1# int16_t AngularVelocityZ; +/*19 #1# int16_t AngularVelocityY; +/*21 #1# int16_t AccelerometerX; +/*23 #1# int16_t AccelerometerY; +/*25 #1# int16_t AccelerometerZ; +/*27 #1# uint32_t SensorTimestamp; +/*31 #1# int8_t Temperature; // reserved2 in Linux driver +/*32 #1# TouchData Touch; +/*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#1# uint8_t TriggerRightStatus: 4; +/*42.0#1# uint8_t TriggerLeftStopLocation: 4; +/*42.4#1# uint8_t TriggerLeftStatus: 4; // 0 feedbackNoLoad // 1 feedbackLoadApplied // 0 weaponReady // 1 weaponFiring // 2 weaponFired // 0 vibrationNotVibrating // 1 vibrationIsVibrating -/*43 */ 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.4*/ uint8_t TriggerLeftEffect: 4; // 0 for reset and all other effects +/*43 #1# uint32_t HostTimestamp; // mirrors data from report write +/*47.0#1# uint8_t TriggerRightEffect: 4; // Active trigger effect, previously we thought this was status max +/*47.4#1# uint8_t TriggerLeftEffect: 4; // 0 for reset and all other effects // 1 for feedback effect // 2 for weapon effect // 3 for vibration -/*48 */ uint32_t DeviceTimeStamp; -/*52.0*/ uint8_t PowerPercent : 4; // 0x00-0x0A -/*52.4*/ PowerState Power : 4; -/*53.0*/ uint8_t PluggedHeadphones : 1; -/*53.1*/ uint8_t PluggedMic : 1; -/*53.2*/ uint8_t MicMuted: 1; // Mic muted by powersave/mute command -/*53.3*/ uint8_t PluggedUsbData : 1; -/*53.4*/ 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*/ uint8_t DockDetect : 1; -/*53.5*/ uint8_t PluggedUnk : 1; -/*54.0*/ 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.2*/ uint8_t PluggedUnk3 : 6; -/*55 */ uint8_t AesCmac[8]; +/*48 #1# uint32_t DeviceTimeStamp; +/*52.0#1# uint8_t PowerPercent : 4; // 0x00-0x0A +/*52.4#1# PowerState Power : 4; +/*53.0#1# uint8_t PluggedHeadphones : 1; +/*53.1#1# uint8_t PluggedMic : 1; +/*53.2#1# uint8_t MicMuted: 1; // Mic muted by powersave/mute command +/*53.3#1# uint8_t PluggedUsbData : 1; +/*53.4#1# uint8_t PluggedUsbPower : 1; // appears that this cannot be 1 if PluggedUsbData is 1 +/*53.5#1# uint8_t UsbPowerOnBT : 1; // appears this is only 1 if BT connected and USB powered +/*53.5#1# uint8_t DockDetect : 1; +/*53.5#1# uint8_t PluggedUnk : 1; +/*54.0#1# uint8_t PluggedExternalMic : 1; // Is external mic active (automatic in mic auto mode) +/*54.1#1# uint8_t HapticLowPassFilter : 1; // Is the Haptic Low-Pass-Filter active? +/*54.2#1# uint8_t PluggedUnk3 : 6; +/*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) {