Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple LED-related & misc enhancements #34

Merged
merged 7 commits into from
Sep 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 134 additions & 45 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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));

/*
Expand Down Expand Up @@ -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) {
Expand All @@ -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;

Expand Down Expand Up @@ -608,28 +632,49 @@ 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;
}

struct dualsense_output_report rp;
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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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;
Expand Down
Loading