diff --git a/main.c b/main.c index 8b41250..ff85c45 100644 --- a/main.c +++ b/main.c @@ -67,9 +67,17 @@ #define DS_OUTPUT_VALID_FLAG1_VIBRATION_ATTENUATION_ENABLE BIT(6) #define DS_OUTPUT_VALID_FLAG1_AUDIO_CONTROL2_ENABLE BIT(7) +#define DS_OUTPUT_VALID_FLAG2_LED_BRIGHTNESS_CONTROL_ENABLE BIT(0) #define DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE BIT(1) +#define DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2 BIT(2) +#define DS_OUTPUT_POWER_SAVE_CONTROL_TOUCH BIT(0) +#define DS_OUTPUT_POWER_SAVE_CONTROL_MOTION BIT(1) +#define DS_OUTPUT_POWER_SAVE_CONTROL_HAPTICS BIT(2) +#define DS_OUTPUT_POWER_SAVE_CONTROL_AUDIO BIT(3) #define DS_OUTPUT_POWER_SAVE_CONTROL_MIC_MUTE BIT(4) -#define DS_OUTPUT_POWER_SAVE_CONTROL_AUDIO_MUTE BIT(5) +#define DS_OUTPUT_POWER_SAVE_CONTROL_SPEAKER_MUTE BIT(5) +#define DS_OUTPUT_POWER_SAVE_CONTROL_HEADPHONES_MUTE BIT(6) +#define DS_OUTPUT_POWER_SAVE_CONTROL_HAPTICS_MUTE BIT(7) #define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_ON BIT(0) #define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT BIT(1) @@ -79,12 +87,17 @@ #define DS_OUTPUT_AUDIO_FLAG_ECHO_CANCEL BIT(2) #define DS_OUTPUT_AUDIO_FLAG_NOISE_CANCEL BIT(3) #define DS_OUTPUT_AUDIO_OUTPUT_PATH_SHIFT 4 +#define DS_OUTPUT_AUDIO_INPUT_PATH_SHIFT 6 #define DS_OUTPUT_AUDIO_FLAG_DISABLE_HEADPHONE BIT(4) #define DS_OUTPUT_AUDIO_FLAG_ENABLE_INTERNAL_SPEAKER BIT(5) /* audio control2 flags */ +#define DS_OUTPUT_AUDIO2_SPEAKER_PREGAIN_SHIFT 0 #define DS_OUTPUT_AUDIO2_FLAG_BEAM_FORMING BIT(4) +/* haptics flags */ +#define DS_OUTPUT_HAPTICS_FLAG_LOW_PASS_FILTER BIT(0) + /* Status field of DualSense input report. */ #define DS_STATUS_BATTERY_CAPACITY 0xF #define DS_STATUS_CHARGING 0xF0 @@ -161,7 +174,8 @@ struct dualsense_output_report_common { /* LEDs and lightbar */ uint8_t valid_flag2; - uint8_t reserved3[2]; + uint8_t haptics_flags; + uint8_t reserved3[1]; uint8_t lightbar_setup; uint8_t led_brightness; uint8_t player_leds; @@ -173,17 +187,22 @@ _Static_assert(sizeof(struct dualsense_output_report_common) == 47, "Bad output struct dualsense_output_report_bt { uint8_t report_id; /* 0x31 */ - uint8_t seq_tag; + uint8_t flags:4; + uint8_t seq_tag:4; uint8_t tag; - struct dualsense_output_report_common common; - uint8_t reserved[24]; + union { + struct dualsense_output_report_common common; + uint8_t data[71]; + }; uint32_t crc32; } __attribute__((packed)); struct dualsense_output_report_usb { uint8_t report_id; /* 0x02 */ - struct dualsense_output_report_common common; - uint8_t reserved[15]; + union { + struct dualsense_output_report_common common; + uint8_t data[62]; + }; } __attribute__((packed)); /* @@ -240,6 +259,11 @@ struct dualsense { uint8_t output_seq; }; +static int atoi_x(const char *s) +{ + return strtol(s, NULL, 0); +} + static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp, void *buf) { if (ds->bt) { @@ -253,7 +277,7 @@ static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_ * Highest 4-bit is a sequence number, which needs to be increased * every report. Lowest 4-bit is tag and can be zero for now. */ - bt->seq_tag = (ds->output_seq << 4) | 0x0; + bt->seq_tag = ds->output_seq; if (++ds->output_seq == 16) ds->output_seq = 0; @@ -608,10 +632,10 @@ static int command_lightbar3(struct dualsense *ds, uint8_t red, uint8_t green, u return 0; } -static int command_player_leds(struct dualsense *ds, uint8_t number) +static int command_led_brightness(struct dualsense *ds, uint8_t number) { - if (number > 5) { - fprintf(stderr, "Invalid player number\n"); + if (number > 2) { + fprintf(stderr, "Invalid brightness level\n"); return 1; } @@ -619,17 +643,38 @@ static int command_player_leds(struct dualsense *ds, uint8_t number) uint8_t rbuf[DS_OUTPUT_REPORT_BT_SIZE]; dualsense_init_output_report(ds, &rp, rbuf); - static const int player_ids[6] = { + rp.common->valid_flag2 = DS_OUTPUT_VALID_FLAG2_LED_BRIGHTNESS_CONTROL_ENABLE; + rp.common->led_brightness = number; + + dualsense_send_output_report(ds, &rp); + + return 0; +} + +static int command_player_leds(struct dualsense *ds, uint8_t number, bool instant) +{ + static const int player_ids[] = { 0, BIT(2), BIT(3) | BIT(1), BIT(4) | BIT(2) | BIT(0), BIT(4) | BIT(3) | BIT(1) | BIT(0), - BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0) + BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0), + BIT(4) | BIT(0), + BIT(3) | BIT(2) | BIT(1), }; + if (number >= sizeof(player_ids)/sizeof(*player_ids)) { + fprintf(stderr, "Invalid player number\n"); + return 1; + } + + struct dualsense_output_report rp; + uint8_t rbuf[DS_OUTPUT_REPORT_BT_SIZE]; + dualsense_init_output_report(ds, &rp, rbuf); + rp.common->valid_flag1 = DS_OUTPUT_VALID_FLAG1_PLAYER_INDICATOR_CONTROL_ENABLE; - rp.common->player_leds = player_ids[number]; + rp.common->player_leds = player_ids[number] | (instant << 5); dualsense_send_output_report(ds, &rp); @@ -668,6 +713,31 @@ static int command_microphone_led(struct dualsense *ds, char *state) rp.common->mute_button_led = 1; } else if (!strcmp(state, "off")) { rp.common->mute_button_led = 0; + } else if (!strcmp(state, "pulse")) { + rp.common->mute_button_led = 2; + } else { + fprintf(stderr, "Invalid state\n"); + return 1; + } + + dualsense_send_output_report(ds, &rp); + + return 0; +} + +static int command_microphone_mode(struct dualsense *ds, char *state) +{ + struct dualsense_output_report rp; + uint8_t rbuf[DS_OUTPUT_REPORT_BT_SIZE]; + dualsense_init_output_report(ds, &rp, rbuf); + + rp.common->valid_flag0 = DS_OUTPUT_VALID_FLAG0_AUDIO_CONTROL_ENABLE; + if (!strcmp(state, "chat")) { + rp.common->audio_flags = 1 << DS_OUTPUT_AUDIO_INPUT_PATH_SHIFT; + } else if (!strcmp(state, "asr")) { + rp.common->audio_flags = 2 << DS_OUTPUT_AUDIO_INPUT_PATH_SHIFT; + } else if (!strcmp(state, "both")) { + rp.common->audio_flags = 0; } else { fprintf(stderr, "Invalid state\n"); return 1; @@ -1170,10 +1240,12 @@ static void print_help(void) printf(" info Get the controller firmware info\n"); printf(" lightbar STATE Enable (on) or disable (off) lightbar\n"); printf(" lightbar RED GREEN BLUE [BRIGHTNESS] Set lightbar color and brightness (0-255)\n"); - printf(" player-leds NUMBER Set player LEDs (1-5) or disabled (0)\n"); + printf(" led-brightness NUMBER Set player and microphone LED dimming (0-2)\n"); + printf(" player-leds NUMBER [instant] Set player LEDs (1-7) or disabled (0)\n"); printf(" microphone STATE Enable (on) or disable (off) microphone\n"); - printf(" microphone-led STATE Enable (on) or disable (off) microphone LED\n"); - printf(" speaker STATE Toggle to 'internal' speaker, 'headphone' or both\n"); + printf(" microphone-led STATE Enable (on), disable (off) or pulsate (pulse) microphone LED\n"); + printf(" microphone-mode STATE Toggle microphone usage to 'chat', 'asr' or 'both'\n"); + printf(" speaker STATE Toggle to 'internal' speaker, 'headphone' or 'both'\n"); printf(" volume VOLUME Set audio volume (0-255) of internal speaker and headphone\n"); printf(" attenuation RUMBLE TRIGGER Set the attenuation (0-7) of rumble/haptic motors and trigger vibration\n"); printf(" trigger TRIGGER off remove all effects\n"); @@ -1292,18 +1364,29 @@ int main(int argc, char *argv[]) if (argc == 3) { return command_lightbar1(&ds, argv[2]); } else if (argc == 5 || argc == 6) { - uint8_t brightness = argc == 6 ? atoi(argv[5]) : 255; - return command_lightbar3(&ds, atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), brightness); + uint8_t brightness = argc == 6 ? atoi_x(argv[5]) : 255; + return command_lightbar3(&ds, atoi_x(argv[2]), atoi_x(argv[3]), atoi_x(argv[4]), brightness); } else { fprintf(stderr, "Invalid arguments\n"); return 2; } - } else if (!strcmp(argv[1], "player-leds")) { + } else if (!strcmp(argv[1], "led-brightness")) { if (argc != 3) { fprintf(stderr, "Invalid arguments\n"); return 2; } - return command_player_leds(&ds, atoi(argv[2])); + return command_led_brightness(&ds, atoi_x(argv[2])); + } else if (!strcmp(argv[1], "player-leds")) { + bool instant; + if (argc == 3) { + instant = false; + } else if (argc == 4) { + instant = !strcmp(argv[3], "instant"); + } else { + fprintf(stderr, "Invalid arguments\n"); + return 2; + } + return command_player_leds(&ds, atoi_x(argv[2]), instant); } else if (!strcmp(argv[1], "microphone")) { if (argc != 3) { fprintf(stderr, "Invalid arguments\n"); @@ -1316,6 +1399,12 @@ int main(int argc, char *argv[]) return 2; } return command_microphone_led(&ds, argv[2]); + } else if (!strcmp(argv[1], "microphone-mode")) { + if (argc != 3) { + fprintf(stderr, "Invalid arguments\n"); + return 2; + } + return command_microphone_mode(&ds, argv[2]); } else if (!strcmp(argv[1], "speaker")) { if (argc != 3) { fprintf(stderr, "Invalid arguments\n"); @@ -1327,21 +1416,21 @@ int main(int argc, char *argv[]) fprintf(stderr, "Invalid arguments\n"); return 2; } - if (atoi(argv[2]) > 255) { + if (atoi_x(argv[2]) > 255) { fprintf(stderr, "Invalid volume\n"); return 1; } - return command_volume(&ds, atoi(argv[2])); + return command_volume(&ds, atoi_x(argv[2])); } else if (!strcmp(argv[1], "attenuation")) { if (argc != 4) { fprintf(stderr, "Invalid arguments\n"); return 2; } - if ((atoi(argv[2]) > 7) | (atoi(argv[3]) > 7)) { + if ((atoi_x(argv[2]) > 7) | (atoi_x(argv[3]) > 7)) { fprintf(stderr, "Invalid attenuation\n"); return 1; } - return command_vibration_attenuation(&ds, atoi(argv[2]), atoi(argv[3])); + return command_vibration_attenuation(&ds, atoi_x(argv[2]), atoi_x(argv[3])); } else if (!strcmp(argv[1], "trigger")) { if (argc < 4) { fprintf(stderr, "Invalid arguments\n"); @@ -1358,65 +1447,65 @@ int main(int argc, char *argv[]) fprintf(stderr, "feedback mode need two parameters\n"); return 2; } - return command_trigger_feedback(&ds, argv[2], atoi(argv[4]), atoi(argv[5])); + return command_trigger_feedback(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5])); } else if (!strcmp(argv[3], "weapon")) { if (argc < 7) { fprintf(stderr, "weapons mode need three parameters\n"); return 2; } - return command_trigger_weapon(&ds, argv[2], atoi(argv[4]), atoi(argv[5]), atoi(argv[6])); + return command_trigger_weapon(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6])); } else if (!strcmp(argv[3], "bow")) { if (argc < 8) { fprintf(stderr, "bow mode need four parameters\n"); return 2; } - return command_trigger_bow(&ds, argv[2], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7])); + return command_trigger_bow(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6]), atoi_x(argv[7])); } else if (!strcmp(argv[3], "galloping")) { if (argc < 9) { fprintf(stderr, "galloping mode need five parameters\n"); return 2; } - return command_trigger_galloping(&ds, argv[2], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), atoi(argv[8])); + return command_trigger_galloping(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6]), atoi_x(argv[7]), atoi_x(argv[8])); } else if (!strcmp(argv[3], "machine")) { if (argc < 10) { fprintf(stderr, "machine mode need six parameters\n"); return 2; } - return command_trigger_machine(&ds, argv[2], atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), atoi(argv[8]), atoi(argv[9])); + return command_trigger_machine(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6]), atoi_x(argv[7]), atoi_x(argv[8]), atoi_x(argv[9])); } else if (!strcmp(argv[3], "vibration")) { if (argc < 7) { fprintf(stderr, "vibration mode need three parameters\n"); return 2; } - return command_trigger_vibration(&ds, argv[2], atoi(argv[4]), atoi(argv[5]), atoi(argv[6])); + return command_trigger_vibration(&ds, argv[2], atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6])); } else if (!strcmp(argv[3], "feedback-raw")) { if (argc < 14) { fprintf(stderr, "feedback-raw mode need ten parameters\n"); return 2; } - uint8_t strengths[10] = { atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), atoi(argv[8]), atoi(argv[9]), atoi(argv[10]), atoi(argv[11]), atoi(argv[12]), atoi(argv[13]) }; + uint8_t strengths[10] = { atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6]), atoi_x(argv[7]), atoi_x(argv[8]), atoi_x(argv[9]), atoi_x(argv[10]), atoi_x(argv[11]), atoi_x(argv[12]), atoi_x(argv[13]) }; return command_trigger_feedback_raw(&ds, argv[2], strengths); } else if (!strcmp(argv[3], "vibration-raw")) { if (argc < 15) { fprintf(stderr, "vibration-raw mode need eleven parameters\n"); return 2; } - uint8_t strengths[10] = { atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), atoi(argv[8]), atoi(argv[9]), atoi(argv[10]), atoi(argv[11]), atoi(argv[12]), atoi(argv[13]) }; - return command_trigger_vibration_raw(&ds, argv[2], strengths, atoi(argv[14])); + uint8_t strengths[10] = { atoi_x(argv[4]), atoi_x(argv[5]), atoi_x(argv[6]), atoi_x(argv[7]), atoi_x(argv[8]), atoi_x(argv[9]), atoi_x(argv[10]), atoi_x(argv[11]), atoi_x(argv[12]), atoi_x(argv[13]) }; + return command_trigger_vibration_raw(&ds, argv[2], strengths, atoi_x(argv[14])); } /* mostly to test raw parameters without any kind of bitpacking or range check */ - uint8_t param1 = argc > 4 ? atoi(argv[4]) : 0; - uint8_t param2 = argc > 5 ? atoi(argv[5]) : 0; - uint8_t param3 = argc > 6 ? atoi(argv[6]) : 0; - uint8_t param4 = argc > 7 ? atoi(argv[7]) : 0; - uint8_t param5 = argc > 8 ? atoi(argv[8]) : 0; - uint8_t param6 = argc > 9 ? atoi(argv[9]) : 0; - uint8_t param7 = argc > 10 ? atoi(argv[10]) : 0; - uint8_t param8 = argc > 11 ? atoi(argv[11]) : 0; - uint8_t param9 = argc > 12 ? atoi(argv[12]) : 0; - - return command_trigger(&ds, argv[2], atoi(argv[3]), param1, param2, param3, param4, param5, param6, param7, param8, param9); + uint8_t param1 = argc > 4 ? atoi_x(argv[4]) : 0; + uint8_t param2 = argc > 5 ? atoi_x(argv[5]) : 0; + uint8_t param3 = argc > 6 ? atoi_x(argv[6]) : 0; + uint8_t param4 = argc > 7 ? atoi_x(argv[7]) : 0; + uint8_t param5 = argc > 8 ? atoi_x(argv[8]) : 0; + uint8_t param6 = argc > 9 ? atoi_x(argv[9]) : 0; + uint8_t param7 = argc > 10 ? atoi_x(argv[10]) : 0; + uint8_t param8 = argc > 11 ? atoi_x(argv[11]) : 0; + uint8_t param9 = argc > 12 ? atoi_x(argv[12]) : 0; + + return command_trigger(&ds, argv[2], atoi_x(argv[3]), param1, param2, param3, param4, param5, param6, param7, param8, param9); } else { fprintf(stderr, "Invalid command\n"); return 2;