WiimoteReal: Add a hidapi IO implementation
Based on ca0c2efe7a
. Credits go to flacs.
However, unlike the original commit, hidapi does not completely replace
the current implementations, so we can still connect Wiimotes with 1+2
(without pairing).
Also, it is only used on Linux and OS X for now. This removes the
advantage of having only one implementation but there is no other
choice: using hidapi on Windows is currently impossible because
hid_write() is implemented in a way that won't work with Wiimotes.
Additionally:
* We now check for the device name in addition to the PID/VID so we can
support the Balance Board and maybe third-party Wiimotes too. This
doesn't achieve anything with the DolphinBar but it does with hidraw.
* Added a check to not connect to the same device more than once.
This commit is contained in:
parent
84467d2ff1
commit
843b030eda
|
@ -835,6 +835,33 @@ else()
|
||||||
endif()
|
endif()
|
||||||
list(APPEND LIBS ${ICONV_LIBRARIES})
|
list(APPEND LIBS ${ICONV_LIBRARIES})
|
||||||
|
|
||||||
|
if(NOT ANDROID)
|
||||||
|
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")
|
||||||
|
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()
|
||||||
|
set(HIDAPI_FOUND 1)
|
||||||
|
add_definitions(-DHAVE_HIDAPI=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_library(OPENSLES_LIBRARIES NAMES OpenSLES)
|
find_library(OPENSLES_LIBRARIES NAMES OpenSLES)
|
||||||
find_path(OPENSLES_INCLUDE_DIR NAMES SLES/OpenSLES.h)
|
find_path(OPENSLES_INCLUDE_DIR NAMES SLES/OpenSLES.h)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -1,5 +1,6 @@
|
||||||
#GameCube Controller Adapter
|
#GameCube Controller Adapter
|
||||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", TAG+="uaccess"
|
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0337", TAG+="uaccess"
|
||||||
|
|
||||||
#Mayflash DolphinBar
|
#Wiimotes or DolphinBar
|
||||||
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0306", TAG+="uaccess"
|
SUBSYSTEM=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0306", TAG+="uaccess"
|
||||||
|
SUBSYSTEM=="hidraw*", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="0330", TAG+="uaccess"
|
||||||
|
|
|
@ -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)
|
|
@ -263,6 +263,9 @@ elseif(UNIX)
|
||||||
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
|
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
if(HIDAPI_FOUND)
|
||||||
|
set(SRCS ${SRCS} HW/WiimoteReal/IOhidapi.cpp)
|
||||||
|
endif(HIDAPI_FOUND)
|
||||||
|
|
||||||
if(PORTAUDIO_FOUND)
|
if(PORTAUDIO_FOUND)
|
||||||
set(LIBS ${LIBS} portaudio)
|
set(LIBS ${LIBS} portaudio)
|
||||||
|
|
|
@ -90,7 +90,7 @@ void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wi
|
||||||
}
|
}
|
||||||
|
|
||||||
NOTICE_LOG(WIIMOTE, "device name %s", name);
|
NOTICE_LOG(WIIMOTE, "device name %s", name);
|
||||||
if (!IsValidBluetoothName(name))
|
if (!IsValidDeviceName(name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
char bdaddr_str[18] = {};
|
char bdaddr_str[18] = {};
|
||||||
|
|
|
@ -983,7 +983,7 @@ void ProcessWiimotes(bool new_scan, T& callback)
|
||||||
DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", btdi.fAuthenticated,
|
DEBUG_LOG(WIIMOTE, "Authenticated %i connected %i remembered %i ", btdi.fAuthenticated,
|
||||||
btdi.fConnected, btdi.fRemembered);
|
btdi.fConnected, btdi.fRemembered);
|
||||||
|
|
||||||
if (IsValidBluetoothName(UTF16ToUTF8(btdi.szName)))
|
if (IsValidDeviceName(UTF16ToUTF8(btdi.szName)))
|
||||||
{
|
{
|
||||||
callback(hRadio, radioInfo, btdi);
|
callback(hRadio, radioInfo, btdi);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,6 @@ public:
|
||||||
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
|
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
|
||||||
void Update() override{}; // not needed
|
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
|
#else
|
||||||
|
@ -35,6 +25,5 @@ public:
|
||||||
namespace WiimoteReal
|
namespace WiimoteReal
|
||||||
{
|
{
|
||||||
using WiimoteScannerDarwin = WiimoteScannerDummy;
|
using WiimoteScannerDarwin = WiimoteScannerDummy;
|
||||||
using WiimoteScannerDarwinHID = WiimoteScannerDummy;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -66,7 +66,7 @@ void WiimoteScannerDarwin::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||||
for (int i = 0; i < found_devices; i++)
|
for (int i = 0; i < found_devices; i++)
|
||||||
{
|
{
|
||||||
IOBluetoothDevice* dev = [en nextObject];
|
IOBluetoothDevice* dev = [en nextObject];
|
||||||
if (!IsValidBluetoothName([[dev name] UTF8String]))
|
if (!IsValidDeviceName([[dev name] UTF8String]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Wiimote* wm = new WiimoteDarwin([dev retain]);
|
Wiimote* wm = new WiimoteDarwin([dev retain]);
|
||||||
|
@ -92,55 +92,6 @@ bool WiimoteScannerDarwin::IsReady() const
|
||||||
return true;
|
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)
|
WiimoteDarwin::WiimoteDarwin(IOBluetoothDevice* device) : m_btd(device)
|
||||||
{
|
{
|
||||||
m_inputlen = 0;
|
m_inputlen = 0;
|
||||||
|
@ -296,117 +247,6 @@ void WiimoteDarwin::DisablePowerAssertionInternal()
|
||||||
ERROR_LOG(WIIMOTE, "Could not release power management assertion: %08x", ret);
|
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
|
} // namespace
|
||||||
|
|
||||||
@implementation SearchBT
|
@implementation SearchBT
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
|
||||||
|
|
||||||
// Work around an Apple bug: for some reason, IOBluetooth.h errors on
|
// 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
|
// inclusion in Mavericks, but only in Objective-C++ C++11 mode. I filed
|
||||||
// this as <rdar://15312520>; in the meantime...
|
// this as <rdar://15312520>; in the meantime...
|
||||||
|
@ -13,10 +11,11 @@
|
||||||
#undef NS_ENUM_AVAILABLE
|
#undef NS_ENUM_AVAILABLE
|
||||||
#define NS_ENUM_AVAILABLE(...)
|
#define NS_ENUM_AVAILABLE(...)
|
||||||
// end hack
|
// end hack
|
||||||
#include <IOBluetooth/IOBluetooth.h>
|
#import <IOBluetooth/IOBluetooth.h>
|
||||||
#include <IOKit/hid/IOHIDManager.h>
|
|
||||||
#include <IOKit/pwr_mgt/IOPMLib.h>
|
#include <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
|
|
||||||
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||||
|
|
||||||
namespace WiimoteReal
|
namespace WiimoteReal
|
||||||
{
|
{
|
||||||
class WiimoteDarwin final : public Wiimote
|
class WiimoteDarwin final : public Wiimote
|
||||||
|
@ -47,31 +46,4 @@ private:
|
||||||
CFRunLoopRef m_wiimote_thread_run_loop;
|
CFRunLoopRef m_wiimote_thread_run_loop;
|
||||||
IOPMAssertionID m_pm_assertion;
|
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
|
} // namespace WiimoteReal
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright 2016 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#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"
|
||||||
|
|
||||||
|
// This is used to store connected Wiimotes' paths, so we don't connect
|
||||||
|
// more than once to the same device.
|
||||||
|
static std::unordered_set<std::string> s_known_paths;
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
s_known_paths.clear();
|
||||||
|
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 || s_known_paths.count(device->path) != 0 || !IsDeviceUsable(device->path))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool is_balance_board = IsBalanceBoardName(name);
|
||||||
|
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);
|
||||||
|
|
||||||
|
Wiimote* wiimote = new WiimoteHidapi(device->path);
|
||||||
|
if (is_balance_board)
|
||||||
|
board = wiimote;
|
||||||
|
else
|
||||||
|
wiimotes.push_back(wiimote);
|
||||||
|
}
|
||||||
|
hid_free_enumeration(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
WiimoteHidapi::WiimoteHidapi(const std::string& device_path) : m_device_path(device_path)
|
||||||
|
{
|
||||||
|
s_known_paths.insert(m_device_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
WiimoteHidapi::~WiimoteHidapi()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
s_known_paths.erase(m_device_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WiimoteHidapi::ConnectInternal()
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
s_known_paths.erase(m_device_path);
|
||||||
|
}
|
||||||
|
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
|
|
@ -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;
|
||||||
|
|
||||||
|
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
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Core/HW/WiimoteReal/IOLinux.h"
|
#include "Core/HW/WiimoteReal/IOLinux.h"
|
||||||
#include "Core/HW/WiimoteReal/IOWin.h"
|
#include "Core/HW/WiimoteReal/IOWin.h"
|
||||||
#include "Core/HW/WiimoteReal/IOdarwin.h"
|
#include "Core/HW/WiimoteReal/IOdarwin.h"
|
||||||
|
#include "Core/HW/WiimoteReal/IOhidapi.h"
|
||||||
#include "Core/Host.h"
|
#include "Core/Host.h"
|
||||||
#include "InputCommon/InputConfig.h"
|
#include "InputCommon/InputConfig.h"
|
||||||
|
|
||||||
|
@ -616,7 +617,7 @@ void Initialize(::Wiimote::InitializeMode init_mode)
|
||||||
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerAndroid>());
|
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerAndroid>());
|
||||||
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerWindows>());
|
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerWindows>());
|
||||||
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerDarwin>());
|
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();
|
g_wiimote_scanner.StartThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,7 +811,7 @@ void StateChange(EMUSTATE_CHANGE newState)
|
||||||
// TODO: disable/enable auto reporting, maybe
|
// 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 ||
|
return "Nintendo RVL-CNT-01" == name || "Nintendo RVL-CNT-01-TR" == name ||
|
||||||
IsBalanceBoardName(name);
|
IsBalanceBoardName(name);
|
||||||
|
|
|
@ -160,7 +160,7 @@ void ConnectOnInput(int _WiimoteNumber);
|
||||||
void StateChange(EMUSTATE_CHANGE newState);
|
void StateChange(EMUSTATE_CHANGE newState);
|
||||||
void ChangeWiimoteSource(unsigned int index, int source);
|
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 IsBalanceBoardName(const std::string& name);
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
|
Loading…
Reference in New Issue