Merge pull request #4054 from leoetlino/wiimote-scanner

WiimoteReal: Change the scanner to support several backends
This commit is contained in:
Markus Wick 2016-07-31 10:39:13 +02:00 committed by GitHub
commit ad1c5a8e2e
14 changed files with 505 additions and 399 deletions

View File

@ -256,12 +256,10 @@ elseif(APPLE)
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) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND BLUEZ_FOUND)
set(SRCS ${SRCS} HW/WiimoteReal/IONix.cpp) set(SRCS ${SRCS} HW/WiimoteReal/IOLinux.cpp)
set(LIBS ${LIBS} bluetooth) set(LIBS ${LIBS} bluetooth)
elseif(ANDROID) elseif(ANDROID)
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp) set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
else()
set(SRCS ${SRCS} HW/WiimoteReal/IODummy.cpp)
endif() endif()
endif() endif()

View File

@ -12,7 +12,7 @@
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/IOAndroid.h"
// Global java_vm class // Global java_vm class
extern JavaVM* g_java_vm; extern JavaVM* g_java_vm;
@ -22,45 +22,8 @@ namespace WiimoteReal
// Java classes // Java classes
static jclass s_adapter_class; static jclass s_adapter_class;
class WiimoteAndroid final : public Wiimote void WiimoteScannerAndroid::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
{ Wiimote*& found_board)
public:
WiimoteAndroid(int index);
~WiimoteAndroid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() {}
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
int m_mayflash_index;
bool is_connected = true;
JNIEnv* m_env;
jmethodID m_input_func;
jmethodID m_output_func;
jbyteArray m_java_wiimote_payload;
};
WiimoteScanner::WiimoteScanner()
{
}
WiimoteScanner::~WiimoteScanner()
{
}
void WiimoteScanner::Update()
{
}
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
{ {
found_wiimotes.clear(); found_wiimotes.clear();
found_board = nullptr; found_board = nullptr;
@ -87,11 +50,6 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote
g_java_vm->DetachCurrentThread(); g_java_vm->DetachCurrentThread();
} }
bool WiimoteScanner::IsReady() const
{
return true;
}
WiimoteAndroid::WiimoteAndroid(int index) : Wiimote(), m_mayflash_index(index) WiimoteAndroid::WiimoteAndroid(int index) : Wiimote(), m_mayflash_index(index)
{ {
} }

View File

@ -0,0 +1,57 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef ANDROID
#include <jni.h>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteAndroid final : public Wiimote
{
public:
WiimoteAndroid(int index);
~WiimoteAndroid() 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:
int m_mayflash_index;
bool is_connected = true;
JNIEnv* m_env;
jmethodID m_input_func;
jmethodID m_output_func;
jbyteArray m_java_wiimote_payload;
};
class WiimoteScannerAndroid final : public WiimoteScannerBackend
{
public:
WiimoteScannerAndroid() = default;
~WiimoteScannerAndroid() override = default;
bool IsReady() const override { return true; }
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override {}
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerAndroid = WiimoteScannerDummy;
}
#endif

View File

@ -1,32 +0,0 @@
// Copyright 2008 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;
}
};

View File

@ -0,0 +1,20 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteScannerDummy final : public WiimoteScannerBackend
{
public:
WiimoteScannerDummy() = default;
~WiimoteScannerDummy() override = default;
bool IsReady() const override { return false; }
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override {}
void Update() override {}
};
}

View File

@ -2,78 +2,57 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#ifndef _WIN32
#include <unistd.h>
#endif
#include <bluetooth/bluetooth.h> #include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h> #include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h> #include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h> #include <bluetooth/l2cap.h>
#include <unistd.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/IOLinux.h"
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteLinux final : public Wiimote // This is used to store the Bluetooth address of connected Wiimotes,
// so we can ignore Wiimotes that are already connected.
static std::vector<std::string> s_known_addrs;
static bool IsNewWiimote(const std::string& addr)
{ {
public: return std::find(s_known_addrs.begin(), s_known_addrs.end(), addr) == s_known_addrs.end();
WiimoteLinux(bdaddr_t bdaddr); }
~WiimoteLinux() override;
const bdaddr_t& Address() const;
protected: WiimoteScannerLinux::WiimoteScannerLinux() : m_device_id(-1), m_device_sock(-1)
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() : device_id(-1), device_sock(-1)
{ {
// Get the id of the first Bluetooth device. // Get the id of the first Bluetooth device.
device_id = hci_get_route(nullptr); m_device_id = hci_get_route(nullptr);
if (device_id < 0) if (m_device_id < 0)
{ {
NOTICE_LOG(WIIMOTE, "Bluetooth not found."); NOTICE_LOG(WIIMOTE, "Bluetooth not found.");
return; return;
} }
// Create a socket to the device // Create a socket to the device
device_sock = hci_open_dev(device_id); m_device_sock = hci_open_dev(m_device_id);
if (device_sock < 0) if (m_device_sock < 0)
{ {
ERROR_LOG(WIIMOTE, "Unable to open Bluetooth."); ERROR_LOG(WIIMOTE, "Unable to open Bluetooth.");
return; return;
} }
} }
bool WiimoteScanner::IsReady() const WiimoteScannerLinux::~WiimoteScannerLinux()
{
return device_sock > 0;
}
WiimoteScanner::~WiimoteScanner()
{ {
if (IsReady()) if (IsReady())
close(device_sock); close(m_device_sock);
} }
void WiimoteScanner::Update() bool WiimoteScannerLinux::IsReady() const
{ {
return m_device_sock > 0;
} }
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board) void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
{ {
// supposedly 1.28 seconds // supposedly 1.28 seconds
int const wait_len = 1; int const wait_len = 1;
@ -88,7 +67,7 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote
// Scan for Bluetooth devices // Scan for Bluetooth devices
int const found_devices = int const found_devices =
hci_inquiry(device_id, wait_len, max_infos, lap, &scan_infos_ptr, IREQ_CACHE_FLUSH); hci_inquiry(m_device_id, wait_len, max_infos, lap, &scan_infos_ptr, IREQ_CACHE_FLUSH);
if (found_devices < 0) if (found_devices < 0)
{ {
ERROR_LOG(WIIMOTE, "Error searching for Bluetooth devices."); ERROR_LOG(WIIMOTE, "Error searching for Bluetooth devices.");
@ -104,46 +83,34 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote
// BT names are a maximum of 248 bytes apparently // BT names are a maximum of 248 bytes apparently
char name[255] = {}; char name[255] = {};
if (hci_read_remote_name(device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 1000) < 0) if (hci_read_remote_name(m_device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 1000) < 0)
{ {
ERROR_LOG(WIIMOTE, "name request failed"); ERROR_LOG(WIIMOTE, "name request failed");
continue; continue;
} }
ERROR_LOG(WIIMOTE, "device name %s", name); ERROR_LOG(WIIMOTE, "device name %s", name);
if (IsValidBluetoothName(name)) if (!IsValidBluetoothName(name))
continue;
char bdaddr_str[18] = {};
ba2str(&scan_infos[i].bdaddr, bdaddr_str);
if (!IsNewWiimote(bdaddr_str))
continue;
// Found a new device
s_known_addrs.push_back(bdaddr_str);
Wiimote* wm = new WiimoteLinux(scan_infos[i].bdaddr);
if (IsBalanceBoardName(name))
{ {
bool new_wiimote = true; found_board = wm;
NOTICE_LOG(WIIMOTE, "Found balance board (%s).", bdaddr_str);
// Determine if this Wiimote has already been found. }
for (int j = 0; j < MAX_BBMOTES && new_wiimote; ++j) else
{ {
// compare this address with the stored addresses in our global array found_wiimotes.push_back(wm);
// static_cast is OK here, since we're only ever going to have this subclass in g_wiimotes NOTICE_LOG(WIIMOTE, "Found Wiimote (%s).", bdaddr_str);
// on Linux (and likewise, only WiimoteWindows on Windows, etc)
auto connected_wiimote = static_cast<WiimoteLinux*>(g_wiimotes[j]);
if (connected_wiimote && bacmp(&scan_infos[i].bdaddr, &connected_wiimote->Address()) == 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);
}
}
} }
} }
} }
@ -172,11 +139,6 @@ WiimoteLinux::~WiimoteLinux()
close(m_wakeup_pipe_r); close(m_wakeup_pipe_r);
} }
const bdaddr_t& WiimoteLinux::Address() const
{
return m_bdaddr;
}
// Connect to a Wiimote with a known address. // Connect to a Wiimote with a known address.
bool WiimoteLinux::ConnectInternal() bool WiimoteLinux::ConnectInternal()
{ {
@ -218,6 +180,10 @@ void WiimoteLinux::DisconnectInternal()
m_cmd_sock = -1; m_cmd_sock = -1;
m_int_sock = -1; m_int_sock = -1;
char bdaddr_str[18] = {};
ba2str(&m_bdaddr, bdaddr_str);
s_known_addrs.erase(std::remove(s_known_addrs.begin(), s_known_addrs.end(), bdaddr_str),
s_known_addrs.end());
} }
bool WiimoteLinux::IsConnected() const bool WiimoteLinux::IsConnected() const

View File

@ -0,0 +1,57 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#if defined(__linux__) && HAVE_BLUEZ
#include <bluetooth/bluetooth.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;
};
class WiimoteScannerLinux final : public WiimoteScannerBackend
{
public:
WiimoteScannerLinux();
~WiimoteScannerLinux() override;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed on Linux
private:
int m_device_id;
int m_device_sock;
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerLinux = WiimoteScannerDummy;
}
#endif

View File

@ -27,8 +27,7 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h" #include "Core/HW/WiimoteReal/IOWin.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
//#define AUTHENTICATE_WIIMOTES //#define AUTHENTICATE_WIIMOTES
#define SHARE_WRITE_WIIMOTES #define SHARE_WRITE_WIIMOTES
@ -192,28 +191,6 @@ inline void init_lib()
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteWindows final : public Wiimote
{
public:
WiimoteWindows(const std::basic_string<TCHAR>& path, WinWriteMethod initial_write_method);
~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;
WinWriteMethod m_write_method; // Type of Write Method to use
};
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack, int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack,
const u8* buf, size_t len, DWORD* written); const u8* buf, size_t len, DWORD* written);
int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index); int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index);
@ -225,12 +202,12 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_
void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&); bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
WiimoteScanner::WiimoteScanner() WiimoteScannerWindows::WiimoteScannerWindows()
{ {
init_lib(); init_lib();
} }
WiimoteScanner::~WiimoteScanner() WiimoteScannerWindows::~WiimoteScannerWindows()
{ {
// TODO: what do we want here? // TODO: what do we want here?
#if 0 #if 0
@ -241,7 +218,7 @@ WiimoteScanner::~WiimoteScanner()
#endif #endif
} }
void WiimoteScanner::Update() void WiimoteScannerWindows::Update()
{ {
if (!s_loaded_ok) if (!s_loaded_ok)
return; return;
@ -371,7 +348,8 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
// Does not replace already found Wiimotes even if they are disconnected. // Does not replace already found Wiimotes even if they are disconnected.
// wm is an array of max_wiimotes Wiimotes // wm is an array of max_wiimotes Wiimotes
// Returns the total number of found and connected Wiimotes. // Returns the total number of found and connected Wiimotes.
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board) void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{ {
if (!s_loaded_ok) if (!s_loaded_ok)
return; return;
@ -480,8 +458,9 @@ int CheckDeviceType_Read(HANDLE& dev_handle, u8* buf, int attempts)
// Wiimote. // Wiimote.
// Because nothing on Windows should be easy. // Because nothing on Windows should be easy.
// (We can't seem to easily identify the Bluetooth device an HID device belongs to...) // (We can't seem to easily identify the Bluetooth device an HID device belongs to...)
void WiimoteScanner::CheckDeviceType(std::basic_string<TCHAR>& devicepath, void WiimoteScannerWindows::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
WinWriteMethod& write_method, bool& real_wiimote, bool& is_bb) WinWriteMethod& write_method, bool& real_wiimote,
bool& is_bb)
{ {
real_wiimote = false; real_wiimote = false;
is_bb = false; is_bb = false;
@ -606,7 +585,7 @@ void WiimoteScanner::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
CloseHandle(dev_handle); CloseHandle(dev_handle);
} }
bool WiimoteScanner::IsReady() const bool WiimoteScannerWindows::IsReady() const
{ {
if (!s_loaded_ok) if (!s_loaded_ok)
{ {

View File

@ -0,0 +1,66 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef _WIN32
#include <windows.h>
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
// Different methods to send data Wiimote on Windows depending on OS and Bluetooth Stack
enum WinWriteMethod
{
WWM_WRITE_FILE_LARGEST_REPORT_SIZE,
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE,
WWM_SET_OUTPUT_REPORT
};
class WiimoteWindows final : public Wiimote
{
public:
WiimoteWindows(const std::basic_string<TCHAR>& path, WinWriteMethod initial_write_method);
~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;
WinWriteMethod m_write_method; // Type of Write Method to use
};
class WiimoteScannerWindows final : public WiimoteScannerBackend
{
public:
WiimoteScannerWindows();
~WiimoteScannerWindows() override;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override;
private:
void CheckDeviceType(std::basic_string<TCHAR>& devicepath, WinWriteMethod& write_method,
bool& real_wiimote, bool& is_bb);
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerWindows = WiimoteScannerDummy;
}
#endif

View File

@ -0,0 +1,106 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef __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/hid/IOHIDManager.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
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;
};
class WiimoteDarwinHid final : public Wiimote
{
public:
WiimoteDarwinHid(IOHIDDeviceRef device);
~WiimoteDarwinHid() 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:
static void ReportCallback(void* context, IOReturn result, void* sender, IOHIDReportType type,
u32 reportID, u8* report, CFIndex reportLength);
static void RemoveCallback(void* context, IOReturn result, void* sender);
void QueueBufferReport(int length);
IOHIDDeviceRef m_device;
bool m_connected;
std::atomic<bool> m_interrupted;
Report m_report_buffer;
Common::FifoQueue<Report> m_buffered_reports;
};
class WiimoteScannerDarwin final : public WiimoteScannerBackend
{
public:
WiimoteScannerDarwin() = default;
~WiimoteScannerDarwin() override = default;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed
};
class WiimoteScannerDarwinHID final : public WiimoteScannerBackend
{
public:
WiimoteScannerDarwinHID() = default;
~WiimoteScannerDarwinHID() override = default;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerDarwin = WiimoteScannerDummy;
using WiimoteScannerDarwinHID = WiimoteScannerDummy;
}
#endif

View File

@ -1,9 +1,9 @@
#define BLUETOOTH_VERSION_USE_CURRENT #define BLUETOOTH_VERSION_USE_CURRENT
#include "Core/HW/WiimoteReal/IOdarwin.h"
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h" #include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
@interface SearchBT : NSObject @interface SearchBT : NSObject
{ {
@ -20,188 +20,123 @@
namespace WiimoteReal namespace WiimoteReal
{ {
class WiimoteDarwin final : public Wiimote void WiimoteScannerDarwin::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
{ Wiimote*& found_board)
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;
};
class WiimoteDarwinHid final : public Wiimote
{
public:
WiimoteDarwinHid(IOHIDDeviceRef device);
~WiimoteDarwinHid() 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:
static void ReportCallback(void* context, IOReturn result, void* sender, IOHIDReportType type,
u32 reportID, u8* report, CFIndex reportLength);
static void RemoveCallback(void* context, IOReturn result, void* sender);
void QueueBufferReport(int length);
IOHIDDeviceRef m_device;
bool m_connected;
std::atomic<bool> m_interrupted;
Report m_report_buffer;
Common::FifoQueue<Report> m_buffered_reports;
};
WiimoteScanner::WiimoteScanner()
{
}
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 // TODO: find the device in the constructor and save it for later
IOBluetoothHostController* bth; IOBluetoothHostController* bth;
IOBluetoothDeviceInquiry* bti; IOBluetoothDeviceInquiry* bti;
IOHIDManagerRef hid;
SearchBT* sbt;
NSEnumerator* en;
found_board = nullptr; found_board = nullptr;
bool btFailed = false;
bool hidFailed = false;
bth = [[IOBluetoothHostController alloc] init]; bth = [[IOBluetoothHostController alloc] init];
btFailed = [bth addressAsString] == nil; bool btFailed = [bth addressAsString] == nil;
if (btFailed) if (btFailed)
WARN_LOG(WIIMOTE, "No Bluetooth host controller");
hid = IOHIDManagerCreate(NULL, kIOHIDOptionsTypeNone);
hidFailed = CFGetTypeID(hid) != IOHIDManagerGetTypeID();
if (hidFailed)
WARN_LOG(WIIMOTE, "No HID manager");
if (hidFailed && btFailed)
{ {
CFRelease(hid); WARN_LOG(WIIMOTE, "No Bluetooth host controller");
[bth release]; [bth release];
return; return;
} }
if (!btFailed) SearchBT* sbt = [[SearchBT alloc] init];
sbt->maxDevices = 32;
bti = [[IOBluetoothDeviceInquiry alloc] init];
[bti setDelegate:sbt];
[bti setInquiryLength:2];
if ([bti start] != kIOReturnSuccess)
{ {
sbt = [[SearchBT alloc] init]; ERROR_LOG(WIIMOTE, "Unable to do Bluetooth discovery");
sbt->maxDevices = 32; [bth release];
bti = [[IOBluetoothDeviceInquiry alloc] init]; [sbt release];
[bti setDelegate:sbt]; btFailed = true;
[bti setInquiryLength:2]; }
if ([bti start] != kIOReturnSuccess) do
{
CFRunLoopRun();
} while (!sbt->done);
int found_devices = [[bti foundDevices] count];
if (found_devices)
NOTICE_LOG(WIIMOTE, "Found %i Bluetooth devices", found_devices);
NSEnumerator* 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]))
{ {
ERROR_LOG(WIIMOTE, "Unable to do Bluetooth discovery"); found_board = wm;
[bth release];
[sbt release];
btFailed = true;
} }
else
do
{ {
CFRunLoopRun(); found_wiimotes.push_back(wm);
} while (!sbt->done); }
}
int found_devices = [[bti foundDevices] count]; [bth release];
[bti release];
[sbt release];
}
bool WiimoteScannerDarwin::IsReady() const
{
// TODO: only return true when a BT device is present
return true;
}
void WiimoteScannerDarwinHID::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{
found_board = nullptr;
IOHIDManagerRef hid = IOHIDManagerCreate(NULL, kIOHIDOptionsTypeNone);
bool hidFailed = CFGetTypeID(hid) != IOHIDManagerGetTypeID();
if (hidFailed)
{
CFRelease(hid);
WARN_LOG(WIIMOTE, "No HID manager");
return;
}
NSArray* criteria = @[
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0306 },
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0330 },
];
IOHIDManagerSetDeviceMatchingMultiple(hid, (CFArrayRef)criteria);
if (IOHIDManagerOpen(hid, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
WARN_LOG(WIIMOTE, "Failed to open HID Manager");
CFSetRef devices = IOHIDManagerCopyDevices(hid);
if (devices)
{
int found_devices = CFSetGetCount(devices);
if (found_devices) 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]; NOTICE_LOG(WIIMOTE, "Found %i HID devices", found_devices);
if (!IsValidBluetoothName([[dev name] UTF8String]))
continue;
Wiimote* wm = new WiimoteDarwin([dev retain]); IOHIDDeviceRef values[found_devices];
CFSetGetValues(devices, reinterpret_cast<const void**>(&values));
if (IsBalanceBoardName([[dev name] UTF8String])) for (int i = 0; i < found_devices; i++)
{
found_board = wm;
}
else
{ {
Wiimote* wm = new WiimoteDarwinHid(values[i]);
found_wiimotes.push_back(wm); found_wiimotes.push_back(wm);
} }
} }
[bth release];
[bti release];
[sbt release];
}
if (!hidFailed)
{
NSArray* criteria = @[
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0306 },
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0330 },
];
IOHIDManagerSetDeviceMatchingMultiple(hid, (CFArrayRef)criteria);
if (IOHIDManagerOpen(hid, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
WARN_LOG(WIIMOTE, "Failed to open HID Manager");
CFSetRef devices = IOHIDManagerCopyDevices(hid);
if (devices)
{
int found_devices = CFSetGetCount(devices);
if (found_devices)
{
NOTICE_LOG(WIIMOTE, "Found %i HID devices", found_devices);
IOHIDDeviceRef values[found_devices];
CFSetGetValues(devices, reinterpret_cast<const void**>(&values));
for (int i = 0; i < found_devices; i++)
{
Wiimote* wm = new WiimoteDarwinHid(values[i]);
found_wiimotes.push_back(wm);
}
}
}
CFRelease(hid);
} }
CFRelease(hid);
} }
bool WiimoteScanner::IsReady() const bool WiimoteScannerDarwinHID::IsReady() const
{ {
// TODO: only return true when a BT device is present // TODO: only return true when !hidFailed
return true; return true;
} }

View File

@ -6,6 +6,8 @@
#include <cstdlib> #include <cstdlib>
#include <queue> #include <queue>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Common/ChunkFile.h" #include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
@ -15,7 +17,10 @@
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h" #include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/IOAndroid.h"
#include "Core/HW/WiimoteReal/IOLinux.h"
#include "Core/HW/WiimoteReal/IOWin.h"
#include "Core/HW/WiimoteReal/IOdarwin.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "InputCommon/InputConfig.h" #include "InputCommon/InputConfig.h"
@ -452,6 +457,17 @@ void WiimoteScanner::SetScanMode(WiimoteScanMode scan_mode)
m_scan_mode_changed_event.Set(); m_scan_mode_changed_event.Set();
} }
bool WiimoteScanner::IsReady() const
{
return std::any_of(m_scanner_backends.begin(), m_scanner_backends.end(),
[](const auto& backend) { return backend->IsReady(); });
}
void WiimoteScanner::AddScannerBackend(std::unique_ptr<WiimoteScannerBackend> backend)
{
m_scanner_backends.emplace_back(std::move(backend));
}
static void CheckForDisconnectedWiimotes() static void CheckForDisconnectedWiimotes()
{ {
std::lock_guard<std::mutex> lk(g_wiimotes_mutex); std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
@ -475,21 +491,24 @@ void WiimoteScanner::ThreadFunc()
if (m_scan_mode.load() == WiimoteScanMode::DO_NOT_SCAN) if (m_scan_mode.load() == WiimoteScanMode::DO_NOT_SCAN)
continue; continue;
if (CalculateWantedWiimotes() != 0 || CalculateWantedBB() != 0) for (const auto& backend : m_scanner_backends)
{ {
std::vector<Wiimote*> found_wiimotes; if (CalculateWantedWiimotes() != 0 || CalculateWantedBB() != 0)
Wiimote* found_board = nullptr;
FindWiimotes(found_wiimotes, found_board);
{ {
std::lock_guard<std::mutex> lk(g_wiimotes_mutex); std::vector<Wiimote*> found_wiimotes;
std::for_each(found_wiimotes.begin(), found_wiimotes.end(), TryToConnectWiimote); Wiimote* found_board = nullptr;
if (found_board) backend->FindWiimotes(found_wiimotes, found_board);
TryToConnectBalanceBoard(found_board); {
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
std::for_each(found_wiimotes.begin(), found_wiimotes.end(), TryToConnectWiimote);
if (found_board)
TryToConnectBalanceBoard(found_board);
}
}
else
{
backend->Update(); // Does stuff needed to detect disconnects on Windows
} }
}
else
{
Update(); // Does stuff needed to detect disconnects on Windows
} }
if (m_scan_mode.load() == WiimoteScanMode::SCAN_ONCE) if (m_scan_mode.load() == WiimoteScanMode::SCAN_ONCE)
@ -615,7 +634,14 @@ void LoadSettings()
void Initialize(::Wiimote::InitializeMode init_mode) void Initialize(::Wiimote::InitializeMode init_mode)
{ {
if (!g_real_wiimotes_initialized) if (!g_real_wiimotes_initialized)
{
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerLinux>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerAndroid>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerWindows>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerDarwin>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerDarwinHID>());
g_wiimote_scanner.StartThread(); g_wiimote_scanner.StartThread();
}
if (SConfig::GetInstance().m_WiimoteContinuousScanning) if (SConfig::GetInstance().m_WiimoteContinuousScanning)
g_wiimote_scanner.SetScanMode(WiimoteScanMode::CONTINUOUSLY_SCAN); g_wiimote_scanner.SetScanMode(WiimoteScanMode::CONTINUOUSLY_SCAN);

View File

@ -114,6 +114,16 @@ private:
Common::FifoQueue<Report> m_write_reports; Common::FifoQueue<Report> m_write_reports;
}; };
class WiimoteScannerBackend
{
public:
virtual ~WiimoteScannerBackend() = default;
virtual bool IsReady() const = 0;
virtual void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) = 0;
// function called when not looking for more Wiimotes
virtual void Update() = 0;
};
enum class WiimoteScanMode enum class WiimoteScanMode
{ {
DO_NOT_SCAN, DO_NOT_SCAN,
@ -124,36 +134,23 @@ enum class WiimoteScanMode
class WiimoteScanner class WiimoteScanner
{ {
public: public:
WiimoteScanner(); WiimoteScanner() = default;
~WiimoteScanner();
bool IsReady() const;
void StartThread(); void StartThread();
void StopThread(); void StopThread();
void SetScanMode(WiimoteScanMode scan_mode); void SetScanMode(WiimoteScanMode scan_mode);
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&); void AddScannerBackend(std::unique_ptr<WiimoteScannerBackend> backend);
bool IsReady() const;
// function called when not looking for more Wiimotes
void Update();
private: private:
void ThreadFunc(); void ThreadFunc();
std::thread m_scan_thread; std::thread m_scan_thread;
Common::Flag m_scan_thread_running;
Common::Event m_scan_mode_changed_event; Common::Event m_scan_mode_changed_event;
Common::Flag m_scan_thread_running;
std::atomic<WiimoteScanMode> m_scan_mode{WiimoteScanMode::DO_NOT_SCAN}; std::atomic<WiimoteScanMode> m_scan_mode{WiimoteScanMode::DO_NOT_SCAN};
#if defined(_WIN32) std::vector<std::unique_ptr<WiimoteScannerBackend>> m_scanner_backends;
void CheckDeviceType(std::basic_string<TCHAR>& devicepath, WinWriteMethod& write_method,
bool& real_wiimote, bool& is_bb);
#elif defined(__linux__) && HAVE_BLUEZ
int device_id;
int device_sock;
#endif
}; };
extern std::mutex g_wiimotes_mutex; extern std::mutex g_wiimotes_mutex;

View File

@ -4,23 +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/hid/IOHIDManager.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
@ -51,13 +34,3 @@
// It's 23. NOT 32! // It's 23. NOT 32!
#define MAX_PAYLOAD 23 #define MAX_PAYLOAD 23
#define WIIMOTE_DEFAULT_TIMEOUT 1000 #define WIIMOTE_DEFAULT_TIMEOUT 1000
#ifdef _WIN32
// Different methods to send data Wiimote on Windows depending on OS and Bluetooth Stack
enum WinWriteMethod
{
WWM_WRITE_FILE_LARGEST_REPORT_SIZE,
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE,
WWM_SET_OUTPUT_REPORT
};
#endif