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.
This commit is contained in:
parent
1c26d41c9a
commit
ca0c2efe7a
|
@ -2,6 +2,7 @@
|
||||||
# General setup
|
# General setup
|
||||||
#
|
#
|
||||||
cmake_minimum_required(VERSION 2.8.8)
|
cmake_minimum_required(VERSION 2.8.8)
|
||||||
|
project(dolphin-emu)
|
||||||
|
|
||||||
option(USE_EGL "Enables EGL OpenGL Interface" OFF)
|
option(USE_EGL "Enables EGL OpenGL Interface" OFF)
|
||||||
option(TRY_X11 "Enables X11 Support" ON)
|
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)
|
option(ENABLE_GENERIC "Enables generic build that should run on any little-endian host" OFF)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
option(OSX_USE_DEFAULT_SEARCH_PATH "Don't prioritize system library paths" OFF)
|
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()
|
endif()
|
||||||
|
|
||||||
option(ENCODE_FRAMEDUMPS "Encode framedumps in AVI format" ON)
|
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(OPROFILING "Enable profiling" OFF)
|
||||||
option(GDBSTUB "Enable gdb stub for remote debugging." 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
|
# Optional Targets
|
||||||
# TODO: Add DSPSpy
|
# TODO: Add DSPSpy
|
||||||
|
@ -45,7 +46,6 @@ if (APPLE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
project(dolphin-emu)
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/CMakeTests)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/CMakeTests)
|
||||||
set(DOLPHIN_IS_STABLE FALSE)
|
set(DOLPHIN_IS_STABLE FALSE)
|
||||||
# Libraries to link
|
# Libraries to link
|
||||||
|
@ -292,7 +292,6 @@ if(APPLE)
|
||||||
find_library(COREFUND_LIBRARY CoreFoundation)
|
find_library(COREFUND_LIBRARY CoreFoundation)
|
||||||
find_library(CORESERV_LIBRARY CoreServices)
|
find_library(CORESERV_LIBRARY CoreServices)
|
||||||
find_library(FOUNDATION_LIBRARY foundation)
|
find_library(FOUNDATION_LIBRARY foundation)
|
||||||
find_library(IOB_LIBRARY IOBluetooth)
|
|
||||||
find_library(IOK_LIBRARY IOKit)
|
find_library(IOK_LIBRARY IOKit)
|
||||||
find_library(QUICKTIME_LIBRARY QuickTime)
|
find_library(QUICKTIME_LIBRARY QuickTime)
|
||||||
find_library(WEBKIT_LIBRARY WebKit)
|
find_library(WEBKIT_LIBRARY WebKit)
|
||||||
|
@ -379,15 +378,6 @@ if(NOT ANDROID)
|
||||||
message("ao NOT found, disabling ao sound backend")
|
message("ao NOT found, disabling ao sound backend")
|
||||||
endif(AO_FOUND)
|
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)
|
check_lib(PULSEAUDIO libpulse QUIET)
|
||||||
if(PULSEAUDIO_FOUND)
|
if(PULSEAUDIO_FOUND)
|
||||||
add_definitions(-DHAVE_PULSEAUDIO=1)
|
add_definitions(-DHAVE_PULSEAUDIO=1)
|
||||||
|
@ -595,10 +585,19 @@ endif()
|
||||||
|
|
||||||
include(FindLibUSB OPTIONAL)
|
include(FindLibUSB OPTIONAL)
|
||||||
if(LIBUSB_FOUND)
|
if(LIBUSB_FOUND)
|
||||||
message("Using shared LibUSB")
|
message("Using shared libusb")
|
||||||
add_definitions(-D__LIBUSB__)
|
add_definitions(-D__LIBUSB__)
|
||||||
include_directories(${LIBUSB_INCLUDE_DIR})
|
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)
|
set(SFML_REQD_VERSION 2.1)
|
||||||
if(NOT APPLE AND NOT ANDROID)
|
if(NOT APPLE AND NOT ANDROID)
|
||||||
|
@ -665,6 +664,32 @@ else()
|
||||||
mark_as_advanced(ICONV_INCLUDE_DIR ICONV_LIBRARIES)
|
mark_as_advanced(ICONV_INCLUDE_DIR ICONV_LIBRARIES)
|
||||||
endif()
|
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)
|
if(ENABLE_QT)
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
message("Found Qt version ${Qt5Core_VERSION}, enabling the Qt backend")
|
message("Found Qt version ${Qt5Core_VERSION}, enabling the Qt backend")
|
||||||
|
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
||||||
|
add_library(hidapi-libusb hid.c)
|
|
@ -0,0 +1 @@
|
||||||
|
add_library(hidapi-hidraw hid.c)
|
|
@ -0,0 +1 @@
|
||||||
|
add_library(hidapi hid.c)
|
|
@ -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)
|
|
@ -0,0 +1,7 @@
|
||||||
|
include_directories(..)
|
||||||
|
add_library(usb-os STATIC
|
||||||
|
linux_netlink.c
|
||||||
|
linux_usbfs.c
|
||||||
|
poll_posix.c
|
||||||
|
threads_posix.c
|
||||||
|
)
|
|
@ -27,7 +27,6 @@
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<PlatformToolset>v120</PlatformToolset>
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
@ -38,7 +37,6 @@
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<PlatformToolset>v120</PlatformToolset>
|
<PlatformToolset>v120</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
|
|
@ -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"
|
|
@ -116,6 +116,7 @@ set(SRCS ActionReplay.cpp
|
||||||
HW/SI_DeviceGBA.cpp
|
HW/SI_DeviceGBA.cpp
|
||||||
HW/SI_DeviceGCController.cpp
|
HW/SI_DeviceGCController.cpp
|
||||||
HW/SI_DeviceGCSteeringWheel.cpp
|
HW/SI_DeviceGCSteeringWheel.cpp
|
||||||
|
HW/SI_GCAdapter.cpp
|
||||||
HW/Sram.cpp
|
HW/Sram.cpp
|
||||||
HW/StreamADPCM.cpp
|
HW/StreamADPCM.cpp
|
||||||
HW/SystemTimers.cpp
|
HW/SystemTimers.cpp
|
||||||
|
@ -132,6 +133,7 @@ set(SRCS ActionReplay.cpp
|
||||||
HW/WiimoteEmu/EmuSubroutines.cpp
|
HW/WiimoteEmu/EmuSubroutines.cpp
|
||||||
HW/WiimoteEmu/Encryption.cpp
|
HW/WiimoteEmu/Encryption.cpp
|
||||||
HW/WiimoteEmu/Speaker.cpp
|
HW/WiimoteEmu/Speaker.cpp
|
||||||
|
HW/WiimoteReal/IOhidapi.cpp
|
||||||
HW/WiimoteReal/WiimoteReal.cpp
|
HW/WiimoteReal/WiimoteReal.cpp
|
||||||
HW/WiiSaveCrypted.cpp
|
HW/WiiSaveCrypted.cpp
|
||||||
IPC_HLE/ICMPLin.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_es.cpp
|
||||||
IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp
|
IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp
|
||||||
IPC_HLE/WII_IPC_HLE_Device_fs.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_Socket.cpp
|
||||||
IPC_HLE/WII_IPC_HLE_Device_net.cpp
|
IPC_HLE/WII_IPC_HLE_Device_net.cpp
|
||||||
IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
|
IPC_HLE/WII_IPC_HLE_Device_net_ssl.cpp
|
||||||
|
@ -245,29 +248,15 @@ set(LIBS
|
||||||
z
|
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})
|
set(LIBS ${LIBS} ${POLARSSL_LIBRARY})
|
||||||
|
|
||||||
if(WIN32)
|
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)
|
elseif(APPLE)
|
||||||
set(SRCS ${SRCS} HW/BBA-TAP/TAP_Apple.cpp HW/WiimoteReal/IOdarwin.mm)
|
set(SRCS ${SRCS} HW/BBA-TAP/TAP_Apple.cpp)
|
||||||
set(LIBS ${LIBS}
|
|
||||||
${IOB_LIBRARY})
|
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
set(SRCS ${SRCS} HW/BBA-TAP/TAP_Unix.cpp)
|
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()
|
endif()
|
||||||
|
|
||||||
if(PORTAUDIO_FOUND)
|
if(PORTAUDIO_FOUND)
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
<ClCompile Include="HW\WiimoteEmu\Encryption.cpp" />
|
<ClCompile Include="HW\WiimoteEmu\Encryption.cpp" />
|
||||||
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp" />
|
<ClCompile Include="HW\WiimoteEmu\Speaker.cpp" />
|
||||||
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp" />
|
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp" />
|
||||||
<ClCompile Include="HW\WiimoteReal\IOWin.cpp" />
|
<ClCompile Include="HW\WiimoteReal\IOhidapi.cpp" />
|
||||||
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp" />
|
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp" />
|
||||||
<ClCompile Include="HW\WII_IPC.cpp" />
|
<ClCompile Include="HW\WII_IPC.cpp" />
|
||||||
<ClCompile Include="HW\WiiSaveCrypted.cpp" />
|
<ClCompile Include="HW\WiiSaveCrypted.cpp" />
|
||||||
|
@ -470,6 +470,9 @@
|
||||||
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
|
<ProjectReference Include="$(CoreDir)VideoCommon\VideoCommon.vcxproj">
|
||||||
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
|
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="$(ExternalsDir)hidapi\hidapi.vcxproj">
|
||||||
|
<Project>{549d32d8-1640-46f9-9d78-bae6eb0d723d}</Project>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|
|
@ -484,7 +484,7 @@
|
||||||
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp">
|
<ClCompile Include="HW\WiimoteEmu\WiimoteEmu.cpp">
|
||||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Emu</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HW\WiimoteReal\IOWin.cpp">
|
<ClCompile Include="HW\WiimoteReal\IOhidapi.cpp">
|
||||||
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Real</Filter>
|
<Filter>HW %28Flipper/Hollywood%29\Wiimote\Real</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp">
|
<ClCompile Include="HW\WiimoteReal\WiimoteReal.cpp">
|
||||||
|
|
|
@ -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<Wiimote*> & found_wiimotes, Wiimote* & found_board)
|
|
||||||
{
|
|
||||||
found_wiimotes.clear();
|
|
||||||
found_board = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WiimoteScanner::IsReady() const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,279 +0,0 @@
|
||||||
// Copyright 2014 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <bluetooth/bluetooth.h>
|
|
||||||
#include <bluetooth/hci.h>
|
|
||||||
#include <bluetooth/hci_lib.h>
|
|
||||||
#include <bluetooth/l2cap.h>
|
|
||||||
|
|
||||||
#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<Wiimote*> & 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
|
|
|
@ -1,954 +0,0 @@
|
||||||
// Copyright 2013 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <ctime>
|
|
||||||
#include <hidsdi.h>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <windows.h>
|
|
||||||
// The following Windows headers must be included AFTER windows.h.
|
|
||||||
#include <BluetoothAPIs.h> //NOLINT
|
|
||||||
#include <dbt.h> //NOLINT
|
|
||||||
#include <setupapi.h> //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<BTH_ADDR, std::time_t> g_connect_times;
|
|
||||||
|
|
||||||
#ifdef SHARE_WRITE_WIIMOTES
|
|
||||||
std::unordered_set<std::basic_string<TCHAR>> 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<TCHAR>& 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<TCHAR> 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 <typename T>
|
|
||||||
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<Wiimote*> & 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<TCHAR> 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<TCHAR> &devicepath, bool &real_wiimote, bool &is_bb)
|
|
||||||
{
|
|
||||||
real_wiimote = false;
|
|
||||||
is_bb = false;
|
|
||||||
|
|
||||||
#ifdef SHARE_WRITE_WIIMOTES
|
|
||||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> lk(g_connected_wiimotes_lock);
|
|
||||||
g_connected_wiimotes.erase(m_devicepath);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
WiimoteWindows::WiimoteWindows(const std::basic_string<TCHAR>& 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<u8*>(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 <typename T>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
|
@ -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<Wiimote*> & 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*>((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<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
|
||||||
{
|
|
||||||
if (WiimoteReal::g_wiimotes[i] == nullptr)
|
|
||||||
continue;
|
|
||||||
wm = static_cast<WiimoteReal::WiimoteDarwin*>(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<std::recursive_mutex> lk(WiimoteReal::g_refresh_lock);
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_WIIMOTES; i++)
|
|
||||||
{
|
|
||||||
if (WiimoteReal::g_wiimotes[i] == nullptr)
|
|
||||||
continue;
|
|
||||||
wm = static_cast<WiimoteReal::WiimoteDarwin*>(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
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright 2014 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <hidapi.h>
|
||||||
|
|
||||||
|
#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<Wiimote*>& 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
|
|
@ -135,13 +135,6 @@ private:
|
||||||
volatile bool m_run_thread;
|
volatile bool m_run_thread;
|
||||||
volatile bool m_want_wiimotes;
|
volatile bool m_want_wiimotes;
|
||||||
volatile bool m_want_bb;
|
volatile bool m_want_bb;
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
void CheckDeviceType(std::basic_string<TCHAR> &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;
|
extern std::recursive_mutex g_refresh_lock;
|
||||||
|
|
|
@ -4,22 +4,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#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 <rdar://15312520>; in the meantime...
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#undef NS_ENUM_AVAILABLE
|
|
||||||
#define NS_ENUM_AVAILABLE(...)
|
|
||||||
// end hack
|
|
||||||
#import <IOBluetooth/IOBluetooth.h>
|
|
||||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
|
||||||
#elif defined(__linux__) && HAVE_BLUEZ
|
|
||||||
#include <bluetooth/bluetooth.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Wiimote internal codes
|
// Wiimote internal codes
|
||||||
|
|
||||||
// Communication channels
|
// Communication channels
|
||||||
|
|
Loading…
Reference in New Issue