mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
Merge pull request #4090 from leoetlino/hidapi
WiimoteReal: Add a hidapi IO implementation
This commit is contained in:
@ -263,6 +263,9 @@ elseif(UNIX)
|
||||
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
|
||||
endif()
|
||||
endif()
|
||||
if(HIDAPI_FOUND)
|
||||
set(SRCS ${SRCS} HW/WiimoteReal/IOhidapi.cpp)
|
||||
endif(HIDAPI_FOUND)
|
||||
|
||||
if(PORTAUDIO_FOUND)
|
||||
set(LIBS ${LIBS} portaudio)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifdef ANDROID
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
@ -16,7 +17,7 @@ class WiimoteAndroid final : public Wiimote
|
||||
public:
|
||||
WiimoteAndroid(int index);
|
||||
~WiimoteAndroid() override;
|
||||
|
||||
std::string GetId() const override { return "Android " + StringFromInt(m_mayflash_index); }
|
||||
protected:
|
||||
bool ConnectInternal() override;
|
||||
void DisconnectInternal() override;
|
||||
|
@ -14,14 +14,6 @@
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
return std::find(s_known_addrs.begin(), s_known_addrs.end(), addr) == s_known_addrs.end();
|
||||
}
|
||||
|
||||
WiimoteScannerLinux::WiimoteScannerLinux() : m_device_id(-1), m_device_sock(-1)
|
||||
{
|
||||
// Get the id of the first Bluetooth device.
|
||||
@ -90,7 +82,7 @@ void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wi
|
||||
}
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "device name %s", name);
|
||||
if (!IsValidBluetoothName(name))
|
||||
if (!IsValidDeviceName(name))
|
||||
continue;
|
||||
|
||||
char bdaddr_str[18] = {};
|
||||
@ -100,7 +92,6 @@ void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wi
|
||||
continue;
|
||||
|
||||
// Found a new device
|
||||
s_known_addrs.push_back(bdaddr_str);
|
||||
Wiimote* wm = new WiimoteLinux(scan_infos[i].bdaddr);
|
||||
if (IsBalanceBoardName(name))
|
||||
{
|
||||
@ -180,10 +171,6 @@ void WiimoteLinux::DisconnectInternal()
|
||||
|
||||
m_cmd_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
|
||||
|
@ -16,6 +16,12 @@ class WiimoteLinux final : public Wiimote
|
||||
public:
|
||||
WiimoteLinux(bdaddr_t bdaddr);
|
||||
~WiimoteLinux() override;
|
||||
std::string GetId() const override
|
||||
{
|
||||
char bdaddr_str[18] = {};
|
||||
ba2str(&m_bdaddr, bdaddr_str);
|
||||
return bdaddr_str;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool ConnectInternal() override;
|
||||
|
@ -25,13 +25,9 @@
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Thread.h"
|
||||
#include "Core/HW/WiimoteReal/IOWin.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) \
|
||||
@ -64,11 +60,6 @@ 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.
|
||||
@ -344,6 +335,61 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
|
||||
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE);
|
||||
}
|
||||
|
||||
static int WriteToHandle(HANDLE& dev_handle, WinWriteMethod& method, const u8* buf, size_t size)
|
||||
{
|
||||
OVERLAPPED hid_overlap_write = OVERLAPPED();
|
||||
hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
|
||||
DWORD written = 0;
|
||||
IOWrite(dev_handle, hid_overlap_write, method, buf, size, &written);
|
||||
|
||||
CloseHandle(hid_overlap_write.hEvent);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static int ReadFromHandle(HANDLE& dev_handle, u8* buf)
|
||||
{
|
||||
OVERLAPPED hid_overlap_read = OVERLAPPED();
|
||||
hid_overlap_read.hEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
const int read = IORead(dev_handle, hid_overlap_read, buf, 1);
|
||||
CloseHandle(hid_overlap_read.hEvent);
|
||||
return read;
|
||||
}
|
||||
|
||||
static bool IsWiimote(const std::basic_string<TCHAR>& device_path, WinWriteMethod& method)
|
||||
{
|
||||
HANDLE dev_handle = CreateFile(device_path.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 false;
|
||||
|
||||
u8 buf[MAX_PAYLOAD];
|
||||
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
int invalid_report_count = 0;
|
||||
int rc = WriteToHandle(dev_handle, method, req_status_report, sizeof(req_status_report));
|
||||
while (rc > 0)
|
||||
{
|
||||
rc = ReadFromHandle(dev_handle, buf);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
|
||||
switch (buf[1])
|
||||
{
|
||||
case WM_STATUS_REPORT:
|
||||
return true;
|
||||
default:
|
||||
WARN_LOG(WIIMOTE, "IsWiimote(): Received unexpected report %02x", buf[1]);
|
||||
invalid_report_count++;
|
||||
// If we receive over 15 invalid reports, then this is probably not a Wiimote.
|
||||
if (invalid_report_count > 15)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find and connect Wiimotes.
|
||||
// Does not replace already found Wiimotes even if they are disconnected.
|
||||
// wm is an array of max_wiimotes Wiimotes
|
||||
@ -389,27 +435,21 @@ void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||
&device_info_data))
|
||||
{
|
||||
std::basic_string<TCHAR> device_path(detail_data->DevicePath);
|
||||
bool real_wiimote = false;
|
||||
bool is_bb = false;
|
||||
bool IsUsingToshibaStack = CheckForToshibaStack(device_info_data.DevInst);
|
||||
|
||||
WinWriteMethod write_method = GetInitialWriteMethod(IsUsingToshibaStack);
|
||||
|
||||
CheckDeviceType(device_path, write_method, real_wiimote, is_bb);
|
||||
|
||||
if (real_wiimote)
|
||||
if (!IsNewWiimote(UTF16ToUTF8(device_path)) || !IsWiimote(device_path, write_method))
|
||||
{
|
||||
Wiimote* wm = new WiimoteWindows(device_path, write_method);
|
||||
|
||||
if (is_bb)
|
||||
{
|
||||
found_board = wm;
|
||||
}
|
||||
else
|
||||
{
|
||||
found_wiimotes.push_back(wm);
|
||||
}
|
||||
free(detail_data);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* wiimote = new WiimoteWindows(device_path, write_method);
|
||||
if (wiimote->IsBalanceBoard())
|
||||
found_board = wiimote;
|
||||
else
|
||||
found_wiimotes.push_back(wiimote);
|
||||
}
|
||||
|
||||
free(detail_data);
|
||||
@ -418,173 +458,6 @@ void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||
SetupDiDestroyDeviceInfoList(device_info);
|
||||
}
|
||||
|
||||
int CheckDeviceType_Write(HANDLE& dev_handle, WinWriteMethod& write_method, const u8* buf,
|
||||
size_t size, int attempts)
|
||||
{
|
||||
OVERLAPPED hid_overlap_write = OVERLAPPED();
|
||||
hid_overlap_write.hEvent = CreateEvent(nullptr, true, false, nullptr);
|
||||
|
||||
DWORD written = 0;
|
||||
|
||||
for (; attempts > 0; --attempts)
|
||||
{
|
||||
if (IOWrite(dev_handle, hid_overlap_write, write_method, 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 WiimoteScannerWindows::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
|
||||
WinWriteMethod& write_method, 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, write_method, disable_enc_pt1_report,
|
||||
sizeof(disable_enc_pt1_report), 1);
|
||||
CheckDeviceType_Write(dev_handle, write_method, disable_enc_pt2_report,
|
||||
sizeof(disable_enc_pt2_report), 1);
|
||||
|
||||
int rc = CheckDeviceType_Write(dev_handle, write_method, 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, write_method, 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 WiimoteScannerWindows::IsReady() const
|
||||
{
|
||||
if (!s_loaded_ok)
|
||||
@ -615,21 +488,12 @@ bool WiimoteScannerWindows::IsReady() const
|
||||
bool WiimoteWindows::ConnectInternal()
|
||||
{
|
||||
if (IsConnected())
|
||||
return false;
|
||||
return true;
|
||||
|
||||
#ifdef SHARE_WRITE_WIIMOTES
|
||||
std::lock_guard<std::mutex> lk(g_connected_wiimotes_lock);
|
||||
if (g_connected_wiimotes.count(m_devicepath) != 0)
|
||||
if (!IsNewWiimote(UTF16ToUTF8(m_devicepath)))
|
||||
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);
|
||||
@ -665,18 +529,15 @@ bool WiimoteWindows::ConnectInternal()
|
||||
}
|
||||
#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
|
||||
// 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");
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -688,11 +549,6 @@ void WiimoteWindows::DisconnectInternal()
|
||||
|
||||
CloseHandle(m_dev_handle);
|
||||
m_dev_handle = nullptr;
|
||||
|
||||
#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,
|
||||
@ -983,7 +839,7 @@ void ProcessWiimotes(bool new_scan, T& callback)
|
||||
DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", btdi.fAuthenticated,
|
||||
btdi.fConnected, btdi.fRemembered);
|
||||
|
||||
if (IsValidBluetoothName(UTF16ToUTF8(btdi.szName)))
|
||||
if (IsValidDeviceName(UTF16ToUTF8(btdi.szName)))
|
||||
{
|
||||
callback(hRadio, radioInfo, btdi);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
@ -25,7 +26,7 @@ class WiimoteWindows final : public Wiimote
|
||||
public:
|
||||
WiimoteWindows(const std::basic_string<TCHAR>& path, WinWriteMethod initial_write_method);
|
||||
~WiimoteWindows() override;
|
||||
|
||||
std::string GetId() const override { return UTF16ToUTF8(m_devicepath); }
|
||||
protected:
|
||||
bool ConnectInternal() override;
|
||||
void DisconnectInternal() override;
|
||||
@ -50,10 +51,6 @@ public:
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -18,16 +18,6 @@ public:
|
||||
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
|
||||
@ -35,6 +25,5 @@ public:
|
||||
namespace WiimoteReal
|
||||
{
|
||||
using WiimoteScannerDarwin = WiimoteScannerDummy;
|
||||
using WiimoteScannerDarwinHID = WiimoteScannerDummy;
|
||||
}
|
||||
#endif
|
||||
|
@ -66,7 +66,7 @@ void WiimoteScannerDarwin::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||
for (int i = 0; i < found_devices; i++)
|
||||
{
|
||||
IOBluetoothDevice* dev = [en nextObject];
|
||||
if (!IsValidBluetoothName([[dev name] UTF8String]))
|
||||
if (!IsValidDeviceName([[dev name] UTF8String]))
|
||||
continue;
|
||||
|
||||
Wiimote* wm = new WiimoteDarwin([dev retain]);
|
||||
@ -92,55 +92,6 @@ bool WiimoteScannerDarwin::IsReady() const
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
bool WiimoteScannerDarwinHID::IsReady() const
|
||||
{
|
||||
// TODO: only return true when !hidFailed
|
||||
return true;
|
||||
}
|
||||
|
||||
WiimoteDarwin::WiimoteDarwin(IOBluetoothDevice* device) : m_btd(device)
|
||||
{
|
||||
m_inputlen = 0;
|
||||
@ -296,117 +247,6 @@ void WiimoteDarwin::DisablePowerAssertionInternal()
|
||||
ERROR_LOG(WIIMOTE, "Could not release power management assertion: %08x", ret);
|
||||
}
|
||||
}
|
||||
|
||||
WiimoteDarwinHid::WiimoteDarwinHid(IOHIDDeviceRef device) : m_device(device)
|
||||
{
|
||||
CFRetain(m_device);
|
||||
m_connected = false;
|
||||
m_report_buffer = Report(MAX_PAYLOAD);
|
||||
}
|
||||
|
||||
WiimoteDarwinHid::~WiimoteDarwinHid()
|
||||
{
|
||||
Shutdown();
|
||||
CFRelease(m_device);
|
||||
}
|
||||
|
||||
bool WiimoteDarwinHid::ConnectInternal()
|
||||
{
|
||||
IOReturn ret = IOHIDDeviceOpen(m_device, kIOHIDOptionsTypeNone);
|
||||
m_connected = ret == kIOReturnSuccess;
|
||||
if (m_connected)
|
||||
{
|
||||
IOHIDDeviceScheduleWithRunLoop(m_device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOHIDDeviceRegisterInputReportCallback(m_device, m_report_buffer.data() + 1, MAX_PAYLOAD - 1,
|
||||
&WiimoteDarwinHid::ReportCallback, this);
|
||||
IOHIDDeviceRegisterRemovalCallback(m_device, &WiimoteDarwinHid::RemoveCallback, this);
|
||||
NOTICE_LOG(WIIMOTE, "Connected to Wiimote %i", m_index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Could not open IOHID Wiimote: %08x", ret);
|
||||
}
|
||||
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
void WiimoteDarwinHid::DisconnectInternal()
|
||||
{
|
||||
IOHIDDeviceUnscheduleFromRunLoop(m_device, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
IOWakeup();
|
||||
IOReturn ret = IOHIDDeviceClose(m_device, kIOHIDOptionsTypeNone);
|
||||
if (ret != kIOReturnSuccess)
|
||||
ERROR_LOG(WIIMOTE, "Error closing IOHID Wiimote: %08x", ret);
|
||||
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Disconnecting Wiimote %i", m_index + 1);
|
||||
|
||||
m_buffered_reports.Clear();
|
||||
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
bool WiimoteDarwinHid::IsConnected() const
|
||||
{
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
void WiimoteDarwinHid::IOWakeup()
|
||||
{
|
||||
m_interrupted.store(true);
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}
|
||||
|
||||
int WiimoteDarwinHid::IORead(u8* buf)
|
||||
{
|
||||
Report rpt;
|
||||
m_interrupted.store(false);
|
||||
while (m_buffered_reports.Empty() && !m_interrupted.load())
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true);
|
||||
|
||||
if (m_buffered_reports.Pop(rpt))
|
||||
{
|
||||
memcpy(buf, rpt.data(), rpt.size());
|
||||
return rpt.size();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WiimoteDarwinHid::IOWrite(u8 const* buf, size_t len)
|
||||
{
|
||||
IOReturn ret = IOHIDDeviceSetReport(m_device, kIOHIDReportTypeOutput, buf[1], buf + 1, len - 1);
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Error writing to Wiimote: %08x", ret);
|
||||
return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void WiimoteDarwinHid::QueueBufferReport(int length)
|
||||
{
|
||||
Report rpt(m_report_buffer);
|
||||
rpt[0] = 0xa1;
|
||||
rpt.resize(length + 1);
|
||||
m_buffered_reports.Push(std::move(rpt));
|
||||
}
|
||||
|
||||
void WiimoteDarwinHid::ReportCallback(void* context, IOReturn result, void*, IOHIDReportType type,
|
||||
u32 report_id, u8* report, CFIndex report_length)
|
||||
{
|
||||
WiimoteDarwinHid* wm = static_cast<WiimoteDarwinHid*>(context);
|
||||
report[0] = report_id;
|
||||
wm->QueueBufferReport(report_length);
|
||||
}
|
||||
|
||||
void WiimoteDarwinHid::RemoveCallback(void* context, IOReturn result, void*)
|
||||
{
|
||||
WiimoteDarwinHid* wm = static_cast<WiimoteDarwinHid*>(context);
|
||||
wm->DisconnectInternal();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@implementation SearchBT
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
// 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...
|
||||
@ -13,10 +11,11 @@
|
||||
#undef NS_ENUM_AVAILABLE
|
||||
#define NS_ENUM_AVAILABLE(...)
|
||||
// end hack
|
||||
#include <IOBluetooth/IOBluetooth.h>
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
#import <IOBluetooth/IOBluetooth.h>
|
||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
class WiimoteDarwin final : public Wiimote
|
||||
@ -24,7 +23,7 @@ class WiimoteDarwin final : public Wiimote
|
||||
public:
|
||||
WiimoteDarwin(IOBluetoothDevice* device);
|
||||
~WiimoteDarwin() override;
|
||||
|
||||
std::string GetId() const override { return [[m_btd addressString] UTF8String]; }
|
||||
// These are not protected/private because ConnectBT needs them.
|
||||
void DisconnectInternal() override;
|
||||
IOBluetoothDevice* m_btd;
|
||||
@ -47,31 +46,4 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace WiimoteReal
|
||||
|
146
Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp
Normal file
146
Source/Core/Core/HW/WiimoteReal/IOhidapi.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
|
||||
#include "Core/HW/WiimoteReal/IOhidapi.h"
|
||||
|
||||
static bool IsDeviceUsable(const std::string& device_path)
|
||||
{
|
||||
hid_device* handle = hid_open_path(device_path.c_str());
|
||||
if (handle == nullptr)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Could not connect to Wiimote at \"%s\". "
|
||||
"Do you have permission to access the device?",
|
||||
device_path.c_str());
|
||||
return false;
|
||||
}
|
||||
// Some third-party adapters (DolphinBar) always expose all four Wiimotes as HIDs
|
||||
// even when they are not connected, which causes an endless error loop when we try to use them.
|
||||
// Try to write a report to the device to see if this Wiimote is really usable.
|
||||
static const u8 report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
const int result = hid_write(handle, report, sizeof(report));
|
||||
// The DolphinBar uses EPIPE to signal the absence of a Wiimote connected to this HID.
|
||||
if (result == -1 && errno != EPIPE)
|
||||
ERROR_LOG(WIIMOTE, "Couldn't write to Wiimote at \"%s\".", device_path.c_str());
|
||||
|
||||
hid_close(handle);
|
||||
return result != -1;
|
||||
}
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
WiimoteScannerHidapi::WiimoteScannerHidapi()
|
||||
{
|
||||
int ret = hid_init();
|
||||
_assert_msg_(WIIMOTE, ret == 0, "Couldn't initialise hidapi.");
|
||||
}
|
||||
|
||||
WiimoteScannerHidapi::~WiimoteScannerHidapi()
|
||||
{
|
||||
if (hid_exit() == -1)
|
||||
ERROR_LOG(WIIMOTE, "Failed to clean up hidapi.");
|
||||
}
|
||||
|
||||
bool WiimoteScannerHidapi::IsReady() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void WiimoteScannerHidapi::FindWiimotes(std::vector<Wiimote*>& wiimotes, Wiimote*& board)
|
||||
{
|
||||
hid_device_info* list = hid_enumerate(0x0, 0x0);
|
||||
for (hid_device_info* device = list; device; device = device->next)
|
||||
{
|
||||
const std::string name = device->product_string ? UTF16ToUTF8(device->product_string) : "";
|
||||
const bool is_wiimote =
|
||||
IsValidDeviceName(name) || (device->vendor_id == 0x057e &&
|
||||
(device->product_id == 0x0306 || device->product_id == 0x0330));
|
||||
if (!is_wiimote || !IsNewWiimote(device->path) || !IsDeviceUsable(device->path))
|
||||
continue;
|
||||
|
||||
auto* wiimote = new WiimoteHidapi(device->path);
|
||||
const bool is_balance_board = IsBalanceBoardName(name) || wiimote->IsBalanceBoard();
|
||||
if (is_balance_board)
|
||||
board = wiimote;
|
||||
else
|
||||
wiimotes.push_back(wiimote);
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Found %s at %s: %ls %ls (%04hx:%04hx)",
|
||||
is_balance_board ? "balance board" : "Wiimote", device->path,
|
||||
device->manufacturer_string, device->product_string, device->vendor_id,
|
||||
device->product_id);
|
||||
}
|
||||
hid_free_enumeration(list);
|
||||
}
|
||||
|
||||
WiimoteHidapi::WiimoteHidapi(const std::string& device_path) : m_device_path(device_path)
|
||||
{
|
||||
}
|
||||
|
||||
WiimoteHidapi::~WiimoteHidapi()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool WiimoteHidapi::ConnectInternal()
|
||||
{
|
||||
if (m_handle != nullptr)
|
||||
return true;
|
||||
|
||||
m_handle = hid_open_path(m_device_path.c_str());
|
||||
if (m_handle == nullptr)
|
||||
{
|
||||
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 != nullptr;
|
||||
}
|
||||
|
||||
void WiimoteHidapi::DisconnectInternal()
|
||||
{
|
||||
hid_close(m_handle);
|
||||
m_handle = nullptr;
|
||||
}
|
||||
|
||||
bool WiimoteHidapi::IsConnected() const
|
||||
{
|
||||
return m_handle != nullptr;
|
||||
}
|
||||
|
||||
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());
|
||||
return 0; // error
|
||||
}
|
||||
if (result == 0)
|
||||
{
|
||||
return -1; // didn't read packet
|
||||
}
|
||||
buf[0] = WM_SET_REPORT | WM_BT_INPUT;
|
||||
return result + 1; // number of bytes read
|
||||
}
|
||||
|
||||
int WiimoteHidapi::IOWrite(const u8* 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());
|
||||
return 0;
|
||||
}
|
||||
return (result == 0) ? 1 : result;
|
||||
}
|
||||
}; // WiimoteReal
|
50
Source/Core/Core/HW/WiimoteReal/IOhidapi.h
Normal file
50
Source/Core/Core/HW/WiimoteReal/IOhidapi.h
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_HIDAPI
|
||||
#include <hidapi.h>
|
||||
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
class WiimoteHidapi final : public Wiimote
|
||||
{
|
||||
public:
|
||||
explicit WiimoteHidapi(const std::string& device_path);
|
||||
~WiimoteHidapi() override;
|
||||
std::string GetId() const override { return m_device_path; }
|
||||
protected:
|
||||
bool ConnectInternal() override;
|
||||
void DisconnectInternal() override;
|
||||
bool IsConnected() const override;
|
||||
void IOWakeup() override {}
|
||||
int IORead(u8* buf) override;
|
||||
int IOWrite(const u8* buf, size_t len) override;
|
||||
|
||||
private:
|
||||
std::string m_device_path;
|
||||
hid_device* m_handle = nullptr;
|
||||
};
|
||||
|
||||
class WiimoteScannerHidapi final : public WiimoteScannerBackend
|
||||
{
|
||||
public:
|
||||
WiimoteScannerHidapi();
|
||||
~WiimoteScannerHidapi();
|
||||
bool IsReady() const override;
|
||||
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
|
||||
void Update() override{}; // not needed for hidapi
|
||||
};
|
||||
}
|
||||
|
||||
#else
|
||||
#include "Core/HW/WiimoteReal/IODummy.h"
|
||||
namespace WiimoteReal
|
||||
{
|
||||
using WiimoteScannerHidapi = WiimoteScannerDummy;
|
||||
}
|
||||
#endif
|
@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
@ -21,6 +22,7 @@
|
||||
#include "Core/HW/WiimoteReal/IOLinux.h"
|
||||
#include "Core/HW/WiimoteReal/IOWin.h"
|
||||
#include "Core/HW/WiimoteReal/IOdarwin.h"
|
||||
#include "Core/HW/WiimoteReal/IOhidapi.h"
|
||||
#include "Core/Host.h"
|
||||
#include "InputCommon/InputConfig.h"
|
||||
|
||||
@ -36,6 +38,11 @@ void HandleWiimoteDisconnect(int index);
|
||||
|
||||
static bool g_real_wiimotes_initialized = false;
|
||||
|
||||
// This is used to store connected Wiimotes' IDs, so we don't connect
|
||||
// more than once to the same device.
|
||||
static std::unordered_set<std::string> s_known_ids;
|
||||
static std::mutex s_known_ids_mutex;
|
||||
|
||||
std::mutex g_wiimotes_mutex;
|
||||
|
||||
Wiimote* g_wiimotes[MAX_BBMOTES];
|
||||
@ -217,10 +224,11 @@ void Wiimote::Read()
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::Write()
|
||||
bool Wiimote::Write()
|
||||
{
|
||||
// nothing written, but this is not an error
|
||||
if (m_write_reports.Empty())
|
||||
return;
|
||||
return true;
|
||||
|
||||
Report const& rpt = m_write_reports.Front();
|
||||
|
||||
@ -230,12 +238,79 @@ void Wiimote::Write()
|
||||
Socket.send((char*)rpt.data(), rpt.size(), sf::IpAddress::LocalHost,
|
||||
SConfig::GetInstance().iBBDumpPort);
|
||||
}
|
||||
IOWrite(rpt.data(), rpt.size());
|
||||
int ret = IOWrite(rpt.data(), rpt.size());
|
||||
|
||||
m_write_reports.Pop();
|
||||
|
||||
if (!m_write_reports.Empty())
|
||||
IOWakeup();
|
||||
|
||||
return ret != 0;
|
||||
}
|
||||
|
||||
bool Wiimote::IsBalanceBoard()
|
||||
{
|
||||
if (!ConnectInternal())
|
||||
return false;
|
||||
// Initialise the extension by writing 0x55 to 0xa400f0, then writing 0x00 to 0xa400fb.
|
||||
static const u8 init_extension_rpt1[MAX_PAYLOAD] = {
|
||||
WM_SET_REPORT | WM_BT_OUTPUT, WM_WRITE_DATA, 0x04, 0xa4, 0x00, 0xf0, 0x01, 0x55};
|
||||
static const u8 init_extension_rpt2[MAX_PAYLOAD] = {
|
||||
WM_SET_REPORT | WM_BT_OUTPUT, WM_WRITE_DATA, 0x04, 0xa4, 0x00, 0xfb, 0x01, 0x00};
|
||||
static const u8 status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
if (!IOWrite(init_extension_rpt1, sizeof(init_extension_rpt1)) ||
|
||||
!IOWrite(init_extension_rpt2, sizeof(init_extension_rpt2)))
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "IsBalanceBoard(): Failed to initialise extension.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = IOWrite(status_report, sizeof(status_report));
|
||||
u8 buf[MAX_PAYLOAD];
|
||||
while (ret != 0)
|
||||
{
|
||||
ret = IORead(buf);
|
||||
if (ret == -1)
|
||||
continue;
|
||||
|
||||
switch (buf[1])
|
||||
{
|
||||
case WM_STATUS_REPORT:
|
||||
{
|
||||
const auto* status = reinterpret_cast<wm_status_report*>(&buf[2]);
|
||||
// A Balance Board has a Balance Board extension.
|
||||
if (!status->extension)
|
||||
return false;
|
||||
// Read two bytes from 0xa400fe to identify the extension.
|
||||
static const u8 identify_ext_rpt[] = {
|
||||
WM_SET_REPORT | WM_BT_OUTPUT, WM_READ_DATA, 0x04, 0xa4, 0x00, 0xfe, 0x02, 0x00};
|
||||
ret = IOWrite(identify_ext_rpt, sizeof(identify_ext_rpt));
|
||||
break;
|
||||
}
|
||||
case WM_READ_DATA_REPLY:
|
||||
{
|
||||
const auto* reply = reinterpret_cast<wm_read_data_reply*>(&buf[2]);
|
||||
if (Common::swap16(reply->address) != 0x00fe)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "IsBalanceBoard(): Received unexpected data reply for address %X",
|
||||
Common::swap16(reply->address));
|
||||
return false;
|
||||
}
|
||||
// A Balance Board ext can be identified by checking for 0x0402.
|
||||
return reply->data[0] == 0x04 && reply->data[1] == 0x02;
|
||||
}
|
||||
case WM_ACK_DATA:
|
||||
{
|
||||
const auto* ack = reinterpret_cast<wm_acknowledge*>(&buf[2]);
|
||||
if (ack->reportID == WM_READ_DATA && ack->errorID != 0x00)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Failed to read from 0xa400fe, assuming Wiimote is not a Balance Board.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsDataReport(const Report& rpt)
|
||||
@ -499,6 +574,8 @@ void WiimoteScanner::ThreadFunc()
|
||||
Wiimote* found_board = nullptr;
|
||||
backend->FindWiimotes(found_wiimotes, found_board);
|
||||
{
|
||||
if (!g_real_wiimotes_initialized)
|
||||
continue;
|
||||
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
|
||||
std::for_each(found_wiimotes.begin(), found_wiimotes.end(), TryToConnectWiimote);
|
||||
if (found_board)
|
||||
@ -574,7 +651,11 @@ void Wiimote::ThreadFunc()
|
||||
m_index + 1);
|
||||
break;
|
||||
}
|
||||
Write();
|
||||
if (!Write())
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "Wiimote::Write failed. Disconnecting Wiimote %d.", m_index + 1);
|
||||
break;
|
||||
}
|
||||
Read();
|
||||
}
|
||||
|
||||
@ -612,11 +693,12 @@ void Initialize(::Wiimote::InitializeMode init_mode)
|
||||
{
|
||||
if (!g_real_wiimotes_initialized)
|
||||
{
|
||||
s_known_ids.clear();
|
||||
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.AddScannerBackend(std::make_unique<WiimoteScannerHidapi>());
|
||||
g_wiimote_scanner.StartThread();
|
||||
}
|
||||
|
||||
@ -656,6 +738,7 @@ void Stop()
|
||||
// called when the Dolphin app exits
|
||||
void Shutdown()
|
||||
{
|
||||
g_real_wiimotes_initialized = false;
|
||||
g_wiimote_scanner.StopThread();
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "WiimoteReal::Shutdown");
|
||||
@ -714,6 +797,8 @@ static bool TryToConnectWiimoteToSlot(Wiimote* wm, unsigned int i)
|
||||
NOTICE_LOG(WIIMOTE, "Connected to Wiimote %i.", i + 1);
|
||||
g_wiimotes[i] = wm;
|
||||
Host_ConnectWiimote(i, true);
|
||||
std::lock_guard<std::mutex> lk(s_known_ids_mutex);
|
||||
s_known_ids.insert(wm->GetId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -748,6 +833,8 @@ void HandleWiimoteDisconnect(int index)
|
||||
std::swap(wm, g_wiimotes[index]);
|
||||
if (wm)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_known_ids_mutex);
|
||||
s_known_ids.erase(wm->GetId());
|
||||
delete wm;
|
||||
NOTICE_LOG(WIIMOTE, "Disconnected Wiimote %i.", index + 1);
|
||||
}
|
||||
@ -810,7 +897,7 @@ void StateChange(EMUSTATE_CHANGE newState)
|
||||
// TODO: disable/enable auto reporting, maybe
|
||||
}
|
||||
|
||||
bool IsValidBluetoothName(const std::string& name)
|
||||
bool IsValidDeviceName(const std::string& name)
|
||||
{
|
||||
return "Nintendo RVL-CNT-01" == name || "Nintendo RVL-CNT-01-TR" == name ||
|
||||
IsBalanceBoardName(name);
|
||||
@ -821,4 +908,11 @@ bool IsBalanceBoardName(const std::string& name)
|
||||
return "Nintendo RVL-WBC-01" == name;
|
||||
}
|
||||
|
||||
// This is called from the scanner backends (currently on the scanner thread).
|
||||
bool IsNewWiimote(const std::string& identifier)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_known_ids_mutex);
|
||||
return s_known_ids.count(identifier) == 0;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
@ -31,6 +31,8 @@ public:
|
||||
// This needs to be called in derived destructors!
|
||||
void Shutdown();
|
||||
|
||||
virtual std::string GetId() const = 0;
|
||||
|
||||
void ControlChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void InterruptChannel(const u16 channel, const void* const data, const u32 size);
|
||||
void Update();
|
||||
@ -39,7 +41,9 @@ public:
|
||||
const Report& ProcessReadQueue();
|
||||
|
||||
void Read();
|
||||
void Write();
|
||||
bool Write();
|
||||
|
||||
bool IsBalanceBoard();
|
||||
|
||||
void StartThread();
|
||||
void StopThread();
|
||||
@ -160,8 +164,9 @@ void ConnectOnInput(int _WiimoteNumber);
|
||||
void StateChange(EMUSTATE_CHANGE newState);
|
||||
void ChangeWiimoteSource(unsigned int index, int source);
|
||||
|
||||
bool IsValidBluetoothName(const std::string& name);
|
||||
bool IsValidDeviceName(const std::string& name);
|
||||
bool IsBalanceBoardName(const std::string& name);
|
||||
bool IsNewWiimote(const std::string& identifier);
|
||||
|
||||
#ifdef ANDROID
|
||||
void InitAdapterClass();
|
||||
|
@ -82,6 +82,8 @@ std::string wxStringTranslator(const char*);
|
||||
|
||||
CFrame* main_frame = nullptr;
|
||||
|
||||
static std::mutex s_init_mutex;
|
||||
|
||||
bool DolphinApp::Initialize(int& c, wxChar** v)
|
||||
{
|
||||
#if defined HAVE_X11 && HAVE_X11
|
||||
@ -94,6 +96,7 @@ bool DolphinApp::Initialize(int& c, wxChar** v)
|
||||
|
||||
bool DolphinApp::OnInit()
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_init_mutex);
|
||||
if (!wxApp::OnInit())
|
||||
return false;
|
||||
|
||||
@ -548,6 +551,7 @@ bool Host_RendererIsFullscreen()
|
||||
|
||||
void Host_ConnectWiimote(int wm_idx, bool connect)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_init_mutex);
|
||||
if (connect)
|
||||
{
|
||||
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_FORCE_CONNECT_WIIMOTE1 + wm_idx);
|
||||
|
Reference in New Issue
Block a user