mirror of
https://github.com/dolphin-emu/dolphin
synced 2025-10-06 00:13:03 +02:00
WiimoteReal/IOLinux: Improvements, fixes, and code cleanups.
This commit is contained in:
@@ -4,16 +4,26 @@
|
||||
#include "Core/HW/WiimoteReal/IOLinux.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <bluetooth/hci_lib.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Network.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/UnixUtil.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
@@ -21,68 +31,195 @@ namespace WiimoteReal
|
||||
constexpr u16 L2CAP_PSM_HID_CNTL = 0x0011;
|
||||
constexpr u16 L2CAP_PSM_HID_INTR = 0x0013;
|
||||
|
||||
WiimoteScannerLinux::WiimoteScannerLinux() : m_device_id(-1), m_device_sock(-1)
|
||||
static void AddAutoConnectAddresses(std::vector<Wiimote*>& found_wiimotes)
|
||||
{
|
||||
std::string entries = Config::Get(Config::MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES);
|
||||
for (auto& bt_address_str : SplitString(entries, ','))
|
||||
{
|
||||
const auto bt_addr = Common::StringToBluetoothAddress(bt_address_str);
|
||||
if (!bt_addr.has_value())
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Bad Auto Connect Bluetooth Address: {}", bt_address_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
Common::ToLower(&bt_address_str);
|
||||
if (!IsNewWiimote(bt_address_str))
|
||||
continue;
|
||||
|
||||
found_wiimotes.push_back(new WiimoteLinux(*bt_addr));
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Added Wiimote with fixed address ({}).", bt_address_str);
|
||||
}
|
||||
}
|
||||
|
||||
WiimoteScannerLinux::WiimoteScannerLinux()
|
||||
{
|
||||
Open();
|
||||
}
|
||||
|
||||
bool WiimoteScannerLinux::Open()
|
||||
{
|
||||
if (IsReady())
|
||||
return true;
|
||||
|
||||
// Get the id of the first Bluetooth device.
|
||||
m_device_id = hci_get_route(nullptr);
|
||||
if (m_device_id < 0)
|
||||
{
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Bluetooth not found.");
|
||||
return;
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Bluetooth not found. hci_get_route: {}", Common::LastStrerrorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a socket to the device
|
||||
m_device_sock = hci_open_dev(m_device_id);
|
||||
if (m_device_sock < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Unable to open Bluetooth.");
|
||||
return;
|
||||
ERROR_LOG_FMT(WIIMOTE, "Unable to open Bluetooth. hci_open_dev: {}",
|
||||
Common::LastStrerrorString());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_is_device_open.store(true, std::memory_order_relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
WiimoteScannerLinux::~WiimoteScannerLinux()
|
||||
{
|
||||
if (IsReady())
|
||||
close(m_device_sock);
|
||||
Close();
|
||||
}
|
||||
|
||||
void WiimoteScannerLinux::Close()
|
||||
{
|
||||
if (!IsReady())
|
||||
return;
|
||||
|
||||
m_is_device_open.store(false, std::memory_order_relaxed);
|
||||
|
||||
close(std::exchange(m_device_sock, -1));
|
||||
m_device_id = -1;
|
||||
}
|
||||
|
||||
bool WiimoteScannerLinux::IsReady() const
|
||||
{
|
||||
return m_device_sock > 0;
|
||||
return m_is_device_open.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
struct InquiryRequest : hci_inquiry_req
|
||||
{
|
||||
static constexpr int MAX_INFOS = 255;
|
||||
std::array<inquiry_info, MAX_INFOS> scan_infos;
|
||||
};
|
||||
static_assert(sizeof(InquiryRequest) ==
|
||||
sizeof(hci_inquiry_req) + sizeof(inquiry_info) * InquiryRequest::MAX_INFOS);
|
||||
|
||||
// Returns 0 on success or some error number on failure.
|
||||
static int HciInquiry(int device_socket, InquiryRequest* request)
|
||||
{
|
||||
const auto done_event = UnixUtil::CreateEventFD(0, 0);
|
||||
Common::ScopeGuard close_guard([&] { close(done_event); });
|
||||
|
||||
int hci_inquiry_errorno = 0;
|
||||
|
||||
// Unplugging a BT adapter causes `hci_inquiry` to block forever (inside `ioctl`).
|
||||
// Fortunately it does produce a signal on the socket so we can poll for that.
|
||||
// Performing the inquiry on thread lets us interrupt `ioctl` if the socket signals.
|
||||
|
||||
// Using `pthread_cancel` with `std::thread` isn't technically correct so we use `pthread_create`.
|
||||
UnixUtil::PThreadWrapper hci_inquiry_thread{[&] {
|
||||
// We're manually doing the `ioctl` because `hci_inquiry` isn't pthread_cancel-safe.
|
||||
// It uses `malloc` and whatnot.
|
||||
const int ret = ioctl(device_socket, HCIINQUIRY, reinterpret_cast<unsigned long>(request));
|
||||
if (ret < 0)
|
||||
hci_inquiry_errorno = errno;
|
||||
|
||||
// Signal doneness to `poll`.
|
||||
u64 val = 1;
|
||||
write(done_event, &val, sizeof(val));
|
||||
}};
|
||||
Common::ScopeGuard join_guard([&] { pthread_join(hci_inquiry_thread.handle, nullptr); });
|
||||
|
||||
// Wait for the above thread or some socket signal.
|
||||
std::array<pollfd, 2> pollfds{
|
||||
pollfd{.fd = device_socket},
|
||||
pollfd{.fd = done_event, .events = POLLIN},
|
||||
};
|
||||
UnixUtil::RetryOnEINTR(poll, pollfds.data(), pollfds.size(), -1);
|
||||
|
||||
if (pollfds[0].revents != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "HciInquiry device socket had error. Cancelling thread.");
|
||||
// I am not entirely sure if this is foolproof, depending on where `ioctl` is internally?
|
||||
// It's worked every time in testing though, and it's better than *always* freezing.
|
||||
pthread_cancel(hci_inquiry_thread.handle);
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
return hci_inquiry_errorno;
|
||||
}
|
||||
|
||||
void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
|
||||
{
|
||||
WiimoteScannerLinux::AddAutoConnectAddresses(found_wiimotes);
|
||||
|
||||
int const wait_len = BLUETOOTH_INQUIRY_LENGTH;
|
||||
int const max_infos = 255;
|
||||
inquiry_info scan_infos[max_infos] = {};
|
||||
auto* scan_infos_ptr = scan_infos;
|
||||
found_board = nullptr;
|
||||
// Use Limited Dedicated Inquiry Access Code (LIAC) to query, since third-party Wiimotes
|
||||
// cannot be discovered without it.
|
||||
const u8 lap[3] = {0x00, 0x8b, 0x9e};
|
||||
|
||||
// Scan for Bluetooth devices
|
||||
int const found_devices =
|
||||
hci_inquiry(m_device_id, wait_len, max_infos, lap, &scan_infos_ptr, IREQ_CACHE_FLUSH);
|
||||
if (found_devices < 0)
|
||||
if (!Open())
|
||||
return;
|
||||
|
||||
AddAutoConnectAddresses(found_wiimotes);
|
||||
|
||||
InquiryRequest request{};
|
||||
request.dev_id = m_device_id;
|
||||
request.flags = IREQ_CACHE_FLUSH;
|
||||
request.length = BLUETOOTH_INQUIRY_LENGTH;
|
||||
request.num_rsp = InquiryRequest::MAX_INFOS;
|
||||
// Use Limited Dedicated Inquiry Access Code (LIAC) like the Wii does.
|
||||
// Third-party Wiimotes cannot be discovered without it.
|
||||
std::ranges::copy(std::to_array({0x00, 0x8b, 0x9e}), request.lap);
|
||||
|
||||
const int hci_inquiry_result = HciInquiry(m_device_sock, &request);
|
||||
switch (hci_inquiry_result)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Error searching for Bluetooth devices.");
|
||||
case 0:
|
||||
break;
|
||||
case ENODEV:
|
||||
Close();
|
||||
[[fallthrough]];
|
||||
default:
|
||||
ERROR_LOG_FMT(WIIMOTE, "Error searching for Bluetooth devices: {}",
|
||||
Common::StrerrorString(hci_inquiry_result));
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG_FMT(WIIMOTE, "Found {} Bluetooth device(s).", found_devices);
|
||||
DEBUG_LOG_FMT(WIIMOTE, "Found {} Bluetooth device(s).", request.num_rsp);
|
||||
|
||||
// Display discovered devices
|
||||
for (auto& scan_info : scan_infos | std::ranges::views::take(found_devices))
|
||||
for (auto& scan_info : request.scan_infos | std::ranges::views::take(request.num_rsp))
|
||||
{
|
||||
// BT names are a maximum of 248 bytes apparently
|
||||
char name[255] = {};
|
||||
if (hci_read_remote_name(m_device_sock, &scan_info.bdaddr, sizeof(name), name, 1000) < 0)
|
||||
const auto bdaddr_str =
|
||||
BluetoothAddressToString(std::bit_cast<Common::BluetoothAddress>(scan_info.bdaddr));
|
||||
|
||||
// Did AddAutoConnectAddresses already add this remote?
|
||||
const auto eq_this_bdaddr = [&](auto* wm) { return wm->GetId() == bdaddr_str; };
|
||||
if (std::ranges::any_of(found_wiimotes, eq_this_bdaddr))
|
||||
continue;
|
||||
|
||||
if (!IsNewWiimote(bdaddr_str))
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Bluetooth read remote name failed.");
|
||||
WARN_LOG_FMT(WIIMOTE, "Discovered already connected device: {}", bdaddr_str);
|
||||
continue;
|
||||
}
|
||||
|
||||
// The Wii can actually connect remotes with a 10 second name response time.
|
||||
// We won't wait quite so long since we're doing this in a blocking manner right now.
|
||||
// Note that waiting just 1 second can be problematic sometimes.
|
||||
const int read_name_timeout_ms = 3000;
|
||||
|
||||
// BT names are a maximum of 248 bytes.
|
||||
char name[255]{};
|
||||
if (hci_read_remote_name_with_clock_offset(m_device_sock, &scan_info.bdaddr,
|
||||
scan_info.pscan_rep_mode, scan_info.clock_offset,
|
||||
sizeof(name), name, read_name_timeout_ms) < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Bluetooth read remote name failed. hci_read_remote_name: {}",
|
||||
Common::LastStrerrorString());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -91,131 +228,91 @@ void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wi
|
||||
if (!IsValidDeviceName(name))
|
||||
continue;
|
||||
|
||||
char bdaddr_str[18] = {};
|
||||
ba2str(&scan_info.bdaddr, bdaddr_str);
|
||||
|
||||
if (!IsNewWiimote(bdaddr_str))
|
||||
continue;
|
||||
|
||||
// Found a new device
|
||||
Wiimote* wm = new WiimoteLinux(scan_info.bdaddr);
|
||||
auto wm =
|
||||
std::make_unique<WiimoteLinux>(std::bit_cast<Common::BluetoothAddress>(scan_info.bdaddr));
|
||||
if (IsBalanceBoardName(name))
|
||||
{
|
||||
found_board = wm;
|
||||
delete std::exchange(found_board, wm.release());
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Found balance board ({}).", bdaddr_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
found_wiimotes.push_back(wm);
|
||||
found_wiimotes.push_back(wm.release());
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Found Wiimote ({}).", bdaddr_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WiimoteScannerLinux::AddAutoConnectAddresses(std::vector<Wiimote*>& found_wiimotes)
|
||||
{
|
||||
std::string entries = Config::Get(Config::MAIN_WIIMOTE_AUTO_CONNECT_ADDRESSES);
|
||||
if (entries.empty())
|
||||
return;
|
||||
for (const auto& bt_address_str : SplitString(entries, ','))
|
||||
{
|
||||
bdaddr_t bt_addr;
|
||||
if (str2ba(bt_address_str.c_str(), &bt_addr) < 0)
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Bad Known Bluetooth Address: {}", bt_address_str);
|
||||
continue;
|
||||
}
|
||||
if (!IsNewWiimote(bt_address_str))
|
||||
continue;
|
||||
Wiimote* wm = new WiimoteLinux(bt_addr);
|
||||
found_wiimotes.push_back(wm);
|
||||
NOTICE_LOG_FMT(WIIMOTE, "Added Wiimote with fixed address ({}).", bt_address_str);
|
||||
}
|
||||
void WiimoteScannerLinux::Update()
|
||||
{ // Nothing needed on Linux.
|
||||
}
|
||||
|
||||
WiimoteLinux::WiimoteLinux(bdaddr_t bdaddr) : m_bdaddr(bdaddr)
|
||||
void WiimoteScannerLinux::RequestStopSearching()
|
||||
{ // Nothing needed on Linux.
|
||||
}
|
||||
|
||||
WiimoteLinux::WiimoteLinux(Common::BluetoothAddress bdaddr)
|
||||
: m_bdaddr{bdaddr}, m_wakeup_fd{UnixUtil::CreateEventFD(0, 0)}
|
||||
{
|
||||
m_really_disconnect = true;
|
||||
|
||||
m_cmd_sock = -1;
|
||||
m_int_sock = -1;
|
||||
|
||||
int fds[2];
|
||||
if (pipe(fds))
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "pipe failed");
|
||||
abort();
|
||||
}
|
||||
m_wakeup_pipe_w = fds[1];
|
||||
m_wakeup_pipe_r = fds[0];
|
||||
}
|
||||
|
||||
WiimoteLinux::~WiimoteLinux()
|
||||
{
|
||||
Shutdown();
|
||||
close(m_wakeup_pipe_w);
|
||||
close(m_wakeup_pipe_r);
|
||||
close(m_wakeup_fd);
|
||||
}
|
||||
|
||||
std::string WiimoteLinux::GetId() const
|
||||
{
|
||||
return BluetoothAddressToString(m_bdaddr);
|
||||
}
|
||||
|
||||
// Connect to a Wiimote with a known address.
|
||||
bool WiimoteLinux::ConnectInternal()
|
||||
{
|
||||
sockaddr_l2 addr = {};
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_bdaddr = m_bdaddr;
|
||||
addr.l2_cid = 0;
|
||||
sockaddr_l2 addr{
|
||||
.l2_family = AF_BLUETOOTH,
|
||||
.l2_bdaddr = std::bit_cast<bdaddr_t>(m_bdaddr),
|
||||
.l2_cid = 0,
|
||||
};
|
||||
|
||||
// Control channel
|
||||
addr.l2_psm = htobs(L2CAP_PSM_HID_CNTL);
|
||||
if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
|
||||
{
|
||||
int retry = 0;
|
||||
while (connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
const auto open_channel = [&](u16 l2_psm) {
|
||||
addr.l2_psm = htobs(l2_psm);
|
||||
|
||||
constexpr int total_tries = 3;
|
||||
for (int i = 0; i != total_tries; ++i)
|
||||
{
|
||||
// If opening channel fails sleep and try again
|
||||
if (retry == 3)
|
||||
const int descriptor = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
||||
if (descriptor == -1)
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Unable to connect control channel of Wiimote: {}", strerror(errno));
|
||||
close(m_cmd_sock);
|
||||
m_cmd_sock = -1;
|
||||
return false;
|
||||
WARN_LOG_FMT(WIIMOTE, "Failed to create L2CAP socket: {}", Common::LastStrerrorString());
|
||||
return -1;
|
||||
}
|
||||
retry++;
|
||||
sleep(1);
|
||||
|
||||
if (connect(descriptor, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) == 0)
|
||||
return descriptor;
|
||||
|
||||
// If connecting fails sleep and try again.
|
||||
WARN_LOG_FMT(WIIMOTE, "Failed to connect L2CAP PSM({}) socket: {}", l2_psm,
|
||||
Common::LastStrerrorString());
|
||||
// A socket state is unspecified after connect() fails, so close and recreate it.
|
||||
close(descriptor);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{500});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Unable to open control socket to Wiimote: {}", strerror(errno));
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
m_cmd_sock = open_channel(L2CAP_PSM_HID_CNTL);
|
||||
if (m_cmd_sock == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Interrupt channel
|
||||
addr.l2_psm = htobs(L2CAP_PSM_HID_INTR);
|
||||
if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
|
||||
m_int_sock = open_channel(L2CAP_PSM_HID_INTR);
|
||||
if (m_int_sock == -1)
|
||||
{
|
||||
int retry = 0;
|
||||
while (connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
// If opening channel fails sleep and try again
|
||||
if (retry == 3)
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Unable to connect interrupt channel of Wiimote: {}",
|
||||
strerror(errno));
|
||||
close(m_int_sock);
|
||||
close(m_cmd_sock);
|
||||
m_int_sock = m_cmd_sock = -1;
|
||||
return false;
|
||||
}
|
||||
retry++;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_FMT(WIIMOTE, "Unable to open interrupt socket to Wiimote: {}", strerror(errno));
|
||||
close(m_cmd_sock);
|
||||
m_int_sock = m_cmd_sock = -1;
|
||||
close(std::exchange(m_cmd_sock, -1));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -224,11 +321,8 @@ bool WiimoteLinux::ConnectInternal()
|
||||
|
||||
void WiimoteLinux::DisconnectInternal()
|
||||
{
|
||||
close(m_cmd_sock);
|
||||
close(m_int_sock);
|
||||
|
||||
m_cmd_sock = -1;
|
||||
m_int_sock = -1;
|
||||
close(std::exchange(m_cmd_sock, -1));
|
||||
close(std::exchange(m_int_sock, -1));
|
||||
}
|
||||
|
||||
bool WiimoteLinux::IsConnected() const
|
||||
@@ -238,10 +332,10 @@ bool WiimoteLinux::IsConnected() const
|
||||
|
||||
void WiimoteLinux::IOWakeup()
|
||||
{
|
||||
char c = 0;
|
||||
if (write(m_wakeup_pipe_w, &c, 1) != 1)
|
||||
u64 counter = 1;
|
||||
if (write(m_wakeup_fd, &counter, sizeof(counter)) != sizeof(counter))
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Unable to write to wakeup pipe.");
|
||||
ERROR_LOG_FMT(WIIMOTE, "failed to write to wakeup eventfd: {}", Common::LastStrerrorString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,60 +344,47 @@ void WiimoteLinux::IOWakeup()
|
||||
// zero = error
|
||||
int WiimoteLinux::IORead(u8* buf)
|
||||
{
|
||||
std::array<pollfd, 2> pollfds = {};
|
||||
std::array<pollfd, 2> pollfds{
|
||||
pollfd{.fd = m_wakeup_fd, .events = POLLIN},
|
||||
pollfd{.fd = m_int_sock, .events = POLLIN},
|
||||
};
|
||||
UnixUtil::RetryOnEINTR(poll, pollfds.data(), pollfds.size(), -1);
|
||||
|
||||
auto& poll_wakeup = pollfds[0];
|
||||
poll_wakeup.fd = m_wakeup_pipe_r;
|
||||
poll_wakeup.events = POLLIN;
|
||||
|
||||
auto& poll_sock = pollfds[1];
|
||||
poll_sock.fd = m_int_sock;
|
||||
poll_sock.events = POLLIN;
|
||||
|
||||
if (poll(pollfds.data(), pollfds.size(), -1) == -1)
|
||||
// Handle IOWakeup.
|
||||
if (pollfds[0].revents != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Unable to poll Wiimote {} input socket.", m_index + 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (poll_wakeup.revents & POLLIN)
|
||||
{
|
||||
char c;
|
||||
if (read(m_wakeup_pipe_r, &c, 1) != 1)
|
||||
DEBUG_LOG_FMT(WIIMOTE, "IOWakeup");
|
||||
u64 counter{};
|
||||
if (read(m_wakeup_fd, &counter, sizeof(counter)) != sizeof(counter))
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Unable to read from wakeup pipe.");
|
||||
ERROR_LOG_FMT(WIIMOTE, "Failed to read from wakeup eventfd: {}",
|
||||
Common::LastStrerrorString());
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(poll_sock.revents & POLLIN))
|
||||
return -1;
|
||||
|
||||
// Read the pending message into the buffer
|
||||
int r = read(m_int_sock, buf, MAX_PAYLOAD);
|
||||
if (r == -1)
|
||||
// Handle event on interrupt channel.
|
||||
auto result = int(read(m_int_sock, buf, MAX_PAYLOAD));
|
||||
if (result == -1)
|
||||
{
|
||||
// Error reading data
|
||||
ERROR_LOG_FMT(WIIMOTE, "Receiving data from Wiimote {}.", m_index + 1);
|
||||
|
||||
if (errno == ENOTCONN)
|
||||
{
|
||||
// This can happen if the Bluetooth dongle is disconnected
|
||||
ERROR_LOG_FMT(WIIMOTE,
|
||||
"Bluetooth appears to be disconnected. "
|
||||
"Wiimote {} will be disconnected.",
|
||||
m_index + 1);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
ERROR_LOG_FMT(WIIMOTE, "Wiimote {} read failed: {}", m_index + 1, Common::LastStrerrorString());
|
||||
result = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
return result;
|
||||
}
|
||||
|
||||
int WiimoteLinux::IOWrite(u8 const* buf, size_t len)
|
||||
{
|
||||
return write(m_int_sock, buf, (int)len);
|
||||
auto result = int(write(m_int_sock, buf, int(len)));
|
||||
if (result == -1)
|
||||
{
|
||||
ERROR_LOG_FMT(WIIMOTE, "Wiimote {} write failed: {}", m_index + 1,
|
||||
Common::LastStrerrorString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}; // namespace WiimoteReal
|
||||
} // namespace WiimoteReal
|
||||
|
@@ -4,8 +4,10 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__linux__) && HAVE_BLUEZ
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "Common/Network.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
|
||||
namespace WiimoteReal
|
||||
@@ -13,14 +15,10 @@ namespace WiimoteReal
|
||||
class WiimoteLinux final : public Wiimote
|
||||
{
|
||||
public:
|
||||
WiimoteLinux(bdaddr_t bdaddr);
|
||||
explicit WiimoteLinux(Common::BluetoothAddress bdaddr);
|
||||
~WiimoteLinux() override;
|
||||
std::string GetId() const override
|
||||
{
|
||||
char bdaddr_str[18] = {};
|
||||
ba2str(&m_bdaddr, bdaddr_str);
|
||||
return bdaddr_str;
|
||||
}
|
||||
|
||||
std::string GetId() const override;
|
||||
|
||||
protected:
|
||||
bool ConnectInternal() override;
|
||||
@@ -31,11 +29,10 @@ protected:
|
||||
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;
|
||||
const Common::BluetoothAddress m_bdaddr;
|
||||
const int m_wakeup_fd{-1}; // Used to kick the read thread.
|
||||
int m_cmd_sock{-1}; // Command socket
|
||||
int m_int_sock{-1}; // Interrupt socket
|
||||
};
|
||||
|
||||
class WiimoteScannerLinux final : public WiimoteScannerBackend
|
||||
@@ -43,16 +40,23 @@ 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
|
||||
void RequestStopSearching() override {} // not needed on Linux
|
||||
private:
|
||||
int m_device_id;
|
||||
int m_device_sock;
|
||||
void Update() override;
|
||||
void RequestStopSearching() override;
|
||||
|
||||
void AddAutoConnectAddresses(std::vector<Wiimote*>&);
|
||||
private:
|
||||
bool Open();
|
||||
void Close();
|
||||
|
||||
int m_device_id{-1};
|
||||
int m_device_sock{-1};
|
||||
|
||||
// FYI: Atomic because UI calls IsReady.
|
||||
std::atomic<bool> m_is_device_open{};
|
||||
};
|
||||
|
||||
} // namespace WiimoteReal
|
||||
|
||||
#else
|
||||
|
@@ -175,7 +175,10 @@ class WiimoteScannerBackend
|
||||
{
|
||||
public:
|
||||
virtual ~WiimoteScannerBackend() = default;
|
||||
|
||||
// Note: Invoked from UI thread.
|
||||
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;
|
||||
|
Reference in New Issue
Block a user