Merge pull request #8985 from jordan-woyak/btemu-cleanup
BTEmu/Wiimote: Fixes and Cleanups.
This commit is contained in:
commit
4f1f849c9d
|
@ -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);
|
||||
|
||||
if (source == WiimoteSource::Emulated)
|
||||
::Wiimote::Connect(index, true);
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
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 @@ 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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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::EventLinked()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Wiimote::HIDOutputReport(const void* data, u32 size)
|
||||
void Wiimote::EventUnlinked()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Wiimote::InterruptDataOutput(const u8* data, u32 size)
|
||||
{
|
||||
if (!size)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "HIDOutputReport: zero sized data");
|
||||
ERROR_LOG(WIIMOTE, "OutputData: zero sized data");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& rpt = *static_cast<const OutputReportGeneric*>(data);
|
||||
auto& rpt = *reinterpret_cast<const OutputReportGeneric*>(data);
|
||||
const int rpt_size = size - OutputReportGeneric::HEADER_SIZE;
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "HIDOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index,
|
||||
m_reporting_channel, int(rpt.rpt_id));
|
||||
if (!rpt_size)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "OutputData: zero sized report");
|
||||
return;
|
||||
}
|
||||
|
||||
// WiiBrew:
|
||||
// In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature.
|
||||
|
@ -132,21 +135,16 @@ void Wiimote::HIDOutputReport(const void* data, u32 size)
|
|||
}
|
||||
}
|
||||
|
||||
void Wiimote::CallbackInterruptChannel(const u8* data, u32 size)
|
||||
{
|
||||
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, data, size);
|
||||
}
|
||||
|
||||
void Wiimote::SendAck(OutputReportID rpt_id, ErrorCode error_code)
|
||||
{
|
||||
TypedHIDInputData<InputReportAck> rpt(InputReportID::Ack);
|
||||
auto& ack = rpt.data;
|
||||
TypedInputData<InputReportAck> rpt(InputReportID::Ack);
|
||||
auto& ack = rpt.payload;
|
||||
|
||||
ack.buttons = m_status.buttons;
|
||||
ack.rpt_id = rpt_id;
|
||||
ack.error_code = error_code;
|
||||
|
||||
CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
}
|
||||
|
||||
void Wiimote::HandleExtensionSwap()
|
||||
|
@ -246,9 +244,9 @@ void Wiimote::HandleRequestStatus(const OutputReportRequestStatus&)
|
|||
// Less than 0x20 triggers the low-battery flag:
|
||||
m_status.battery_low = m_status.battery < 0x20;
|
||||
|
||||
TypedHIDInputData<InputReportStatus> rpt(InputReportID::Status);
|
||||
rpt.data = m_status;
|
||||
CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
|
||||
TypedInputData<InputReportStatus> rpt(InputReportID::Status);
|
||||
rpt.payload = m_status;
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
}
|
||||
|
||||
void Wiimote::HandleWriteData(const OutputReportWriteData& wd)
|
||||
|
@ -442,8 +440,8 @@ bool Wiimote::ProcessReadDataRequest()
|
|||
return false;
|
||||
}
|
||||
|
||||
TypedHIDInputData<InputReportReadDataReply> rpt(InputReportID::ReadDataReply);
|
||||
auto& reply = rpt.data;
|
||||
TypedInputData<InputReportReadDataReply> rpt(InputReportID::ReadDataReply);
|
||||
auto& reply = rpt.payload;
|
||||
|
||||
reply.buttons = m_status.buttons;
|
||||
reply.address = Common::swap16(m_read_request.address);
|
||||
|
@ -539,7 +537,7 @@ bool Wiimote::ProcessReadDataRequest()
|
|||
|
||||
reply.error = static_cast<u8>(error_code);
|
||||
|
||||
CallbackInterruptChannel(rpt.GetData(), rpt.GetSize());
|
||||
InterruptDataInputCallback(rpt.GetData(), rpt.GetSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -552,7 +550,6 @@ void Wiimote::DoState(PointerWrap& p)
|
|||
// No need to sync. This is not wiimote state.
|
||||
// p.Do(m_sensor_bar_on_top);
|
||||
|
||||
p.Do(m_reporting_channel);
|
||||
p.Do(m_reporting_mode);
|
||||
p.Do(m_reporting_continuous);
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@ void Wiimote::Reset()
|
|||
SetRumble(false);
|
||||
|
||||
// Wiimote starts in non-continuous CORE mode:
|
||||
m_reporting_channel = 0;
|
||||
m_reporting_mode = InputReportID::ReportCore;
|
||||
m_reporting_continuous = false;
|
||||
|
||||
|
@ -404,10 +403,6 @@ void Wiimote::UpdateButtonsStatus()
|
|||
// This is called every ::Wiimote::UPDATE_FREQ (200hz)
|
||||
void Wiimote::Update()
|
||||
{
|
||||
// Check if connected.
|
||||
if (0 == m_reporting_channel)
|
||||
return;
|
||||
|
||||
const auto lock = GetStateLock();
|
||||
|
||||
// Hotkey / settings modifier
|
||||
|
@ -567,7 +562,7 @@ void Wiimote::SendDataReport()
|
|||
Movie::CheckWiimoteStatus(m_index, rpt_builder, m_active_extension, GetExtensionEncryptionKey());
|
||||
|
||||
// Send the report:
|
||||
CallbackInterruptChannel(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());
|
||||
InterruptDataInputCallback(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());
|
||||
|
||||
// The interleaved reporting modes toggle back and forth:
|
||||
if (InputReportID::ReportInterleave1 == m_reporting_mode)
|
||||
|
@ -576,97 +571,7 @@ void Wiimote::SendDataReport()
|
|||
m_reporting_mode = InputReportID::ReportInterleave1;
|
||||
}
|
||||
|
||||
void Wiimote::ControlChannel(const u16 channel_id, const void* data, u32 size)
|
||||
{
|
||||
// Check for custom communication
|
||||
if (channel_id == ::Wiimote::DOLPHIN_DISCONNET_CONTROL_CHANNEL)
|
||||
{
|
||||
// Wii Remote disconnected.
|
||||
Reset();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "ControlChannel: zero sized data");
|
||||
return;
|
||||
}
|
||||
|
||||
m_reporting_channel = channel_id;
|
||||
|
||||
const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);
|
||||
|
||||
DEBUG_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index,
|
||||
hidp.type, hidp.param);
|
||||
|
||||
switch (hidp.type)
|
||||
{
|
||||
case HID_TYPE_HANDSHAKE:
|
||||
PanicAlert("HID_TYPE_HANDSHAKE - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUPUT");
|
||||
break;
|
||||
|
||||
case HID_TYPE_SET_REPORT:
|
||||
if (HID_PARAM_INPUT == hidp.param)
|
||||
{
|
||||
PanicAlert("HID_TYPE_SET_REPORT - INPUT");
|
||||
}
|
||||
else
|
||||
{
|
||||
// AyuanX: My experiment shows Control Channel is never used
|
||||
// shuffle2: but lwbt uses this, so we'll do what we must :)
|
||||
HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE);
|
||||
|
||||
// TODO: Should this be above the previous?
|
||||
u8 handshake = HID_HANDSHAKE_SUCCESS;
|
||||
CallbackInterruptChannel(&handshake, sizeof(handshake));
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_TYPE_DATA:
|
||||
PanicAlert("HID_TYPE_DATA - %s", (hidp.param == HID_PARAM_INPUT) ? "INPUT" : "OUTPUT");
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("HidControlChannel: Unknown type %x and param %x", hidp.type, hidp.param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Wiimote::InterruptChannel(const u16 channel_id, const void* data, u32 size)
|
||||
{
|
||||
if (!size)
|
||||
{
|
||||
ERROR_LOG(WIIMOTE, "InterruptChannel: zero sized data");
|
||||
return;
|
||||
}
|
||||
|
||||
m_reporting_channel = channel_id;
|
||||
|
||||
const auto& hidp = *reinterpret_cast<const HIDPacket*>(data);
|
||||
|
||||
switch (hidp.type)
|
||||
{
|
||||
case HID_TYPE_DATA:
|
||||
switch (hidp.param)
|
||||
{
|
||||
case HID_PARAM_OUTPUT:
|
||||
HIDOutputReport(hidp.data, size - HIDPacket::HEADER_SIZE);
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("HidInput: HID_TYPE_DATA - param 0x%02x", hidp.param);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlert("HidInput: Unknown type 0x%02x and param 0x%02x", hidp.type, hidp.param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Wiimote::CheckForButtonPress()
|
||||
bool Wiimote::IsButtonPressed()
|
||||
{
|
||||
u16 buttons = 0;
|
||||
const auto lock = GetStateLock();
|
||||
|
|
|
@ -85,7 +85,7 @@ void UpdateCalibrationDataChecksum(T& data, int cksum_bytes)
|
|||
}
|
||||
}
|
||||
|
||||
class Wiimote : public ControllerEmu::EmulatedController
|
||||
class Wiimote : public ControllerEmu::EmulatedController, public WiimoteCommon::HIDWiimote
|
||||
{
|
||||
public:
|
||||
static constexpr u16 IR_LOW_X = 0x7F;
|
||||
|
@ -124,12 +124,12 @@ 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;
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
constexpr u16 L2CAP_PSM_HID_CNTL = 0x0011;
|
||||
constexpr u16 L2CAP_PSM_HID_INTR = 0x0013;
|
||||
|
||||
WiimoteScannerLinux::WiimoteScannerLinux() : m_device_id(-1), m_device_sock(-1)
|
||||
{
|
||||
// Get the id of the first Bluetooth device.
|
||||
|
@ -139,8 +142,8 @@ bool WiimoteLinux::ConnectInternal()
|
|||
addr.l2_bdaddr = m_bdaddr;
|
||||
addr.l2_cid = 0;
|
||||
|
||||
// Output channel
|
||||
addr.l2_psm = htobs(WC_OUTPUT);
|
||||
// Control channel
|
||||
addr.l2_psm = htobs(L2CAP_PSM_HID_CNTL);
|
||||
if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
|
||||
{
|
||||
int retry = 0;
|
||||
|
@ -149,7 +152,7 @@ bool WiimoteLinux::ConnectInternal()
|
|||
// If opening channel fails sleep and try again
|
||||
if (retry == 3)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to connect output channel to Wiimote: %s", strerror(errno));
|
||||
WARN_LOG(WIIMOTE, "Unable to connect control channel of Wiimote: %s", strerror(errno));
|
||||
close(m_cmd_sock);
|
||||
m_cmd_sock = -1;
|
||||
return false;
|
||||
|
@ -160,12 +163,12 @@ bool WiimoteLinux::ConnectInternal()
|
|||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to open output socket to Wiimote: %s", strerror(errno));
|
||||
WARN_LOG(WIIMOTE, "Unable to open control socket to Wiimote: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Input channel
|
||||
addr.l2_psm = htobs(WC_INPUT);
|
||||
// Interrupt channel
|
||||
addr.l2_psm = htobs(L2CAP_PSM_HID_INTR);
|
||||
if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)))
|
||||
{
|
||||
int retry = 0;
|
||||
|
@ -174,7 +177,7 @@ bool WiimoteLinux::ConnectInternal()
|
|||
// If opening channel fails sleep and try again
|
||||
if (retry == 3)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to connect input channel to Wiimote: %s", strerror(errno));
|
||||
WARN_LOG(WIIMOTE, "Unable to connect interrupt channel of Wiimote: %s", strerror(errno));
|
||||
close(m_int_sock);
|
||||
close(m_cmd_sock);
|
||||
m_int_sock = m_cmd_sock = -1;
|
||||
|
@ -186,7 +189,7 @@ bool WiimoteLinux::ConnectInternal()
|
|||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to open input socket from Wiimote: %s", strerror(errno));
|
||||
WARN_LOG(WIIMOTE, "Unable to open interrupt socket to Wiimote: %s", strerror(errno));
|
||||
close(m_cmd_sock);
|
||||
m_int_sock = m_cmd_sock = -1;
|
||||
return false;
|
||||
|
|
|
@ -49,6 +49,7 @@ static std::mutex s_known_ids_mutex;
|
|||
std::recursive_mutex g_wiimotes_mutex;
|
||||
|
||||
// Real wii remotes assigned to a particular slot.
|
||||
// Assignments must be done from the CPU thread with the above mutex held.
|
||||
std::unique_ptr<Wiimote> g_wiimotes[MAX_BBMOTES];
|
||||
|
||||
struct WiimotePoolEntry
|
||||
|
@ -129,6 +130,8 @@ void AddWiimoteToPool(std::unique_ptr<Wiimote> wiimote)
|
|||
return;
|
||||
}
|
||||
|
||||
wiimote->EmuStop();
|
||||
|
||||
std::lock_guard lk(g_wiimotes_mutex);
|
||||
s_wiimote_pool.emplace_back(WiimotePoolEntry{std::move(wiimote)});
|
||||
}
|
||||
|
@ -221,54 +224,32 @@ void Wiimote::ClearReadQueue()
|
|||
}
|
||||
}
|
||||
|
||||
void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
|
||||
void Wiimote::EventLinked()
|
||||
{
|
||||
// Check for custom communication
|
||||
if (channel == ::Wiimote::DOLPHIN_DISCONNET_CONTROL_CHANNEL)
|
||||
{
|
||||
if (m_really_disconnect)
|
||||
{
|
||||
DisconnectInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
EmuStop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InterruptChannel(channel, data, size);
|
||||
const auto& hidp = *static_cast<const HIDPacket*>(data);
|
||||
if (hidp.type == HID_TYPE_SET_REPORT)
|
||||
{
|
||||
u8 handshake = HID_HANDSHAKE_SUCCESS;
|
||||
Core::Callback_WiimoteInterruptChannel(m_index, channel, &handshake, sizeof(handshake));
|
||||
}
|
||||
}
|
||||
m_is_linked = true;
|
||||
|
||||
ClearReadQueue();
|
||||
ResetDataReporting();
|
||||
EnablePowerAssertionInternal();
|
||||
}
|
||||
|
||||
void Wiimote::InterruptChannel(const u16 channel, const void* const data, const u32 size)
|
||||
void Wiimote::EventUnlinked()
|
||||
{
|
||||
// first interrupt/control channel sent
|
||||
if (channel != m_channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
if (m_really_disconnect)
|
||||
DisconnectInternal();
|
||||
else
|
||||
EmuStop();
|
||||
}
|
||||
|
||||
ClearReadQueue();
|
||||
|
||||
EmuStart();
|
||||
}
|
||||
|
||||
auto const report_data = static_cast<const u8*>(data);
|
||||
Report rpt(report_data, report_data + size);
|
||||
void Wiimote::InterruptDataOutput(const u8* data, const u32 size)
|
||||
{
|
||||
Report rpt(size + REPORT_HID_HEADER_SIZE);
|
||||
std::copy_n(data, size, rpt.data() + REPORT_HID_HEADER_SIZE);
|
||||
|
||||
// Convert output DATA packets to SET_REPORT packets.
|
||||
// Nintendo Wiimotes work without this translation, but 3rd
|
||||
// party ones don't.
|
||||
if (rpt[0] == 0xa2)
|
||||
{
|
||||
rpt[0] = WR_SET_REPORT | BT_OUTPUT;
|
||||
}
|
||||
rpt[0] = WR_SET_REPORT | BT_OUTPUT;
|
||||
|
||||
// Disallow games from turning off all of the LEDs.
|
||||
// It makes Wiimote connection status confusing.
|
||||
|
@ -299,7 +280,11 @@ void Wiimote::Read()
|
|||
Report rpt(MAX_PAYLOAD);
|
||||
auto const result = IORead(rpt.data());
|
||||
|
||||
if (result > 0 && m_channel > 0)
|
||||
// Drop the report if not connected.
|
||||
if (!m_is_linked)
|
||||
return;
|
||||
|
||||
if (result > 0)
|
||||
{
|
||||
if (SConfig::GetInstance().iBBDumpPort > 0 && m_index == WIIMOTE_BALANCE_BOARD)
|
||||
{
|
||||
|
@ -456,25 +441,18 @@ Report& Wiimote::ProcessReadQueue()
|
|||
|
||||
void Wiimote::Update()
|
||||
{
|
||||
if (!IsConnected())
|
||||
{
|
||||
HandleWiimoteDisconnect(m_index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pop through the queued reports
|
||||
const Report& rpt = ProcessReadQueue();
|
||||
|
||||
// Send the report
|
||||
if (!rpt.empty() && m_channel > 0)
|
||||
{
|
||||
Core::Callback_WiimoteInterruptChannel(m_index, m_channel, rpt.data(), (u32)rpt.size());
|
||||
}
|
||||
if (!rpt.empty())
|
||||
InterruptCallback(rpt.front(), rpt.data() + REPORT_HID_HEADER_SIZE,
|
||||
u32(rpt.size() - REPORT_HID_HEADER_SIZE));
|
||||
}
|
||||
|
||||
bool Wiimote::CheckForButtonPress()
|
||||
bool Wiimote::IsButtonPressed()
|
||||
{
|
||||
Report& rpt = ProcessReadQueue();
|
||||
Report& rpt = m_last_input_report;
|
||||
if (rpt.size() >= 4)
|
||||
{
|
||||
const auto mode = InputReportID(rpt[1]);
|
||||
|
@ -512,23 +490,16 @@ bool Wiimote::PrepareOnThread()
|
|||
(Common::SleepCurrentThread(200), IOWrite(req_status_report, sizeof(req_status_report)));
|
||||
}
|
||||
|
||||
void Wiimote::EmuStart()
|
||||
{
|
||||
ResetDataReporting();
|
||||
EnablePowerAssertionInternal();
|
||||
}
|
||||
|
||||
void Wiimote::EmuStop()
|
||||
{
|
||||
m_channel = 0;
|
||||
m_is_linked = false;
|
||||
|
||||
ResetDataReporting();
|
||||
DisablePowerAssertionInternal();
|
||||
}
|
||||
|
||||
void Wiimote::EmuResume()
|
||||
{
|
||||
m_last_input_report.clear();
|
||||
|
||||
EnablePowerAssertionInternal();
|
||||
}
|
||||
|
||||
|
@ -811,11 +782,6 @@ int Wiimote::GetIndex() const
|
|||
return m_index;
|
||||
}
|
||||
|
||||
void Wiimote::SetChannel(u16 channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
}
|
||||
|
||||
void LoadSettings()
|
||||
{
|
||||
std::string ini_filename = File::GetUserPath(D_CONFIG_IDX) + WIIMOTE_INI_NAME ".ini";
|
||||
|
@ -933,8 +899,10 @@ static bool TryToConnectWiimoteToSlot(std::unique_ptr<Wiimote>& wm, unsigned int
|
|||
led_report.leds = u8(1 << (i % WIIMOTE_BALANCE_BOARD));
|
||||
wm->QueueReport(led_report);
|
||||
|
||||
g_wiimotes[i] = std::move(wm);
|
||||
Core::RunAsCPUThread([i] { ::Wiimote::Connect(i, true); });
|
||||
Core::RunAsCPUThread([i, &wm] {
|
||||
g_wiimotes[i] = std::move(wm);
|
||||
WiimoteCommon::UpdateSource(i);
|
||||
});
|
||||
|
||||
NOTICE_LOG(WIIMOTE, "Connected real wiimote to slot %i.", i + 1);
|
||||
|
||||
|
@ -951,7 +919,10 @@ static void TryToConnectBalanceBoard(std::unique_ptr<Wiimote> wm)
|
|||
|
||||
static void HandleWiimoteDisconnect(int index)
|
||||
{
|
||||
g_wiimotes[index] = nullptr;
|
||||
Core::RunAsCPUThread([index] {
|
||||
g_wiimotes[index] = nullptr;
|
||||
WiimoteCommon::UpdateSource(index);
|
||||
});
|
||||
}
|
||||
|
||||
// This is called from the GUI thread
|
||||
|
@ -961,52 +932,6 @@ void Refresh()
|
|||
s_wiimote_scanner.SetScanMode(WiimoteScanMode::SCAN_ONCE);
|
||||
}
|
||||
|
||||
void InterruptChannel(int wiimote_number, u16 channel_id, const void* data, u32 size)
|
||||
{
|
||||
std::lock_guard lk(g_wiimotes_mutex);
|
||||
if (g_wiimotes[wiimote_number])
|
||||
g_wiimotes[wiimote_number]->InterruptChannel(channel_id, data, size);
|
||||
}
|
||||
|
||||
void ControlChannel(int wiimote_number, u16 channel_id, const void* data, u32 size)
|
||||
{
|
||||
std::lock_guard lk(g_wiimotes_mutex);
|
||||
if (g_wiimotes[wiimote_number])
|
||||
g_wiimotes[wiimote_number]->ControlChannel(channel_id, data, size);
|
||||
}
|
||||
|
||||
// Read the Wiimote once
|
||||
void Update(int wiimote_number)
|
||||
{
|
||||
// Try to get a lock and return without doing anything if we fail
|
||||
// This avoids blocking the CPU thread
|
||||
if (!g_wiimotes_mutex.try_lock())
|
||||
return;
|
||||
|
||||
if (g_wiimotes[wiimote_number])
|
||||
g_wiimotes[wiimote_number]->Update();
|
||||
|
||||
g_wiimotes_mutex.unlock();
|
||||
|
||||
// Wiimote::Update() may remove the Wiimote if it was disconnected.
|
||||
if (!g_wiimotes[wiimote_number])
|
||||
::Wiimote::Connect(wiimote_number, false);
|
||||
}
|
||||
|
||||
bool CheckForButtonPress(int wiimote_number)
|
||||
{
|
||||
if (!g_wiimotes_mutex.try_lock())
|
||||
return false;
|
||||
|
||||
bool button_pressed = false;
|
||||
|
||||
if (g_wiimotes[wiimote_number])
|
||||
button_pressed = g_wiimotes[wiimote_number]->CheckForButtonPress();
|
||||
|
||||
g_wiimotes_mutex.unlock();
|
||||
return button_pressed;
|
||||
}
|
||||
|
||||
bool IsValidDeviceName(const std::string& name)
|
||||
{
|
||||
return "Nintendo RVL-CNT-01" == name || "Nintendo RVL-CNT-01-TR" == name ||
|
||||
|
@ -1029,9 +954,10 @@ void HandleWiimoteSourceChange(unsigned int index)
|
|||
{
|
||||
std::lock_guard wm_lk(g_wiimotes_mutex);
|
||||
|
||||
if (auto removed_wiimote = std::move(g_wiimotes[index]))
|
||||
AddWiimoteToPool(std::move(removed_wiimote));
|
||||
|
||||
Core::RunAsCPUThread([index] {
|
||||
if (auto removed_wiimote = std::move(g_wiimotes[index]))
|
||||
AddWiimoteToPool(std::move(removed_wiimote));
|
||||
});
|
||||
ProcessWiimotePool();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,14 +26,12 @@ namespace WiimoteReal
|
|||
{
|
||||
using WiimoteCommon::MAX_PAYLOAD;
|
||||
|
||||
// Includes HID "type" header byte.
|
||||
using Report = std::vector<u8>;
|
||||
constexpr int REPORT_HID_HEADER_SIZE = 1;
|
||||
|
||||
constexpr u32 WIIMOTE_DEFAULT_TIMEOUT = 1000;
|
||||
|
||||
// Communication channels
|
||||
constexpr u8 WC_OUTPUT = 0x11;
|
||||
constexpr u8 WC_INPUT = 0x13;
|
||||
|
||||
// The 4 most significant bits of the first byte of an outgoing command must be
|
||||
// 0x50 if sending on the command channel and 0xA0 if sending on the interrupt
|
||||
// channel. On Mac and Linux we use interrupt channel; on Windows, command.
|
||||
|
@ -46,7 +44,7 @@ constexpr u8 WR_SET_REPORT = 0xA0;
|
|||
constexpr u8 BT_INPUT = 0x01;
|
||||
constexpr u8 BT_OUTPUT = 0x02;
|
||||
|
||||
class Wiimote
|
||||
class Wiimote : public WiimoteCommon::HIDWiimote
|
||||
{
|
||||
public:
|
||||
Wiimote(const Wiimote&) = delete;
|
||||
|
@ -60,45 +58,25 @@ 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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
// 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";
|
||||
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
|
||||
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);
|
||||
|
||||
SHCIEventInquiryResult* inquiry_result = (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();
|
||||
|
||||
for (size_t i = 0; i < m_wiimotes.size(); i++)
|
||||
const auto iter = std::find_if(m_wiimotes.begin(), m_wiimotes.end(),
|
||||
std::mem_fn(&WiimoteDevice::IsInquiryScanEnabled));
|
||||
if (iter == m_wiimotes.end())
|
||||
{
|
||||
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]);
|
||||
// No remotes are discoverable.
|
||||
SendEventInquiryComplete(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
AddEventToQueue(event);
|
||||
const auto& wiimote = *iter;
|
||||
|
||||
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->num_responses = num_responses;
|
||||
|
||||
u8* const buffer = event.buffer + sizeof(SHCIEventInquiryResult);
|
||||
const auto response = reinterpret_cast<hci_inquiry_response*>(buffer);
|
||||
|
||||
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)
|
||||
{
|
||||
SendEventRoleChange(accept_connection.bdaddr, true);
|
||||
}
|
||||
WiimoteDevice* wiimote = AccessWiimote(accept_connection.bdaddr);
|
||||
const bool successful = wiimote && wiimote->EventConnectionAccept();
|
||||
|
||||
SendEventConnectionComplete(accept_connection.bdaddr);
|
||||
if (successful)
|
||||
{
|
||||
// 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, 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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
|
||||
bool IsConnected() const { return m_connection_state == ConnectionState::Complete; }
|
||||
bool IsInactive() const { return m_connection_state == ConnectionState::Inactive; }
|
||||
bool LinkChannel();
|
||||
void ResetChannels();
|
||||
u32 GetNumber() const;
|
||||
|
||||
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 = -1,
|
||||
Ready,
|
||||
Inactive,
|
||||
RequestConnection,
|
||||
Complete,
|
||||
};
|
||||
|
||||
enum class HIDState
|
||||
{
|
||||
Inactive,
|
||||
Linking,
|
||||
Complete
|
||||
};
|
||||
|
||||
struct HIDChannelState
|
||||
{
|
||||
bool connected = false;
|
||||
bool connected_wait = false;
|
||||
bool config = false;
|
||||
bool config_wait = false;
|
||||
};
|
||||
|
||||
ConnectionState m_connection_state;
|
||||
|
||||
HIDChannelState m_hid_control_channel;
|
||||
HIDChannelState m_hid_interrupt_channel;
|
||||
|
||||
// STATE_TO_SAVE
|
||||
bdaddr_t m_bd;
|
||||
u16 m_connection_handle;
|
||||
u8 m_uclass[HCI_CLASS_SIZE];
|
||||
u8 m_features[HCI_FEATURES_SIZE];
|
||||
u8 m_lmp_version;
|
||||
u16 m_lmp_subversion;
|
||||
u8 m_link_key[HCI_KEY_SIZE];
|
||||
std::string m_name;
|
||||
Device::BluetoothEmu* m_host;
|
||||
|
||||
struct SChannel
|
||||
{
|
||||
u16 scid;
|
||||
u16 dcid;
|
||||
u16 psm;
|
||||
enum class State
|
||||
{
|
||||
Inactive,
|
||||
ConfigurationPending,
|
||||
Complete,
|
||||
};
|
||||
|
||||
u16 mtu;
|
||||
u16 flush_time_out;
|
||||
SChannel();
|
||||
|
||||
bool IsAccepted() const;
|
||||
bool IsRemoteConfigured() const;
|
||||
bool IsComplete() const;
|
||||
|
||||
State state = State::Inactive;
|
||||
u16 psm;
|
||||
u16 remote_cid;
|
||||
u16 remote_mtu = 0;
|
||||
};
|
||||
|
||||
typedef std::map<u32, SChannel> CChannelMap;
|
||||
CChannelMap m_channel;
|
||||
using ChannelMap = std::map<u16, SChannel>;
|
||||
|
||||
bool DoesChannelExist(u16 scid) const { return m_channel.find(scid) != m_channel.end(); }
|
||||
Device::BluetoothEmu* m_host;
|
||||
WiimoteCommon::HIDWiimote* m_hid_source = nullptr;
|
||||
|
||||
// State to save:
|
||||
BasebandState m_baseband_state = BasebandState::Inactive;
|
||||
HIDState m_hid_state = HIDState::Inactive;
|
||||
bdaddr_t m_bd;
|
||||
ClassType m_class;
|
||||
FeaturesType m_features;
|
||||
u8 m_lmp_version;
|
||||
u16 m_lmp_subversion;
|
||||
LinkKeyType m_link_key;
|
||||
std::string m_name;
|
||||
ChannelMap m_channels;
|
||||
u8 m_connection_request_counter = 0;
|
||||
|
||||
void SetBasebandState(BasebandState);
|
||||
|
||||
const SChannel* FindChannelWithPSM(u16 psm) const;
|
||||
SChannel* FindChannelWithPSM(u16 psm);
|
||||
|
||||
bool LinkChannel(u16 psm);
|
||||
u16 GenerateChannelID() const;
|
||||
|
||||
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);
|
||||
|
|
|
@ -2652,6 +2652,7 @@ struct SHCIEventInquiryComplete
|
|||
u8 EventType;
|
||||
u8 PayloadLength;
|
||||
u8 EventStatus;
|
||||
u8 num_responses;
|
||||
};
|
||||
|
||||
struct SHCIEventReadClockOffsetComplete
|
||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 122; // Last changed in PR 8571
|
||||
constexpr u32 STATE_VERSION = 123; // Last changed in PR 8985
|
||||
|
||||
// Maps savestate versions to Dolphin versions.
|
||||
// Versions after 42 don't need to be added to this list,
|
||||
|
|
|
@ -1706,10 +1706,12 @@ void MainWindow::OnConnectWiiRemote(int id)
|
|||
if (!ios || SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
return;
|
||||
Core::RunAsCPUThread([&] {
|
||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||
ios->GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||
const bool is_connected = bt && bt->AccessWiimoteByIndex(id)->IsConnected();
|
||||
Wiimote::Connect(id, !is_connected);
|
||||
if (const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||
ios->GetDeviceByName("/dev/usb/oh1/57e/305")))
|
||||
{
|
||||
const auto wm = bt->AccessWiimoteByIndex(id);
|
||||
wm->Activate(!wm->IsConnected());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ void Device::QueueReport(T&& report, std::function<void(ErrorCode)> ack_callback
|
|||
// Maintain proper rumble state.
|
||||
report.rumble = m_rumble;
|
||||
|
||||
m_wiimote->QueueReport(std::forward<T>(report));
|
||||
m_wiimote->QueueReport(report.REPORT_ID, &report, sizeof(report));
|
||||
|
||||
if (ack_callback)
|
||||
AddReportHandler(MakeAckHandler(report.REPORT_ID, std::move(ack_callback)));
|
||||
|
@ -117,9 +117,7 @@ void AddDevice(std::unique_ptr<WiimoteReal::Wiimote> wiimote)
|
|||
}
|
||||
|
||||
wiimote->Prepare();
|
||||
|
||||
// Our silly real wiimote interface needs a non-zero "channel" to not drop input reports.
|
||||
wiimote->SetChannel(26);
|
||||
wiimote->EventLinked();
|
||||
|
||||
g_controller_interface.AddDevice(std::make_shared<Device>(std::move(wiimote)));
|
||||
}
|
||||
|
@ -1063,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>)
|
||||
|
|
Loading…
Reference in New Issue