mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Add support for wired Xbox 360 controllers (pending testing)
This commit is contained in:
parent
ceef4510fb
commit
23c54f6813
@ -0,0 +1,55 @@
|
||||
package com.limelight.binding.input.driver;
|
||||
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
|
||||
import com.limelight.LimeLog;
|
||||
import com.limelight.binding.video.MediaCodecHelper;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public abstract class AbstractController {
|
||||
|
||||
private final int deviceId;
|
||||
|
||||
private UsbDriverListener listener;
|
||||
|
||||
protected short buttonFlags;
|
||||
protected float leftTrigger, rightTrigger;
|
||||
protected float rightStickX, rightStickY;
|
||||
protected float leftStickX, leftStickY;
|
||||
|
||||
public int getControllerId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
protected void setButtonFlag(int buttonFlag, int data) {
|
||||
if (data != 0) {
|
||||
buttonFlags |= buttonFlag;
|
||||
}
|
||||
else {
|
||||
buttonFlags &= ~buttonFlag;
|
||||
}
|
||||
}
|
||||
|
||||
protected void reportInput() {
|
||||
listener.reportControllerState(deviceId, buttonFlags, leftStickX, leftStickY,
|
||||
rightStickX, rightStickY, leftTrigger, rightTrigger);
|
||||
}
|
||||
|
||||
public abstract boolean start();
|
||||
public abstract void stop();
|
||||
|
||||
public AbstractController(int deviceId, UsbDriverListener listener) {
|
||||
this.deviceId = deviceId;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
protected void notifyDeviceRemoved() {
|
||||
listener.deviceRemoved(deviceId);
|
||||
}
|
||||
|
||||
protected void notifyDeviceAdded() {
|
||||
listener.deviceAdded(deviceId);
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
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 com.limelight.binding.video.MediaCodecHelper;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public abstract class AbstractXboxController extends AbstractController {
|
||||
protected final UsbDevice device;
|
||||
protected final UsbDeviceConnection connection;
|
||||
|
||||
private Thread inputThread;
|
||||
private boolean stopped;
|
||||
|
||||
protected UsbEndpoint inEndpt, outEndpt;
|
||||
|
||||
public AbstractXboxController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||
super(deviceId, listener);
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
private Thread createInputThread() {
|
||||
return new Thread() {
|
||||
public void run() {
|
||||
while (!isInterrupted() && !stopped) {
|
||||
byte[] buffer = new byte[64];
|
||||
|
||||
int res;
|
||||
|
||||
//
|
||||
// There's no way that I can tell to determine if a device has failed
|
||||
// or if the timeout has simply expired. We'll check how long the transfer
|
||||
// took to fail and assume the device failed if it happened before the timeout
|
||||
// expired.
|
||||
//
|
||||
|
||||
do {
|
||||
// Read the next input state packet
|
||||
long lastMillis = MediaCodecHelper.getMonotonicMillis();
|
||||
res = connection.bulkTransfer(inEndpt, buffer, buffer.length, 3000);
|
||||
|
||||
// If we get a zero length response, treat it as an error
|
||||
if (res == 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res == -1 && MediaCodecHelper.getMonotonicMillis() - lastMillis < 1000) {
|
||||
LimeLog.warning("Detected device I/O error");
|
||||
AbstractXboxController.this.stop();
|
||||
break;
|
||||
}
|
||||
} while (res == -1 && !isInterrupted() && !stopped);
|
||||
|
||||
if (res == -1 || stopped) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (handleRead(ByteBuffer.wrap(buffer, 0, res).order(ByteOrder.LITTLE_ENDIAN))) {
|
||||
// Report input if handleRead() returns true
|
||||
reportInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
// Force claim all interfaces
|
||||
for (int i = 0; i < device.getInterfaceCount(); i++) {
|
||||
UsbInterface iface = device.getInterface(i);
|
||||
|
||||
if (!connection.claimInterface(iface, true)) {
|
||||
LimeLog.warning("Failed to claim interfaces");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the endpoints
|
||||
UsbInterface iface = device.getInterface(0);
|
||||
for (int i = 0; i < iface.getEndpointCount(); i++) {
|
||||
UsbEndpoint endpt = iface.getEndpoint(i);
|
||||
if (endpt.getDirection() == UsbConstants.USB_DIR_IN) {
|
||||
if (inEndpt != null) {
|
||||
LimeLog.warning("Found duplicate IN endpoint");
|
||||
return false;
|
||||
}
|
||||
inEndpt = endpt;
|
||||
}
|
||||
else if (endpt.getDirection() == UsbConstants.USB_DIR_OUT) {
|
||||
if (outEndpt != null) {
|
||||
LimeLog.warning("Found duplicate OUT endpoint");
|
||||
return false;
|
||||
}
|
||||
outEndpt = endpt;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the required endpoints were present
|
||||
if (inEndpt == null || outEndpt == null) {
|
||||
LimeLog.warning("Missing required endpoint");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the init function
|
||||
if (!doInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening for controller input
|
||||
inputThread = createInputThread();
|
||||
inputThread.start();
|
||||
|
||||
// Now report we're added
|
||||
notifyDeviceAdded();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = true;
|
||||
|
||||
// Stop the input thread
|
||||
if (inputThread != null) {
|
||||
inputThread.interrupt();
|
||||
inputThread = null;
|
||||
}
|
||||
|
||||
// Report the device removed
|
||||
notifyDeviceRemoved();
|
||||
|
||||
// Close the USB connection
|
||||
connection.close();
|
||||
}
|
||||
|
||||
protected abstract boolean handleRead(ByteBuffer buffer);
|
||||
protected abstract boolean doInit();
|
||||
}
|
@ -26,7 +26,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
private final UsbEventReceiver receiver = new UsbEventReceiver();
|
||||
private final UsbDriverBinder binder = new UsbDriverBinder();
|
||||
|
||||
private final ArrayList<XboxOneController> controllers = new ArrayList<>();
|
||||
private final ArrayList<AbstractController> controllers = new ArrayList<>();
|
||||
|
||||
private UsbDriverListener listener;
|
||||
private static int nextDeviceId;
|
||||
@ -42,7 +42,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
@Override
|
||||
public void deviceRemoved(int controllerId) {
|
||||
// Remove the the controller from our list (if not removed already)
|
||||
for (XboxOneController controller : controllers) {
|
||||
for (AbstractController controller : controllers) {
|
||||
if (controller.getControllerId() == controllerId) {
|
||||
controllers.remove(controller);
|
||||
break;
|
||||
@ -93,7 +93,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
|
||||
// Report all controllerMap that already exist
|
||||
if (listener != null) {
|
||||
for (XboxOneController controller : controllers) {
|
||||
for (AbstractController controller : controllers) {
|
||||
listener.deviceAdded(controller.getControllerId());
|
||||
}
|
||||
}
|
||||
@ -117,8 +117,21 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to initialize it
|
||||
XboxOneController controller = new XboxOneController(device, connection, nextDeviceId++, this);
|
||||
|
||||
AbstractController controller;
|
||||
|
||||
if (XboxOneController.canClaimDevice(device)) {
|
||||
controller = new XboxOneController(device, connection, nextDeviceId++, this);
|
||||
}
|
||||
else if (Xbox360Controller.canClaimDevice(device)) {
|
||||
controller = new Xbox360Controller(device, connection, nextDeviceId++, this);
|
||||
}
|
||||
else {
|
||||
// Unreachable
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the controller
|
||||
if (!controller.start()) {
|
||||
connection.close();
|
||||
return;
|
||||
@ -141,7 +154,7 @@ public class UsbDriverService extends Service implements UsbDriverListener {
|
||||
|
||||
// Enumerate existing devices
|
||||
for (UsbDevice dev : usbManager.getDeviceList().values()) {
|
||||
if (XboxOneController.canClaimDevice(dev)) {
|
||||
if (XboxOneController.canClaimDevice(dev) || Xbox360Controller.canClaimDevice(dev)) {
|
||||
// Start the process of claiming this device
|
||||
handleUsbDeviceState(dev);
|
||||
}
|
||||
|
@ -0,0 +1,137 @@
|
||||
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 Xbox360Controller extends AbstractXboxController {
|
||||
|
||||
// This list is taken from the Xpad driver in the Linux kernel.
|
||||
// I've excluded the devices that aren't "controllers" in the traditional sense, but
|
||||
// if people really want to use their dancepads or fight sticks with Moonlight, I can
|
||||
// put them in.
|
||||
private static final DeviceIdTuple[] supportedDeviceTuples = {
|
||||
new DeviceIdTuple(0x045e, 0x028e, "Microsoft X-Box 360 pad"),
|
||||
new DeviceIdTuple(0x044f, 0xb326, "Thrustmaster Gamepad GP XID"),
|
||||
new DeviceIdTuple(0x046d, 0xc21d, "Logitech Gamepad F310"),
|
||||
new DeviceIdTuple(0x046d, 0xc21e, "Logitech Gamepad F510"),
|
||||
new DeviceIdTuple(0x046d, 0xc21f, "Logitech Gamepad F710"),
|
||||
new DeviceIdTuple(0x046d, 0xc242, "Logitech Chillstream Controller"),
|
||||
new DeviceIdTuple(0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x0738, 0x4726, "Mad Catz Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x0738, 0xb726, "Mad Catz Xbox controller - MW2"),
|
||||
new DeviceIdTuple(0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad"),
|
||||
new DeviceIdTuple(0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360"),
|
||||
new DeviceIdTuple(0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360"),
|
||||
new DeviceIdTuple(0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360"),
|
||||
new DeviceIdTuple(0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360"),
|
||||
new DeviceIdTuple(0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360"),
|
||||
new DeviceIdTuple(0x0e6f, 0x0301, "Logic3 Controller"),
|
||||
new DeviceIdTuple(0x0e6f, 0x0401, "Logic3 Controller"),
|
||||
new DeviceIdTuple(0x12ab, 0x0301, "PDP AFTERGLOW AX.1"),
|
||||
new DeviceIdTuple(0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller"),
|
||||
new DeviceIdTuple(0x1532, 0x0037, "Razer Sabertooth"),
|
||||
new DeviceIdTuple(0x15e4, 0x3f00, "Power A Mini Pro Elite"),
|
||||
new DeviceIdTuple(0x15e4, 0x3f0a, "Xbox Airflo wired controller"),
|
||||
new DeviceIdTuple(0x15e4, 0x3f10, "Batarang Xbox 360 controller"),
|
||||
new DeviceIdTuple(0x162e, 0xbeef, "Joytech Neo-Se Take2"),
|
||||
new DeviceIdTuple(0x1689, 0xfd00, "Razer Onza Tournament Edition"),
|
||||
new DeviceIdTuple(0x1689, 0xfd01, "Razer Onza Classic Edition"),
|
||||
new DeviceIdTuple(0x24c6, 0x5d04, "Razer Sabertooth"),
|
||||
new DeviceIdTuple(0x1bad, 0xf016, "Mad Catz Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)"),
|
||||
new DeviceIdTuple(0x1bad, 0xf900, "Harmonix Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x1bad, 0xf901, "Gamestop Xbox 360 Controller"),
|
||||
new DeviceIdTuple(0x1bad, 0xf903, "Tron Xbox 360 controller"),
|
||||
new DeviceIdTuple(0x24c6, 0x5300, "PowerA MINI PROEX Controller"),
|
||||
new DeviceIdTuple(0x24c6, 0x5303, "Xbox Airflo wired controller"),
|
||||
};
|
||||
|
||||
public static boolean canClaimDevice(UsbDevice device) {
|
||||
for (DeviceIdTuple tuple : supportedDeviceTuples) {
|
||||
if (device.getVendorId() == tuple.vid && device.getProductId() == tuple.pid) {
|
||||
LimeLog.info("XB360 can claim device: " + tuple.name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Xbox360Controller(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||
super(device, connection, deviceId, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handleRead(ByteBuffer buffer) {
|
||||
// Skip first byte
|
||||
buffer.position(buffer.position()+1);
|
||||
|
||||
// DPAD
|
||||
byte b = buffer.get();
|
||||
setButtonFlag(ControllerPacket.LEFT_FLAG, b & 0x04);
|
||||
setButtonFlag(ControllerPacket.RIGHT_FLAG, b & 0x08);
|
||||
setButtonFlag(ControllerPacket.UP_FLAG, b & 0x01);
|
||||
setButtonFlag(ControllerPacket.DOWN_FLAG, b & 0x02);
|
||||
|
||||
// Start/Select
|
||||
setButtonFlag(ControllerPacket.PLAY_FLAG, b & 0x10);
|
||||
setButtonFlag(ControllerPacket.BACK_FLAG, b & 0x20);
|
||||
|
||||
// LS/RS
|
||||
setButtonFlag(ControllerPacket.LS_CLK_FLAG, b & 0x40);
|
||||
setButtonFlag(ControllerPacket.RS_CLK_FLAG, b & 0x80);
|
||||
|
||||
// ABXY buttons
|
||||
b = buffer.get();
|
||||
setButtonFlag(ControllerPacket.A_FLAG, b & 0x10);
|
||||
setButtonFlag(ControllerPacket.B_FLAG, b & 0x20);
|
||||
setButtonFlag(ControllerPacket.X_FLAG, b & 0x40);
|
||||
setButtonFlag(ControllerPacket.Y_FLAG, b & 0x80);
|
||||
|
||||
// LB/RB
|
||||
setButtonFlag(ControllerPacket.LB_FLAG, b & 0x01);
|
||||
setButtonFlag(ControllerPacket.RB_FLAG, b & 0x02);
|
||||
|
||||
// Xbox button
|
||||
setButtonFlag(ControllerPacket.SPECIAL_BUTTON_FLAG, b & 0x04);
|
||||
|
||||
// Triggers
|
||||
leftTrigger = buffer.get() / 255.0f;
|
||||
rightTrigger = buffer.get() / 255.0f;
|
||||
|
||||
// Left stick
|
||||
leftStickX = buffer.getShort() / 32767.0f;
|
||||
leftStickY = ~buffer.getShort() / 32767.0f;
|
||||
|
||||
// Right stick
|
||||
rightStickX = buffer.getShort() / 32767.0f;
|
||||
rightStickY = ~buffer.getShort() / 32767.0f;
|
||||
|
||||
// Return true to send input
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doInit() {
|
||||
// Xbox 360 wired controller requires no initialization
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class DeviceIdTuple {
|
||||
public final int vid;
|
||||
public final int pid;
|
||||
public final String name;
|
||||
|
||||
public DeviceIdTuple(int vid, int pid, String name) {
|
||||
this.vid = vid;
|
||||
this.pid = pid;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,19 +13,7 @@ import com.limelight.nvstream.input.ControllerPacket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class XboxOneController {
|
||||
private final UsbDevice device;
|
||||
private final UsbDeviceConnection connection;
|
||||
private final int deviceId;
|
||||
|
||||
private Thread inputThread;
|
||||
private UsbDriverListener listener;
|
||||
private boolean stopped;
|
||||
|
||||
private short buttonFlags;
|
||||
private float leftTrigger, rightTrigger;
|
||||
private float rightStickX, rightStickY;
|
||||
private float leftStickX, leftStickY;
|
||||
public class XboxOneController extends AbstractXboxController {
|
||||
|
||||
private static final int MICROSOFT_VID = 0x045e;
|
||||
private static final int XB1_IFACE_SUBCLASS = 71;
|
||||
@ -35,28 +23,7 @@ public class XboxOneController {
|
||||
private static final byte[] XB1_INIT_DATA = {0x05, 0x20, 0x00, 0x01, 0x00};
|
||||
|
||||
public XboxOneController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
this.deviceId = deviceId;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public int getControllerId() {
|
||||
return this.deviceId;
|
||||
}
|
||||
|
||||
private void setButtonFlag(int buttonFlag, int data) {
|
||||
if (data != 0) {
|
||||
buttonFlags |= buttonFlag;
|
||||
}
|
||||
else {
|
||||
buttonFlags &= ~buttonFlag;
|
||||
}
|
||||
}
|
||||
|
||||
private void reportInput() {
|
||||
listener.reportControllerState(deviceId, buttonFlags, leftStickX, leftStickY,
|
||||
rightStickX, rightStickY, leftTrigger, rightTrigger);
|
||||
super(device, connection, deviceId, listener);
|
||||
}
|
||||
|
||||
private void processButtons(ByteBuffer buffer) {
|
||||
@ -90,143 +57,24 @@ public class XboxOneController {
|
||||
|
||||
rightStickX = buffer.getShort() / 32767.0f;
|
||||
rightStickY = ~buffer.getShort() / 32767.0f;
|
||||
|
||||
reportInput();
|
||||
}
|
||||
|
||||
private void processPacket(ByteBuffer buffer) {
|
||||
@Override
|
||||
protected boolean handleRead(ByteBuffer buffer) {
|
||||
switch (buffer.get())
|
||||
{
|
||||
case 0x20:
|
||||
buffer.position(buffer.position()+3);
|
||||
processButtons(buffer);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case 0x07:
|
||||
buffer.position(buffer.position() + 3);
|
||||
setButtonFlag(ControllerPacket.SPECIAL_BUTTON_FLAG, buffer.get() & 0x01);
|
||||
reportInput();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void startInputThread(final UsbEndpoint inEndpt) {
|
||||
inputThread = new Thread() {
|
||||
public void run() {
|
||||
while (!isInterrupted() && !stopped) {
|
||||
byte[] buffer = new byte[64];
|
||||
|
||||
int res;
|
||||
|
||||
//
|
||||
// There's no way that I can tell to determine if a device has failed
|
||||
// or if the timeout has simply expired. We'll check how long the transfer
|
||||
// took to fail and assume the device failed if it happened before the timeout
|
||||
// expired.
|
||||
//
|
||||
|
||||
do {
|
||||
// Read the next input state packet
|
||||
long lastMillis = MediaCodecHelper.getMonotonicMillis();
|
||||
res = connection.bulkTransfer(inEndpt, buffer, buffer.length, 3000);
|
||||
|
||||
// If we get a zero length response, treat it as an error
|
||||
if (res == 0) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
if (res == -1 && MediaCodecHelper.getMonotonicMillis() - lastMillis < 1000) {
|
||||
LimeLog.warning("Detected device I/O error");
|
||||
XboxOneController.this.stop();
|
||||
break;
|
||||
}
|
||||
} while (res == -1 && !isInterrupted() && !stopped);
|
||||
|
||||
if (res == -1 || stopped) {
|
||||
break;
|
||||
}
|
||||
|
||||
processPacket(ByteBuffer.wrap(buffer, 0, res).order(ByteOrder.LITTLE_ENDIAN));
|
||||
}
|
||||
}
|
||||
};
|
||||
inputThread.setName("Xbox One Controller - Input Thread");
|
||||
inputThread.start();
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
// Force claim all interfaces
|
||||
for (int i = 0; i < device.getInterfaceCount(); i++) {
|
||||
UsbInterface iface = device.getInterface(i);
|
||||
|
||||
if (!connection.claimInterface(iface, true)) {
|
||||
LimeLog.warning("Failed to claim interfaces");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the endpoints
|
||||
UsbEndpoint outEndpt = null;
|
||||
UsbEndpoint inEndpt = null;
|
||||
UsbInterface iface = device.getInterface(0);
|
||||
for (int i = 0; i < iface.getEndpointCount(); i++) {
|
||||
UsbEndpoint endpt = iface.getEndpoint(i);
|
||||
if (endpt.getDirection() == UsbConstants.USB_DIR_IN) {
|
||||
if (inEndpt != null) {
|
||||
LimeLog.warning("Found duplicate IN endpoint");
|
||||
return false;
|
||||
}
|
||||
inEndpt = endpt;
|
||||
}
|
||||
else if (endpt.getDirection() == UsbConstants.USB_DIR_OUT) {
|
||||
if (outEndpt != null) {
|
||||
LimeLog.warning("Found duplicate OUT endpoint");
|
||||
return false;
|
||||
}
|
||||
outEndpt = endpt;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the required endpoints were present
|
||||
if (inEndpt == null || outEndpt == null) {
|
||||
LimeLog.warning("Missing required endpoint");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send the initialization packet
|
||||
int res = connection.bulkTransfer(outEndpt, XB1_INIT_DATA, XB1_INIT_DATA.length, 3000);
|
||||
if (res != XB1_INIT_DATA.length) {
|
||||
LimeLog.warning("Initialization transfer failed: "+res);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening for controller input
|
||||
startInputThread(inEndpt);
|
||||
|
||||
// Report this device added via the listener
|
||||
listener.deviceAdded(deviceId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = true;
|
||||
|
||||
// Stop the input thread
|
||||
if (inputThread != null) {
|
||||
inputThread.interrupt();
|
||||
inputThread = null;
|
||||
}
|
||||
|
||||
// Report the device removed
|
||||
listener.deviceRemoved(deviceId);
|
||||
|
||||
// Close the USB connection
|
||||
connection.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canClaimDevice(UsbDevice device) {
|
||||
@ -236,4 +84,16 @@ public class XboxOneController {
|
||||
device.getInterface(0).getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||
device.getInterface(0).getInterfaceProtocol() == XB1_IFACE_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doInit() {
|
||||
// Send the initialization packet
|
||||
int res = connection.bulkTransfer(outEndpt, XB1_INIT_DATA, XB1_INIT_DATA.length, 3000);
|
||||
if (res != XB1_INIT_DATA.length) {
|
||||
LimeLog.warning("Initialization transfer failed: "+res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user