diff --git a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java index d2fd2edc..b520d0f0 100644 --- a/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java +++ b/app/src/main/java/com/limelight/binding/input/driver/UsbDriverService.java @@ -184,8 +184,8 @@ public class UsbDriverService extends Service implements UsbDriverListener { else if (Xbox360Controller.canClaimDevice(device)) { controller = new Xbox360Controller(device, connection, nextDeviceId++, this); } - else if (Xbox360WirelessController.canClaimDevice(device)) { - controller = new Xbox360WirelessController(device, connection, nextDeviceId++, this); + else if (Xbox360WirelessDongle.canClaimDevice(device)) { + controller = new Xbox360WirelessDongle(device, connection, nextDeviceId++, this); } else { // Unreachable @@ -289,7 +289,7 @@ public class UsbDriverService extends Service implements UsbDriverListener { public static boolean shouldClaimDevice(UsbDevice device, boolean claimAllAvailable) { return ((!kernelSupportsXboxOne() || !isRecognizedInputDevice(device) || claimAllAvailable) && XboxOneController.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() { diff --git a/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessController.java b/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessController.java deleted file mode 100644 index fdcda540..00000000 --- a/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessController.java +++ /dev/null @@ -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. - } -} diff --git a/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessDongle.java b/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessDongle.java new file mode 100644 index 00000000..7b4aab49 --- /dev/null +++ b/app/src/main/java/com/limelight/binding/input/driver/Xbox360WirelessDongle.java @@ -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. + } +}