Skip to content
Permalink
Browse files
Merge pull request #8985 from jordan-woyak/btemu-cleanup
BTEmu/Wiimote: Fixes and Cleanups.
  • Loading branch information
JMC47 committed Sep 14, 2020
2 parents eae6819 + 0ad123b commit 4f1f849
Show file tree
Hide file tree
Showing 22 changed files with 877 additions and 1,036 deletions.
@@ -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);

@@ -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);
}
}
}
@@ -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

@@ -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()
@@ -94,11 +94,11 @@ class DataReportBuilder

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;
};
@@ -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
@@ -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)
@@ -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;

0 comments on commit 4f1f849

Please sign in to comment.