summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2020-09-14 02:09:27 -0400
committerGitHub <noreply@github.com>2020-09-14 02:09:27 -0400
commit4f1f849c9d7db042726fd2dd5effafcb434bbcec (patch)
treef0bcf8b53b58dbe0d012c4b7cc496b0704e9dbe7 /Source
parenteae68194b30610c60f224c676f573de024ed3a78 (diff)
parent0ad123bdbb935a2a3a7de8d26f15e406a690781e (diff)
Merge pull request #8985 from jordan-woyak/btemu-cleanup
BTEmu/Wiimote: Fixes and Cleanups.
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/Core/Core.h2
-rw-r--r--Source/Core/Core/HW/Wiimote.cpp147
-rw-r--r--Source/Core/Core/HW/Wiimote.h16
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/DataReport.cpp4
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/DataReport.h6
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h3
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h68
-rw-r--r--Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h2
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp57
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp99
-rw-r--r--Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h17
-rw-r--r--Source/Core/Core/HW/WiimoteReal/IOLinux.cpp19
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp162
-rw-r--r--Source/Core/Core/HW/WiimoteReal/WiimoteReal.h69
-rw-r--r--Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp302
-rw-r--r--Source/Core/Core/IOS/USB/Bluetooth/BTEmu.h18
-rw-r--r--Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp740
-rw-r--r--Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.h141
-rw-r--r--Source/Core/Core/IOS/USB/Bluetooth/hci.h1
-rw-r--r--Source/Core/Core/State.cpp2
-rw-r--r--Source/Core/DolphinQt/MainWindow.cpp10
-rw-r--r--Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp28
22 files changed, 877 insertions, 1036 deletions
diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h
index 9960658fde..54dfb24644 100644
--- a/Source/Core/Core/Core.h
+++ b/Source/Core/Core/Core.h
@@ -115,8 +115,6 @@ void WaitUntilDoneBooting();
void SaveScreenShot();
void SaveScreenShot(std::string_view name);
-void Callback_WiimoteInterruptChannel(int number, u16 channel_id, const u8* data, u32 size);
-
// This displays messages in a user-visible way.
void DisplayMessage(std::string message, int time_in_ms);
diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp
index 2c911a4577..4fc857b82a 100644
--- a/Source/Core/Core/HW/Wiimote.cpp
+++ b/Source/Core/Core/HW/Wiimote.cpp
@@ -4,8 +4,6 @@
#include "Core/HW/Wiimote.h"
-#include <fmt/format.h>
-
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
@@ -46,15 +44,44 @@ void SetSource(unsigned int index, WiimoteSource source)
WiimoteReal::HandleWiimoteSourceChange(index);
- // Reconnect to the emulator.
- Core::RunAsCPUThread([index, previous_source, source] {
- if (previous_source != WiimoteSource::None)
- ::Wiimote::Connect(index, false);
+ Core::RunAsCPUThread([index] { UpdateSource(index); });
+}
+
+void UpdateSource(unsigned int index)
+{
+ const auto ios = IOS::HLE::GetIOS();
+ if (!ios)
+ return;
+
+ const auto bluetooth = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
+ ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
+ if (!bluetooth)
+ return;
+
+ bluetooth->AccessWiimoteByIndex(index)->SetSource(GetHIDWiimoteSource(index));
+}
+
+HIDWiimote* GetHIDWiimoteSource(unsigned int index)
+{
+ HIDWiimote* hid_source = nullptr;
+
+ switch (GetSource(index))
+ {
+ case WiimoteSource::Emulated:
+ hid_source = static_cast<WiimoteEmu::Wiimote*>(::Wiimote::GetConfig()->GetController(index));
+ break;
+
+ case WiimoteSource::Real:
+ hid_source = WiimoteReal::g_wiimotes[index].get();
+ break;
+
+ default:
+ break;
+ }
- if (source == WiimoteSource::Emulated)
- ::Wiimote::Connect(index, true);
- });
+ return hid_source;
}
+
} // namespace WiimoteCommon
namespace Wiimote
@@ -143,25 +170,6 @@ void Initialize(InitializeMode init_mode)
Movie::ChangeWiiPads();
}
-void Connect(unsigned int index, bool connect)
-{
- if (SConfig::GetInstance().m_bt_passthrough_enabled || index >= MAX_BBMOTES)
- return;
-
- const auto ios = IOS::HLE::GetIOS();
- if (!ios)
- return;
-
- const auto bluetooth = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
- ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
-
- if (bluetooth)
- bluetooth->AccessWiimoteByIndex(index)->Activate(connect);
-
- const char* const message = connect ? "Wii Remote {} connected" : "Wii Remote {} disconnected";
- Core::DisplayMessage(fmt::format(message, index + 1), 3000);
-}
-
void ResetAllWiimotes()
{
for (int i = WIIMOTE_CHAN_0; i < MAX_BBMOTES; ++i)
@@ -184,84 +192,6 @@ void Pause()
WiimoteReal::Pause();
}
-// An L2CAP packet is passed from the Core to the Wiimote on the HID CONTROL channel.
-void ControlChannel(int number, u16 channel_id, const void* data, u32 size)
-{
- if (WiimoteCommon::GetSource(number) == WiimoteSource::Emulated)
- {
- static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(number))
- ->ControlChannel(channel_id, data, size);
- }
- else
- {
- WiimoteReal::ControlChannel(number, channel_id, data, size);
- }
-}
-
-// An L2CAP packet is passed from the Core to the Wiimote on the HID INTERRUPT channel.
-void InterruptChannel(int number, u16 channel_id, const void* data, u32 size)
-{
- if (WiimoteCommon::GetSource(number) == WiimoteSource::Emulated)
- {
- static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(number))
- ->InterruptChannel(channel_id, data, size);
- }
- else
- {
- WiimoteReal::InterruptChannel(number, channel_id, data, size);
- }
-}
-
-bool ButtonPressed(int number)
-{
- const WiimoteSource source = WiimoteCommon::GetSource(number);
-
- if (s_last_connect_request_counter[number] > 0)
- {
- --s_last_connect_request_counter[number];
- if (source != WiimoteSource::None && NetPlay::IsNetPlayRunning())
- Wiimote::NetPlay_GetButtonPress(number, false);
- return false;
- }
-
- bool button_pressed = false;
-
- if (source == WiimoteSource::Emulated)
- button_pressed =
- static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(number))->CheckForButtonPress();
-
- if (source == WiimoteSource::Real)
- button_pressed = WiimoteReal::CheckForButtonPress(number);
-
- if (source != WiimoteSource::None && NetPlay::IsNetPlayRunning())
- button_pressed = Wiimote::NetPlay_GetButtonPress(number, button_pressed);
-
- return button_pressed;
-}
-
-// This function is called periodically by the Core to update Wiimote state.
-void Update(int number, bool connected)
-{
- if (connected)
- {
- if (WiimoteCommon::GetSource(number) == WiimoteSource::Emulated)
- static_cast<WiimoteEmu::Wiimote*>(s_config.GetController(number))->Update();
- else
- WiimoteReal::Update(number);
- }
- else
- {
- if (ButtonPressed(number))
- {
- Connect(number, true);
- // arbitrary value so it doesn't try to send multiple requests before Dolphin can react
- // if Wii Remotes are polled at 200Hz then this results in one request being sent per 500ms
- s_last_connect_request_counter[number] = 100;
- }
- }
-}
-
-// Save/Load state
void DoState(PointerWrap& p)
{
for (int i = 0; i < MAX_BBMOTES; ++i)
@@ -281,10 +211,7 @@ void DoState(PointerWrap& p)
// If using a real wiimote or the save-state source does not match the current source,
// then force a reconnection on load.
if (source == WiimoteSource::Real || source != WiimoteSource(state_wiimote_source))
- {
- Connect(i, false);
- Connect(i, true);
- }
+ WiimoteCommon::UpdateSource(i);
}
}
}
diff --git a/Source/Core/Core/HW/Wiimote.h b/Source/Core/Core/HW/Wiimote.h
index 8709c064b6..88450fecde 100644
--- a/Source/Core/Core/HW/Wiimote.h
+++ b/Source/Core/Core/HW/Wiimote.h
@@ -53,8 +53,17 @@ enum class WiimoteSource
namespace WiimoteCommon
{
+class HIDWiimote;
+
WiimoteSource GetSource(unsigned int index);
void SetSource(unsigned int index, WiimoteSource source);
+
+// Used to reconnect WiimoteDevice instance to HID source.
+// Must be run from CPU thread.
+void UpdateSource(unsigned int index);
+
+HIDWiimote* GetHIDWiimoteSource(unsigned int index);
+
} // namespace WiimoteCommon
namespace Wiimote
@@ -67,12 +76,9 @@ enum class InitializeMode
// The Real Wii Remote sends report every ~5ms (200 Hz).
constexpr int UPDATE_FREQ = 200;
-// Custom channel ID used in ControlChannel to indicate disconnects
-constexpr int DOLPHIN_DISCONNET_CONTROL_CHANNEL = 99;
void Shutdown();
void Initialize(InitializeMode init_mode);
-void Connect(unsigned int index, bool connect);
void ResetAllWiimotes();
void LoadConfig();
void Resume();
@@ -91,10 +97,6 @@ ControllerEmu::ControlGroup* GetDrawsomeTabletGroup(int number,
WiimoteEmu::DrawsomeTabletGroup group);
ControllerEmu::ControlGroup* GetTaTaConGroup(int number, WiimoteEmu::TaTaConGroup group);
-void ControlChannel(int number, u16 channel_id, const void* data, u32 size);
-void InterruptChannel(int number, u16 channel_id, const void* data, u32 size);
-bool ButtonPressed(int number);
-void Update(int number, bool connected);
bool NetPlay_GetButtonPress(int wiimote, bool pressed);
} // namespace Wiimote
diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
index 0197553517..ef8cc799f3 100644
--- a/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
+++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.cpp
@@ -324,7 +324,7 @@ DataReportBuilder::DataReportBuilder(InputReportID rpt_id) : m_data(rpt_id)
void DataReportBuilder::SetMode(InputReportID rpt_id)
{
m_data.report_id = rpt_id;
- m_manip = MakeDataReportManipulator(rpt_id, GetDataPtr() + HEADER_SIZE);
+ m_manip = MakeDataReportManipulator(rpt_id, GetDataPtr() + sizeof(m_data.report_id));
}
InputReportID DataReportBuilder::GetMode() const
@@ -405,7 +405,7 @@ u8* DataReportBuilder::GetDataPtr()
u32 DataReportBuilder::GetDataSize() const
{
- return m_manip->GetDataSize() + HEADER_SIZE;
+ return m_manip->GetDataSize() + sizeof(m_data.report_id);
}
u8* DataReportBuilder::GetIRDataPtr()
diff --git a/Source/Core/Core/HW/WiimoteCommon/DataReport.h b/Source/Core/Core/HW/WiimoteCommon/DataReport.h
index 75b649d854..bddfbd456c 100644
--- a/Source/Core/Core/HW/WiimoteCommon/DataReport.h
+++ b/Source/Core/Core/HW/WiimoteCommon/DataReport.h
@@ -94,11 +94,11 @@ public:
u32 GetDataSize() const;
- static constexpr int HEADER_SIZE = 2;
- static constexpr int MAX_DATA_SIZE = MAX_PAYLOAD - 2;
+ // The largest report is 0x3d (21 extension bytes).
+ static constexpr int MAX_DATA_SIZE = 21;
private:
- TypedHIDInputData<std::array<u8, MAX_DATA_SIZE>> m_data;
+ TypedInputData<std::array<u8, MAX_DATA_SIZE>> m_data;
std::unique_ptr<DataReportManipulator> m_manip;
};
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
index 99cf0e04e7..71f73bf85b 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteConstants.h
@@ -8,6 +8,9 @@
namespace WiimoteCommon
{
+// Note this size includes the HID header.
+// e.g. 0xa1 0x3d 0x...
+// TODO: Kill/rename this constant so it's more clear.
constexpr u8 MAX_PAYLOAD = 23;
enum class InputReportID : u8
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
index 6afa1fc38c..05ca77f0c6 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteHid.h
@@ -21,6 +21,44 @@ constexpr u8 HID_HANDSHAKE_SUCCESS = 0;
constexpr u8 HID_PARAM_INPUT = 1;
constexpr u8 HID_PARAM_OUTPUT = 2;
+class HIDWiimote
+{
+public:
+ using InterruptCallbackType = std::function<void(u8 hid_type, const u8* data, u32 size)>;
+
+ virtual ~HIDWiimote() = default;
+
+ virtual void EventLinked() = 0;
+ virtual void EventUnlinked() = 0;
+
+ // Called every ~200hz after HID channels are established.
+ virtual void Update() = 0;
+
+ void SetInterruptCallback(InterruptCallbackType callback) { m_callback = std::move(callback); }
+
+ // HID report type:0xa2 (data output) payloads sent to the wiimote interrupt channel.
+ // Does not include HID-type header.
+ virtual void InterruptDataOutput(const u8* data, u32 size) = 0;
+
+ // Used to connect a disconnected wii remote on button press.
+ virtual bool IsButtonPressed() = 0;
+
+protected:
+ void InterruptDataInputCallback(const u8* data, u32 size)
+ {
+ InterruptCallback((WiimoteCommon::HID_TYPE_DATA << 4) | WiimoteCommon::HID_PARAM_INPUT, data,
+ size);
+ }
+
+ void InterruptCallback(u8 hid_type, const u8* data, u32 size)
+ {
+ m_callback(hid_type, data, size);
+ }
+
+private:
+ InterruptCallbackType m_callback;
+};
+
#ifdef _MSC_VER
#pragma warning(push)
// Disable warning for zero-sized array:
@@ -29,41 +67,19 @@ constexpr u8 HID_PARAM_OUTPUT = 2;
#pragma pack(push, 1)
-struct HIDPacket
-{
- static constexpr int HEADER_SIZE = 1;
-
- u8 param : 4;
- u8 type : 4;
-
- u8 data[0];
-};
-
template <typename T>
-struct TypedHIDInputData
+struct TypedInputData
{
- TypedHIDInputData(InputReportID _rpt_id)
- : param(HID_PARAM_INPUT), type(HID_TYPE_DATA), report_id(_rpt_id)
- {
- }
-
- u8 param : 4;
- u8 type : 4;
+ TypedInputData(InputReportID _rpt_id) : report_id(_rpt_id) {}
InputReportID report_id;
-
- T data;
+ T payload = {};
static_assert(std::is_standard_layout_v<T> && std::is_trivially_copyable_v<T>);
u8* GetData() { return reinterpret_cast<u8*>(this); }
const u8* GetData() const { return reinterpret_cast<const u8*>(this); }
-
- constexpr u32 GetSize() const
- {
- static_assert(sizeof(*this) == sizeof(T) + 2);
- return sizeof(*this);
- }
+ constexpr u32 GetSize() const { return sizeof(*this); }
};
#pragma pack(pop)
diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
index e641b85d8b..158552b143 100644
--- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
+++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h
@@ -167,7 +167,7 @@ static_assert(sizeof(OutputReportSpeakerData) == 21, "Wrong size");
// FYI: Also contains LSB of accel data:
union ButtonData
{
- static constexpr u16 BUTTON_MASK = ~0x6060;
+ static constexpr u16 BUTTON_MASK = ~0x60e0;
u16 hex;
diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
index bd25279867..862a4a49ad 100644
--- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp
@@ -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);
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
index 4805d7b95c..2702ae5b6b 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp
@@ -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();
diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
index 8ded6f0dc4..a01d4c545f 100644
--- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
+++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h
@@ -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 @@ public:
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 @@ private:
// 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 @@ private:
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 @@ private:
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 @@ private:
// Wiimote index, 0-3
const u8 m_index;
- u16 m_reporting_channel;
WiimoteCommon::InputReportID m_reporting_mode;
bool m_reporting_continuous;
diff --git a/Source/Core/Core/HW/WiimoteReal/IOLinux.cpp b/Source/Core/Core/HW/WiimoteReal/IOLinux.cpp
index aa094aa92b..7fec2bd9a2 100644
--- a/Source/Core/Core/HW/WiimoteReal/IOLinux.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/IOLinux.cpp
@@ -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;
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
index 031ad1690a..39aece1e2e 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.cpp
@@ -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();
}
diff --git a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
index 14aa2b9c4d..a0695ffae7 100644
--- a/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
+++ b/Source/Core/Core/HW/WiimoteReal/WiimoteReal.h
@@ -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 @@ public:
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,14 +88,11 @@ public:
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.
@@ -125,6 +100,23 @@ protected:
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 @@ private:
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);
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp
index f86f5e5d18..e3b7cc4340 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp
+++ b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp
@@ -9,8 +9,6 @@
#include <memory>
#include <string>
-#include <fmt/format.h>
-
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
@@ -45,30 +43,25 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)
BackUpBTInfoSection(&sysconf);
ConfPads bt_dinf{};
- bdaddr_t tmp_bd;
- u8 i = 0;
- while (i < MAX_BBMOTES)
+
+ for (u8 i = 0; i != MAX_BBMOTES; ++i)
{
+ // Note: BluetoothEmu::GetConnectionHandle and WiimoteDevice::GetNumber rely on final byte.
+ const bdaddr_t tmp_bd = {0x11, 0x02, 0x19, 0x79, 0, i};
+
// Previous records can be safely overwritten, since they are backed up
- tmp_bd[5] = bt_dinf.active[i].bdaddr[0] = bt_dinf.registered[i].bdaddr[0] = i;
- tmp_bd[4] = bt_dinf.active[i].bdaddr[1] = bt_dinf.registered[i].bdaddr[1] = 0;
- tmp_bd[3] = bt_dinf.active[i].bdaddr[2] = bt_dinf.registered[i].bdaddr[2] = 0x79;
- tmp_bd[2] = bt_dinf.active[i].bdaddr[3] = bt_dinf.registered[i].bdaddr[3] = 0x19;
- tmp_bd[1] = bt_dinf.active[i].bdaddr[4] = bt_dinf.registered[i].bdaddr[4] = 2;
- tmp_bd[0] = bt_dinf.active[i].bdaddr[5] = bt_dinf.registered[i].bdaddr[5] = 0x11;
-
- const char* wm_name;
- if (i == WIIMOTE_BALANCE_BOARD)
- wm_name = "Nintendo RVL-WBC-01";
- else
- wm_name = "Nintendo RVL-CNT-01";
+ std::copy(tmp_bd.begin(), tmp_bd.end(), std::rbegin(bt_dinf.active[i].bdaddr));
+ std::copy(tmp_bd.begin(), tmp_bd.end(), std::rbegin(bt_dinf.registered[i].bdaddr));
+
+ const auto& wm_name =
+ (i == WIIMOTE_BALANCE_BOARD) ? "Nintendo RVL-WBC-01" : "Nintendo RVL-CNT-01";
memcpy(bt_dinf.registered[i].name, wm_name, 20);
memcpy(bt_dinf.active[i].name, wm_name, 20);
DEBUG_LOG(IOS_WIIMOTE, "Wii Remote %d BT ID %x,%x,%x,%x,%x,%x", i, tmp_bd[0], tmp_bd[1],
tmp_bd[2], tmp_bd[3], tmp_bd[4], tmp_bd[5]);
- m_wiimotes.emplace_back(this, i, tmp_bd, WiimoteCommon::GetSource(i) != WiimoteSource::None);
- i++;
+
+ m_wiimotes.emplace_back(std::make_unique<WiimoteDevice>(this, i, tmp_bd));
}
bt_dinf.num_registered = MAX_BBMOTES;
@@ -82,10 +75,7 @@ BluetoothEmu::BluetoothEmu(Kernel& ios, const std::string& device_name)
PanicAlertT("Failed to write BT.DINF to SYSCONF");
}
-BluetoothEmu::~BluetoothEmu()
-{
- m_wiimotes.clear();
-}
+BluetoothEmu::~BluetoothEmu() = default;
template <typename T>
static void DoStateForMessage(Kernel& ios, PointerWrap& p, std::unique_ptr<T>& message)
@@ -122,12 +112,22 @@ void BluetoothEmu::DoState(PointerWrap& p)
m_acl_pool.DoState(p);
for (unsigned int i = 0; i < MAX_BBMOTES; i++)
- m_wiimotes[i].DoState(p);
+ m_wiimotes[i]->DoState(p);
}
-bool BluetoothEmu::RemoteDisconnect(u16 connection_handle)
+bool BluetoothEmu::RemoteConnect(WiimoteDevice& wiimote)
{
- return SendEventDisconnect(connection_handle, 0x13);
+ // If page scan is disabled the controller will not see this connection request.
+ if (!(m_scan_enable & HCI_PAGE_SCAN_ENABLE))
+ return false;
+
+ SendEventRequestConnection(wiimote);
+ return true;
+}
+
+bool BluetoothEmu::RemoteDisconnect(const bdaddr_t& address)
+{
+ return SendEventDisconnect(GetConnectionHandle(address), 0x13);
}
IPCCommandResult BluetoothEmu::Close(u32 fd)
@@ -226,13 +226,15 @@ void BluetoothEmu::SendToDevice(u16 connection_handle, u8* data, u32 size)
void BluetoothEmu::IncDataPacket(u16 connection_handle)
{
- m_packet_count[connection_handle & 0xff]++;
+ m_packet_count[GetWiimoteNumberFromConnectionHandle(connection_handle)]++;
}
// Here we send ACL packets to CPU. They will consist of header + data.
// The header is for example 07 00 41 00 which means size 0x0007 and channel 0x0041.
-void BluetoothEmu::SendACLPacket(u16 connection_handle, const u8* data, u32 size)
+void BluetoothEmu::SendACLPacket(const bdaddr_t& source, const u8* data, u32 size)
{
+ const u16 connection_handle = GetConnectionHandle(source);
+
DEBUG_LOG(IOS_WIIMOTE, "ACL packet from %x ready to send to stack...", connection_handle);
if (m_acl_endpoint && !m_hci_endpoint && m_event_queue.empty())
@@ -331,30 +333,8 @@ void BluetoothEmu::Update()
m_acl_endpoint.reset();
}
- // We wait for ScanEnable to be sent from the Bluetooth stack through HCI_CMD_WRITE_SCAN_ENABLE
- // before we initiate the connection.
- //
- // FiRES: TODO find a better way to do this
-
- // Create ACL connection
- if (m_hci_endpoint && (m_scan_enable & HCI_PAGE_SCAN_ENABLE))
- {
- for (const auto& wiimote : m_wiimotes)
- {
- if (wiimote.EventPagingChanged(m_scan_enable))
- SendEventRequestConnection(wiimote);
- }
- }
-
- // Link channels when connected
- if (m_acl_endpoint)
- {
- for (auto& wiimote : m_wiimotes)
- {
- if (wiimote.LinkChannel())
- break;
- }
- }
+ for (auto& wiimote : m_wiimotes)
+ wiimote->Update();
const u64 interval = SystemTimers::GetTicksPerSecond() / Wiimote::UPDATE_FREQ;
const u64 now = CoreTiming::GetTicks();
@@ -362,8 +342,8 @@ void BluetoothEmu::Update()
if (now - m_last_ticks > interval)
{
g_controller_interface.UpdateInput();
- for (unsigned int i = 0; i < m_wiimotes.size(); i++)
- Wiimote::Update(i, m_wiimotes[i].IsConnected());
+ for (auto& wiimote : m_wiimotes)
+ wiimote->UpdateInput();
m_last_ticks = now;
}
@@ -414,7 +394,7 @@ void BluetoothEmu::ACLPool::WriteToEndpoint(const USB::V0BulkMessage& endpoint)
m_ios.EnqueueIPCReply(endpoint.ios_request, sizeof(hci_acldata_hdr_t) + size);
}
-bool BluetoothEmu::SendEventInquiryComplete()
+bool BluetoothEmu::SendEventInquiryComplete(u8 num_responses)
{
SQueuedEvent event(sizeof(SHCIEventInquiryComplete), 0);
@@ -422,6 +402,7 @@ bool BluetoothEmu::SendEventInquiryComplete()
inquiry_complete->EventType = HCI_EVENT_INQUIRY_COMPL;
inquiry_complete->PayloadLength = sizeof(SHCIEventInquiryComplete) - 2;
inquiry_complete->EventStatus = 0x00;
+ inquiry_complete->num_responses = num_responses;
AddEventToQueue(event);
@@ -432,77 +413,73 @@ bool BluetoothEmu::SendEventInquiryComplete()
bool BluetoothEmu::SendEventInquiryResponse()
{
- if (m_wiimotes.empty())
- return false;
+ // We only respond with the first discoverable remote.
+ // The Wii instructs users to press 1+2 in the desired play order.
+ // Responding with all remotes at once can place them in undesirable slots.
+ // Additional scans will connect each remote in the proper order.
+ constexpr u8 num_responses = 1;
- DEBUG_ASSERT(sizeof(SHCIEventInquiryResult) - 2 +
- (m_wiimotes.size() * sizeof(hci_inquiry_response)) <
- 256);
+ static_assert(
+ sizeof(SHCIEventInquiryResult) - 2 + (num_responses * sizeof(hci_inquiry_response)) < 256);
- SQueuedEvent event(static_cast<u32>(sizeof(SHCIEventInquiryResult) +
- m_wiimotes.size() * sizeof(hci_inquiry_response)),
- 0);
+ const auto iter = std::find_if(m_wiimotes.begin(), m_wiimotes.end(),
+ std::mem_fn(&WiimoteDevice::IsInquiryScanEnabled));
+ if (iter == m_wiimotes.end())
+ {
+ // No remotes are discoverable.
+ SendEventInquiryComplete(0);
+ return false;
+ }
+
+ const auto& wiimote = *iter;
- SHCIEventInquiryResult* inquiry_result = (SHCIEventInquiryResult*)event.buffer;
+ SQueuedEvent event(
+ u32(sizeof(SHCIEventInquiryResult) + num_responses * sizeof(hci_inquiry_response)), 0);
+ const auto inquiry_result = reinterpret_cast<SHCIEventInquiryResult*>(event.buffer);
inquiry_result->EventType = HCI_EVENT_INQUIRY_RESULT;
- inquiry_result->PayloadLength =
- (u8)(sizeof(SHCIEventInquiryResult) - 2 + (m_wiimotes.size() * sizeof(hci_inquiry_response)));
- inquiry_result->num_responses = (u8)m_wiimotes.size();
+ inquiry_result->num_responses = num_responses;
- for (size_t i = 0; i < m_wiimotes.size(); i++)
- {
- if (m_wiimotes[i].IsConnected())
- continue;
-
- u8* buffer = event.buffer + sizeof(SHCIEventInquiryResult) + i * sizeof(hci_inquiry_response);
- hci_inquiry_response* response = (hci_inquiry_response*)buffer;
-
- response->bdaddr = m_wiimotes[i].GetBD();
- response->uclass[0] = m_wiimotes[i].GetClass()[0];
- response->uclass[1] = m_wiimotes[i].GetClass()[1];
- response->uclass[2] = m_wiimotes[i].GetClass()[2];
-
- response->page_scan_rep_mode = 1;
- response->page_scan_period_mode = 0;
- response->page_scan_mode = 0;
- response->clock_offset = 0x3818;
-
- DEBUG_LOG(IOS_WIIMOTE, "Event: Send Fake Inquiry of one controller");
- DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", response->bdaddr[0],
- response->bdaddr[1], response->bdaddr[2], response->bdaddr[3], response->bdaddr[4],
- response->bdaddr[5]);
- }
+ u8* const buffer = event.buffer + sizeof(SHCIEventInquiryResult);
+ const auto response = reinterpret_cast<hci_inquiry_response*>(buffer);
- AddEventToQueue(event);
+ response->bdaddr = wiimote->GetBD();
+ response->page_scan_rep_mode = 1;
+ response->page_scan_period_mode = 0;
+ response->page_scan_mode = 0;
+ std::copy_n(wiimote->GetClass().begin(), HCI_CLASS_SIZE, response->uclass);
+ response->clock_offset = 0x3818;
+
+ DEBUG_LOG(IOS_WIIMOTE, "Event: Send Fake Inquiry of one controller");
+ DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", response->bdaddr[0],
+ response->bdaddr[1], response->bdaddr[2], response->bdaddr[3], response->bdaddr[4],
+ response->bdaddr[5]);
+ inquiry_result->PayloadLength =
+ u8(sizeof(SHCIEventInquiryResult) - 2 +
+ (inquiry_result->num_responses * sizeof(hci_inquiry_response)));
+
+ AddEventToQueue(event);
+ SendEventInquiryComplete(num_responses);
return true;
}
-bool BluetoothEmu::SendEventConnectionComplete(const bdaddr_t& bd)
+bool BluetoothEmu::SendEventConnectionComplete(const bdaddr_t& bd, u8 status)
{
- WiimoteDevice* wiimote = AccessWiimote(bd);
- if (wiimote == nullptr)
- return false;
-
SQueuedEvent event(sizeof(SHCIEventConnectionComplete), 0);
SHCIEventConnectionComplete* connection_complete = (SHCIEventConnectionComplete*)event.buffer;
connection_complete->EventType = HCI_EVENT_CON_COMPL;
connection_complete->PayloadLength = sizeof(SHCIEventConnectionComplete) - 2;
- connection_complete->EventStatus = 0x00;
- connection_complete->Connection_Handle = wiimote->GetConnectionHandle();
+ connection_complete->EventStatus = status;
+ connection_complete->Connection_Handle = GetConnectionHandle(bd);
connection_complete->bdaddr = bd;
connection_complete->LinkType = HCI_LINK_ACL;
connection_complete->EncryptionEnabled = HCI_ENCRYPTION_MODE_NONE;
AddEventToQueue(event);
- WiimoteDevice* connection_wiimote = AccessWiimote(connection_complete->Connection_Handle);
- if (connection_wiimote)
- connection_wiimote->EventConnectionAccepted();
-
static constexpr const char* link_type[] = {
"HCI_LINK_SCO 0x00 - Voice",
"HCI_LINK_ACL 0x01 - Data",
@@ -521,7 +498,6 @@ bool BluetoothEmu::SendEventConnectionComplete(const bdaddr_t& bd)
return true;
}
-// This is called from Update() after ScanEnable has been enabled.
bool BluetoothEmu::SendEventRequestConnection(const WiimoteDevice& wiimote)
{
SQueuedEvent event(sizeof(SHCIEventRequestConnection), 0);
@@ -646,14 +622,7 @@ bool BluetoothEmu::SendEventReadRemoteFeatures(u16 connection_handle)
read_remote_features->PayloadLength = sizeof(SHCIEventReadRemoteFeatures) - 2;
read_remote_features->EventStatus = 0x00;
read_remote_features->ConnectionHandle = connection_handle;
- read_remote_features->features[0] = wiimote->GetFeatures()[0];
- read_remote_features->features[1] = wiimote->GetFeatures()[1];
- read_remote_features->features[2] = wiimote->GetFeatures()[2];
- read_remote_features->features[3] = wiimote->GetFeatures()[3];
- read_remote_features->features[4] = wiimote->GetFeatures()[4];
- read_remote_features->features[5] = wiimote->GetFeatures()[5];
- read_remote_features->features[6] = wiimote->GetFeatures()[6];
- read_remote_features->features[7] = wiimote->GetFeatures()[7];
+ std::copy_n(wiimote->GetFeatures().begin(), HCI_FEATURES_SIZE, read_remote_features->features);
DEBUG_LOG(IOS_WIIMOTE, "Event: SendEventReadRemoteFeatures");
DEBUG_LOG(IOS_WIIMOTE, " Connection_Handle: 0x%04x", read_remote_features->ConnectionHandle);
@@ -788,7 +757,7 @@ bool BluetoothEmu::SendEventNumberOfCompletedPackets()
event_hdr->length += sizeof(hci_num_compl_pkts_info);
hci_event->num_con_handles++;
info->compl_pkts = m_packet_count[i];
- info->con_handle = m_wiimotes[i].GetConnectionHandle();
+ info->con_handle = GetConnectionHandle(m_wiimotes[i]->GetBD());
DEBUG_LOG(IOS_WIIMOTE, " Connection_Handle: 0x%04x", info->con_handle);
DEBUG_LOG(IOS_WIIMOTE, " Number_Of_Completed_Packets: %i", info->compl_pkts);
@@ -855,8 +824,8 @@ bool BluetoothEmu::SendEventLinkKeyNotification(const u8 num_to_send)
{
hci_link_key_rep_cp* link_key_info =
(hci_link_key_rep_cp*)((u8*)&event_link_key->bdaddr + sizeof(hci_link_key_rep_cp) * i);
- link_key_info->bdaddr = m_wiimotes[i].GetBD();
- memcpy(link_key_info->key, m_wiimotes[i].GetLinkKey(), HCI_KEY_SIZE);
+ link_key_info->bdaddr = m_wiimotes[i]->GetBD();
+ std::copy_n(m_wiimotes[i]->GetLinkKey().begin(), HCI_KEY_SIZE, link_key_info->key);
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", link_key_info->bdaddr[0],
link_key_info->bdaddr[1], link_key_info->bdaddr[2], link_key_info->bdaddr[3],
@@ -1147,7 +1116,6 @@ void BluetoothEmu::CommandInquiry(const u8* input)
SendEventCommandStatus(HCI_CMD_INQUIRY);
SendEventInquiryResponse();
- SendEventInquiryComplete();
}
void BluetoothEmu::CommandInquiryCancel(const u8* input)
@@ -1170,8 +1138,6 @@ void BluetoothEmu::CommandCreateCon(const u8* input)
DEBUG_LOG(IOS_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", create_connection.bdaddr[0],
create_connection.bdaddr[1], create_connection.bdaddr[2], create_connection.bdaddr[3],
create_connection.bdaddr[4], create_connection.bdaddr[5]);
- INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_ACCEPT_CON");
-
DEBUG_LOG(IOS_WIIMOTE, " pkt_type: %i", create_connection.pkt_type);
DEBUG_LOG(IOS_WIIMOTE, " page_scan_rep_mode: %i", create_connection.page_scan_rep_mode);
DEBUG_LOG(IOS_WIIMOTE, " page_scan_mode: %i", create_connection.page_scan_mode);
@@ -1179,7 +1145,12 @@ void BluetoothEmu::CommandCreateCon(const u8* input)
DEBUG_LOG(IOS_WIIMOTE, " accept_role_switch: %i", create_connection.accept_role_switch);
SendEventCommandStatus(HCI_CMD_CREATE_CON);
- SendEventConnectionComplete(create_connection.bdaddr);
+
+ WiimoteDevice* wiimote = AccessWiimote(create_connection.bdaddr);
+ const bool successful = wiimote && wiimote->EventConnectionRequest();
+
+ // Status 0x08 (Connection Timeout) if WiimoteDevice does not accept the connection.
+ SendEventConnectionComplete(create_connection.bdaddr, successful ? 0x00 : 0x08);
}
void BluetoothEmu::CommandDisconnect(const u8* input)
@@ -1191,14 +1162,12 @@ void BluetoothEmu::CommandDisconnect(const u8* input)
DEBUG_LOG(IOS_WIIMOTE, " ConnectionHandle: 0x%04x", disconnect.con_handle);
DEBUG_LOG(IOS_WIIMOTE, " Reason: 0x%02x", disconnect.reason);
- DisplayDisconnectMessage((disconnect.con_handle & 0xFF) + 1, disconnect.reason);
-
SendEventCommandStatus(HCI_CMD_DISCONNECT);
SendEventDisconnect(disconnect.con_handle, disconnect.reason);
WiimoteDevice* wiimote = AccessWiimote(disconnect.con_handle);
if (wiimote)
- wiimote->EventDisconnect();
+ wiimote->EventDisconnect(disconnect.reason);
}
void BluetoothEmu::CommandAcceptCon(const u8* input)
@@ -1219,13 +1188,23 @@ void BluetoothEmu::CommandAcceptCon(const u8* input)
SendEventCommandStatus(HCI_CMD_ACCEPT_CON);
- // this connection wants to be the master
- if (accept_connection.role == 0)
+ WiimoteDevice* wiimote = AccessWiimote(accept_connection.bdaddr);
+ const bool successful = wiimote && wiimote->EventConnectionAccept();
+
+ if (successful)
{
- SendEventRoleChange(accept_connection.bdaddr, true);
- }
+ // This connection wants to be the master.
+ // The controller performs a master-slave switch and notifies the host.
+ if (accept_connection.role == 0)
+ SendEventRoleChange(accept_connection.bdaddr, true);
- SendEventConnectionComplete(accept_connection.bdaddr);
+ SendEventConnectionComplete(accept_connection.bdaddr, 0x00);
+ }
+ else
+ {
+ // Status 0x08 (Connection Timeout) if WiimoteDevice no longer wants this connection.
+ SendEventConnectionComplete(accept_connection.bdaddr, 0x08);
+ }
}
void BluetoothEmu::CommandLinkKeyRep(const u8* input)
@@ -1377,8 +1356,9 @@ void BluetoothEmu::CommandReset(const u8* input)
reply.status = 0x00;
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_RESET");
-
SendEventCommandComplete(HCI_CMD_RESET, &reply, sizeof(hci_status_rp));
+
+ // TODO: We should actually reset connections and channels and everything here.
}
void BluetoothEmu::CommandSetEventFilter(const u8* input)
@@ -1386,6 +1366,12 @@ void BluetoothEmu::CommandSetEventFilter(const u8* input)
hci_set_event_filter_cp set_event_filter;
std::memcpy(&set_event_filter, input, sizeof(set_event_filter));
+ // It looks like software only ever sets a "new device inquiry response" filter.
+ // This is one we can safely ignore because of our fake inquiry implementation
+ // and documentation says controllers can opt to not implement this filter anyways.
+
+ // TODO: There should be a warn log if an actual filter is being set.
+
hci_set_event_filter_rp reply;
reply.status = 0x00;
@@ -1421,13 +1407,9 @@ void BluetoothEmu::CommandReadStoredLinkKey(const u8* input)
reply.max_num_keys = 255;
if (read_stored_link_key.read_all == 1)
- {
reply.num_keys_read = static_cast<u16>(m_wiimotes.size());
- }
else
- {
ERROR_LOG(IOS_WIIMOTE, "CommandReadStoredLinkKey isn't looking for all devices");
- }
INFO_LOG(IOS_WIIMOTE, "Command: HCI_CMD_READ_STORED_LINK_KEY:");
DEBUG_LOG(IOS_WIIMOTE, "input:");
@@ -1486,8 +1468,6 @@ void BluetoothEmu::CommandWriteLocalName(const u8* input)
SendEventCommandComplete(HCI_CMD_WRITE_LOCAL_NAME, &reply, sizeof(hci_write_local_name_rp));
}
-// Here we normally receive the timeout interval.
-// But not from homebrew games that use lwbt. Why not?
void BluetoothEmu::CommandWritePageTimeOut(const u8* input)
{
hci_write_page_timeout_cp write_page_timeout;
@@ -1502,7 +1482,6 @@ void BluetoothEmu::CommandWritePageTimeOut(const u8* input)
SendEventCommandComplete(HCI_CMD_WRITE_PAGE_TIMEOUT, &reply, sizeof(hci_host_buffer_size_rp));
}
-// This will enable ScanEnable so that Update() can start the Wii Remote.
void BluetoothEmu::CommandWriteScanEnable(const u8* input)
{
hci_write_scan_enable_cp write_scan_enable;
@@ -1601,6 +1580,8 @@ void BluetoothEmu::CommandWriteInquiryMode(const u8* input)
hci_write_inquiry_mode_rp reply;
reply.status = 0x00;
+ // TODO: Software seems to set an RSSI mode but our fake inquiries generate standard events.
+
static constexpr const char* inquiry_mode_tag[] = {
"Standard Inquiry Result event format (default)",
"Inquiry Result format with RSSI",
@@ -1743,45 +1724,50 @@ void BluetoothEmu::CommandVendorSpecific_FC4C(const u8* input, u32 size)
SendEventCommandComplete(0xFC4C, &reply, sizeof(hci_status_rp));
}
-//
-//
-// --- helper
-//
-//
WiimoteDevice* BluetoothEmu::AccessWiimoteByIndex(std::size_t index)
{
- const u16 connection_handle = static_cast<u16>(0x100 + index);
- return AccessWiimote(connection_handle);
+ if (index < MAX_BBMOTES)
+ return m_wiimotes[index].get();
+
+ return nullptr;
+}
+
+u16 BluetoothEmu::GetConnectionHandle(const bdaddr_t& address)
+{
+ // Handles are normally generated per connection but HLE allows fixed values for each remote.
+ return 0x100 + address.back();
+}
+
+u32 BluetoothEmu::GetWiimoteNumberFromConnectionHandle(u16 connection_handle)
+{
+ // Fixed handle values are generated in GetConnectionHandle.
+ return connection_handle & 0xff;
}
WiimoteDevice* BluetoothEmu::AccessWiimote(const bdaddr_t& address)
{
- const auto iterator =
- std::find_if(m_wiimotes.begin(), m_wiimotes.end(),
- [&address](const WiimoteDevice& remote) { return remote.GetBD() == address; });
- return iterator != m_wiimotes.cend() ? &*iterator : nullptr;
+ // Fixed bluetooth addresses are generated in WiimoteDevice::WiimoteDevice.
+ const auto wiimote = AccessWiimoteByIndex(address.back());
+
+ if (wiimote && wiimote->GetBD() == address)
+ return wiimote;
+
+ return nullptr;
}
WiimoteDevice* BluetoothEmu::AccessWiimote(u16 connection_handle)
{
- for (auto& wiimote : m_wiimotes)
- {
- if (wiimote.GetConnectionHandle() == connection_handle)
- return &wiimote;
- }
+ const auto wiimote =
+ AccessWiimoteByIndex(GetWiimoteNumberFromConnectionHandle(connection_handle));
+
+ if (wiimote)
+ return wiimote;
ERROR_LOG(IOS_WIIMOTE, "Can't find Wiimote by connection handle %02x", connection_handle);
PanicAlertT("Can't find Wii Remote by connection handle %02x", connection_handle);
+
return nullptr;
}
-void BluetoothEmu::DisplayDisconnectMessage(const int wiimote_number, const int reason)
-{
- // TODO: If someone wants to be fancy we could also figure out what the values for pDiscon->reason
- // mean
- // and display things like "Wii Remote %i disconnected due to inactivity!" etc.
- Core::DisplayMessage(
- fmt::format("Wii Remote {} disconnected by emulated software", wiimote_number), 3000);
-}
} // namespace Device
} // namespace IOS::HLE
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.h b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.h
index 0f7a5686b0..6a518a2180 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.h
+++ b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.h
@@ -52,16 +52,18 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
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
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp
index 4cdc16a3b2..43169b5872 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp
+++ b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp
@@ -9,6 +9,8 @@
#include <utility>
#include <vector>
+#include <fmt/format.h>
+
#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
@@ -18,6 +20,8 @@
#include "Common/Swap.h"
#include "Core/Core.h"
#include "Core/HW/Wiimote.h"
+#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
+#include "Core/HW/WiimoteCommon/WiimoteHid.h"
#include "Core/Host.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteHIDAttr.h"
@@ -49,214 +53,318 @@ private:
u8* m_buffer;
};
-WiimoteDevice::WiimoteDevice(Device::BluetoothEmu* host, int number, bdaddr_t bd, bool ready)
- : m_bd(bd),
- m_name(number == WIIMOTE_BALANCE_BOARD ? "Nintendo RVL-WBC-01" : "Nintendo RVL-CNT-01"),
- m_host(host)
+constexpr int CONNECTION_MESSAGE_TIME = 3000;
+
+WiimoteDevice::WiimoteDevice(Device::BluetoothEmu* host, int number, bdaddr_t bd)
+ : m_host(host), m_bd(bd),
+ m_name(number == WIIMOTE_BALANCE_BOARD ? "Nintendo RVL-WBC-01" : "Nintendo RVL-CNT-01")
+
{
INFO_LOG(IOS_WIIMOTE, "Wiimote: #%i Constructed", number);
- m_connection_state = ready ? ConnectionState::Ready : ConnectionState::Inactive;
- m_connection_handle = 0x100 + number;
- memset(m_link_key, 0xA0 + number, HCI_KEY_SIZE);
-
- if (m_bd == BDADDR_ANY)
- m_bd = {{0x11, 0x02, 0x19, 0x79, static_cast<u8>(number)}};
+ m_link_key.fill(0xa0 + number);
+ m_class = {0x00, 0x04, 0x48};
+ m_features = {0xBC, 0x02, 0x04, 0x38, 0x08, 0x00, 0x00, 0x00};
+ m_lmp_version = 0x2;
+ m_lmp_subversion = 0x229;
- m_uclass[0] = 0x00;
- m_uclass[1] = 0x04;
- m_uclass[2] = 0x48;
+ const auto hid_source = WiimoteCommon::GetHIDWiimoteSource(GetNumber());
- m_features[0] = 0xBC;
- m_features[1] = 0x02;
- m_features[2] = 0x04;
- m_features[3] = 0x38;
- m_features[4] = 0x08;
- m_features[5] = 0x00;
- m_features[6] = 0x00;
- m_features[7] = 0x00;
+ // UGLY: This prevents an OSD message in SetSource -> Activate.
+ if (hid_source)
+ SetBasebandState(BasebandState::RequestConnection);
- m_lmp_version = 0x2;
- m_lmp_subversion = 0x229;
+ SetSource(hid_source);
}
-void WiimoteDevice::DoState(PointerWrap& p)
+WiimoteDevice::~WiimoteDevice() = default;
+
+WiimoteDevice::SChannel::SChannel() : psm(L2CAP_PSM_ANY), remote_cid(L2CAP_NULL_CID)
{
- bool passthrough_bluetooth = false;
- p.Do(passthrough_bluetooth);
- if (passthrough_bluetooth && p.GetMode() == PointerWrap::MODE_READ)
- {
- Core::DisplayMessage("State needs Bluetooth passthrough to be enabled. Aborting load state.",
- 3000);
- p.SetMode(PointerWrap::MODE_VERIFY);
- return;
- }
+}
- // this function is usually not called... see Device::BluetoothEmu::DoState
+bool WiimoteDevice::SChannel::IsAccepted() const
+{
+ return remote_cid != L2CAP_NULL_CID;
+}
- p.Do(m_connection_state);
+bool WiimoteDevice::SChannel::IsRemoteConfigured() const
+{
+ return remote_mtu != 0;
+}
- p.Do(m_hid_control_channel.connected);
- p.Do(m_hid_control_channel.connected_wait);
- p.Do(m_hid_control_channel.config);
- p.Do(m_hid_control_channel.config_wait);
- p.Do(m_hid_interrupt_channel.connected);
- p.Do(m_hid_interrupt_channel.connected_wait);
- p.Do(m_hid_interrupt_channel.config);
- p.Do(m_hid_interrupt_channel.config_wait);
+bool WiimoteDevice::SChannel::IsComplete() const
+{
+ return IsAccepted() && IsRemoteConfigured() && state == State::Complete;
+}
+void WiimoteDevice::DoState(PointerWrap& p)
+{
+ p.Do(m_baseband_state);
+ p.Do(m_hid_state);
p.Do(m_bd);
- p.Do(m_connection_handle);
- p.Do(m_uclass);
+ p.Do(m_class);
p.Do(m_features);
p.Do(m_lmp_version);
p.Do(m_lmp_subversion);
p.Do(m_link_key);
p.Do(m_name);
+ p.Do(m_channels);
+ p.Do(m_connection_request_counter);
+}
- p.Do(m_channel);
+u32 WiimoteDevice::GetNumber() const
+{
+ return GetBD().back();
}
-//
-//
-//
-//
-// --- Simple and ugly state machine
-//
-//
-//
-//
-//
+bool WiimoteDevice::IsInquiryScanEnabled() const
+{
+ // Our Wii Remote is conveniently discoverable as long as it's enabled and doesn't have a
+ // baseband connection.
+ return !IsConnected() && IsSourceValid();
+}
-bool WiimoteDevice::LinkChannel()
+bool WiimoteDevice::IsPageScanEnabled() const
{
- if (m_connection_state != ConnectionState::Linking)
- return false;
+ // Our Wii Remote will accept a connection as long as it isn't currently connected.
+ return !IsConnected() && IsSourceValid();
+}
- // try to connect L2CAP_PSM_HID_CNTL
- if (!m_hid_control_channel.connected)
- {
- if (m_hid_control_channel.connected_wait)
- return false;
+void WiimoteDevice::SetBasebandState(BasebandState new_state)
+{
+ // Prevent button press from immediately causing connection attempts.
+ m_connection_request_counter = ::Wiimote::UPDATE_FREQ;
- m_hid_control_channel.connected_wait = true;
- SendConnectionRequest(0x0040, L2CAP_PSM_HID_CNTL);
- return true;
- }
+ const bool was_connected = IsConnected();
- // try to config L2CAP_PSM_HID_CNTL
- if (!m_hid_control_channel.config)
- {
- if (m_hid_control_channel.config_wait)
- return false;
+ m_baseband_state = new_state;
- m_hid_control_channel.config_wait = true;
- SendConfigurationRequest(0x0040);
- return true;
- }
+ // Update wiimote connection checkboxes in UI.
+ Host_UpdateDisasmDialog();
- // try to connect L2CAP_PSM_HID_INTR
- if (!m_hid_interrupt_channel.connected)
- {
- if (m_hid_interrupt_channel.connected_wait)
- return false;
+ if (!IsSourceValid())
+ return;
+
+ if (IsConnected() && !was_connected)
+ m_hid_source->EventLinked();
+ else if (!IsConnected() && was_connected)
+ m_hid_source->EventUnlinked();
+}
- m_hid_interrupt_channel.connected_wait = true;
- SendConnectionRequest(0x0041, L2CAP_PSM_HID_INTR);
- return true;
+const WiimoteDevice::SChannel* WiimoteDevice::FindChannelWithPSM(u16 psm) const
+{
+ for (auto& [cid, channel] : m_channels)
+ {
+ if (channel.psm == psm)
+ return &channel;
}
- // try to config L2CAP_PSM_HID_INTR
- if (!m_hid_interrupt_channel.config)
+ return nullptr;
+}
+
+WiimoteDevice::SChannel* WiimoteDevice::FindChannelWithPSM(u16 psm)
+{
+ for (auto& [cid, channel] : m_channels)
{
- if (m_hid_interrupt_channel.config_wait)
- return false;
+ if (channel.psm == psm)
+ return &channel;
+ }
- m_hid_interrupt_channel.config_wait = true;
- SendConfigurationRequest(0x0041);
- return true;
+ return nullptr;
+}
+
+u16 WiimoteDevice::GenerateChannelID() const
+{
+ // "Identifiers from 0x0001 to 0x003F are reserved"
+ constexpr u16 starting_id = 0x40;
+
+ u16 cid = starting_id;
+
+ while (m_channels.count(cid) != 0)
+ ++cid;
+
+ return cid;
+}
+
+bool WiimoteDevice::LinkChannel(u16 psm)
+{
+ const auto* const channel = FindChannelWithPSM(psm);
+
+ // Attempt to connect the channel.
+ if (!channel)
+ {
+ SendConnectionRequest(psm);
+ return false;
}
- DEBUG_LOG(IOS_WIIMOTE, "ConnectionState CONN_LINKING -> CONN_COMPLETE");
- m_connection_state = ConnectionState::Complete;
+ return channel->IsComplete();
+}
- // Update wiimote connection status in the UI
- Host_UpdateDisasmDialog();
+bool WiimoteDevice::IsSourceValid() const
+{
+ return m_hid_source != nullptr;
+}
- return false;
+bool WiimoteDevice::IsConnected() const
+{
+ return m_baseband_state == BasebandState::Complete;
}
-//
-//
-//
-//
-// --- Events
-//
-//
-//
-//
-//
-void WiimoteDevice::Activate(bool ready)
+void WiimoteDevice::Activate(bool connect)
{
- if (ready && (m_connection_state == ConnectionState::Inactive))
+ const char* message = nullptr;
+
+ if (connect && m_baseband_state == BasebandState::Inactive)
{
- m_connection_state = ConnectionState::Ready;
+ SetBasebandState(BasebandState::RequestConnection);
+
+ message = "Wii Remote {} connected";
}
- else if (!ready)
+ else if (!connect && IsConnected())
{
- m_host->RemoteDisconnect(m_connection_handle);
- EventDisconnect();
+ Reset();
+
+ // Does a real remote gracefully disconnect l2cap channels first?
+ // Not doing that doesn't seem to break anything.
+ m_host->RemoteDisconnect(GetBD());
+
+ message = "Wii Remote {} disconnected";
}
+
+ if (message)
+ Core::DisplayMessage(fmt::format(message, GetNumber() + 1), CONNECTION_MESSAGE_TIME);
+}
+
+bool WiimoteDevice::EventConnectionRequest()
+{
+ if (!IsPageScanEnabled())
+ return false;
+
+ Core::DisplayMessage(
+ fmt::format("Wii Remote {} connected from emulated software", GetNumber() + 1),
+ CONNECTION_MESSAGE_TIME);
+
+ SetBasebandState(BasebandState::Complete);
+
+ return true;
}
-void WiimoteDevice::EventConnectionAccepted()
+bool WiimoteDevice::EventConnectionAccept()
{
- DEBUG_LOG(IOS_WIIMOTE, "ConnectionState %x -> CONN_LINKING", int(m_connection_state));
- m_connection_state = ConnectionState::Linking;
+ if (!IsPageScanEnabled())
+ return false;
+
+ SetBasebandState(BasebandState::Complete);
+
+ // A connection acceptance means the remote seeked out the connection.
+ // In this situation the remote actively creates HID channels.
+ m_hid_state = HIDState::Linking;
+
+ return true;
}
-void WiimoteDevice::EventDisconnect()
+void WiimoteDevice::EventDisconnect(u8 reason)
{
- // Send disconnect message to plugin
- Wiimote::ControlChannel(m_connection_handle & 0xFF, Wiimote::DOLPHIN_DISCONNET_CONTROL_CHANNEL,
- nullptr, 0);
+ // If someone wants to be fancy we could also figure out the values for reason
+ // and display things like "Wii Remote %i disconnected due to inactivity!" etc.
+ // FYI: It looks like reason is always 0x13 (User Ended Connection).
+
+ Core::DisplayMessage(
+ fmt::format("Wii Remote {} disconnected by emulated software", GetNumber() + 1),
+ CONNECTION_MESSAGE_TIME);
- m_connection_state = ConnectionState::Inactive;
+ Reset();
+}
+
+void WiimoteDevice::SetSource(WiimoteCommon::HIDWiimote* hid_source)
+{
+ if (m_hid_source && IsConnected())
+ {
+ Activate(false);
+ }
- // Clear channel flags
- ResetChannels();
+ m_hid_source = hid_source;
- // Update wiimote connection status in the UI
- Host_UpdateDisasmDialog();
+ if (m_hid_source)
+ {
+ m_hid_source->SetInterruptCallback(std::bind(&WiimoteDevice::InterruptDataInputCallback, this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3));
+ Activate(true);
+ }
}
-bool WiimoteDevice::EventPagingChanged(u8 page_mode) const
+void WiimoteDevice::Reset()
{
- return (m_connection_state == ConnectionState::Ready) && (page_mode & HCI_PAGE_SCAN_ENABLE);
+ SetBasebandState(BasebandState::Inactive);
+ m_hid_state = HIDState::Inactive;
+ m_channels = {};
}
-void WiimoteDevice::ResetChannels()
+void WiimoteDevice::Update()
{
- // reset connection process
- m_hid_control_channel = {};
- m_hid_interrupt_channel = {};
+ if (m_baseband_state == BasebandState::RequestConnection)
+ {
+ if (m_host->RemoteConnect(*this))
+ {
+ // After a connection request is visible to the controller switch to inactive.
+ SetBasebandState(BasebandState::Inactive);
+ }
+ }
+
+ if (!IsConnected())
+ return;
+
+ // Send configuration for any newly connected channels.
+ for (auto& [cid, channel] : m_channels)
+ {
+ if (channel.IsAccepted() && channel.state == SChannel::State::Inactive)
+ {
+ // A real wii remote has been observed requesting this MTU.
+ constexpr u16 REQUEST_MTU = 185;
+
+ channel.state = SChannel::State::ConfigurationPending;
+ SendConfigurationRequest(channel.remote_cid, REQUEST_MTU, L2CAP_FLUSH_TIMO_DEFAULT);
+ }
+ }
+
+ // If the connection originated from the wii remote it will create
+ // HID control and interrupt channels (in that order).
+ if (m_hid_state == HIDState::Linking)
+ {
+ if (LinkChannel(L2CAP_PSM_HID_CNTL) && LinkChannel(L2CAP_PSM_HID_INTR))
+ {
+ DEBUG_LOG(IOS_WIIMOTE, "HID linking is complete.");
+ m_hid_state = HIDState::Inactive;
+ }
+ }
}
-//
-//
-//
-//
-// --- Input parsing
-//
-//
-//
-//
-//
+void WiimoteDevice::UpdateInput()
+{
+ if (m_connection_request_counter)
+ --m_connection_request_counter;
+
+ if (!IsSourceValid())
+ return;
+
+ // Allow button press to trigger activation after a second of no connection activity.
+ if (!m_connection_request_counter && m_baseband_state == BasebandState::Inactive)
+ {
+ if (Wiimote::NetPlay_GetButtonPress(GetNumber(), m_hid_source->IsButtonPressed()))
+ Activate(true);
+ }
+
+ // Verify interrupt channel is connected and configured.
+ const auto* channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR);
+ if (channel && channel->IsComplete())
+ m_hid_source->Update();
+}
// This function receives L2CAP commands from the CPU
void WiimoteDevice::ExecuteL2capCmd(u8* ptr, u32 size)
{
- // parse the command
l2cap_hdr_t* header = (l2cap_hdr_t*)ptr;
u8* data = ptr + sizeof(l2cap_hdr_t);
const u32 data_size = size - sizeof(l2cap_hdr_t);
@@ -269,55 +377,70 @@ void WiimoteDevice::ExecuteL2capCmd(u8* ptr, u32 size)
return;
}
- switch (header->dcid)
+ if (header->dcid == L2CAP_SIGNAL_CID)
{
- case L2CAP_SIGNAL_CID:
SignalChannel(data, data_size);
- break;
+ return;
+ }
- default:
+ const auto itr = m_channels.find(header->dcid);
+ if (itr == m_channels.end())
{
- DEBUG_ASSERT_MSG(IOS_WIIMOTE, DoesChannelExist(header->dcid),
- "L2CAP: SendACLPacket to unknown channel %i", header->dcid);
+ ERROR_LOG(IOS_WIIMOTE, "L2CAP: SendACLPacket to unknown channel %i", header->dcid);
+ return;
+ }
- const auto itr = m_channel.find(header->dcid);
- const int number = m_connection_handle & 0xFF;
+ const SChannel& channel = itr->second;
+ switch (channel.psm)
+ {
+ case L2CAP_PSM_SDP:
+ HandleSDP(header->dcid, data, data_size);
+ break;
- if (itr != m_channel.end())
+ // Original (non-TR) remotes process "set reports" on control channel.
+ // Commercial games don't use this. Some homebrew does. (e.g. Gecko OS)
+ case L2CAP_PSM_HID_CNTL:
+ {
+ const u8 hid_type = data[0];
+ if (hid_type == ((WiimoteCommon::HID_TYPE_SET_REPORT << 4) | WiimoteCommon::HID_PARAM_OUTPUT))
{
- const SChannel& channel = itr->second;
- switch (channel.psm)
+ struct DataFrame
{
- case L2CAP_PSM_SDP:
- HandleSDP(header->dcid, data, data_size);
- break;
+ l2cap_hdr_t header;
+ u8 hid_type;
+ } data_frame;
- case L2CAP_PSM_HID_CNTL:
- if (number < MAX_BBMOTES)
- Wiimote::ControlChannel(number, header->dcid, data, data_size);
- break;
+ static_assert(sizeof(data_frame) == sizeof(data_frame.hid_type) + sizeof(l2cap_hdr_t));
- case L2CAP_PSM_HID_INTR:
- {
- if (number < MAX_BBMOTES)
- {
- DEBUG_LOG(WIIMOTE, "Wiimote_InterruptChannel");
- DEBUG_LOG(WIIMOTE, " Channel ID: %04x", header->dcid);
- const std::string temp = ArrayToString(data, data_size);
- DEBUG_LOG(WIIMOTE, " Data: %s", temp.c_str());
-
- Wiimote::InterruptChannel(number, header->dcid, data, data_size);
- }
- }
- break;
+ data_frame.header.dcid = channel.remote_cid;
+ data_frame.header.length = sizeof(data_frame.hid_type);
+ data_frame.hid_type = WiimoteCommon::HID_HANDSHAKE_SUCCESS;
+
+ m_host->SendACLPacket(GetBD(), reinterpret_cast<const u8*>(&data_frame), sizeof(data_frame));
- default:
- ERROR_LOG(IOS_WIIMOTE, "Channel 0x04%x has unknown PSM %x", header->dcid, channel.psm);
- break;
- }
+ // Does the wii remote reply on the control or interrupt channel in this situation?
+ m_hid_source->InterruptDataOutput(data + sizeof(hid_type), data_size - sizeof(hid_type));
+ }
+ else
+ {
+ ERROR_LOG(IOS_WIIMOTE, "Unknown HID-type (0x%x) on L2CAP_PSM_HID_CNTL", hid_type);
}
}
break;
+
+ case L2CAP_PSM_HID_INTR:
+ {
+ const u8 hid_type = data[0];
+ if (hid_type == ((WiimoteCommon::HID_TYPE_DATA << 4) | WiimoteCommon::HID_PARAM_OUTPUT))
+ m_hid_source->InterruptDataOutput(data + sizeof(hid_type), data_size - sizeof(hid_type));
+ else
+ ERROR_LOG(IOS_WIIMOTE, "Unknown HID-type (0x%x) on L2CAP_PSM_HID_INTR", hid_type);
+ }
+ break;
+
+ default:
+ ERROR_LOG(IOS_WIIMOTE, "Channel 0x%x has unknown PSM %x", header->dcid, channel.psm);
+ break;
}
}
@@ -365,40 +488,47 @@ void WiimoteDevice::SignalChannel(u8* data, u32 size)
}
}
-//
-//
-//
-//
-// --- Receive Commands from CPU
-//
-//
-//
-//
-//
-
void WiimoteDevice::ReceiveConnectionReq(u8 ident, u8* data, u32 size)
{
l2cap_con_req_cp* command_connection_req = (l2cap_con_req_cp*)data;
- // create the channel
- SChannel& channel = m_channel[command_connection_req->scid];
- channel.psm = command_connection_req->psm;
- channel.scid = command_connection_req->scid;
- channel.dcid = command_connection_req->scid;
-
DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] ReceiveConnectionRequest");
DEBUG_LOG(IOS_WIIMOTE, " Ident: 0x%02x", ident);
- DEBUG_LOG(IOS_WIIMOTE, " PSM: 0x%04x", channel.psm);
- DEBUG_LOG(IOS_WIIMOTE, " SCID: 0x%04x", channel.scid);
- DEBUG_LOG(IOS_WIIMOTE, " DCID: 0x%04x", channel.dcid);
-
- // response
- l2cap_con_rsp_cp rsp;
- rsp.scid = channel.scid;
- rsp.dcid = channel.dcid;
- rsp.result = L2CAP_SUCCESS;
+ DEBUG_LOG(IOS_WIIMOTE, " PSM: 0x%04x", command_connection_req->psm);
+ DEBUG_LOG(IOS_WIIMOTE, " SCID: 0x%04x", command_connection_req->scid);
+
+ l2cap_con_rsp_cp rsp = {};
+ rsp.scid = command_connection_req->scid;
rsp.status = L2CAP_NO_INFO;
+ if (FindChannelWithPSM(command_connection_req->psm) != nullptr)
+ {
+ ERROR_LOG(IOS_WIIMOTE, "Multiple channels with same PSM (%d) are not allowed.",
+ command_connection_req->psm);
+
+ // A real wii remote refuses multiple connections with the same PSM.
+ rsp.result = L2CAP_NO_RESOURCES;
+ rsp.dcid = L2CAP_NULL_CID;
+ }
+ else
+ {
+ // Create the channel.
+ const u16 local_cid = GenerateChannelID();
+
+ SChannel& channel = m_channels[local_cid];
+ channel.psm = command_connection_req->psm;
+ channel.remote_cid = command_connection_req->scid;
+
+ if (channel.psm != L2CAP_PSM_SDP && channel.psm != L2CAP_PSM_HID_CNTL &&
+ channel.psm != L2CAP_PSM_HID_INTR)
+ {
+ WARN_LOG(IOS_WIIMOTE, "L2CAP connection with unknown psm (0x%x)", channel.psm);
+ }
+
+ rsp.result = L2CAP_SUCCESS;
+ rsp.dcid = local_cid;
+ }
+
DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] SendConnectionResponse");
SendCommandToACL(ident, L2CAP_CONNECT_RSP, sizeof(l2cap_con_rsp_cp), (u8*)&rsp);
}
@@ -419,14 +549,8 @@ void WiimoteDevice::ReceiveConnectionResponse(u8 ident, u8* data, u32 size)
DEBUG_ASSERT(rsp->status == L2CAP_NO_INFO);
DEBUG_ASSERT(DoesChannelExist(rsp->scid));
- SChannel& channel = m_channel[rsp->scid];
- channel.dcid = rsp->dcid;
-
- // update state machine
- if (channel.psm == L2CAP_PSM_HID_CNTL)
- m_hid_control_channel.connected = true;
- else if (channel.psm == L2CAP_PSM_HID_INTR)
- m_hid_interrupt_channel.connected = true;
+ SChannel& channel = m_channels[rsp->scid];
+ channel.remote_cid = rsp->dcid;
}
void WiimoteDevice::ReceiveConfigurationReq(u8 ident, u8* data, u32 size)
@@ -438,7 +562,7 @@ void WiimoteDevice::ReceiveConfigurationReq(u8 ident, u8* data, u32 size)
DEBUG_ASSERT(command_config_req->flags == 0x00);
DEBUG_ASSERT(DoesChannelExist(command_config_req->dcid));
- SChannel& channel = m_channel[command_config_req->dcid];
+ SChannel& channel = m_channels[command_config_req->dcid];
DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] ReceiveConfigurationRequest");
DEBUG_LOG(IOS_WIIMOTE, " Ident: 0x%02x", ident);
@@ -451,13 +575,16 @@ void WiimoteDevice::ReceiveConfigurationReq(u8 ident, u8* data, u32 size)
u32 resp_len = 0;
l2cap_cfg_rsp_cp* rsp = (l2cap_cfg_rsp_cp*)temp_buffer;
- rsp->scid = channel.dcid;
+ rsp->scid = channel.remote_cid;
rsp->flags = 0x00;
rsp->result = L2CAP_SUCCESS;
resp_len += sizeof(l2cap_cfg_rsp_cp);
- // read configuration options
+ // If the option is not provided, configure the default.
+ u16 remote_mtu = L2CAP_MTU_DEFAULT;
+
+ // Read configuration options.
while (offset < size)
{
l2cap_cfg_opt_t* options = (l2cap_cfg_opt_t*)&data[offset];
@@ -469,16 +596,16 @@ void WiimoteDevice::ReceiveConfigurationReq(u8 ident, u8* data, u32 size)
{
DEBUG_ASSERT(options->length == L2CAP_OPT_MTU_SIZE);
l2cap_cfg_opt_val_t* mtu = (l2cap_cfg_opt_val_t*)&data[offset];
- channel.mtu = mtu->mtu;
+ remote_mtu = mtu->mtu;
DEBUG_LOG(IOS_WIIMOTE, " MTU: 0x%04x", mtu->mtu);
}
break;
+ // We don't care what the flush timeout is. Our packets are not dropped.
case L2CAP_OPT_FLUSH_TIMO:
{
DEBUG_ASSERT(options->length == L2CAP_OPT_FLUSH_TIMO_SIZE);
l2cap_cfg_opt_val_t* flush_time_out = (l2cap_cfg_opt_val_t*)&data[offset];
- channel.flush_time_out = flush_time_out->flush_timo;
DEBUG_LOG(IOS_WIIMOTE, " FlushTimeOut: 0x%04x", flush_time_out->flush_timo);
}
break;
@@ -498,11 +625,7 @@ void WiimoteDevice::ReceiveConfigurationReq(u8 ident, u8* data, u32 size)
DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] SendConfigurationResponse");
SendCommandToACL(ident, L2CAP_CONFIG_RSP, resp_len, temp_buffer);
- // update state machine
- if (channel.psm == L2CAP_PSM_HID_CNTL)
- m_hid_control_channel.connected = true;
- else if (channel.psm == L2CAP_PSM_HID_INTR)
- m_hid_interrupt_channel.connected = true;
+ channel.remote_mtu = remote_mtu;
}
void WiimoteDevice::ReceiveConfigurationResponse(u8 ident, u8* data, u32 size)
@@ -516,13 +639,7 @@ void WiimoteDevice::ReceiveConfigurationResponse(u8 ident, u8* data, u32 size)
DEBUG_ASSERT(rsp->result == L2CAP_SUCCESS);
- // update state machine
- const SChannel& channel = m_channel[rsp->scid];
-
- if (channel.psm == L2CAP_PSM_HID_CNTL)
- m_hid_control_channel.config = true;
- else if (channel.psm == L2CAP_PSM_HID_INTR)
- m_hid_interrupt_channel.config = true;
+ m_channels[rsp->scid].state = SChannel::State::Complete;
}
void WiimoteDevice::ReceiveDisconnectionReq(u8 ident, u8* data, u32 size)
@@ -534,7 +651,10 @@ void WiimoteDevice::ReceiveDisconnectionReq(u8 ident, u8* data, u32 size)
DEBUG_LOG(IOS_WIIMOTE, " DCID: 0x%04x", command_disconnection_req->dcid);
DEBUG_LOG(IOS_WIIMOTE, " SCID: 0x%04x", command_disconnection_req->scid);
- // response
+ DEBUG_ASSERT(DoesChannelExist(command_disconnection_req->dcid));
+
+ m_channels.erase(command_disconnection_req->dcid);
+
l2cap_discon_req_cp rsp;
rsp.dcid = command_disconnection_req->dcid;
rsp.scid = command_disconnection_req->scid;
@@ -543,28 +663,19 @@ void WiimoteDevice::ReceiveDisconnectionReq(u8 ident, u8* data, u32 size)
SendCommandToACL(ident, L2CAP_DISCONNECT_RSP, sizeof(l2cap_discon_req_cp), (u8*)&rsp);
}
-//
-//
-//
-//
-// --- Send Commands To CPU
-//
-//
-//
-//
-//
-
-// We assume Wiimote is always connected
-void WiimoteDevice::SendConnectionRequest(u16 scid, u16 psm)
+void WiimoteDevice::SendConnectionRequest(u16 psm)
{
- // create the channel
- SChannel& channel = m_channel[scid];
+ DEBUG_ASSERT(FindChannelWithPSM(psm) == nullptr);
+
+ const u16 local_cid = GenerateChannelID();
+
+ // Create the channel.
+ SChannel& channel = m_channels[local_cid];
channel.psm = psm;
- channel.scid = scid;
l2cap_con_req_cp cr;
cr.psm = psm;
- cr.scid = scid;
+ cr.scid = local_cid;
DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] SendConnectionRequest");
DEBUG_LOG(IOS_WIIMOTE, " Psm: 0x%04x", cr.psm);
@@ -573,34 +684,13 @@ void WiimoteDevice::SendConnectionRequest(u16 scid, u16 psm)
SendCommandToACL(L2CAP_CONNECT_REQ, L2CAP_CONNECT_REQ, sizeof(l2cap_con_req_cp), (u8*)&cr);
}
-// We don't initially disconnect Wiimote though ...
-void WiimoteDevice::SendDisconnectRequest(u16 scid)
-{
- // create the channel
- const SChannel& channel = m_channel[scid];
-
- l2cap_discon_req_cp cr;
- cr.dcid = channel.dcid;
- cr.scid = channel.scid;
-
- DEBUG_LOG(IOS_WIIMOTE, "[L2CAP] SendDisconnectionRequest");
- DEBUG_LOG(IOS_WIIMOTE, " Dcid: 0x%04x", cr.dcid);
- DEBUG_LOG(IOS_WIIMOTE, " Scid: 0x%04x", cr.scid);
-
- SendCommandToACL(L2CAP_DISCONNECT_REQ, L2CAP_DISCONNECT_REQ, sizeof(l2cap_discon_req_cp),
- (u8*)&cr);
-}
-
-void WiimoteDevice::SendConfigurationRequest(u16 scid, u16 mtu, u16 flush_time_out)
+void WiimoteDevice::SendConfigurationRequest(u16 cid, u16 mtu, u16 flush_time_out)
{
- DEBUG_ASSERT(DoesChannelExist(scid));
- const SChannel& channel = m_channel[scid];
-
u8 buffer[1024];
int offset = 0;
l2cap_cfg_req_cp* cr = (l2cap_cfg_req_cp*)&buffer[offset];
- cr->dcid = channel.dcid;
+ cr->dcid = cid;
cr->flags = 0;
offset += sizeof(l2cap_cfg_req_cp);
@@ -610,14 +700,8 @@ void WiimoteDevice::SendConfigurationRequest(u16 scid, u16 mtu, u16 flush_time_o
l2cap_cfg_opt_t* options;
- // (shuffle2) currently we end up not appending options. this is because we don't
- // negotiate after trying to set MTU = 0 fails (stack will respond with
- // "configuration failed" msg...). This is still fine, we'll just use whatever the
- // Bluetooth stack defaults to.
- if (mtu || channel.mtu)
+ if (mtu != L2CAP_MTU_DEFAULT)
{
- if (mtu == 0)
- mtu = channel.mtu;
options = (l2cap_cfg_opt_t*)&buffer[offset];
offset += sizeof(l2cap_cfg_opt_t);
options->type = L2CAP_OPT_MTU;
@@ -627,10 +711,8 @@ void WiimoteDevice::SendConfigurationRequest(u16 scid, u16 mtu, u16 flush_time_o
DEBUG_LOG(IOS_WIIMOTE, " MTU: 0x%04x", mtu);
}
- if (flush_time_out || channel.flush_time_out)
+ if (flush_time_out != L2CAP_FLUSH_TIMO_DEFAULT)
{
- if (flush_time_out == 0)
- flush_time_out = channel.flush_time_out;
options = (l2cap_cfg_opt_t*)&buffer[offset];
offset += sizeof(l2cap_cfg_opt_t);
options->type = L2CAP_OPT_FLUSH_TIMO;
@@ -643,22 +725,11 @@ void WiimoteDevice::SendConfigurationRequest(u16 scid, u16 mtu, u16 flush_time_o
SendCommandToACL(L2CAP_CONFIG_REQ, L2CAP_CONFIG_REQ, offset, buffer);
}
-//
-//
-//
-//
-// --- SDP
-//
-//
-//
-//
-//
-
-#define SDP_UINT8 0x08
-#define SDP_UINT16 0x09
-#define SDP_UINT32 0x0A
-#define SDP_SEQ8 0x35
-#define SDP_SEQ16 0x36
+constexpr u8 SDP_UINT8 = 0x08;
+constexpr u8 SDP_UINT16 = 0x09;
+constexpr u8 SDP_UINT32 = 0x0A;
+constexpr u8 SDP_SEQ8 = 0x35;
+constexpr u8 SDP_SEQ16 = 0x36;
void WiimoteDevice::SDPSendServiceSearchResponse(u16 cid, u16 transaction_id,
u8* service_search_pattern,
@@ -699,7 +770,7 @@ void WiimoteDevice::SDPSendServiceSearchResponse(u16 cid, u16 transaction_id,
offset++; // No continuation state;
header->length = (u16)(offset - sizeof(l2cap_hdr_t));
- m_host->SendACLPacket(GetConnectionHandle(), data_frame, header->length + sizeof(l2cap_hdr_t));
+ m_host->SendACLPacket(GetBD(), data_frame, header->length + sizeof(l2cap_hdr_t));
}
static u32 ParseCont(u8* cont)
@@ -793,7 +864,7 @@ void WiimoteDevice::SDPSendServiceAttributeResponse(u16 cid, u16 transaction_id,
offset += packet_size;
header->length = (u16)(offset - sizeof(l2cap_hdr_t));
- m_host->SendACLPacket(GetConnectionHandle(), data_frame, header->length + sizeof(l2cap_hdr_t));
+ m_host->SendACLPacket(GetBD(), data_frame, header->length + sizeof(l2cap_hdr_t));
}
void WiimoteDevice::HandleSDP(u16 cid, u8* data, u32 size)
@@ -843,23 +914,12 @@ void WiimoteDevice::HandleSDP(u16 cid, u8* data, u32 size)
break;
default:
- ERROR_LOG(IOS_WIIMOTE, "WIIMOTE: Unknown SDP command %x", data[0]);
+ ERROR_LOG(IOS_WIIMOTE, "Unknown SDP command %x", data[0]);
PanicAlert("WIIMOTE: Unknown SDP command %x", data[0]);
break;
}
}
-//
-//
-//
-//
-// --- Data Send Functions
-//
-//
-//
-//
-//
-
void WiimoteDevice::SendCommandToACL(u8 ident, u8 code, u8 command_length, u8* command_data)
{
u8 data_frame[1024];
@@ -882,50 +942,38 @@ void WiimoteDevice::SendCommandToACL(u8 ident, u8 code, u8 command_length, u8* c
DEBUG_LOG(IOS_WIIMOTE, " Ident: 0x%02x", ident);
DEBUG_LOG(IOS_WIIMOTE, " Code: 0x%02x", code);
- // send ....
- m_host->SendACLPacket(GetConnectionHandle(), data_frame, header->length + sizeof(l2cap_hdr_t));
+ m_host->SendACLPacket(GetBD(), data_frame, header->length + sizeof(l2cap_hdr_t));
}
-void WiimoteDevice::ReceiveL2capData(u16 scid, const void* data, u32 size)
+void WiimoteDevice::InterruptDataInputCallback(u8 hid_type, const u8* data, u32 size)
{
- // Allocate DataFrame
- u8 data_frame[1024];
- u32 offset = 0;
- l2cap_hdr_t* header = (l2cap_hdr_t*)data_frame;
- offset += sizeof(l2cap_hdr_t);
+ const auto* const channel = FindChannelWithPSM(L2CAP_PSM_HID_INTR);
- // Check if we are already reporting on this channel
- DEBUG_ASSERT(DoesChannelExist(scid));
- const SChannel& channel = m_channel[scid];
+ if (!channel)
+ {
+ WARN_LOG(IOS_WIIMOTE, "Data callback with invalid L2CAP_PSM_HID_INTR channel.");
+ return;
+ }
- // Add an additional 4 byte header to the Wiimote report
- header->dcid = channel.dcid;
- header->length = size;
+ struct DataFrame
+ {
+ l2cap_hdr_t header;
+ u8 hid_type;
+ std::array<u8, WiimoteCommon::MAX_PAYLOAD - sizeof(hid_type)> data;
+ } data_frame;
- // Copy the Wiimote report to data_frame
- memcpy(data_frame + offset, data, size);
- // Update offset to the final size of the report
- offset += size;
+ static_assert(sizeof(data_frame) == sizeof(data_frame.data) + sizeof(u8) + sizeof(l2cap_hdr_t));
- // Send the report
- m_host->SendACLPacket(GetConnectionHandle(), data_frame, offset);
-}
-} // namespace IOS::HLE
+ data_frame.header.dcid = channel->remote_cid;
+ data_frame.header.length = u16(sizeof(hid_type) + size);
+ data_frame.hid_type = hid_type;
+ std::copy_n(data, size, data_frame.data.data());
-namespace Core
-{
-// This is called continuously from the Wiimote plugin as soon as it has received
-// a reporting mode. size is the byte size of the report.
-void Callback_WiimoteInterruptChannel(int number, u16 channel_id, const u8* data, u32 size)
-{
- DEBUG_LOG(WIIMOTE, "====================");
- DEBUG_LOG(WIIMOTE, "Callback_WiimoteInterruptChannel: (Wiimote: #%i)", number);
- DEBUG_LOG(WIIMOTE, " Data: %s", ArrayToString(data, size, 50).c_str());
- DEBUG_LOG(WIIMOTE, " Channel: %x", channel_id);
+ const u32 data_frame_size = data_frame.header.length + sizeof(l2cap_hdr_t);
- const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
- IOS::HLE::GetIOS()->GetDeviceByName("/dev/usb/oh1/57e/305"));
- if (bt)
- bt->AccessWiimoteByIndex(number)->ReceiveL2capData(channel_id, data, size);
+ // This should never be a problem as l2cap requires a minimum MTU of 48 bytes.
+ DEBUG_ASSERT(data_frame_size <= channel->remote_mtu);
+
+ m_host->SendACLPacket(GetBD(), reinterpret_cast<const u8*>(&data_frame), data_frame_size);
}
-} // namespace Core
+} // namespace IOS::HLE
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.h b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.h
index c6cadf0cf7..6573db08f2 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.h
+++ b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.h
@@ -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,89 +24,129 @@ 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);
@@ -113,8 +154,6 @@ private:
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);
diff --git a/Source/Core/Core/IOS/USB/Bluetooth/hci.h b/Source/Core/Core/IOS/USB/Bluetooth/hci.h
index 794b9af0d7..c36bb3d8d6 100644
--- a/Source/Core/Core/IOS/USB/Bluetooth/hci.h
+++ b/Source/Core/Core/IOS/USB/Bluetooth/hci.h
@@ -2652,6 +2652,7 @@ struct SHCIEventInquiryComplete
u8 EventType;
u8 PayloadLength;
u8 EventStatus;
+ u8 num_responses;
};
struct SHCIEventReadClockOffsetComplete
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index 789d08f275..95f052290d 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -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,
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index db18ec8a02..4e654c8b64 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -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());
+ }
});
}
diff --git a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp
index 28426fc114..6398bfa9e4 100644
--- a/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/Wiimote/Wiimote.cpp
@@ -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,13 +1061,13 @@ 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;)
{
@@ -1077,8 +1075,8 @@ void Device::ProcessInputReport(WiimoteReal::Report& report)
{
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>)