@@ -63,29 +63,32 @@ void Wiimote::InvokeHandler(H&& handler, const WiimoteCommon::OutputReportGeneri
(this->*handler)(Common::BitCastPtr<T>(rpt.data));
}

// Here we process the Output Reports that the Wii sends. Our response will be
// an Input Report back to the Wii. Input and Output is from the Wii's
// perspective, Output means data to the Wiimote (from the Wii), Input means
// data from the Wiimote.
//
// The call browser:
//
// 1. Wiimote_InterruptChannel > InterruptChannel > HIDOutputReport
// 2. Wiimote_ControlChannel > ControlChannel > HIDOutputReport

void Wiimote::HIDOutputReport(const void* data, u32 size)
void Wiimote::EventLinked()
{
Reset();
}

void Wiimote::EventUnlinked()
{
Reset();
}

void Wiimote::InterruptDataOutput(const u8* data, u32 size)
{
if (!size)
{
ERROR_LOG(WIIMOTE, "HIDOutputReport: zero sized data");
ERROR_LOG(WIIMOTE, "OutputData: zero sized data");
return;
}

auto& rpt = *static_cast<const OutputReportGeneric*>(data);
auto& rpt = *reinterpret_cast<const OutputReportGeneric*>(data);
const int rpt_size = size - OutputReportGeneric::HEADER_SIZE;

DEBUG_LOG(WIIMOTE, "HIDOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index,
m_reporting_channel, int(rpt.rpt_id));
if (!rpt_size)
{
ERROR_LOG(WIIMOTE, "OutputData: zero sized report");
return;
}

// WiiBrew:
// In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature.
@@ -132,21 +135,16 @@ void Wiimote::HIDOutputReport(const void* data, u32 size)
}
}

void Wiimote::CallbackInterruptChannel(const u8* data, u32 size)
{
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, size);
}

void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code)
{
TypedHIDInputData<InputReportAck> rpt(InputReportID::Ack);
auto& ack = rpt.data;
TypedInputData<InputReportAck> rpt(InputReportID::Ack);
auto& ack = rpt.payload;

ack.buttons = m_status.buttons;
ack.rpt_id = rpt_id;
ack.error_code = error_code;

CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
}

void Wiimote::HandleExtensionSwap()
@@ -246,9 +244,9 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&)
// Less than 0x20 triggers the low-battery flag:
m_status.battery_low = m_status.battery < 0x20;

TypedHIDInputData<InputReportStatus> rpt(InputReportID::Status);
rpt.data = m_status;
CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
TypedInputData<InputReportStatus> rpt(InputReportID::Status);
rpt.payload = m_status;
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
}

void Wiimote::HandleWriteData(const OutputReportWriteData& wd)
@@ -442,8 +440,8 @@ bool Wiimote::ProcessReadDataRequest()
return false;
}

TypedHIDInputData<InputReportReadDataReply> rpt(InputReportID::ReadDataReply);
auto& reply = rpt.data;
TypedInputData<InputReportReadDataReply> rpt(InputReportID::ReadDataReply);
auto& reply = rpt.payload;

reply.buttons = m_status.buttons;
reply.address = Common::swap16(m_read_request.address);
@@ -539,7 +537,7 @@ bool Wiimote::ProcessReadDataRequest()

reply.error = static_cast<u8>(error_code);

CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());

return true;
}
@@ -552,7 +550,6 @@ void Wiimote::DoState(PointerWrap& p)
// No need to sync. This is not wiimote state.
// p.Do(m_sensor_bar_on_top);

p.Do(m_reporting_channel);
p.Do(m_reporting_mode);
p.Do(m_reporting_continuous);

@@ -72,7 +72,6 @@ void Wiimote::Reset()
SetRumble(false);

// Wiimote starts in non-continuous CORE mode:
m_reporting_channel = 0;
m_reporting_mode = InputReportID::ReportCore;
m_reporting_continuous = false;

@@ -404,10 +403,6 @@ void Wiimote::UpdateButtonsStatus()
// This is called every ::Wiimote::UPDATE_FREQ (200hz)
void Wiimote::Update()
{
// Check if connected.
if (0 == m_reporting_channel)
return;

const auto lock = GetStateLock();

// Hotkey / settings modifier
@@ -567,7 +562,7 @@ void Wiimote::SendDataReport()
Movie::CheckWiimoteStatus(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey());

// Send the report:
CallbackInterruptChannel(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());
InterruptDataInputCallback(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());

// The interleaved reporting modes toggle back and forth:
if (InputReportID::ReportInterleave1 == m_reporting_mode)
@@ -576,97 +571,7 @@ void Wiimote::SendDataReport()
m_reporting_mode = InputReportID::ReportInterleave1;
}

void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
{
// Check for custom communication
if (channel_id == ::Wiimote::DOLPHIN_DISCONNET_CONTROL_CHANNEL)
{
// Wii Remote disconnected.
Reset();

return;
}

if (!size)
{
ERROR_LOG(WIIMOTE, "ControlChannel: zero sized data");
return;
}

m_reporting_channel = channel_id;

const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);

DEBUG_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index,
hidp.type, hidp.param);

switch (hidp.type)
{
case HID_TYPE_HANDSHAKE:
PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT");
break;

case HID_TYPE_SET_REPORT:
if (HID_PARAM_INPUT == hidp.param)
{
PanicAlert("HID_TYPE_SET_REPORT - INPUT");
}
else
{
// AyuanX: My experiment shows Control Channel is never used
// shuffle2: but lwbt uses this, so we'll do what we must :)
HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE);

// TODO: Should this be above the previous?
u8 handshake = HID_HANDSHAKE_SUCCESS;
CallbackInterruptChannel(&handshake, sizeof(handshake));
}
break;

case HID_TYPE_DATA:
PanicAlert("HID_TYPE_DATA - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT");
break;

default:
PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp.type, hidp.param);
break;
}
}

void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size)
{
if (!size)
{
ERROR_LOG(WIIMOTE, "InterruptChannel: zero sized data");
return;
}

m_reporting_channel = channel_id;

const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);

switch (hidp.type)
{
case HID_TYPE_DATA:
switch (hidp.param)
{
case HID_PARAM_OUTPUT:
HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE);
break;

default:
PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp.param);
break;
}
break;

default:
PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp.type, hidp.param);
break;
}
}

bool Wiimote::CheckForButtonPress()
bool Wiimote::IsButtonPressed()
{
u16 buttons = 0;
const auto lock = GetStateLock();
@@ -85,7 +85,7 @@ void UpdateCalibrationDataChecksum(T& data, int cksum_bytes)
}
}

class Wiimote : public ControllerEmu::EmulatedController
class Wiimote : public ControllerEmu::EmulatedController, public WiimoteCommon::HIDWiimote
{
public:
static constexpr u16 IR_LOW_X = 0x7F;
@@ -124,12 +124,12 @@ class Wiimote : public ControllerEmu::EmulatedController
ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(DrawsomeTabletGroup group) const;
ControllerEmu::ControlGroup* GetTaTaConGroup(TaTaConGroup group) const;

void Update();
void StepDynamics();
void Update() override;
void EventLinked() override;
void EventUnlinked() override;
void InterruptDataOutput(const u8* data, u32 size) override;
bool IsButtonPressed() override;

void InterruptChannel(u16 channel_id, const void* data, u32 size);
void ControlChannel(u16 channel_id, const void* data, u32 size);
bool CheckForButtonPress();
void Reset();

void DoState(PointerWrap& p);
@@ -145,6 +145,7 @@ class Wiimote : public ControllerEmu::EmulatedController
// This is the region exposed over bluetooth:
static constexpr int EEPROM_FREE_SIZE = 0x1700;

void StepDynamics();
void UpdateButtonsStatus();

// Returns simulated accelerometer data in m/s^2.
@@ -167,8 +168,6 @@ class Wiimote : public ControllerEmu::EmulatedController
Common::Vec3 GetTotalAngularVelocity() const;
Common::Matrix44 GetTotalTransformation() const;

void HIDOutputReport(const void* data, u32 size);

void HandleReportRumble(const WiimoteCommon::OutputReportRumble&);
void HandleReportLeds(const WiimoteCommon::OutputReportLeds&);
void HandleReportMode(const WiimoteCommon::OutputReportMode&);
@@ -191,7 +190,6 @@ class Wiimote : public ControllerEmu::EmulatedController

void SetRumble(bool on);

void CallbackInterruptChannel(const u8* data, u32 size);
void SendAck(WiimoteCommon::OutputReportID rpt_id, WiimoteCommon::ErrorCode err);

bool IsSideways() const;
@@ -276,7 +274,6 @@ class Wiimote : public ControllerEmu::EmulatedController
// Wiimote index, 0-3
const u8 m_index;

u16 m_reporting_channel;
WiimoteCommon::InputReportID m_reporting_mode;
bool m_reporting_continuous;

@@ -15,6 +15,9 @@

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)
{
// Get the id of the first Bluetooth device.
@@ -139,8 +142,8 @@ bool WiimoteLinux::ConnectInternal()
addr.l2_bdaddr = m_bdaddr;
addr.l2_cid = 0;

// Output channel
addr.l2_psm = htobs(WC_OUTPUT);
// Control channel
addr.l2_psm = htobs(L2CAP_PSM_HID_CNTL);
if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
{
int retry = 0;
@@ -149,7 +152,7 @@ bool WiimoteLinux::ConnectInternal()
// If opening channel fails sleep and try again
if (retry == 3)
{
WARN_LOG(WIIMOTE, "Unable to connect output channel to Wiimote: %s", strerror(errno));
WARN_LOG(WIIMOTE, "Unable to connect control channel of Wiimote: %s", strerror(errno));
close(m_cmd_sock);
m_cmd_sock = -1;
return false;
@@ -160,12 +163,12 @@ bool WiimoteLinux::ConnectInternal()
}
else
{
WARN_LOG(WIIMOTE, "Unable to open output socket to Wiimote: %s", strerror(errno));
WARN_LOG(WIIMOTE, "Unable to open control socket to Wiimote: %s", strerror(errno));
return false;
}

// Input channel
addr.l2_psm = htobs(WC_INPUT);
// Interrupt channel
addr.l2_psm = htobs(L2CAP_PSM_HID_INTR);
if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
{
int retry = 0;
@@ -174,7 +177,7 @@ bool WiimoteLinux::ConnectInternal()
// If opening channel fails sleep and try again
if (retry == 3)
{
WARN_LOG(WIIMOTE, "Unable to connect input channel to Wiimote: %s", strerror(errno));
WARN_LOG(WIIMOTE, "Unable to connect interrupt channel of Wiimote: %s", strerror(errno));
close(m_int_sock);
close(m_cmd_sock);
m_int_sock = m_cmd_sock = -1;
@@ -186,7 +189,7 @@ bool WiimoteLinux::ConnectInternal()
}
else
{
WARN_LOG(WIIMOTE, "Unable to open input socket from Wiimote: %s", strerror(errno));
WARN_LOG(WIIMOTE, "Unable to open interrupt socket to Wiimote: %s", strerror(errno));
close(m_cmd_sock);
m_int_sock = m_cmd_sock = -1;
return false;
@@ -49,6 +49,7 @@ static std::mutex s_known_ids_mutex;
std::recursive_mutex g_wiimotes_mutex;

// Real wii remotes assigned to a particular slot.
// Assignments must be done from the CPU thread with the above mutex held.
std::unique_ptr<Wiimote> g_wiimotes[MAX_BBMOTES];

struct WiimotePoolEntry
@@ -129,6 +130,8 @@ void AddWiimoteToPool(std::unique_ptr<Wiimote> wiimote)
return;
}

wiimote->EmuStop();

std::lock_guard lk(g_wiimotes_mutex);
s_wiimote_pool.emplace_back(WiimotePoolEntry{std::move(wiimote)});
}
@@ -221,54 +224,32 @@ void Wiimote::ClearReadQueue()
}
}

void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
void Wiimote::EventLinked()
{
// Check for custom communication
if (channel == ::Wiimote::DOLPHIN_DISCONNET_CONTROL_CHANNEL)
{
if (m_really_disconnect)
{
DisconnectInternal();
}
else
{
EmuStop();
}
}
else
{
InterruptChannel(channel, data, size);
const auto& hidp = *static_cast<const HIDPacket*>(data);
if (hidp.type == HID_TYPE_SET_REPORT)
{
u8 handshake = HID_HANDSHAKE_SUCCESS;
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake, sizeof(handshake));
}
}
m_is_linked = true;

ClearReadQueue();
ResetDataReporting();
EnablePowerAssertionInternal();
}

void Wiimote::InterruptChannel(const u16 channel, const void* const data, const u32 size)
void Wiimote::EventUnlinked()
{
// first interrupt/control channel sent
if (channel != m_channel)
{
m_channel = channel;

ClearReadQueue();

EmuStart();
}
if (m_really_disconnect)
DisconnectInternal();
else
EmuStop();
}

auto const report_data = static_cast<const u8*>(data);
Report rpt(report_data, report_data + size);
void Wiimote::InterruptDataOutput(const u8* data, const u32 size)
{
Report rpt(size + REPORT_HID_HEADER_SIZE);
std::copy_n(data, size, rpt.data() + REPORT_HID_HEADER_SIZE);

// Convert output DATA packets to SET_REPORT packets.
// Nintendo Wiimotes work without this translation, but 3rd
// party ones don't.
if (rpt[0] == 0xa2)
{
rpt[0] = WR_SET_REPORT | BT_OUTPUT;
}
rpt[0] = WR_SET_REPORT | BT_OUTPUT;

// Disallow games from turning off all of the LEDs.
// It makes Wiimote connection status confusing.
@@ -299,7 +280,11 @@ void Wiimote::Read()
Report rpt(MAX_PAYLOAD);
auto const result = IORead(rpt.data());

if (result > 0 && m_channel > 0)
// Drop the report if not connected.
if (!m_is_linked)
return;

if (result > 0)
{
if (SConfig::GetInstance().iBBDumpPort > 0 && m_index == WIIMOTE_BALANCE_BOARD)
{
@@ -456,25 +441,18 @@ Report& Wiimote::ProcessReadQueue()

void Wiimote::Update()
{
if (!IsConnected())
{
HandleWiimoteDisconnect(m_index);
return;
}

// Pop through the queued reports
const Report& rpt = ProcessReadQueue();

// Send the report
if (!rpt.empty() && m_channel > 0)
{
Core::Callback_WiimoteInterruptChannel(m_index, m_channel, rpt.data(), (u32)rpt.size());
}
if (!rpt.empty())
InterruptCallback(rpt.front(), rpt.data() + REPORT_HID_HEADER_SIZE,
u32(rpt.size() - REPORT_HID_HEADER_SIZE));
}

bool Wiimote::CheckForButtonPress()
bool Wiimote::IsButtonPressed()
{
Report& rpt = ProcessReadQueue();
Report& rpt = m_last_input_report;
if (rpt.size() >= 4)
{
const auto mode = InputReportID(rpt[1]);
@@ -512,23 +490,16 @@ bool Wiimote::PrepareOnThread()
(Common::SleepCurrentThread(200), IOWrite(req_status_report, sizeof(req_status_report)));
}

void Wiimote::EmuStart()
{
ResetDataReporting();
EnablePowerAssertionInternal();
}

void Wiimote::EmuStop()
{
m_channel = 0;
m_is_linked = false;

ResetDataReporting();
DisablePowerAssertionInternal();
}

void Wiimote::EmuResume()
{
m_last_input_report.clear();

EnablePowerAssertionInternal();
}

@@ -811,11 +782,6 @@ int Wiimote::GetIndex() const
return m_index;
}

void Wiimote::SetChannel(u16 channel)
{
m_channel = channel;
}

void LoadSettings()
{
std::string ini_filename = File::GetUserPath(D_CONFIG_IDX) + WIIMOTE_INI_NAME ".ini";
@@ -933,8 +899,10 @@ static bool TryToConnectWiimoteToSlot(std::unique_ptr<Wiimote>& wm, unsigned int
led_report.leds = u8(1 << (i % WIIMOTE_BALANCE_BOARD));
wm->QueueReport(led_report);

g_wiimotes[i] = std::move(wm);
Core::RunAsCPUThread([i] { ::Wiimote::Connect(i, true); });
Core::RunAsCPUThread([i, &wm] {
g_wiimotes[i] = std::move(wm);
WiimoteCommon::UpdateSource(i);
});

NOTICE_LOG(WIIMOTE, "Connected real wiimote to slot %i.", i + 1);

@@ -951,7 +919,10 @@ static void TryToConnectBalanceBoard(std::unique_ptr<Wiimote> wm)

static void HandleWiimoteDisconnect(int index)
{
g_wiimotes[index] = nullptr;
Core::RunAsCPUThread([index] {
g_wiimotes[index] = nullptr;
WiimoteCommon::UpdateSource(index);
});
}

// This is called from the GUI thread
@@ -961,52 +932,6 @@ void Refresh()
s_wiimote_scanner.SetScanMode(WiimoteScanMode::SCAN_ONCE);
}

void InterruptChannel(int wiimote_number, u16 channel_id, const void* data, u32 size)
{
std::lock_guard lk(g_wiimotes_mutex);
if (g_wiimotes[wiimote_number])
g_wiimotes[wiimote_number]->InterruptChannel(channel_id, data, size);
}

void ControlChannel(int wiimote_number, u16 channel_id, const void* data, u32 size)
{
std::lock_guard lk(g_wiimotes_mutex);
if (g_wiimotes[wiimote_number])
g_wiimotes[wiimote_number]->ControlChannel(channel_id, data, size);
}

// Read the Wiimote once
void Update(int wiimote_number)
{
// Try to get a lock and return without doing anything if we fail
// This avoids blocking the CPU thread
if (!g_wiimotes_mutex.try_lock())
return;

if (g_wiimotes[wiimote_number])
g_wiimotes[wiimote_number]->Update();

g_wiimotes_mutex.unlock();

// Wiimote::Update() may remove the Wiimote if it was disconnected.
if (!g_wiimotes[wiimote_number])
::Wiimote::Connect(wiimote_number, false);
}

bool CheckForButtonPress(int wiimote_number)
{
if (!g_wiimotes_mutex.try_lock())
return false;

bool button_pressed = false;

if (g_wiimotes[wiimote_number])
button_pressed = g_wiimotes[wiimote_number]->CheckForButtonPress();

g_wiimotes_mutex.unlock();
return button_pressed;
}

bool IsValidDeviceName(const std::string& name)
{
return "Nintendo RVL-CNT-01" == name || "Nintendo RVL-CNT-01-TR" == name ||
@@ -1029,9 +954,10 @@ void HandleWiimoteSourceChange(unsigned int index)
{
std::lock_guard wm_lk(g_wiimotes_mutex);

if (auto removed_wiimote = std::move(g_wiimotes[index]))
AddWiimoteToPool(std::move(removed_wiimote));

Core::RunAsCPUThread([index] {
if (auto removed_wiimote = std::move(g_wiimotes[index]))
AddWiimoteToPool(std::move(removed_wiimote));
});
ProcessWiimotePool();
}

@@ -26,14 +26,12 @@ namespace WiimoteReal
{
using WiimoteCommon::MAX_PAYLOAD;

// Includes HID "type" header byte.
using Report = std::vector<u8>;
constexpr int REPORT_HID_HEADER_SIZE = 1;

constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000;

// Communication channels
constexpr u8 WC_OUTPUT = 0x11;
constexpr u8 WC_INPUT = 0x13;

// The 4 most significant bits of the first byte of an outgoing command must be
// 0x50 if sending on the command channel and 0xA0 if sending on the interrupt
// channel. On Mac and Linux we use interrupt channel; on Windows, command.
@@ -46,7 +44,7 @@ constexpr u8 WR_SET_REPORT = 0xA0;
constexpr u8 BT_INPUT = 0x01;
constexpr u8 BT_OUTPUT = 0x02;

class Wiimote
class Wiimote : public WiimoteCommon::HIDWiimote
{
public:
Wiimote(const Wiimote&) = delete;
@@ -60,45 +58,25 @@ class Wiimote

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();
bool CheckForButtonPress();

bool GetNextReport(Report* report);
Report& ProcessReadQueue();

void Read();
bool Write();

bool IsBalanceBoard();

void StartThread();
void StopThread();
void InterruptDataOutput(const u8* data, const u32 size) override;
void Update() override;
void EventLinked() override;
void EventUnlinked() override;
bool IsButtonPressed() override;

// "handshake" / stop packets
void EmuStart();
void EmuStop();

void EmuResume();
void EmuPause();

virtual void EnablePowerAssertionInternal() {}
virtual void DisablePowerAssertionInternal() {}
// connecting and disconnecting from physical devices
// (using address inserted by FindWiimotes)
// these are called from the Wiimote's thread.
virtual bool ConnectInternal() = 0;
virtual void DisconnectInternal() = 0;

bool Connect(int index);

// TODO: change to something like IsRelevant
virtual bool IsConnected() const = 0;

void Prepare();
bool PrepareOnThread();

void ResetDataReporting();
virtual bool IsConnected() const = 0;

void QueueReport(WiimoteCommon::OutputReportID rpt_id, const void* data, unsigned int size);

@@ -110,21 +88,35 @@ class Wiimote

int GetIndex() const;

void SetChannel(u16 channel);

protected:
Wiimote();

int m_index = 0;
Report m_last_input_report;
u16 m_channel = 0;

// If true, the Wiimote will be really disconnected when it is disconnected by Dolphin.
// In any other case, data reporting is not paused to allow reconnecting on any button press.
// This is not enabled on all platforms as connecting a Wiimote can be a pain on some platforms.
bool m_really_disconnect = false;

private:
void Read();
bool Write();

void StartThread();
void StopThread();

bool PrepareOnThread();

void ResetDataReporting();

virtual void EnablePowerAssertionInternal() {}
virtual void DisablePowerAssertionInternal() {}

virtual bool ConnectInternal() = 0;
virtual void DisconnectInternal() = 0;

Report& ProcessReadQueue();
void ClearReadQueue();
void WriteReport(Report rpt);

@@ -134,6 +126,8 @@ class Wiimote

void ThreadFunc();

bool m_is_linked = false;

// We track the speaker state to convert unnecessary speaker data into rumble reports.
bool m_speaker_enable = false;
bool m_speaker_mute = false;
@@ -199,11 +193,6 @@ extern std::unique_ptr<Wiimote> g_wiimotes[MAX_BBMOTES];

void AddWiimoteToPool(std::unique_ptr<Wiimote>);

void InterruptChannel(int wiimote_number, u16 channel_id, const void* data, u32 size);
void ControlChannel(int wiimote_number, u16 channel_id, const void* data, u32 size);
void Update(int wiimote_number);
bool CheckForButtonPress(int wiimote_number);

bool IsValidDeviceName(const std::string& name);
bool IsBalanceBoardName(const std::string& name);
bool IsNewWiimote(const std::string& identifier);

Large diffs are not rendered by default.

@@ -52,16 +52,18 @@ class BluetoothEmu final : public BluetoothBase
void Update() override;

// Send ACL data back to Bluetooth stack
void SendACLPacket(u16 connection_handle, const u8* data, u32 size);
void SendACLPacket(const bdaddr_t& source, const u8* data, u32 size);

bool RemoteDisconnect(u16 connection_handle);
// Returns true if controller is configured to see the connection request.
bool RemoteConnect(WiimoteDevice&);
bool RemoteDisconnect(const bdaddr_t& address);

WiimoteDevice* AccessWiimoteByIndex(std::size_t index);

void DoState(PointerWrap& p) override;

private:
std::vector<WiimoteDevice> m_wiimotes;
std::vector<std::unique_ptr<WiimoteDevice>> m_wiimotes;

bdaddr_t m_controller_bd{{0x11, 0x02, 0x19, 0x79, 0x00, 0xff}};

@@ -100,9 +102,13 @@ class BluetoothEmu final : public BluetoothBase
u32 m_packet_count[MAX_BBMOTES] = {};
u64 m_last_ticks = 0;

static u16 GetConnectionHandle(const bdaddr_t&);

WiimoteDevice* AccessWiimote(const bdaddr_t& address);
WiimoteDevice* AccessWiimote(u16 connection_handle);

static u32 GetWiimoteNumberFromConnectionHandle(u16 connection_handle);

// Send ACL data to a device (wiimote)
void IncDataPacket(u16 connection_handle);
void SendToDevice(u16 connection_handle, u8* data, u32 size);
@@ -112,10 +118,10 @@ class BluetoothEmu final : public BluetoothBase
bool SendEventCommandStatus(u16 opcode);
void SendEventCommandComplete(u16 opcode, const void* data, u32 data_size);
bool SendEventInquiryResponse();
bool SendEventInquiryComplete();
bool SendEventInquiryComplete(u8 num_responses);
bool SendEventRemoteNameReq(const bdaddr_t& bd);
bool SendEventRequestConnection(const WiimoteDevice& wiimote);
bool SendEventConnectionComplete(const bdaddr_t& bd);
bool SendEventConnectionComplete(const bdaddr_t& bd, u8 status);
bool SendEventReadClockOffsetComplete(u16 connection_handle);
bool SendEventConPacketTypeChange(u16 connection_handle, u16 packet_type);
bool SendEventReadRemoteVerInfo(u16 connection_handle);
@@ -176,8 +182,6 @@ class BluetoothEmu final : public BluetoothBase
void CommandVendorSpecific_FC4C(const u8* input, u32 size);
void CommandVendorSpecific_FC4F(const u8* input, u32 size);

static void DisplayDisconnectMessage(int wiimote_number, int reason);

#pragma pack(push, 1)
#define CONF_PAD_MAX_REGISTERED 10

Large diffs are not rendered by default.

@@ -9,6 +9,7 @@
#include <string>

#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/IOS/USB/Bluetooth/hci.h"

class PointerWrap;
@@ -23,98 +24,136 @@ class BluetoothEmu;
class WiimoteDevice
{
public:
WiimoteDevice(Device::BluetoothEmu* host, int number, bdaddr_t bd, bool ready = false);
using ClassType = std::array<u8, HCI_CLASS_SIZE>;
using FeaturesType = std::array<u8, HCI_FEATURES_SIZE>;
using LinkKeyType = std::array<u8, HCI_KEY_SIZE>;

WiimoteDevice(Device::BluetoothEmu* host, int number, bdaddr_t bd);
~WiimoteDevice();

WiimoteDevice(const WiimoteDevice&) = delete;
WiimoteDevice& operator=(const WiimoteDevice&) = delete;
WiimoteDevice(WiimoteDevice&&) = delete;
WiimoteDevice& operator=(WiimoteDevice&&) = delete;

void Reset();

// Called every BluetoothEmu::Update.
void Update();

// Called every ~200hz.
void UpdateInput();

void DoState(PointerWrap& p);

// ugly Host handling....
// we really have to clean all this code
bool IsInquiryScanEnabled() const;
bool IsPageScanEnabled() const;

u32 GetNumber() const;

bool IsConnected() const { return m_connection_state == ConnectionState::Complete; }
bool IsInactive() const { return m_connection_state == ConnectionState::Inactive; }
bool LinkChannel();
void ResetChannels();
bool IsSourceValid() const;
bool IsConnected() const;

// User-initiated. Produces UI messages.
void Activate(bool ready);
void ExecuteL2capCmd(u8* ptr, u32 size); // From CPU
void ReceiveL2capData(u16 scid, const void* data, u32 size); // From Wiimote

void EventConnectionAccepted();
void EventDisconnect();
bool EventPagingChanged(u8 page_mode) const;
// From CPU
void ExecuteL2capCmd(u8* ptr, u32 size);
// From Wiimote
void InterruptDataInputCallback(u8 hid_type, const u8* data, u32 size);

bool EventConnectionAccept();
bool EventConnectionRequest();
void EventDisconnect(u8 reason);

// nullptr may be passed to disable the remote.
void SetSource(WiimoteCommon::HIDWiimote*);

const bdaddr_t& GetBD() const { return m_bd; }
const u8* GetClass() const { return m_uclass; }
u16 GetConnectionHandle() const { return m_connection_handle; }
const u8* GetFeatures() const { return m_features; }
const char* GetName() const { return m_name.c_str(); }
u8 GetLMPVersion() const { return m_lmp_version; }
u16 GetLMPSubVersion() const { return m_lmp_subversion; }
u16 GetManufactorID() const { return 0x000F; } // Broadcom Corporation
const u8* GetLinkKey() const { return m_link_key; }
// Broadcom Corporation
u16 GetManufactorID() const { return 0x000F; }
const ClassType& GetClass() const { return m_class; }
const FeaturesType& GetFeatures() const { return m_features; }
const LinkKeyType& GetLinkKey() const { return m_link_key; }

private:
enum class ConnectionState
enum class BasebandState
{
Inactive,
RequestConnection,
Complete,
};

enum class HIDState
{
Inactive = -1,
Ready,
Inactive,
Linking,
Complete
};

struct HIDChannelState
struct SChannel
{
bool connected = false;
bool connected_wait = false;
bool config = false;
bool config_wait = false;
enum class State
{
Inactive,
ConfigurationPending,
Complete,
};

SChannel();

bool IsAccepted() const;
bool IsRemoteConfigured() const;
bool IsComplete() const;

State state = State::Inactive;
u16 psm;
u16 remote_cid;
u16 remote_mtu = 0;
};

ConnectionState m_connection_state;
using ChannelMap = std::map<u16, SChannel>;

HIDChannelState m_hid_control_channel;
HIDChannelState m_hid_interrupt_channel;
Device::BluetoothEmu* m_host;
WiimoteCommon::HIDWiimote* m_hid_source = nullptr;

// STATE_TO_SAVE
// State to save:
BasebandState m_baseband_state = BasebandState::Inactive;
HIDState m_hid_state = HIDState::Inactive;
bdaddr_t m_bd;
u16 m_connection_handle;
u8 m_uclass[HCI_CLASS_SIZE];
u8 m_features[HCI_FEATURES_SIZE];
ClassType m_class;
FeaturesType m_features;
u8 m_lmp_version;
u16 m_lmp_subversion;
u8 m_link_key[HCI_KEY_SIZE];
LinkKeyType m_link_key;
std::string m_name;
Device::BluetoothEmu* m_host;
ChannelMap m_channels;
u8 m_connection_request_counter = 0;

struct SChannel
{
u16 scid;
u16 dcid;
u16 psm;
void SetBasebandState(BasebandState);

u16 mtu;
u16 flush_time_out;
};
const SChannel* FindChannelWithPSM(u16 psm) const;
SChannel* FindChannelWithPSM(u16 psm);

typedef std::map<u32, SChannel> CChannelMap;
CChannelMap m_channel;
bool LinkChannel(u16 psm);
u16 GenerateChannelID() const;

bool DoesChannelExist(u16 scid) const { return m_channel.find(scid) != m_channel.end(); }
bool DoesChannelExist(u16 scid) const { return m_channels.count(scid) != 0; }
void SendCommandToACL(u8 ident, u8 code, u8 command_length, u8* command_data);

void SignalChannel(u8* data, u32 size);

void SendConnectionRequest(u16 scid, u16 psm);
void SendConfigurationRequest(u16 scid, u16 mtu = 0, u16 flush_time_out = 0);
void SendDisconnectRequest(u16 scid);
void SendConnectionRequest(u16 psm);
void SendConfigurationRequest(u16 cid, u16 mtu, u16 flush_time_out);

void ReceiveConnectionReq(u8 ident, u8* data, u32 size);
void ReceiveConnectionResponse(u8 ident, u8* data, u32 size);
void ReceiveDisconnectionReq(u8 ident, u8* data, u32 size);
void ReceiveConfigurationReq(u8 ident, u8* data, u32 size);
void ReceiveConfigurationResponse(u8 ident, u8* data, u32 size);

// some new ugly stuff
// should be inside the plugin
void HandleSDP(u16 cid, u8* data, u32 size);
void SDPSendServiceSearchResponse(u16 cid, u16 transaction_id, u8* service_search_pattern,
u16 maximum_service_record_count);
@@ -2652,6 +2652,7 @@ struct SHCIEventInquiryComplete
u8 EventType;
u8 PayloadLength;
u8 EventStatus;
u8 num_responses;
};

struct SHCIEventReadClockOffsetComplete
@@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 122; // Last changed in PR 8571
constexpr u32 STATE_VERSION = 123; // Last changed in PR 8985

// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
@@ -1706,10 +1706,12 @@ void MainWindow::OnConnectWiiRemote(int id)
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
return;
Core::RunAsCPUThread([&] {
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
const bool is_connected = bt && bt->AccessWiimoteByIndex(id)->IsConnected();
Wiimote::Connect(id, !is_connected);
if (const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
ios->GetDeviceByName("/dev/usb/oh1/57e/305")))
{
const auto wm = bt->AccessWiimoteByIndex(id);
wm->Activate(!wm->IsConnected());
}
});
}

@@ -98,7 +98,7 @@ void Device::QueueReport(T&& report, std::function<void(ErrorCode)> ack_callback
// Maintain proper rumble state.
report.rumble = m_rumble;

m_wiimote->QueueReport(std::forward<T>(report));
m_wiimote->QueueReport(report.REPORT_ID, &report, sizeof(report));

if (ack_callback)
AddReportHandler(MakeAckHandler(report.REPORT_ID, std::move(ack_callback)));
@@ -117,9 +117,7 @@ void AddDevice(std::unique_ptr<WiimoteReal::Wiimote> wiimote)
}

wiimote->Prepare();

// Our silly real wiimote interface needs a non-zero "channel" to not drop input reports.
wiimote->SetChannel(26);
wiimote->EventLinked();

g_controller_interface.AddDevice(std::make_shared<Device>(std::move(wiimote)));
}
@@ -1063,22 +1061,22 @@ bool Device::IsMotionPlusInDesiredMode() const

void Device::ProcessInputReport(WiimoteReal::Report& report)
{
if (report.size() < WiimoteCommon::DataReportBuilder::HEADER_SIZE)
if (report.size() < WiimoteReal::REPORT_HID_HEADER_SIZE)
{
WARN_LOG(WIIMOTE, "WiiRemote: Bad report size.");
return;
}

auto report_id = InputReportID(report[1]);
auto report_id = InputReportID(report[WiimoteReal::REPORT_HID_HEADER_SIZE]);

for (auto it = m_report_handlers.begin(); true;)
{
if (it == m_report_handlers.end())
{
if (report_id == InputReportID::Status)
{
if (report.size() <
sizeof(InputReportStatus) + WiimoteCommon::DataReportBuilder::HEADER_SIZE)
if (report.size() - WiimoteReal::REPORT_HID_HEADER_SIZE <
sizeof(TypedInputData<InputReportStatus>))
{
WARN_LOG(WIIMOTE, "WiiRemote: Bad report size.");
}
@@ -1126,9 +1124,9 @@ void Device::ProcessInputReport(WiimoteReal::Report& report)
}

auto manipulator = MakeDataReportManipulator(
report_id, report.data() + WiimoteCommon::DataReportBuilder::HEADER_SIZE);
report_id, report.data() + WiimoteReal::REPORT_HID_HEADER_SIZE + sizeof(InputReportID));

if (manipulator->GetDataSize() + WiimoteCommon::DataReportBuilder::HEADER_SIZE > report.size())
if (manipulator->GetDataSize() + WiimoteReal::REPORT_HID_HEADER_SIZE > report.size())
{
WARN_LOG(WIIMOTE, "WiiRemote: Bad report size.");
return;
@@ -1535,24 +1533,24 @@ template <typename R, typename T>
void Device::ReportHandler::AddHandler(std::function<R(const T&)> handler)
{
m_callbacks.emplace_back([handler = std::move(handler)](const WiimoteReal::Report& report) {
if (report[1] != u8(T::REPORT_ID))
if (report[WiimoteReal::REPORT_HID_HEADER_SIZE] != u8(T::REPORT_ID))
return ReportHandler::HandlerResult::NotHandled;

T data;

if (report.size() < sizeof(T) + WiimoteCommon::DataReportBuilder::HEADER_SIZE)
if (report.size() < sizeof(T) + WiimoteReal::REPORT_HID_HEADER_SIZE + 1)
{
// Off-brand "NEW 2in1" Wii Remote likes to shorten read data replies.
WARN_LOG(WIIMOTE, "WiiRemote: Bad report size (%d) for report 0x%x. Zero-filling.",
int(report.size()), int(T::REPORT_ID));

data = {};
std::memcpy(&data, report.data() + WiimoteCommon::DataReportBuilder::HEADER_SIZE,
report.size() - WiimoteCommon::DataReportBuilder::HEADER_SIZE);
std::memcpy(&data, report.data() + WiimoteReal::REPORT_HID_HEADER_SIZE + 1,
report.size() - WiimoteReal::REPORT_HID_HEADER_SIZE + 1);
}
else
{
data = Common::BitCastPtr<T>(report.data() + WiimoteCommon::DataReportBuilder::HEADER_SIZE);
data = Common::BitCastPtr<T>(report.data() + WiimoteReal::REPORT_HID_HEADER_SIZE + 1);
}

if constexpr (std::is_same_v<decltype(handler(data)), void>)