mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-06-17 22:31:35 +00:00
Rework Xbox360W support to handle multiple controllers
This commit is contained in:
@@ -184,8 +184,8 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
|||||||
else if (Xbox360Controller.canClaimDevice(device)) {
|
else if (Xbox360Controller.canClaimDevice(device)) {
|
||||||
controller = new Xbox360Controller(device, connection, nextDeviceId++, this);
|
controller = new Xbox360Controller(device, connection, nextDeviceId++, this);
|
||||||
}
|
}
|
||||||
else if (Xbox360WirelessController.canClaimDevice(device)) {
|
else if (Xbox360WirelessDongle.canClaimDevice(device)) {
|
||||||
controller = new Xbox360WirelessController(device, connection, nextDeviceId++, this);
|
controller = new Xbox360WirelessDongle(device, connection, nextDeviceId++, this);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Unreachable
|
// Unreachable
|
||||||
@@ -289,7 +289,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
|||||||
public static boolean shouldClaimDevice(UsbDevice device, boolean claimAllAvailable) {
|
public static boolean shouldClaimDevice(UsbDevice device, boolean claimAllAvailable) {
|
||||||
return ((!kernelSupportsXboxOne() || !isRecognizedInputDevice(device) || claimAllAvailable) && XboxOneController.canClaimDevice(device)) ||
|
return ((!kernelSupportsXboxOne() || !isRecognizedInputDevice(device) || claimAllAvailable) && XboxOneController.canClaimDevice(device)) ||
|
||||||
((!isRecognizedInputDevice(device) || claimAllAvailable) && Xbox360Controller.canClaimDevice(device)) ||
|
((!isRecognizedInputDevice(device) || claimAllAvailable) && Xbox360Controller.canClaimDevice(device)) ||
|
||||||
((!kernelSupportsXbox360W() || !isRecognizedInputDevice(device) || claimAllAvailable) && Xbox360WirelessController.canClaimDevice(device));
|
((!kernelSupportsXbox360W() || !isRecognizedInputDevice(device) || claimAllAvailable) && Xbox360WirelessDongle.canClaimDevice(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void start() {
|
private void start() {
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
package com.limelight.binding.input.driver;
|
|
||||||
|
|
||||||
import android.hardware.usb.UsbConstants;
|
|
||||||
import android.hardware.usb.UsbDevice;
|
|
||||||
import android.hardware.usb.UsbDeviceConnection;
|
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
|
||||||
import com.limelight.nvstream.input.ControllerPacket;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public class Xbox360WirelessController extends AbstractXboxController {
|
|
||||||
private static final int XB360W_IFACE_SUBCLASS = 93;
|
|
||||||
private static final int XB360W_IFACE_PROTOCOL = 129; // Wireless only
|
|
||||||
|
|
||||||
private static final int[] SUPPORTED_VENDORS = {
|
|
||||||
0x045e, // Microsoft
|
|
||||||
};
|
|
||||||
|
|
||||||
public static boolean canClaimDevice(UsbDevice device) {
|
|
||||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
|
||||||
if (device.getVendorId() == supportedVid &&
|
|
||||||
device.getInterfaceCount() >= 1 &&
|
|
||||||
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
|
||||||
device.getInterface(0).getInterfaceSubclass() == XB360W_IFACE_SUBCLASS &&
|
|
||||||
device.getInterface(0).getInterfaceProtocol() == XB360W_IFACE_PROTOCOL) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Xbox360WirelessController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
|
||||||
super(device, connection, deviceId, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean handleRead(ByteBuffer buffer) {
|
|
||||||
// Unreachable
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean sendLedCommand(byte command) {
|
|
||||||
byte[] commandBuffer = {
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x08,
|
|
||||||
(byte) (0x40 + command),
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00};
|
|
||||||
|
|
||||||
int res = connection.bulkTransfer(outEndpt, commandBuffer, commandBuffer.length, 3000);
|
|
||||||
if (res != commandBuffer.length) {
|
|
||||||
LimeLog.warning("LED set transfer failed: "+res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean doInit() {
|
|
||||||
// Turn the LED on corresponding to our device ID
|
|
||||||
sendLedCommand((byte)(2 + (getControllerId() % 4)));
|
|
||||||
|
|
||||||
// Close the interface and return false to give control back to the kernel.
|
|
||||||
connection.releaseInterface(device.getInterface(0));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rumble(short lowFreqMotor, short highFreqMotor) {
|
|
||||||
// Unreachable.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package com.limelight.binding.input.driver;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbConstants;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.hardware.usb.UsbDeviceConnection;
|
||||||
|
import android.hardware.usb.UsbEndpoint;
|
||||||
|
import android.hardware.usb.UsbInterface;
|
||||||
|
|
||||||
|
import com.limelight.LimeLog;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class Xbox360WirelessDongle extends AbstractController {
|
||||||
|
private UsbDevice device;
|
||||||
|
private UsbDeviceConnection connection;
|
||||||
|
|
||||||
|
private static final int XB360W_IFACE_SUBCLASS = 93;
|
||||||
|
private static final int XB360W_IFACE_PROTOCOL = 129; // Wireless only
|
||||||
|
|
||||||
|
private static final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x045e, // Microsoft
|
||||||
|
};
|
||||||
|
|
||||||
|
public static boolean canClaimDevice(UsbDevice device) {
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (device.getVendorId() == supportedVid &&
|
||||||
|
device.getInterfaceCount() >= 1 &&
|
||||||
|
device.getInterface(0).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
device.getInterface(0).getInterfaceSubclass() == XB360W_IFACE_SUBCLASS &&
|
||||||
|
device.getInterface(0).getInterfaceProtocol() == XB360W_IFACE_PROTOCOL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Xbox360WirelessDongle(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||||
|
super(deviceId, listener, device.getVendorId(), device.getProductId());
|
||||||
|
this.device = device;
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendLedCommandToEndpoint(UsbEndpoint endpoint, int controllerIndex) {
|
||||||
|
byte[] commandBuffer = {
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
(byte) (0x40 + (2 + (controllerIndex % 4))),
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00};
|
||||||
|
|
||||||
|
int res = connection.bulkTransfer(endpoint, commandBuffer, commandBuffer.length, 3000);
|
||||||
|
if (res != commandBuffer.length) {
|
||||||
|
LimeLog.warning("LED set transfer failed: "+res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendLedCommandToInterface(UsbInterface iface, int controllerIndex) {
|
||||||
|
// Claim this interface to kick xpad off it (temporarily)
|
||||||
|
if (!connection.claimInterface(iface, true)) {
|
||||||
|
LimeLog.warning("Failed to claim interface: "+iface.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the out endpoint for this interface
|
||||||
|
for (int i = 0; i < iface.getEndpointCount(); i++) {
|
||||||
|
UsbEndpoint endpt = iface.getEndpoint(i);
|
||||||
|
if (endpt.getDirection() == UsbConstants.USB_DIR_OUT) {
|
||||||
|
// Send the LED command
|
||||||
|
sendLedCommandToEndpoint(endpt, controllerIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the interface to allow xpad to take over again
|
||||||
|
connection.releaseInterface(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start() {
|
||||||
|
int controllerIndex = getControllerId();
|
||||||
|
|
||||||
|
// Send LED commands on the out endpoint of each interface. There is one interface
|
||||||
|
// corresponding to each possible attached controller.
|
||||||
|
for (int i = 0; i < device.getInterfaceCount(); i++) {
|
||||||
|
UsbInterface iface = device.getInterface(i);
|
||||||
|
|
||||||
|
// Skip the non-input interfaces
|
||||||
|
if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_VENDOR_SPEC ||
|
||||||
|
iface.getInterfaceSubclass() != XB360W_IFACE_SUBCLASS ||
|
||||||
|
iface.getInterfaceProtocol() != XB360W_IFACE_PROTOCOL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsbDriverService assumes each device corresponds to a single controller. That isn't
|
||||||
|
// true for this dongle, so we will use a little hack to assign consecutive IDs for
|
||||||
|
// each attached controller.
|
||||||
|
sendLedCommandToInterface(iface, controllerIndex++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Fail" to give control back to the kernel driver
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rumble(short lowFreqMotor, short highFreqMotor) {
|
||||||
|
// Unreachable.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user