From ca0c2efe7ab19c85449d52dd1bf4beec8603bbfe Mon Sep 17 00:00:00 2001 From: Tillmann Karras Date: Sat, 10 Jan 2015 18:10:45 +0100 Subject: [PATCH] WiimoteReal: reimplement using HIDAPI Using the HIDAPI library has the advantage that we get Wiimote and DolphinBar support on all platforms with only one IO implementation. --- CMakeLists.txt | 57 +- CMakeTests/FindHIDAPI.cmake | 9 + Externals/hidapi/libusb/CMakeLists.txt | 1 + Externals/hidapi/linux/CMakeLists.txt | 1 + Externals/hidapi/mac/CMakeLists.txt | 1 + Externals/libusb/libusb/CMakeLists.txt | 17 + Externals/libusb/libusb/os/CMakeLists.txt | 7 + .../libusb/msvc/libusb_static_2013.vcxproj | 2 - Installer/90-wiimote.rules | 8 + Source/Core/Core/CMakeLists.txt | 21 +- Source/Core/Core/Core.vcxproj | 5 +- Source/Core/Core/Core.vcxproj.filters | 2 +- Source/Core/Core/HW/WiimoteReal/IODummy.cpp | 31 - Source/Core/Core/HW/WiimoteReal/IONix.cpp | 279 ----- Source/Core/Core/HW/WiimoteReal/IOWin.cpp | 954 ------------------ Source/Core/Core/HW/WiimoteReal/IOdarwin.mm | 380 ------- Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp | 150 +++ Source/Core/Core/HW/WiimoteReal/WiimoteReal.h | 7 - .../Core/HW/WiimoteReal/WiimoteRealBase.h | 16 - 19 files changed, 245 insertions(+), 1703 deletions(-) create mode 100644 CMakeTests/FindHIDAPI.cmake create mode 100644 Externals/hidapi/libusb/CMakeLists.txt create mode 100644 Externals/hidapi/linux/CMakeLists.txt create mode 100644 Externals/hidapi/mac/CMakeLists.txt create mode 100644 Externals/libusb/libusb/CMakeLists.txt create mode 100644 Externals/libusb/libusb/os/CMakeLists.txt create mode 100644 Installer/90-wiimote.rules delete mode 100644 Source/Core/Core/HW/WiimoteReal/IODummy.cpp delete mode 100644 Source/Core/Core/HW/WiimoteReal/IONix.cpp delete mode 100644 Source/Core/Core/HW/WiimoteReal/IOWin.cpp delete mode 100644 Source/Core/Core/HW/WiimoteReal/IOdarwin.mm create mode 100644 Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 528b1cbb17..4d292421a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ # General setup # cmake_minimum_required(VERSION 2.8.8) +project(dolphin-emu) option(USE_EGL "Enables EGL OpenGL Interface" OFF) option(TRY_X11 "Enables X11 Support" ON) @@ -13,6 +14,9 @@ option(ENABLE_LTO "Enables Link Time Optimization" OFF) option(ENABLE_GENERIC "Enables generic build that should run on any little-endian host" OFF) if(APPLE) option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF) + option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + option(INSTALL_UDEV_RULES "Install udev rules for hidraw/usb devices" OFF) endif() option(ENCODE_FRAMEDUMPS "Encode framedumps in AVI format" ON) @@ -21,9 +25,6 @@ option(FASTLOG "Enable all logs" OFF) option(OPROFILING "Enable profiling" OFF) option(GDBSTUB "Enable gdb stub for remote debugging." OFF) -if(APPLE) - option(SKIP_POSTPROCESS_BUNDLE "Skip postprocessing bundle for redistributability" OFF) -endif() ######################################## # Optional Targets # TODO: Add DSPSpy @@ -45,7 +46,6 @@ if (APPLE) endif() endif() endif() -project(dolphin-emu) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/CMakeTests) set(DOLPHIN_IS_STABLE FALSE) # Libraries to link @@ -292,7 +292,6 @@ if(APPLE) find_library(COREFUND_LIBRARY CoreFoundation) find_library(CORESERV_LIBRARY CoreServices) find_library(FOUNDATION_LIBRARY foundation) - find_library(IOB_LIBRARY IOBluetooth) find_library(IOK_LIBRARY IOKit) find_library(QUICKTIME_LIBRARY QuickTime) find_library(WEBKIT_LIBRARY WebKit) @@ -379,15 +378,6 @@ if(NOT ANDROID) message("ao NOT found, disabling ao sound backend") endif(AO_FOUND) - check_lib(BLUEZ bluez QUIET) - if(BLUEZ_FOUND) - add_definitions(-DHAVE_BLUEZ=1) - message("bluez found, enabling bluetooth support") - else() - add_definitions(-DHAVE_BLUEZ=0) - message("bluez NOT found, disabling bluetooth support") - endif(BLUEZ_FOUND) - check_lib(PULSEAUDIO libpulse QUIET) if(PULSEAUDIO_FOUND) add_definitions(-DHAVE_PULSEAUDIO=1) @@ -595,10 +585,19 @@ endif() include(FindLibUSB OPTIONAL) if(LIBUSB_FOUND) - message("Using shared LibUSB") + message("Using shared libusb") add_definitions(-D__LIBUSB__) include_directories(${LIBUSB_INCLUDE_DIR}) -endif(LIBUSB_FOUND) + list(APPEND LIBS ${LIBUSB_LIBRARIES}) +elseif(ANDROID) + message("Using static libusb from Externals") + add_subdirectory(Externals/libusb/libusb) + include_directories(Externals/libusb/libusb) + list(APPEND LIBS usb-1.0) +else() + message(FATAL_ERROR "libusb is required. Either extend the CMakeLists.txt file to support your OS" + " or just install libusb as a shared library.") +endif() set(SFML_REQD_VERSION 2.1) if(NOT APPLE AND NOT ANDROID) @@ -665,6 +664,32 @@ else() mark_as_advanced(ICONV_INCLUDE_DIR ICONV_LIBRARIES) endif() +include(FindHIDAPI OPTIONAL) +find_package(HIDAPI) +if(HIDAPI_FOUND) + message("Using shared ${HIDAPI_LIBRARIES} ${HIDAPI_VERSION}") + include_directories(${HIDAPI_INCLUDE_DIRS}) + list(APPEND LIBS ${HIDAPI_LIBRARIES}) +else() + include_directories(Externals/hidapi/hidapi) + if(APPLE) + message("Using static hidapi from Externals") + add_subdirectory(Externals/hidapi/mac) + list(APPEND LIBS hidapi) + elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ANDROID) + message("Using static hidapi-hidraw from Externals") + add_subdirectory(Externals/hidapi/linux) + list(APPEND LIBS hidapi-hidraw udev) + else() + message("Using static hidapi-libusb from Externals") + add_subdirectory(Externals/hidapi/libusb) + list(APPEND LIBS hidapi-libusb) + endif() +endif() +if(INSTALL_UDEV_RULES) + install(FILES Installer/90-wiimote.rules /etc/udev/rules.d/) +endif() + if(ENABLE_QT) find_package(Qt5Widgets REQUIRED) message("Found Qt version ${Qt5Core_VERSION}, enabling the Qt backend") diff --git a/CMakeTests/FindHIDAPI.cmake b/CMakeTests/FindHIDAPI.cmake new file mode 100644 index 0000000000..b75110ab12 --- /dev/null +++ b/CMakeTests/FindHIDAPI.cmake @@ -0,0 +1,9 @@ +find_path(HIDAPI_INCLUDE_DIR NAMES hidapi.h PATH_SUFFIXES hidapi) +find_library(HIDAPI_LIBRARY NAMES hidapi hidapi-hidraw hidapi-libusb) +set(HIDAPI_LIBRARIES ${HIDAPI_LIBRARY}) +set(HIDAPI_INCLUDE_DIRS ${HIDAPI_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(HIDAPI DEFAULT_MSG HIDAPI_LIBRARY HIDAPI_INCLUDE_DIR) + +mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) diff --git a/Externals/hidapi/libusb/CMakeLists.txt b/Externals/hidapi/libusb/CMakeLists.txt new file mode 100644 index 0000000000..5bb2c08688 --- /dev/null +++ b/Externals/hidapi/libusb/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-libusb hid.c) diff --git a/Externals/hidapi/linux/CMakeLists.txt b/Externals/hidapi/linux/CMakeLists.txt new file mode 100644 index 0000000000..f7984bd755 --- /dev/null +++ b/Externals/hidapi/linux/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-hidraw hid.c) diff --git a/Externals/hidapi/mac/CMakeLists.txt b/Externals/hidapi/mac/CMakeLists.txt new file mode 100644 index 0000000000..ccd2898ed4 --- /dev/null +++ b/Externals/hidapi/mac/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi hid.c) diff --git a/Externals/libusb/libusb/CMakeLists.txt b/Externals/libusb/libusb/CMakeLists.txt new file mode 100644 index 0000000000..30f297823e --- /dev/null +++ b/Externals/libusb/libusb/CMakeLists.txt @@ -0,0 +1,17 @@ +# This file is only used for Android (because I'm lazy). +# We don't use CMake on Windows and just require libusb to be installed everywhere else. + +set(SRCS + core.c + descriptor.c + hotplug.c + io.c + strerror.c + sync.c +) + +include_directories(BEFORE ../android) + +add_subdirectory(os) +add_library(usb-1.0 STATIC ${SRCS}) +target_link_libraries(usb-1.0 usb-os) diff --git a/Externals/libusb/libusb/os/CMakeLists.txt b/Externals/libusb/libusb/os/CMakeLists.txt new file mode 100644 index 0000000000..e0add49808 --- /dev/null +++ b/Externals/libusb/libusb/os/CMakeLists.txt @@ -0,0 +1,7 @@ +include_directories(..) +add_library(usb-os STATIC + linux_netlink.c + linux_usbfs.c + poll_posix.c + threads_posix.c +) diff --git a/Externals/libusb/msvc/libusb_static_2013.vcxproj b/Externals/libusb/msvc/libusb_static_2013.vcxproj index 45a88c567c..3ba53075cc 100644 --- a/Externals/libusb/msvc/libusb_static_2013.vcxproj +++ b/Externals/libusb/msvc/libusb_static_2013.vcxproj @@ -27,7 +27,6 @@ StaticLibrary Unicode - true v120 @@ -38,7 +37,6 @@ StaticLibrary Unicode - true v120 diff --git a/Installer/90-wiimote.rules b/Installer/90-wiimote.rules new file mode 100644 index 0000000000..71c9c30fde --- /dev/null +++ b/Installer/90-wiimote.rules @@ -0,0 +1,8 @@ +# This is how you can match Wiimotes (and Wiimote-emulating devices like the DolphinBar) in udev. +# If you are a package maintainer you'll probably want to restrict permissions to a group: GROUP="plugdev", MODE="0660". + +# Wiimote or DolphinBar +SUBSYSTEMS=="hid", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0306", MODE="0666" + +# newer Wiimotes (RVL-CNT-01-TR) +SUBSYSTEMS=="hid", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0330", MODE="0666" diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 3af53428ac..cdd3a694e7 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRCS ActionReplay.cpp HW/SI_DeviceGBA.cpp HW/SI_DeviceGCController.cpp HW/SI_DeviceGCSteeringWheel.cpp + HW/SI_GCAdapter.cpp HW/Sram.cpp HW/StreamADPCM.cpp HW/SystemTimers.cpp @@ -132,6 +133,7 @@ set(SRCS ActionReplay.cpp HW/WiimoteEmu/EmuSubroutines.cpp HW/WiimoteEmu/Encryption.cpp HW/WiimoteEmu/Speaker.cpp + HW/WiimoteReal/IOhidapi.cpp HW/WiimoteReal/WiimoteReal.cpp HW/WiiSaveCrypted.cpp IPC_HLE/ICMPLin.cpp @@ -140,6 +142,7 @@ set(SRCS ActionReplay.cpp IPC_HLE/WII_IPC_HLE_Device_es.cpp IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp IPC_HLE/WII_IPC_HLE_Device_fs.cpp + IPC_HLE/WII_IPC_HLE_Device_hid.cpp IPC_HLE/WII_Socket.cpp IPC_HLE/WII_IPC_HLE_Device_net.cpp IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp @@ -245,29 +248,15 @@ set(LIBS z ) -if(LIBUSB_FOUND) - # Using shared LibUSB - set(LIBS ${LIBS} ${LIBUSB_LIBRARIES}) - set(SRCS ${SRCS} IPC_HLE/WII_IPC_HLE_Device_hid.cpp - HW/SI_GCAdapter.cpp) -endif(LIBUSB_FOUND) set(LIBS ${LIBS} ${POLARSSL_LIBRARY}) if(WIN32) - set(SRCS ${SRCS} HW/BBA-TAP/TAP_Win32.cpp HW/WiimoteReal/IOWin.cpp) + set(SRCS ${SRCS} HW/BBA-TAP/TAP_Win32.cpp) elseif(APPLE) - set(SRCS ${SRCS} HW/BBA-TAP/TAP_Apple.cpp HW/WiimoteReal/IOdarwin.mm) - set(LIBS ${LIBS} - ${IOB_LIBRARY}) + set(SRCS ${SRCS} HW/BBA-TAP/TAP_Apple.cpp) elseif(UNIX) set(SRCS ${SRCS} HW/BBA-TAP/TAP_Unix.cpp) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND BLUEZ_FOUND) - set(SRCS ${SRCS} HW/WiimoteReal/IONix.cpp) - set(LIBS ${LIBS} bluetooth) - else() - set(SRCS ${SRCS} HW/WiimoteReal/IODummy.cpp) - endif() endif() if(PORTAUDIO_FOUND) diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index ca37ca6621..1b356266b5 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -169,7 +169,7 @@ - + @@ -470,6 +470,9 @@ {3de9ee35-3e91-4f27-a014-2866ad8c3fe3} + + {549d32d8-1640-46f9-9d78-bae6eb0d723d} + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 4ddaee9aa4..fda79ce620 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -484,7 +484,7 @@ HW %28Flipper/Hollywood%29\Wiimote\Emu - + HW %28Flipper/Hollywood%29\Wiimote\Real diff --git a/Source/Core/Core/HW/WiimoteReal/IODummy.cpp b/Source/Core/Core/HW/WiimoteReal/IODummy.cpp deleted file mode 100644 index 090c7141e5..0000000000 --- a/Source/Core/Core/HW/WiimoteReal/IODummy.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include "Common/CommonTypes.h" -#include "Core/HW/WiimoteReal/WiimoteReal.h" - -namespace WiimoteReal -{ - -WiimoteScanner::WiimoteScanner() -{} - -WiimoteScanner::~WiimoteScanner() -{} - -void WiimoteScanner::Update() -{} - -void WiimoteScanner::FindWiimotes(std::vector & found_wiimotes, Wiimote* & found_board) -{ - found_wiimotes.clear(); - found_board = nullptr; -} - -bool WiimoteScanner::IsReady() const -{ - return false; -} - -}; diff --git a/Source/Core/Core/HW/WiimoteReal/IONix.cpp b/Source/Core/Core/HW/WiimoteReal/IONix.cpp deleted file mode 100644 index d287121385..0000000000 --- a/Source/Core/Core/HW/WiimoteReal/IONix.cpp +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2014 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include -#include -#include -#include - -#include "Common/Common.h" -#include "Core/HW/WiimoteReal/WiimoteReal.h" - -namespace WiimoteReal -{ - -class WiimoteLinux final : public Wiimote -{ -public: - WiimoteLinux(bdaddr_t bdaddr); - ~WiimoteLinux() override; - -protected: - bool ConnectInternal() override; - void DisconnectInternal() override; - bool IsConnected() const override; - void IOWakeup() override; - int IORead(u8* buf) override; - int IOWrite(u8 const* buf, size_t len) override; - -private: - bdaddr_t m_bdaddr; // Bluetooth address - int m_cmd_sock; // Command socket - int m_int_sock; // Interrupt socket - int m_wakeup_pipe_w; - int m_wakeup_pipe_r; -}; - -WiimoteScanner::WiimoteScanner() - : m_want_wiimotes() - , device_id(-1) - , device_sock(-1) -{ - // Get the id of the first bluetooth device. - device_id = hci_get_route(nullptr); - if (device_id < 0) - { - NOTICE_LOG(WIIMOTE, "Bluetooth not found."); - return; - } - - // Create a socket to the device - device_sock = hci_open_dev(device_id); - if (device_sock < 0) - { - ERROR_LOG(WIIMOTE, "Unable to open bluetooth."); - return; - } -} - -bool WiimoteScanner::IsReady() const -{ - return device_sock > 0; -} - -WiimoteScanner::~WiimoteScanner() -{ - if (IsReady()) - close(device_sock); -} - -void WiimoteScanner::Update() -{} - -void WiimoteScanner::FindWiimotes(std::vector & found_wiimotes, Wiimote* & found_board) -{ - // supposedly 1.28 seconds - int const wait_len = 1; - - int const max_infos = 255; - inquiry_info scan_infos[max_infos] = {}; - auto* scan_infos_ptr = scan_infos; - found_board = nullptr; - - // Scan for bluetooth devices - int const found_devices = hci_inquiry(device_id, wait_len, max_infos, nullptr, &scan_infos_ptr, IREQ_CACHE_FLUSH); - if (found_devices < 0) - { - ERROR_LOG(WIIMOTE, "Error searching for bluetooth devices."); - return; - } - - DEBUG_LOG(WIIMOTE, "Found %i bluetooth device(s).", found_devices); - - // Display discovered devices - for (int i = 0; i < found_devices; ++i) - { - ERROR_LOG(WIIMOTE, "found a device..."); - - // BT names are a maximum of 248 bytes apparently - char name[255] = {}; - if (hci_read_remote_name(device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 1000) < 0) - { - ERROR_LOG(WIIMOTE, "name request failed"); - continue; - } - - ERROR_LOG(WIIMOTE, "device name %s", name); - if (IsValidBluetoothName(name)) - { - bool new_wiimote = true; - - // TODO: do this - - // Determine if this wiimote has already been found. - //for (int j = 0; j < MAX_WIIMOTES && new_wiimote; ++j) - //{ - // if (wm[j] && bacmp(&scan_infos[i].bdaddr,&wm[j]->bdaddr) == 0) - // new_wiimote = false; - //} - - if (new_wiimote) - { - // Found a new device - char bdaddr_str[18] = {}; - ba2str(&scan_infos[i].bdaddr, bdaddr_str); - - Wiimote* wm = new WiimoteLinux(scan_infos[i].bdaddr); - if (IsBalanceBoardName(name)) - { - found_board = wm; - NOTICE_LOG(WIIMOTE, "Found balance board (%s).", bdaddr_str); - } - else - { - found_wiimotes.push_back(wm); - NOTICE_LOG(WIIMOTE, "Found wiimote (%s).", bdaddr_str); - } - } - } - } - -} - -WiimoteLinux::WiimoteLinux(bdaddr_t bdaddr) : Wiimote(), m_bdaddr(bdaddr) -{ - m_cmd_sock = -1; - m_int_sock = -1; - - int fds[2]; - if (pipe(fds)) - { - ERROR_LOG(WIIMOTE, "pipe failed"); - abort(); - } - m_wakeup_pipe_w = fds[1]; - m_wakeup_pipe_r = fds[0]; -} - -WiimoteLinux::~WiimoteLinux() -{ - Shutdown(); - close(m_wakeup_pipe_w); - close(m_wakeup_pipe_r); -} - -// Connect to a wiimote with a known address. -bool WiimoteLinux::ConnectInternal() -{ - sockaddr_l2 addr = {}; - addr.l2_family = AF_BLUETOOTH; - addr.l2_bdaddr = m_bdaddr; - addr.l2_cid = 0; - - // Output channel - addr.l2_psm = htobs(WM_OUTPUT_CHANNEL); - if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 || - connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0) - { - WARN_LOG(WIIMOTE, "Unable to open output socket to wiimote: %s", strerror(errno)); - close(m_cmd_sock); - m_cmd_sock = -1; - return false; - } - - // Input channel - addr.l2_psm = htobs(WM_INPUT_CHANNEL); - if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 || - connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0) - { - WARN_LOG(WIIMOTE, "Unable to open input socket from wiimote: %s", strerror(errno)); - close(m_int_sock); - close(m_cmd_sock); - m_int_sock = m_cmd_sock = -1; - return false; - } - - return true; -} - -void WiimoteLinux::DisconnectInternal() -{ - close(m_cmd_sock); - close(m_int_sock); - - m_cmd_sock = -1; - m_int_sock = -1; -} - -bool WiimoteLinux::IsConnected() const -{ - return m_cmd_sock != -1;// && int_sock != -1; -} - -void WiimoteLinux::IOWakeup() -{ - char c = 0; - if (write(m_wakeup_pipe_w, &c, 1) != 1) - { - ERROR_LOG(WIIMOTE, "Unable to write to wakeup pipe."); - } -} - -// positive = read packet -// negative = didn't read packet -// zero = error -int WiimoteLinux::IORead(u8* buf) -{ - // Block select for 1/2000th of a second - - fd_set fds; - FD_ZERO(&fds); - FD_SET(m_int_sock, &fds); - FD_SET(m_wakeup_pipe_r, &fds); - - if (select(m_int_sock + 1, &fds, nullptr, nullptr, nullptr) == -1) - { - ERROR_LOG(WIIMOTE, "Unable to select wiimote %i input socket.", m_index + 1); - return -1; - } - - if (FD_ISSET(m_wakeup_pipe_r, &fds)) - { - char c; - if (read(m_wakeup_pipe_r, &c, 1) != 1) - { - ERROR_LOG(WIIMOTE, "Unable to read from wakeup pipe."); - } - return -1; - } - - if (!FD_ISSET(m_int_sock, &fds)) - return -1; - - // Read the pending message into the buffer - int r = read(m_int_sock, buf, MAX_PAYLOAD); - if (r == -1) - { - // Error reading data - ERROR_LOG(WIIMOTE, "Receiving data from wiimote %i.", m_index + 1); - - if (errno == ENOTCONN) - { - // This can happen if the bluetooth dongle is disconnected - ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. " - "Wiimote %i will be disconnected.", m_index + 1); - } - - r = 0; - } - - return r; -} - -int WiimoteLinux::IOWrite(u8 const* buf, size_t len) -{ - return write(m_int_sock, buf, (int)len); -} - -}; // WiimoteReal diff --git a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp b/Source/Core/Core/HW/WiimoteReal/IOWin.cpp deleted file mode 100644 index 1605cfd20d..0000000000 --- a/Source/Core/Core/HW/WiimoteReal/IOWin.cpp +++ /dev/null @@ -1,954 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include -// The following Windows headers must be included AFTER windows.h. -#include //NOLINT -#include //NOLINT -#include //NOLINT - -#include "Common/Common.h" -#include "Common/StringUtil.h" - -#include "Core/HW/WiimoteReal/WiimoteReal.h" - -//#define AUTHENTICATE_WIIMOTES -#define SHARE_WRITE_WIIMOTES - -// Create func_t function pointer type and declare a nullptr-initialized static variable of that -// type named "pfunc". -#define DYN_FUNC_DECLARE(func) \ - typedef decltype(&func) func ## _t; \ - static func ## _t p ## func = nullptr; - -DYN_FUNC_DECLARE(HidD_GetHidGuid); -DYN_FUNC_DECLARE(HidD_GetAttributes); -DYN_FUNC_DECLARE(HidD_SetOutputReport); -DYN_FUNC_DECLARE(HidD_GetProductString); - -DYN_FUNC_DECLARE(BluetoothFindDeviceClose); -DYN_FUNC_DECLARE(BluetoothFindFirstDevice); -DYN_FUNC_DECLARE(BluetoothFindFirstRadio); -DYN_FUNC_DECLARE(BluetoothFindNextDevice); -DYN_FUNC_DECLARE(BluetoothFindNextRadio); -DYN_FUNC_DECLARE(BluetoothFindRadioClose); -DYN_FUNC_DECLARE(BluetoothGetRadioInfo); -DYN_FUNC_DECLARE(BluetoothRemoveDevice); -DYN_FUNC_DECLARE(BluetoothSetServiceState); -DYN_FUNC_DECLARE(BluetoothAuthenticateDeviceEx); -DYN_FUNC_DECLARE(BluetoothEnumerateInstalledServices); - -#undef DYN_FUNC_DECLARE - -static HINSTANCE s_hid_lib = nullptr; -static HINSTANCE s_bthprops_lib = nullptr; - -static bool s_loaded_ok = false; - -std::unordered_map g_connect_times; - -#ifdef SHARE_WRITE_WIIMOTES -std::unordered_set> g_connected_wiimotes; -std::mutex g_connected_wiimotes_lock; -#endif - -#define DYN_FUNC_UNLOAD(func) \ - p ## func = nullptr; - -// Attempt to load the function from the given module handle. -#define DYN_FUNC_LOAD(module, func) \ - p ## func = ( func ## _t)::GetProcAddress(module, # func ); \ - if (! p ## func ) \ - { \ - return false; \ - } - -bool load_hid() -{ - auto loader = [&]() - { - s_hid_lib = ::LoadLibrary(_T("hid.dll")); - if (!s_hid_lib) - { - return false; - } - - DYN_FUNC_LOAD(s_hid_lib, HidD_GetHidGuid); - DYN_FUNC_LOAD(s_hid_lib, HidD_GetAttributes); - DYN_FUNC_LOAD(s_hid_lib, HidD_SetOutputReport); - DYN_FUNC_LOAD(s_hid_lib, HidD_GetProductString); - - return true; - }; - - bool loaded_ok = loader(); - - if (!loaded_ok) - { - DYN_FUNC_UNLOAD(HidD_GetHidGuid); - DYN_FUNC_UNLOAD(HidD_GetAttributes); - DYN_FUNC_UNLOAD(HidD_SetOutputReport); - DYN_FUNC_UNLOAD(HidD_GetProductString); - - if (s_hid_lib) - { - ::FreeLibrary(s_hid_lib); - s_hid_lib = nullptr; - } - } - - return loaded_ok; -} - -bool load_bthprops() -{ - auto loader = [&]() - { - s_bthprops_lib = ::LoadLibrary(_T("bthprops.cpl")); - if (!s_bthprops_lib) - { - return false; - } - - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindDeviceClose); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindFirstDevice); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindFirstRadio); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindNextDevice); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindNextRadio); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothFindRadioClose); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothGetRadioInfo); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothRemoveDevice); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothSetServiceState); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothAuthenticateDeviceEx); - DYN_FUNC_LOAD(s_bthprops_lib, BluetoothEnumerateInstalledServices); - - return true; - }; - - bool loaded_ok = loader(); - - if (!loaded_ok) - { - DYN_FUNC_UNLOAD(BluetoothFindDeviceClose); - DYN_FUNC_UNLOAD(BluetoothFindFirstDevice); - DYN_FUNC_UNLOAD(BluetoothFindFirstRadio); - DYN_FUNC_UNLOAD(BluetoothFindNextDevice); - DYN_FUNC_UNLOAD(BluetoothFindNextRadio); - DYN_FUNC_UNLOAD(BluetoothFindRadioClose); - DYN_FUNC_UNLOAD(BluetoothGetRadioInfo); - DYN_FUNC_UNLOAD(BluetoothRemoveDevice); - DYN_FUNC_UNLOAD(BluetoothSetServiceState); - DYN_FUNC_UNLOAD(BluetoothAuthenticateDeviceEx); - DYN_FUNC_UNLOAD(BluetoothEnumerateInstalledServices); - - if (s_bthprops_lib) - { - ::FreeLibrary(s_bthprops_lib); - s_bthprops_lib = nullptr; - } - } - - return loaded_ok; -} - -#undef DYN_FUNC_LOAD -#undef DYN_FUNC_UNLOAD - -inline void init_lib() -{ - static bool initialized = false; - - if (!initialized) - { - // Only try once - initialized = true; - - // After these calls, we know all dynamically loaded APIs will either all be valid or - // all nullptr. - if (!load_hid() || !load_bthprops()) - { - NOTICE_LOG(WIIMOTE, - "Failed to load bluetooth support libraries, wiimotes will not function"); - return; - } - - s_loaded_ok = true; - } -} - -namespace WiimoteReal -{ - -class WiimoteWindows final : public Wiimote -{ -public: - WiimoteWindows(const std::basic_string& path); - ~WiimoteWindows() override; - -protected: - bool ConnectInternal() override; - void DisconnectInternal() override; - bool IsConnected() const override; - void IOWakeup() override; - int IORead(u8* buf) override; - int IOWrite(u8 const* buf, size_t len) override; - -private: - std::basic_string m_devicepath; // Unique wiimote reference - HANDLE m_dev_handle; // HID handle - OVERLAPPED m_hid_overlap_read; // Overlap handles - OVERLAPPED m_hid_overlap_write; - enum win_bt_stack_t m_stack; // Type of bluetooth stack to use -}; - -int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, size_t len, DWORD* written); -int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index); - -template -void ProcessWiimotes(bool new_scan, T& callback); - -bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT&); -void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); -bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); - -WiimoteScanner::WiimoteScanner() - : m_run_thread() - , m_want_wiimotes() -{ - init_lib(); -} - -WiimoteScanner::~WiimoteScanner() -{ - // TODO: what do we want here? -#if 0 - ProcessWiimotes(false, [](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) - { - RemoveWiimote(btdi); - }); -#endif -} - -void WiimoteScanner::Update() -{ - if (!s_loaded_ok) - return; - - bool forgot_some = false; - - ProcessWiimotes(false, [&](HANDLE, BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) - { - forgot_some |= ForgetWiimote(btdi); - }); - - // Some hacks that allows disconnects to be detected before connections are handled - // workaround for wiimote 1 moving to slot 2 on temporary disconnect - if (forgot_some) - SLEEP(100); -} - -// Find and connect wiimotes. -// Does not replace already found wiimotes even if they are disconnected. -// wm is an array of max_wiimotes wiimotes -// Returns the total number of found and connected wiimotes. -void WiimoteScanner::FindWiimotes(std::vector & found_wiimotes, Wiimote* & found_board) -{ - if (!s_loaded_ok) - return; - - ProcessWiimotes(true, [](HANDLE hRadio, const BLUETOOTH_RADIO_INFO& rinfo, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) - { - ForgetWiimote(btdi); - AttachWiimote(hRadio, rinfo, btdi); - }); - - // Get the device id - GUID device_id; - pHidD_GetHidGuid(&device_id); - - // Get all hid devices connected - HDEVINFO const device_info = SetupDiGetClassDevs(&device_id, nullptr, nullptr, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)); - - SP_DEVICE_INTERFACE_DATA device_data; - device_data.cbSize = sizeof(device_data); - PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = nullptr; - - for (int index = 0; SetupDiEnumDeviceInterfaces(device_info, nullptr, &device_id, index, &device_data); ++index) - { - // Get the size of the data block required - DWORD len; - SetupDiGetDeviceInterfaceDetail(device_info, &device_data, nullptr, 0, &len, nullptr); - detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(len); - detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - - // Query the data for this device - if (SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, nullptr, nullptr)) - { - std::basic_string device_path(detail_data->DevicePath); - Wiimote* wm = new WiimoteWindows(device_path); - bool real_wiimote = false, is_bb = false; - - CheckDeviceType(device_path, real_wiimote, is_bb); - if (is_bb) - { - found_board = wm; - } - else if (real_wiimote) - { - found_wiimotes.push_back(wm); - } - else - { - delete wm; - } - } - - free(detail_data); - } - - SetupDiDestroyDeviceInfoList(device_info); - - // Don't mind me, just a random sleep to fix stuff on Windows - //if (!wiimotes.empty()) - // SLEEP(2000); - -} -int CheckDeviceType_Write(HANDLE &dev_handle, const u8* buf, size_t size, int attempts) -{ - OVERLAPPED hid_overlap_write = OVERLAPPED(); - hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr); - enum win_bt_stack_t stack = MSBT_STACK_UNKNOWN; - - DWORD written = 0; - - for (; attempts>0; --attempts) - { - if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, size, &written)) - break; - } - - CloseHandle(hid_overlap_write.hEvent); - - return written; -} - -int CheckDeviceType_Read(HANDLE &dev_handle, u8* buf, int attempts) -{ - OVERLAPPED hid_overlap_read = OVERLAPPED(); - hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr); - int read = 0; - for (; attempts>0; --attempts) - { - read = _IORead(dev_handle, hid_overlap_read, buf, 1); - if (read > 0) - break; - } - - CloseHandle(hid_overlap_read.hEvent); - - return read; -} - -// A convoluted way of checking if a device is a Wii Balance Board and if it is a connectible Wiimote. -// Because nothing on Windows should be easy. -// (We can't seem to easily identify the bluetooth device an HID device belongs to...) -void WiimoteScanner::CheckDeviceType(std::basic_string &devicepath, bool &real_wiimote, bool &is_bb) -{ - real_wiimote = false; - is_bb = false; - -#ifdef SHARE_WRITE_WIIMOTES - std::lock_guard lk(g_connected_wiimotes_lock); - if (g_connected_wiimotes.count(devicepath) != 0) - return; -#endif - - HANDLE dev_handle = CreateFile(devicepath.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, - nullptr); - if (dev_handle == INVALID_HANDLE_VALUE) - return; - // enable to only check for official nintendo wiimotes/bb's - bool check_vidpid = false; - HIDD_ATTRIBUTES attrib; - attrib.Size = sizeof(attrib); - if (!check_vidpid || - (pHidD_GetAttributes(dev_handle, &attrib) && - (attrib.VendorID == 0x057e) && - (attrib.ProductID == 0x0306))) - { - // max_cycles insures we are never stuck here due to bad coding... - int max_cycles = 20; - u8 buf[MAX_PAYLOAD] = {0}; - - u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0}; - // The new way to initialize the extension is by writing 0x55 to 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB - // 52 16 04 A4 00 F0 01 55 - // 52 16 04 A4 00 FB 01 00 - u8 const disable_enc_pt1_report[MAX_PAYLOAD] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_WRITE_DATA, 0x04, 0xa4, 0x00, 0xf0, 0x01, 0x55}; - u8 const disable_enc_pt2_report[MAX_PAYLOAD] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_WRITE_DATA, 0x04, 0xa4, 0x00, 0xfb, 0x01, 0x00}; - - CheckDeviceType_Write(dev_handle, - disable_enc_pt1_report, - sizeof(disable_enc_pt1_report), - 1); - CheckDeviceType_Write(dev_handle, - disable_enc_pt2_report, - sizeof(disable_enc_pt2_report), - 1); - - int rc = CheckDeviceType_Write(dev_handle, - req_status_report, - sizeof(req_status_report), - 1); - - while (rc > 0 && --max_cycles > 0) - { - if ((rc = CheckDeviceType_Read(dev_handle, buf, 1)) <= 0) - { - // DEBUG_LOG(WIIMOTE, "CheckDeviceType: Read failed..."); - break; - } - - switch (buf[1]) - { - case WM_STATUS_REPORT: - { - real_wiimote = true; - - // DEBUG_LOG(WIIMOTE, "CheckDeviceType: Got Status Report"); - wm_status_report * wsr = (wm_status_report*)&buf[2]; - if (wsr->extension) - { - // Wiimote with extension, we ask it what kind. - u8 read_ext[MAX_PAYLOAD] = {0}; - read_ext[0] = WM_SET_REPORT | WM_BT_OUTPUT; - read_ext[1] = WM_READ_DATA; - // Extension type register. - *(u32*)&read_ext[2] = Common::swap32(0x4a400fa); - // Size. - *(u16*)&read_ext[6] = Common::swap16(6); - rc = CheckDeviceType_Write(dev_handle, read_ext, 8, 1); - } - else - { - // Normal Wiimote, exit while and be happy. - rc = -1; - } - break; - } - case WM_ACK_DATA: - { - real_wiimote = true; - //wm_acknowledge * wm = (wm_acknowledge*)&buf[2]; - //DEBUG_LOG(WIIMOTE, "CheckDeviceType: Got Ack Error: %X ReportID: %X", wm->errorID, wm->reportID); - break; - } - case WM_READ_DATA_REPLY: - { - // DEBUG_LOG(WIIMOTE, "CheckDeviceType: Got Data Reply"); - wm_read_data_reply * wrdr - = (wm_read_data_reply*)&buf[2]; - // Check if it has returned what we asked. - if (Common::swap16(wrdr->address) == 0x00fa) - { - real_wiimote = true; - // 0x020420A40000ULL means balance board. - u64 ext_type = (*(u64*)&wrdr->data[0]); - // DEBUG_LOG(WIIMOTE, - // "CheckDeviceType: GOT EXT TYPE %llX", - // ext_type); - is_bb = (ext_type == 0x020420A40000ULL); - } - else - { - ERROR_LOG(WIIMOTE, - "CheckDeviceType: GOT UNREQUESTED ADDRESS %X", - Common::swap16(wrdr->address)); - } - // force end - rc = -1; - - break; - } - default: - { - // We let read try again incase there is another packet waiting. - // DEBUG_LOG(WIIMOTE, "CheckDeviceType: GOT UNKNOWN REPLY: %X", buf[1]); - break; - } - } - } - } - CloseHandle(dev_handle); -} - -bool WiimoteScanner::IsReady() const -{ - if (!s_loaded_ok) - { - return false; - } - - // TODO: don't search for a radio each time - - BLUETOOTH_FIND_RADIO_PARAMS radioParam; - radioParam.dwSize = sizeof(radioParam); - - HANDLE hRadio; - HBLUETOOTH_RADIO_FIND hFindRadio = pBluetoothFindFirstRadio(&radioParam, &hRadio); - - if (nullptr != hFindRadio) - { - pBluetoothFindRadioClose(hFindRadio); - return true; - } - else - { - return false; - } -} - -// Connect to a wiimote with a known device path. -bool WiimoteWindows::ConnectInternal() -{ - if (IsConnected()) - return false; - -#ifdef SHARE_WRITE_WIIMOTES - std::lock_guard lk(g_connected_wiimotes_lock); - if (g_connected_wiimotes.count(m_devicepath) != 0) - return false; - - auto const open_flags = FILE_SHARE_READ | FILE_SHARE_WRITE; -#else - // Having no FILE_SHARE_WRITE disallows us from connecting to the same wiimote twice. - // (And disallows using wiimotes in use by other programs) - // This is what "WiiYourself" does. - // Apparently this doesn't work for everyone. It might be their fault. - auto const open_flags = FILE_SHARE_READ; -#endif - - m_dev_handle = CreateFile(m_devicepath.c_str(), - GENERIC_READ | GENERIC_WRITE, open_flags, - nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); - - if (m_dev_handle == INVALID_HANDLE_VALUE) - { - m_dev_handle = 0; - return false; - } - -#if 0 - TCHAR name[128] = {}; - pHidD_GetProductString(dev_handle, name, 128); - - //ERROR_LOG(WIIMOTE, "Product string: %s", TStrToUTF8(name).c_str()); - - if (!IsValidBluetoothName(TStrToUTF8(name))) - { - CloseHandle(dev_handle); - dev_handle = 0; - return false; - } -#endif - -#if 0 - HIDD_ATTRIBUTES attr; - attr.Size = sizeof(attr); - if (!pHidD_GetAttributes(dev_handle, &attr)) - { - CloseHandle(dev_handle); - dev_handle = 0; - return false; - } -#endif - - // TODO: thread isn't started here now, do this elsewhere - // This isn't as drastic as it sounds, since the process in which the threads - // reside is normal priority. Needed for keeping audio reports at a decent rate -/* - if (!SetThreadPriority(m_wiimote_thread.native_handle(), THREAD_PRIORITY_TIME_CRITICAL)) - { - ERROR_LOG(WIIMOTE, "Failed to set Wiimote thread priority"); - } -*/ -#ifdef SHARE_WRITE_WIIMOTES - g_connected_wiimotes.insert(m_devicepath); -#endif - - return true; -} - -void WiimoteWindows::DisconnectInternal() -{ - if (!IsConnected()) - return; - - CloseHandle(m_dev_handle); - m_dev_handle = 0; - -#ifdef SHARE_WRITE_WIIMOTES - std::lock_guard lk(g_connected_wiimotes_lock); - g_connected_wiimotes.erase(m_devicepath); -#endif -} - -WiimoteWindows::WiimoteWindows(const std::basic_string& path) : m_devicepath(path) -{ - m_dev_handle = 0; - m_stack = MSBT_STACK_UNKNOWN; - - m_hid_overlap_read = OVERLAPPED(); - m_hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr); - - m_hid_overlap_write = OVERLAPPED(); - m_hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr); -} - -WiimoteWindows::~WiimoteWindows() -{ - Shutdown(); - CloseHandle(m_hid_overlap_read.hEvent); - CloseHandle(m_hid_overlap_write.hEvent); -} - -bool WiimoteWindows::IsConnected() const -{ - return m_dev_handle != 0; -} - -// positive = read packet -// negative = didn't read packet -// zero = error -int _IORead(HANDLE &dev_handle, OVERLAPPED &hid_overlap_read, u8* buf, int index) -{ - // Add data report indicator byte (here, 0xa1) - buf[0] = 0xa1; - // Used below for a warning - buf[1] = 0; - - DWORD bytes = 0; - ResetEvent(hid_overlap_read.hEvent); - if (!ReadFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read)) - { - auto const read_err = GetLastError(); - - if (ERROR_IO_PENDING == read_err) - { - auto const wait_result = WaitForSingleObject(hid_overlap_read.hEvent, INFINITE); - - // In case the event was signalled by IOWakeup before the read completed, cancel it. - CancelIo(dev_handle); - - if (WAIT_FAILED == wait_result) - { - WARN_LOG(WIIMOTE, "A wait error occurred on reading from Wiimote %i.", index + 1); - } - - if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, FALSE)) - { - auto const overlapped_err = GetLastError(); - - if (ERROR_OPERATION_ABORTED == overlapped_err) - { - // It was. - return -1; - } - - WARN_LOG(WIIMOTE, "GetOverlappedResult error %d on Wiimote %i.", overlapped_err, index + 1); - return 0; - } - } - else - { - WARN_LOG(WIIMOTE, "ReadFile error %d on Wiimote %i.", read_err, index + 1); - return 0; - } - } - - return bytes + 1; -} - -void WiimoteWindows::IOWakeup() -{ - SetEvent(m_hid_overlap_read.hEvent); -} - - -// positive = read packet -// negative = didn't read packet -// zero = error -int WiimoteWindows::IORead(u8* buf) -{ - return _IORead(m_dev_handle, m_hid_overlap_read, buf, m_index); -} - - -int _IOWrite(HANDLE &dev_handle, OVERLAPPED &hid_overlap_write, enum win_bt_stack_t &stack, const u8* buf, size_t len, DWORD* written) -{ - switch (stack) - { - case MSBT_STACK_UNKNOWN: - { - // Try to auto-detect the stack type - stack = MSBT_STACK_BLUESOLEIL; - if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, len, written)) - return 1; - - stack = MSBT_STACK_MS; - if (_IOWrite(dev_handle, hid_overlap_write, stack, buf, len, written)) - return 1; - - stack = MSBT_STACK_UNKNOWN; - break; - } - case MSBT_STACK_MS: - { - auto result = pHidD_SetOutputReport(dev_handle, const_cast(buf) + 1, (ULONG)(len - 1)); - //FlushFileBuffers(dev_handle); - - if (!result) - { - auto err = GetLastError(); - if (err == 121) - { - // Semaphore timeout - NOTICE_LOG(WIIMOTE, "WiimoteIOWrite[MSBT_STACK_MS]: Unable to send data to the Wiimote"); - } - else if (err != 0x1F) // Some third-party adapters (DolphinBar) use this - // error code to signal the absence of a WiiMote - // linked to the HID device. - { - WARN_LOG(WIIMOTE, "IOWrite[MSBT_STACK_MS]: ERROR: %08x", err); - } - } - - if (written) - *written = (result ? (DWORD)len : 0); - - return result; - } - case MSBT_STACK_BLUESOLEIL: - { - u8 big_buf[MAX_PAYLOAD]; - if (len < MAX_PAYLOAD) - { - std::copy(buf, buf + len, big_buf); - std::fill(big_buf + len, big_buf + MAX_PAYLOAD, 0); - buf = big_buf; - } - - ResetEvent(hid_overlap_write.hEvent); - DWORD bytes = 0; - if (WriteFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_write)) - { - // If the number of written bytes is requested, block until we can provide - // this information to the called. - if (written) - { - auto const wait_result = WaitForSingleObject(hid_overlap_write.hEvent, WIIMOTE_DEFAULT_TIMEOUT); - if (WAIT_TIMEOUT == wait_result) - { - WARN_LOG(WIIMOTE, "_IOWrite: A timeout occurred on writing to Wiimote."); - CancelIo(dev_handle); - *written = 0; - } - else if (WAIT_FAILED == wait_result) - { - WARN_LOG(WIIMOTE, "_IOWrite: A wait error occurred on writing to Wiimote."); - CancelIo(dev_handle); - *written = 0; - } - else if (!GetOverlappedResult(dev_handle, &hid_overlap_write, written, TRUE)) - *written = 0; - } - return 1; - } - else - { - auto const err = GetLastError(); - if (ERROR_IO_PENDING == err) - { - CancelIo(dev_handle); - } - return 0; - } - } - } - - return 0; -} - -int WiimoteWindows::IOWrite(const u8* buf, size_t len) -{ - return _IOWrite(m_dev_handle, m_hid_overlap_write, m_stack, buf, len, nullptr); -} - -// invokes callback for each found wiimote bluetooth device -template -void ProcessWiimotes(bool new_scan, T& callback) -{ - BLUETOOTH_DEVICE_SEARCH_PARAMS srch; - srch.dwSize = sizeof(srch); - srch.fReturnAuthenticated = true; - srch.fReturnRemembered = true; - // Does not filter properly somehow, so we need to do an additional check on - // fConnected BT Devices - srch.fReturnConnected = true; - srch.fReturnUnknown = true; - srch.fIssueInquiry = new_scan; - // multiple of 1.28 seconds - srch.cTimeoutMultiplier = 2; - - BLUETOOTH_FIND_RADIO_PARAMS radioParam; - radioParam.dwSize = sizeof(radioParam); - - HANDLE hRadio; - - // TODO: save radio(s) in the WiimoteScanner constructor? - - // Enumerate BT radios - HBLUETOOTH_RADIO_FIND hFindRadio = pBluetoothFindFirstRadio(&radioParam, &hRadio); - while (hFindRadio) - { - BLUETOOTH_RADIO_INFO radioInfo; - radioInfo.dwSize = sizeof(radioInfo); - - auto const rinfo_result = pBluetoothGetRadioInfo(hRadio, &radioInfo); - if (ERROR_SUCCESS == rinfo_result) - { - srch.hRadio = hRadio; - - BLUETOOTH_DEVICE_INFO btdi; - btdi.dwSize = sizeof(btdi); - - // Enumerate BT devices - HBLUETOOTH_DEVICE_FIND hFindDevice = pBluetoothFindFirstDevice(&srch, &btdi); - while (hFindDevice) - { - // btdi.szName is sometimes missing it's content - it's a bt feature.. - DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", - btdi.fAuthenticated, btdi.fConnected, btdi.fRemembered); - - if (IsValidBluetoothName(UTF16ToUTF8(btdi.szName))) - { - callback(hRadio, radioInfo, btdi); - } - - if (false == pBluetoothFindNextDevice(hFindDevice, &btdi)) - { - pBluetoothFindDeviceClose(hFindDevice); - hFindDevice = nullptr; - } - } - } - - if (false == pBluetoothFindNextRadio(hFindRadio, &hRadio)) - { - pBluetoothFindRadioClose(hFindRadio); - hFindRadio = nullptr; - } - } -} - -void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi) -{ - //if (btdi.fConnected) - { - if (SUCCEEDED(pBluetoothRemoveDevice(&btdi.Address))) - { - NOTICE_LOG(WIIMOTE, "Removed BT Device", GetLastError()); - } - } -} - -bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO& radio_info, BLUETOOTH_DEVICE_INFO_STRUCT& btdi) -{ - // We don't want "remembered" devices. - // SetServiceState will just fail with them.. - if (!btdi.fConnected && !btdi.fRemembered) - { - auto const& wm_addr = btdi.Address.rgBytes; - - NOTICE_LOG(WIIMOTE, "Found Wiimote (%02x:%02x:%02x:%02x:%02x:%02x). Enabling HID service.", - wm_addr[0], wm_addr[1], wm_addr[2], wm_addr[3], wm_addr[4], wm_addr[5]); - -#if defined(AUTHENTICATE_WIIMOTES) - // Authenticate - auto const& radio_addr = radio_info.address.rgBytes; - // FIXME Not sure this usage of OOB_DATA_INFO is correct... - BLUETOOTH_OOB_DATA_INFO oob_data_info = { 0 }; - memcpy(&oob_data_info.C[0], &radio_addr[0], sizeof(WCHAR) * 6); - const DWORD auth_result = pBluetoothAuthenticateDeviceEx(nullptr, hRadio, &btdi, - &oob_data_info, MITMProtectionNotDefined); - - if (ERROR_SUCCESS != auth_result) - { - ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothAuthenticateDeviceEx returned %08x", auth_result); - } - - DWORD pcServices = 16; - GUID guids[16]; - // If this is not done, the Wii device will not remember the pairing - const DWORD srv_result = pBluetoothEnumerateInstalledServices(hRadio, &btdi, &pcServices, guids); - - if (ERROR_SUCCESS != srv_result) - { - ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothEnumerateInstalledServices returned %08x", srv_result); - } -#endif - // Activate service - const DWORD hr = pBluetoothSetServiceState(hRadio, &btdi, - &HumanInterfaceDeviceServiceClass_UUID, BLUETOOTH_SERVICE_ENABLE); - - g_connect_times[btdi.Address.ullLong] = std::time(nullptr); - - if (FAILED(hr)) - { - ERROR_LOG(WIIMOTE, "AttachWiimote: BluetoothSetServiceState returned %08x", hr); - } - else - { - return true; - } - } - - return false; -} - -// Removes remembered non-connected devices -bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT& btdi) -{ - if (!btdi.fConnected && btdi.fRemembered) - { - // Time to avoid RemoveDevice after SetServiceState. - // Sometimes SetServiceState takes a while.. - auto const avoid_forget_seconds = 5.0; - - auto pair_time = g_connect_times.find(btdi.Address.ullLong); - if (pair_time == g_connect_times.end() || - std::difftime(time(nullptr), pair_time->second) >= avoid_forget_seconds) - { - // Make Windows forget about device so it will re-find it if visible. - // This is also required to detect a disconnect for some reason.. - NOTICE_LOG(WIIMOTE, "Removing remembered Wiimote."); - pBluetoothRemoveDevice(&btdi.Address); - return true; - } - } - - return false; -} - -}; diff --git a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm b/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm deleted file mode 100644 index 9e00d946c8..0000000000 --- a/Source/Core/Core/HW/WiimoteReal/IOdarwin.mm +++ /dev/null @@ -1,380 +0,0 @@ -#define BLUETOOTH_VERSION_USE_CURRENT - -#include "Common/Common.h" -#include "Core/HW/WiimoteReal/WiimoteReal.h" - -@interface SearchBT: NSObject { -@public - unsigned int maxDevices; - bool done; -} -@end - -@interface ConnectBT: NSObject {} -@end - -namespace WiimoteReal -{ - -class WiimoteDarwin final : public Wiimote -{ -public: - WiimoteDarwin(IOBluetoothDevice* device); - ~WiimoteDarwin() override; - - // These are not protected/private because ConnectBT needs them. - void DisconnectInternal() override; - IOBluetoothDevice* m_btd; - unsigned char* m_input; - int m_inputlen; - -protected: - bool ConnectInternal() override; - bool IsConnected() const override; - void IOWakeup() override; - int IORead(u8* buf) override; - int IOWrite(u8 const* buf, size_t len) override; - void EnablePowerAssertionInternal() override; - void DisablePowerAssertionInternal() override; - -private: - IOBluetoothL2CAPChannel* m_ichan; - IOBluetoothL2CAPChannel* m_cchan; - bool m_connected; - CFRunLoopRef m_wiimote_thread_run_loop; - IOPMAssertionID m_pm_assertion; -}; - -WiimoteScanner::WiimoteScanner() - : m_run_thread() - , m_want_wiimotes() -{} - -WiimoteScanner::~WiimoteScanner() -{} - -void WiimoteScanner::Update() -{} - -void WiimoteScanner::FindWiimotes(std::vector & found_wiimotes, Wiimote* & found_board) -{ - // TODO: find the device in the constructor and save it for later - IOBluetoothHostController *bth; - IOBluetoothDeviceInquiry *bti; - SearchBT *sbt; - NSEnumerator *en; - found_board = nullptr; - - bth = [[IOBluetoothHostController alloc] init]; - if ([bth addressAsString] == nil) - { - WARN_LOG(WIIMOTE, "No bluetooth host controller"); - [bth release]; - return; - } - - sbt = [[SearchBT alloc] init]; - sbt->maxDevices = 32; - bti = [[IOBluetoothDeviceInquiry alloc] init]; - [bti setDelegate: sbt]; - [bti setInquiryLength: 2]; - - if ([bti start] != kIOReturnSuccess) - { - ERROR_LOG(WIIMOTE, "Unable to do bluetooth discovery"); - [bth release]; - [sbt release]; - return; - } - - do - { - CFRunLoopRun(); - } - while (!sbt->done); - - int found_devices = [[bti foundDevices] count]; - - if (found_devices) - NOTICE_LOG(WIIMOTE, "Found %i bluetooth devices", found_devices); - - en = [[bti foundDevices] objectEnumerator]; - for (int i = 0; i < found_devices; i++) - { - IOBluetoothDevice *dev = [en nextObject]; - if (!IsValidBluetoothName([[dev name] UTF8String])) - continue; - - Wiimote* wm = new WiimoteDarwin([dev retain]); - - if (IsBalanceBoardName([[dev name] UTF8String])) - { - found_board = wm; - } - else - { - found_wiimotes.push_back(wm); - } - } - - [bth release]; - [bti release]; - [sbt release]; -} - -bool WiimoteScanner::IsReady() const -{ - // TODO: only return true when a BT device is present - return true; -} - -WiimoteDarwin::WiimoteDarwin(IOBluetoothDevice* device) : m_btd(device) -{ - m_inputlen = 0; - m_connected = false; - m_wiimote_thread_run_loop = nullptr; - m_pm_assertion = kIOPMNullAssertionID; -} - -WiimoteDarwin::~WiimoteDarwin() -{ - Shutdown(); - if (m_wiimote_thread_run_loop) - { - CFRelease(m_wiimote_thread_run_loop); - m_wiimote_thread_run_loop = nullptr; - } - [m_btd release]; - m_btd = nil; - DisablePowerAssertionInternal(); -} - -// Connect to a wiimote with a known address. -bool WiimoteDarwin::ConnectInternal() -{ - if (IsConnected()) - return false; - - ConnectBT *cbt = [[ConnectBT alloc] init]; - - m_cchan = m_ichan = nil; - - IOReturn ret = [m_btd openConnection]; - if (ret) - { - ERROR_LOG(WIIMOTE, "Unable to open Bluetooth connection to wiimote %i: %x", - m_index + 1, ret); - [cbt release]; - return false; - } - - ret = [m_btd openL2CAPChannelSync: &m_cchan - withPSM: kBluetoothL2CAPPSMHIDControl delegate: cbt]; - if (ret) - { - ERROR_LOG(WIIMOTE, "Unable to open control channel for wiimote %i: %x", - m_index + 1, ret); - goto bad; - } - // Apple docs claim: - // "The L2CAP channel object is already retained when this function returns - // success; the channel must be released when the caller is done with it." - // But without this, the channels get over-autoreleased, even though the - // refcounting behavior here is clearly correct. - [m_cchan retain]; - - ret = [m_btd openL2CAPChannelSync: &m_ichan - withPSM: kBluetoothL2CAPPSMHIDInterrupt delegate: cbt]; - if (ret) - { - WARN_LOG(WIIMOTE, "Unable to open interrupt channel for wiimote %i: %x", - m_index + 1, ret); - goto bad; - } - [m_ichan retain]; - - NOTICE_LOG(WIIMOTE, "Connected to wiimote %i at %s", - m_index + 1, [[m_btd addressString] UTF8String]); - - m_connected = true; - - [cbt release]; - - m_wiimote_thread_run_loop = (CFRunLoopRef) CFRetain(CFRunLoopGetCurrent()); - - return true; - -bad: - DisconnectInternal(); - [cbt release]; - return false; -} - -// Disconnect a wiimote. -void WiimoteDarwin::DisconnectInternal() -{ - [m_ichan closeChannel]; - [m_ichan release]; - m_ichan = nil; - - [m_cchan closeChannel]; - [m_cchan release]; - m_cchan = nil; - - [m_btd closeConnection]; - - if (!IsConnected()) - return; - - NOTICE_LOG(WIIMOTE, "Disconnecting wiimote %i", m_index + 1); - - m_connected = false; -} - -bool WiimoteDarwin::IsConnected() const -{ - return m_connected; -} - -void WiimoteDarwin::IOWakeup() -{ - if (m_wiimote_thread_run_loop) - { - CFRunLoopStop(m_wiimote_thread_run_loop); - } -} - -int WiimoteDarwin::IORead(unsigned char *buf) -{ - m_input = buf; - m_inputlen = -1; - - CFRunLoopRun(); - - return m_inputlen; -} - -int WiimoteDarwin::IOWrite(const unsigned char *buf, size_t len) -{ - IOReturn ret; - - if (!IsConnected()) - return 0; - - ret = [m_ichan writeAsync: const_cast((void *)buf) length: (int)len refcon: nil]; - - if (ret == kIOReturnSuccess) - return len; - else - return 0; -} - -void WiimoteDarwin::EnablePowerAssertionInternal() -{ - if (m_pm_assertion == kIOPMNullAssertionID) - { - if (IOReturn ret = IOPMAssertionCreateWithName(kIOPMAssertPreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Dolphin Wiimote activity"), &m_pm_assertion)) - ERROR_LOG(WIIMOTE, "Could not create power management assertion: %08x", ret); - } -} - -void WiimoteDarwin::DisablePowerAssertionInternal() -{ - if (m_pm_assertion != kIOPMNullAssertionID) - { - if (IOReturn ret = IOPMAssertionRelease(m_pm_assertion)) - ERROR_LOG(WIIMOTE, "Could not release power management assertion: %08x", ret); - } -} - -} // namespace - -@implementation SearchBT -- (void) deviceInquiryComplete: (IOBluetoothDeviceInquiry *) sender - error: (IOReturn) error - aborted: (BOOL) aborted -{ - done = true; - CFRunLoopStop(CFRunLoopGetCurrent()); -} - -- (void) deviceInquiryDeviceFound: (IOBluetoothDeviceInquiry *) sender - device: (IOBluetoothDevice *) device -{ - NOTICE_LOG(WIIMOTE, "Discovered bluetooth device at %s: %s", - [[device addressString] UTF8String], - [[device name] UTF8String]); - - if ([[sender foundDevices] count] == maxDevices) - [sender stop]; -} -@end - -@implementation ConnectBT -- (void) l2capChannelData: (IOBluetoothL2CAPChannel *) l2capChannel - data: (unsigned char *) data - length: (NSUInteger) length -{ - IOBluetoothDevice *device = [l2capChannel device]; - WiimoteReal::WiimoteDarwin *wm = nullptr; - - std::lock_guard lk(WiimoteReal::g_refresh_lock); - - for (int i = 0; i < MAX_WIIMOTES; i++) - { - if (WiimoteReal::g_wiimotes[i] == nullptr) - continue; - wm = static_cast(WiimoteReal::g_wiimotes[i]); - if ([device isEqual: wm->m_btd] != TRUE) - wm = nullptr; - } - - if (wm == nullptr) { - ERROR_LOG(WIIMOTE, "Received packet for unknown wiimote"); - return; - } - - if (length > MAX_PAYLOAD) { - WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, too large", - wm->m_index + 1); - return; - } - - if (wm->m_inputlen != -1) { - WARN_LOG(WIIMOTE, "Dropping packet for wiimote %i, queue full", - wm->m_index + 1); - return; - } - - memcpy(wm->m_input, data, length); - wm->m_inputlen = length; - - CFRunLoopStop(CFRunLoopGetCurrent()); -} - -- (void) l2capChannelClosed: (IOBluetoothL2CAPChannel *) l2capChannel -{ - IOBluetoothDevice *device = [l2capChannel device]; - WiimoteReal::WiimoteDarwin *wm = nullptr; - - std::lock_guard lk(WiimoteReal::g_refresh_lock); - - for (int i = 0; i < MAX_WIIMOTES; i++) - { - if (WiimoteReal::g_wiimotes[i] == nullptr) - continue; - wm = static_cast(WiimoteReal::g_wiimotes[i]); - if ([device isEqual: wm->m_btd] != TRUE) - wm = nullptr; - } - - if (wm == nullptr) { - ERROR_LOG(WIIMOTE, "Channel for unknown wiimote was closed"); - return; - } - - WARN_LOG(WIIMOTE, "Lost channel to wiimote %i", wm->m_index + 1); - - wm->DisconnectInternal(); -} -@end diff --git a/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp new file mode 100644 index 0000000000..5e695d5f42 --- /dev/null +++ b/Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp @@ -0,0 +1,150 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "Common/Common.h" +#include "Core/HW/WiimoteReal/WiimoteReal.h" + +namespace WiimoteReal +{ + +class WiimoteHidapi final : public Wiimote +{ +public: + WiimoteHidapi(char* device_path); + ~WiimoteHidapi() override; + +protected: + bool ConnectInternal() override; + void DisconnectInternal() override; + bool IsConnected() const override; + void IOWakeup() override; + int IORead(u8* buf) override; + int IOWrite(u8 const* buf, size_t len) override; + +private: + std::string m_device_path; + hid_device* m_handle; +}; + +WiimoteScanner::WiimoteScanner() + : m_want_wiimotes() +{ + if (hid_init() == -1) + { + ERROR_LOG(WIIMOTE, "Failed to initialize hidapi."); + } +} + +bool WiimoteScanner::IsReady() const +{ + return true; +} + +WiimoteScanner::~WiimoteScanner() +{ + if (hid_exit() == -1) + { + ERROR_LOG(WIIMOTE, "Failed to clean up hidapi."); + } +} + +void WiimoteScanner::Update() +{} + +void WiimoteScanner::FindWiimotes(std::vector& found_wiimotes, Wiimote*& found_board) +{ + // Search for both old and new Wiimotes. + for (uint16_t product_id : {0x0306, 0x0330}) + { + hid_device_info* list = hid_enumerate(0x057e, product_id); + hid_device_info* item = list; + while (item) + { + NOTICE_LOG(WIIMOTE, "Found Wiimote at %s: %ls %ls", item->path, item->manufacturer_string, item->product_string); + Wiimote* wiimote = new WiimoteHidapi(item->path); + found_wiimotes.push_back(wiimote); + item = item->next; + } + hid_free_enumeration(list); + } +} + +WiimoteHidapi::WiimoteHidapi(char* device_path) : m_device_path(device_path), m_handle(nullptr) +{} + +WiimoteHidapi::~WiimoteHidapi() +{ + Shutdown(); +} + +// Connect to a wiimote with a known address. +bool WiimoteHidapi::ConnectInternal() +{ + m_handle = hid_open_path(m_device_path.c_str()); + if (!m_handle) + { + ERROR_LOG(WIIMOTE, "Could not connect to Wiimote at \"%s\". " + "Do you have permission to access the device?", m_device_path.c_str()); + } + return !!m_handle; +} + +void WiimoteHidapi::DisconnectInternal() +{ + hid_close(m_handle); + m_handle = nullptr; +} + +bool WiimoteHidapi::IsConnected() const +{ + return !!m_handle; +} + +void WiimoteHidapi::IOWakeup() +{} + +// positive = read packet +// negative = didn't read packet +// zero = error +int WiimoteHidapi::IORead(u8* buf) +{ + int timeout = 200; // ms + int result = hid_read_timeout(m_handle, buf + 1, MAX_PAYLOAD - 1, timeout); + // TODO: If and once we use hidapi across plaforms, change our internal API to clean up this mess. + if (result == -1) + { + ERROR_LOG(WIIMOTE, "Failed to read from %s.", m_device_path.c_str()); + result = 0; + } + else if (result == 0) + { + result = -1; + } + else + { + buf[0] = WM_SET_REPORT | WM_BT_INPUT; + result += 1; + } + return result; +} + +int WiimoteHidapi::IOWrite(u8 const* buf, size_t len) +{ + _dbg_assert_(WIIMOTE, buf[0] == (WM_SET_REPORT | WM_BT_OUTPUT)); + int result = hid_write(m_handle, buf + 1, len - 1); + if (result == -1) + { + ERROR_LOG(WIIMOTE, "Failed to write to %s.", m_device_path.c_str()); + result = 0; + } + else + { + result += 1; + } + return result; +} + +}; // WiimoteReal diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h index b7edb53d72..84fad33adb 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h @@ -135,13 +135,6 @@ private: volatile bool m_run_thread; volatile bool m_want_wiimotes; volatile bool m_want_bb; - -#if defined(_WIN32) - void CheckDeviceType(std::basic_string &devicepath, bool &real_wiimote, bool &is_bb); -#elif defined(__linux__) && HAVE_BLUEZ - int device_id; - int device_sock; -#endif }; extern std::recursive_mutex g_refresh_lock; diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteRealBase.h b/Source/Core/Core/HW/WiimoteReal/WiimoteRealBase.h index 847720ba98..ee056e2d87 100644 --- a/Source/Core/Core/HW/WiimoteReal/WiimoteRealBase.h +++ b/Source/Core/Core/HW/WiimoteReal/WiimoteRealBase.h @@ -4,22 +4,6 @@ #pragma once -#ifdef _WIN32 - #include -#elif defined(__APPLE__) - // Work around an Apple bug: for some reason, IOBluetooth.h errors on - // inclusion in Mavericks, but only in Objective-C++ C++11 mode. I filed - // this as ; in the meantime... - #import - #undef NS_ENUM_AVAILABLE - #define NS_ENUM_AVAILABLE(...) - // end hack - #import - #include -#elif defined(__linux__) && HAVE_BLUEZ - #include -#endif - // Wiimote internal codes // Communication channels