Skip to content

Commit

Permalink
Rumble on Xbox FW 5.x
Browse files Browse the repository at this point in the history
Fixes #69
  • Loading branch information
ricardoquesada committed Mar 16, 2024
1 parent 2b008ac commit 1bd46a7
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [4.0-beta3] - ???
### Fixed
- Virtual Device: Don't remove virtual device when Xbox device is connected. Fixes [Github Issue #77][github_issue_77]
- Xbox: Rumble works on Xbox Firmware v5.x. Fixes [Github Issue #69][github_issue_69]

[github_issue_69]: https://github.com/ricardoquesada/bluepad32/issues/69
[github_issue_77]: https://github.com/ricardoquesada/bluepad32/issues/77

## [4.0-beta2] - 2024-03-09
Expand Down
2 changes: 1 addition & 1 deletion examples/posix/src/my_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ static void posix_init(int argc, const char** argv) {
uni_gamepad_set_mappings(&mappings);
#endif
uni_gamepad_set_mappings_type(UNI_GAMEPAD_MAPPINGS_TYPE_XBOX);
uni_bt_service_set_enabled(true);
// uni_bt_service_set_enabled(true);
}

static void posix_on_init_complete(void) {
Expand Down
15 changes: 10 additions & 5 deletions src/components/bluepad32/bt/uni_bt_le.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,18 @@ static void hids_client_packet_handler(uint8_t packet_type, uint16_t channel, ui
loge("Hids Cid: Could not find valid device for hids_cid=%d\n", hids_cid);
break;
}
#if 0
status = hids_client_enable_notifications(hids_cid);
if (status != ERROR_CODE_SUCCESS)
logi("Failed to enable client notifications for hids_cid=%d, status=%#x\n", hids_cid, status);
else
logi("Client notifications enabled for for hids_cid=%d\n", hids_cid);
#endif

uni_hid_device_guess_controller_type_from_pid_vid(device);
uni_hid_device_connect(device);
uni_hid_device_set_ready(device);

resume_scanning_hint();
break;
default:
Expand Down Expand Up @@ -390,10 +399,6 @@ static void device_information_packet_handler(uint8_t packet_type, uint16_t chan
}
logi("Using hids_cid=%d\n", hids_cid);
device->hids_cid = hids_cid;

status = hids_client_enable_notifications(hids_cid);
if (status != ERROR_CODE_SUCCESS)
logi("Failed to enable client notifications for hics_cid=%d, status=%#x\n", hids_cid, status);
break;
default:
logi("Device Information service client connection failed, error=%#x.\n", status);
Expand Down Expand Up @@ -707,7 +712,7 @@ void uni_bt_le_on_hci_event_encryption_change(const uint8_t* packet, uint16_t si
return;
}
// This event is also triggered by Classic, and might crash the stack.
// Real case: Connect a Wii , disconnect it, and try re-connection
// Real case: Connect a Wii, disconnect it, and try re-connection
if (device->conn.protocol != UNI_BT_CONN_PROTOCOL_BLE)
// Abort on non BLE connections
return;
Expand Down
63 changes: 41 additions & 22 deletions src/components/bluepad32/parser/uni_hid_parser_xboxone.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
#include "uni_hid_device.h"
#include "uni_log.h"

// Xbox doesn't report trigger buttons. Instead it reports throttle/brake.
// Xbox doesn't report trigger buttons. Instead, it reports throttle/brake.
// This threshold represents the minimum value of throttle/brake to "report"
// the trigger buttons.
#define TRIGGER_BUTTON_THRESHOLD 32

#define XBOX_RUMBLE_REPORT_ID 0x03

static const uint16_t XBOX_WIRELESS_VID = 0x045e; // Microsoft
static const uint16_t XBOX_WIRELESS_PID = 0x02e0; // Xbox One (Bluetooth)

Expand Down Expand Up @@ -52,8 +54,24 @@ enum xboxone_firmware {
XBOXONE_FIRMWARE_V5, // BLE version
};

struct xboxone_ff_report {
// Report related
uint8_t transaction_type; // type of transaction
uint8_t report_id; // report id, should be XBOX_RUMBLE_REPORT_ID

// Force-feedback related
uint8_t enable_actuators; // LSB 0-3 for each actuator
uint8_t force_left_trigger; // HID descriptor says that it goes from 0-100
uint8_t force_right_trigger;
uint8_t force_left;
uint8_t force_right;
uint8_t duration; // unknown unit, 255 is ~second
uint8_t start_delay;
uint8_t loop_count; // how many times "duration" is repeated
} __attribute__((packed));

// xboxone_instance_t represents data used by the Wii driver instance.
typedef struct wii_instance_s {
typedef struct xboxone_instance_s {
enum xboxone_firmware version;
} xboxone_instance_t;
_Static_assert(sizeof(xboxone_instance_t) < HID_DEVICE_MAX_PARSER_DATA, "Xbox one intance too big");
Expand Down Expand Up @@ -91,8 +109,11 @@ void uni_hid_parser_xboxone_setup(uni_hid_device_t* d) {
xboxone_instance_t* ins = get_xboxone_instance(d);
// FIXME: Parse HID descriptor and see if it supports 0xf buttons. Checking
// for the len is a horrible hack.
if (d->hid_descriptor_len > 330) {
logi("Xbox: Assuming it is firmware v4.8 or v5.x\n");
if (gap_get_connection_type(d->conn.handle) == GAP_CONNECTION_LE) {
logi("Xbox: Assuming it is firmware v5.x\n");
ins->version = XBOXONE_FIRMWARE_V5;
} else if (d->hid_descriptor_len > 330) {
logi("Xbox: Assuming it is firmware v4.8\n");
ins->version = XBOXONE_FIRMWARE_V4_8;
} else {
// If it is really firmware 4.8, it will be set later.
Expand Down Expand Up @@ -433,11 +454,15 @@ static void parse_usage_firmware_v4_v5(uni_hid_device_t* d,
}

void uni_hid_parser_xboxone_set_rumble(uni_hid_device_t* d, uint8_t value, uint8_t duration) {
uint8_t status;

if (d == NULL) {
loge("Xbox: Invalid device\n");
return;
}

xboxone_instance_t* ins = get_xboxone_instance(d);

// Actuators for the force feedback (FF).
enum {
FF_RIGHT = 1 << 0,
Expand All @@ -446,27 +471,12 @@ void uni_hid_parser_xboxone_set_rumble(uni_hid_device_t* d, uint8_t value, uint8
FF_TRIGGER_LEFT = 1 << 3,
};

struct ff_report {
// Report related
uint8_t transaction_type; // type of transaction
uint8_t report_id; // report id
// Force-feedback related
uint8_t enable_actuators; // LSB 0-3 for each actuator
uint8_t force_left_trigger; // HID descriptor says that it goes from 0-100
uint8_t force_right_trigger;
uint8_t force_left;
uint8_t force_right;
uint8_t duration; // unknown unit, 255 is ~second
uint8_t start_delay;
uint8_t loop_count; // how many times "duration" is repeated
} __attribute__((packed));

// TODO: It seems that the max value is 127. Double check
value /= 2;

struct ff_report ff = {
struct xboxone_ff_report ff = {
.transaction_type = (HID_MESSAGE_TYPE_DATA << 4) | HID_REPORT_TYPE_OUTPUT,
.report_id = 0x03, // taken from HID descriptor
.report_id = XBOX_RUMBLE_REPORT_ID, // taken from HID descriptor
.enable_actuators = FF_RIGHT | FF_LEFT | FF_TRIGGER_LEFT | FF_TRIGGER_RIGHT,
.force_left_trigger = value,
.force_right_trigger = value,
Expand All @@ -480,7 +490,16 @@ void uni_hid_parser_xboxone_set_rumble(uni_hid_device_t* d, uint8_t value, uint8
.loop_count = 0,
};

uni_hid_device_send_intr_report(d, (uint8_t*)&ff, sizeof(ff));
if (ins->version == XBOXONE_FIRMWARE_V5) {
status = hids_client_send_write_report(d->hids_cid, XBOX_RUMBLE_REPORT_ID, HID_REPORT_TYPE_OUTPUT,
&ff.enable_actuators, // skip the first type bytes,
sizeof(ff) - 2 // substract the 2 bytes from total
);
if (status != ERROR_CODE_SUCCESS)
loge("Xbox: Failed to send rumble report, error=%#x\n", status);
} else {
uni_hid_device_send_intr_report(d, (uint8_t*)&ff, sizeof(ff));
}
}

void uni_hid_parser_xboxone_device_dump(uni_hid_device_t* d) {
Expand Down

0 comments on commit 1bd46a7

Please sign in to comment.