Commit 16c0e6d7 authored by Matt Reynolds's avatar Matt Reynolds Committed by Commit Bot

Ack Xbox One reports for the Xbox button on Mac

Xbox One gamepads send the state of the Xbox button in a separate
report from the rest of the button and axis state. On some devices,
this report will be sent repeatedly until it is acked, and further
presses and releases of the button are not reported. This causes the
button to appear "stuck" when observed from within Chrome.

This CL makes Chrome ack the Xbox report when it is received,
preventing the button from becoming stuck. This fix only affects OSX,
where Chrome uses an internal driver for Xbox devices.

BUG=846806

Change-Id: I8e44a78c56b146dcf20c530cbc397451fce9e84c
Reviewed-on: https://chromium-review.googlesource.com/1086409
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Reviewed-by: default avatarBrandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565141}
parent a494fc16
......@@ -150,6 +150,7 @@ class XboxControllerMac {
void WriteXbox360Rumble(uint8_t strong_magnitude, uint8_t weak_magnitude);
void WriteXboxOneInit();
void WriteXboxOneRumble(uint8_t strong_magnitude, uint8_t weak_magnitude);
void WriteXboxOneAckGuide(uint8_t sequence_number);
// Handle for the USB device. IOUSBDeviceStruct320 is the latest version of
// the device API that is supported on Mac OS 10.6.
......
......@@ -37,6 +37,9 @@ const int kXboxOneControlEndpoint = 1;
const double kXboxOneMaxEffectDurationMillis = 2500; // 2.5 seconds
const size_t kXbox360HeaderBytes = 2;
const size_t kXboxOneHeaderBytes = 4;
enum {
STATUS_MESSAGE_BUTTONS = 0,
STATUS_MESSAGE_LED = 1,
......@@ -720,7 +723,7 @@ void XboxControllerMac::GotData(void* context, IOReturn result, void* arg0) {
}
void XboxControllerMac::ProcessXbox360Packet(size_t length) {
if (length < 2)
if (length < kXbox360HeaderBytes)
return;
DCHECK(length <= read_buffer_size_);
if (length > read_buffer_size_) {
......@@ -734,8 +737,8 @@ void XboxControllerMac::ProcessXbox360Packet(size_t length) {
return;
uint8_t type = buffer[0];
buffer += 2;
length -= 2;
buffer += kXbox360HeaderBytes;
length -= kXbox360HeaderBytes;
switch (type) {
case STATUS_MESSAGE_BUTTONS: {
if (length != sizeof(Xbox360ButtonData))
......@@ -762,7 +765,7 @@ void XboxControllerMac::ProcessXbox360Packet(size_t length) {
}
void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
if (length < 2)
if (length < kXboxOneHeaderBytes)
return;
DCHECK(length <= read_buffer_size_);
if (length > read_buffer_size_) {
......@@ -770,10 +773,12 @@ void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
return;
}
uint8_t* buffer = read_buffer_.get();
uint8_t type = buffer[0];
buffer += 4;
length -= 4;
bool needs_ack = (buffer[1] == 0x30);
uint8_t sequence_number = buffer[2];
buffer += kXboxOneHeaderBytes;
length -= kXboxOneHeaderBytes;
switch (type) {
case XBOX_ONE_STATUS_MESSAGE_BUTTONS: {
if (length != sizeof(XboxOneButtonData) &&
......@@ -791,6 +796,9 @@ void XboxControllerMac::ProcessXboxOnePacket(size_t length) {
return;
XboxOneGuideData* data = reinterpret_cast<XboxOneGuideData*>(buffer);
delegate_->XboxControllerGotGuideData(this, data->down);
// The Xbox One S controller requires these reports to be acked.
if (needs_ack)
WriteXboxOneAckGuide(sequence_number);
break;
}
default:
......@@ -899,6 +907,38 @@ void XboxControllerMac::WriteXboxOneRumble(uint8_t strong_magnitude,
}
}
// Ack the guide report. The contents of the ack report are modeled after
// xpad's xboxone_ack_mode_report. See the mode_report_ack buffer defined in
// this commit:
// https://github.com/torvalds/linux/commit/57b8443d3e5bd046a519ff714ca31c64c7f04309
void XboxControllerMac::WriteXboxOneAckGuide(uint8_t sequence_number) {
const UInt8 length = 13;
UInt8* buffer = new UInt8[length];
buffer[0] = 0x01;
buffer[1] = 0x20;
buffer[2] = sequence_number;
buffer[3] = 0x09;
buffer[4] = 0x00;
buffer[5] = 0x07;
buffer[6] = 0x20;
buffer[7] = 0x02;
buffer[8] = 0x00;
buffer[9] = 0x00;
buffer[10] = 0x00;
buffer[11] = 0x00;
buffer[12] = 0x00;
kern_return_t kr =
(*interface_)
->WritePipeAsync(interface_, control_endpoint_, buffer,
(UInt32)length, WriteComplete, buffer);
if (kr != KERN_SUCCESS) {
delete[] buffer;
IOError();
return;
}
}
void XboxControllerMac::RunCallbackOnMojoThread(
mojom::GamepadHapticsResult result) {
if (playing_effect_task_runner_->RunsTasksInCurrentSequence()) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment