diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java deleted file mode 100644 index 98a7d51900..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.hardware.usb.UsbConfiguration; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbEndpoint; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.os.Build; - -import androidx.annotation.Keep; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.services.USBPermService; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -public class WiimoteAdapter -{ - final static int MAX_PAYLOAD = 23; - final static int MAX_WIIMOTES = 4; - final static int TIMEOUT = 200; - final static short NINTENDO_VENDOR_ID = 0x057e; - final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306; - public static UsbManager manager; - - static UsbDeviceConnection usbConnection; - static UsbInterface[] usbInterface = new UsbInterface[MAX_WIIMOTES]; - static UsbEndpoint[] usbIn = new UsbEndpoint[MAX_WIIMOTES]; - - @Keep - public static byte[][] wiimotePayload = new byte[MAX_WIIMOTES][MAX_PAYLOAD]; - - private static void requestPermission() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (!manager.hasPermission(dev)) - { - Log.warning("Requesting permission for Wii Remote adapter"); - - Context context = DolphinApplication.getAppContext(); - Intent intent = new Intent(context, USBPermService.class); - - int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - PendingIntent.FLAG_IMMUTABLE : 0; - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); - - manager.requestPermission(dev, pendingIntent); - } - } - } - } - - @Keep - public static boolean queryAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (manager.hasPermission(dev)) - return true; - else - requestPermission(); - } - } - return false; - } - - @Keep - public static int input(int index) - { - return usbConnection.bulkTransfer(usbIn[index], wiimotePayload[index], MAX_PAYLOAD, TIMEOUT); - } - - @Keep - public static int output(int index, byte[] buf, int size) - { - byte report_number = buf[0]; - - // Remove the report number from the buffer - buf = Arrays.copyOfRange(buf, 1, buf.length); - size--; - - final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5); - final int LIBUSB_RECIPIENT_INTERFACE = 0x1; - final int LIBUSB_ENDPOINT_OUT = 0; - - final int HID_SET_REPORT = 0x9; - final int HID_OUTPUT = (2 << 8); - - int write = usbConnection.controlTransfer( - LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, - HID_SET_REPORT, - HID_OUTPUT | report_number, - index, - buf, size, - 1000); - - if (write < 0) - return 0; - - return write + 1; - } - - @Keep - public static boolean openAdapter() - { - // If the adapter is already open. Don't attempt to do it again - if (usbConnection != null && usbConnection.getFileDescriptor() != -1) - return true; - - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (manager.hasPermission(dev)) - { - usbConnection = manager.openDevice(dev); - UsbConfiguration conf = dev.getConfiguration(0); - - Log.info("Number of configurations: " + dev.getConfigurationCount()); - Log.info("Number of Interfaces: " + dev.getInterfaceCount()); - Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount()); - - // Sometimes the interface count is returned as zero. - // Means the device needs to be unplugged and plugged back in again - if (dev.getInterfaceCount() > 0) - { - for (int i = 0; i < MAX_WIIMOTES; ++i) - { - // One interface per Wii Remote - usbInterface[i] = dev.getInterface(i); - usbConnection.claimInterface(usbInterface[i], true); - - // One endpoint per Wii Remote. Input only - // Output reports go through the control channel. - usbIn[i] = usbInterface[i].getEndpoint(0); - Log.info("Interface " + i + " endpoint count:" + usbInterface[i].getEndpointCount()); - } - return true; - } - else - { - // XXX: Message that the device was found, but it needs to be unplugged and plugged back in? - usbConnection.close(); - } - } - } - } - return false; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.kt new file mode 100644 index 0000000000..5f7c2bcb90 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.kt @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.hardware.usb.UsbConfiguration +import android.hardware.usb.UsbDeviceConnection +import android.hardware.usb.UsbEndpoint +import android.hardware.usb.UsbInterface +import android.hardware.usb.UsbManager +import androidx.annotation.Keep +import org.dolphinemu.dolphinemu.DolphinApplication +import org.dolphinemu.dolphinemu.services.USBPermService +import java.util.Arrays + +object WiimoteAdapter { + private const val MAX_PAYLOAD = 23 + private const val MAX_WIIMOTES = 4 + private const val TIMEOUT = 200 + private const val NINTENDO_VENDOR_ID = 0x057e + private const val NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306 + + lateinit var manager: UsbManager + + private var usbConnection: UsbDeviceConnection? = null + private val usbInterface = arrayOfNulls(MAX_WIIMOTES) + private val usbIn = arrayOfNulls(MAX_WIIMOTES) + + @Keep + @JvmField + val wiimotePayload: Array = Array(MAX_WIIMOTES) { ByteArray(MAX_PAYLOAD) } + + private fun requestPermission() { + val devices = manager.deviceList + for (dev in devices.values) { + if (dev.productId == NINTENDO_WIIMOTE_PRODUCT_ID && dev.vendorId == NINTENDO_VENDOR_ID) { + if (!manager.hasPermission(dev)) { + Log.warning("Requesting permission for Wii Remote adapter") + + val context: Context = DolphinApplication.getAppContext() + val intent = Intent(context, USBPermService::class.java) + + val pendingIntent = + PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + + manager.requestPermission(dev, pendingIntent) + } + } + } + } + + @Keep + @JvmStatic + fun queryAdapter(): Boolean { + val devices = manager.deviceList + for (dev in devices.values) { + if (dev.productId == NINTENDO_WIIMOTE_PRODUCT_ID && dev.vendorId == NINTENDO_VENDOR_ID) { + if (manager.hasPermission(dev)) { + return true + } else { + requestPermission() + } + } + } + return false + } + + @Keep + @JvmStatic + fun input(index: Int): Int { + return usbConnection!!.bulkTransfer( + usbIn[index], wiimotePayload[index], MAX_PAYLOAD, TIMEOUT + ) + } + + @Keep + @JvmStatic + fun output(index: Int, buf: ByteArray, size: Int): Int { + val reportNumber = buf[0] + + // Remove the report number from the buffer + val outputBuffer = Arrays.copyOfRange(buf, 1, buf.size) + val outputSize = size - 1 + + val libusbRequestTypeClass = 1 shl 5 + val libusbRecipientInterface = 0x1 + val libusbEndpointOut = 0 + + val hidSetReport = 0x9 + val hidOutput = 2 shl 8 + + val write = usbConnection!!.controlTransfer( + libusbRequestTypeClass or libusbRecipientInterface or libusbEndpointOut, + hidSetReport, + hidOutput or reportNumber.toInt(), + index, + outputBuffer, + outputSize, + 1000 + ) + + if (write < 0) { + return 0 + } + + return write + 1 + } + + @Keep + @JvmStatic + fun openAdapter(): Boolean { + // If the adapter is already open. Don't attempt to do it again + if (usbConnection != null && usbConnection!!.fileDescriptor != -1) { + return true + } + + val devices = manager.deviceList + for (dev in devices.values) { + if (dev.productId == NINTENDO_WIIMOTE_PRODUCT_ID && dev.vendorId == NINTENDO_VENDOR_ID) { + if (manager.hasPermission(dev)) { + usbConnection = manager.openDevice(dev) + val conf: UsbConfiguration = dev.getConfiguration(0) + + Log.info("Number of configurations: " + dev.configurationCount) + Log.info("Number of Interfaces: " + dev.interfaceCount) + Log.info("Number of Interfaces from conf: " + conf.interfaceCount) + + // Sometimes the interface count is returned as zero. + // Means the device needs to be unplugged and plugged back in again + if (dev.interfaceCount > 0) { + for (i in 0 until MAX_WIIMOTES) { + // One interface per Wii Remote + usbInterface[i] = dev.getInterface(i) + usbConnection!!.claimInterface(usbInterface[i], true) + + // One endpoint per Wii Remote. Input only + // Output reports go through the control channel. + usbIn[i] = usbInterface[i]!!.getEndpoint(0) + Log.info("Interface " + i + " endpoint count:" + usbInterface[i]!!.endpointCount) + } + return true + } else { + // XXX: Message that the device was found, but it needs to be unplugged and plugged back in? + usbConnection!!.close() + } + } + } + } + return false + } +}