136 changes: 64 additions & 72 deletions Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.cpp
Expand Up @@ -57,7 +57,7 @@ Wiimote::Wiimote()
#elif defined(_WIN32)
, dev_handle(0), stack(MSBT_STACK_UNKNOWN)
#endif
, m_last_data_report(Report((u8 *)NULL, 0))
, m_last_input_report()
, m_channel(0), m_run_thread(false)
{
#if defined(__linux__) && HAVE_BLUEZ
Expand All @@ -73,29 +73,26 @@ Wiimote::~Wiimote()
Disconnect();

ClearReadQueue();

// clear write queue
Report rpt;
while (m_write_reports.Pop(rpt))
delete[] rpt.first;
m_write_reports.Clear();
}

// to be called from CPU thread
void Wiimote::QueueReport(u8 rpt_id, const void* _data, unsigned int size)
{
auto const data = static_cast<const u8*>(_data);

Report rpt;
rpt.second = size + 2;
rpt.first = new u8[rpt.second];
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT;
rpt.first[1] = rpt_id;
std::copy(data, data + size, rpt.first + 2);
m_write_reports.Push(rpt);
Report rpt(size + 2);
rpt[0] = WM_SET_REPORT | WM_BT_OUTPUT;
rpt[1] = rpt_id;
std::copy_n(data, size, rpt.begin() + 2);
m_write_reports.Push(std::move(rpt));
}

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

// This probably accomplishes nothing.
wm_report_mode rpt = {};
rpt.mode = WM_REPORT_CORE;
rpt.all_the_time = 0;
Expand All @@ -107,15 +104,10 @@ void Wiimote::DisableDataReporting()
void Wiimote::ClearReadQueue()
{
Report rpt;

if (m_last_data_report.first)
{
delete[] m_last_data_report.first;
m_last_data_report.first = NULL;
}


// The "Clear" function isn't thread-safe :/
while (m_read_reports.Pop(rpt))
delete[] rpt.first;
{}
}

void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
Expand Down Expand Up @@ -148,65 +140,58 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const
}

auto const data = static_cast<const u8*>(_data);

Report rpt;
rpt.first = new u8[size];
rpt.second = (u8)size;
std::copy(data, data + size, rpt.first);
Report rpt(data, data + size);

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

// Disallow games from turning off all of the LEDs.
// It makes Wiimote connection status confusing.
if (rpt.first[1] == WM_LEDS)
if (rpt[1] == WM_LEDS)
{
auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt.first[2]);
auto& leds_rpt = *reinterpret_cast<wm_leds*>(&rpt[2]);
if (0 == leds_rpt.leds)
{
// Turn on ALL of the LEDs.
leds_rpt.leds = 0xf;
}
}
else if (rpt.first[1] == WM_WRITE_SPEAKER_DATA
else if (rpt[1] == WM_WRITE_SPEAKER_DATA
&& !SConfig::GetInstance().m_WiimoteEnableSpeaker)
{
// Translate speaker data reports into rumble reports.
rpt.first[1] = WM_CMD_RUMBLE;
rpt[1] = WM_RUMBLE;
// Keep only the rumble bit.
rpt.first[2] &= 0x1;
rpt.second = 3;
rpt[2] &= 0x1;
rpt.resize(3);
}

m_write_reports.Push(rpt);
m_write_reports.Push(std::move(rpt));
}

bool Wiimote::Read()
{
Report rpt;

rpt.first = new unsigned char[MAX_PAYLOAD];
rpt.second = IORead(rpt.first);

if (0 == rpt.second)
{
WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", index + 1);
Disconnect();
}
Report rpt(MAX_PAYLOAD);
auto const result = IORead(rpt.data());

if (rpt.second > 0 && m_channel > 0)
if (result > 0 && m_channel > 0)
{
// Add it to queue
m_read_reports.Push(rpt);
rpt.resize(result);
m_read_reports.Push(std::move(rpt));
return true;
}
else if (0 == result)
{
WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", index + 1);
Disconnect();
}

delete[] rpt.first;
return false;
}

Expand All @@ -216,16 +201,15 @@ bool Wiimote::Write()
{
Report const& rpt = m_write_reports.Front();

bool const is_speaker_data = rpt.first[1] == WM_WRITE_SPEAKER_DATA;
bool const is_speaker_data = rpt[1] == WM_WRITE_SPEAKER_DATA;

if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
{
IOWrite(rpt.first, rpt.second);
IOWrite(rpt.data(), rpt.size());

if (is_speaker_data)
m_last_audio_report.Update();

delete[] rpt.first;
m_write_reports.Pop();
return true;
}
Expand All @@ -234,23 +218,35 @@ bool Wiimote::Write()
return false;
}

bool IsDataReport(const Report& rpt)
{
return rpt.size() >= 2 && rpt[1] >= WM_REPORT_CORE;
}

// Returns the next report that should be sent
Report Wiimote::ProcessReadQueue()
const Report& Wiimote::ProcessReadQueue()
{
// Pop through the queued reports
Report rpt = m_last_data_report;
while (m_read_reports.Pop(rpt))
while (m_read_reports.Pop(m_last_input_report))
{
if (rpt.first[1] >= WM_REPORT_CORE)
// A data report
m_last_data_report = rpt;
else
// Some other kind of report
return rpt;
if (!IsDataReport(m_last_input_report))
{
// A non-data report, use it.
return m_last_input_report;

// Forget the last data report as it may be of the wrong type
// or contain outdated button data
// or it's not supposed to be sent at this time
// It's just easier to be correct this way and it's probably not horrible.
}
}

// The queue was empty, or there were only data reports
return rpt;
// If the last report wasn't a data report it's irrelevant.
if (!IsDataReport(m_last_input_report))
m_last_input_report.clear();

// If it was a data report, we repeat that until something else comes in.
return m_last_input_report;
}

void Wiimote::Update()
Expand All @@ -262,30 +258,26 @@ void Wiimote::Update()
}

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

// Send the report
if (rpt.first != NULL && m_channel > 0)
if (!rpt.empty() && m_channel > 0)
Core::Callback_WiimoteInterruptChannel(index, m_channel,
rpt.first, rpt.second);

// Delete the data if it isn't also the last data rpt
if (rpt != m_last_data_report)
delete[] rpt.first;
rpt.data(), rpt.size());
}

bool Wiimote::Prepare(int _index)
{
index = _index;

// core buttons, no continuous reporting
u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_REPORT_TYPE, 0, 0x30};
u8 const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE};

// Set the active LEDs and turn on rumble.
u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_LED, u8(WIIMOTE_LED_1 << index | 0x1)};
u8 const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << index | 0x1)};

// Turn off rumble
u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_CMD_RUMBLE, 0};
u8 rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0};

// Request status report
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
Expand Down
7 changes: 3 additions & 4 deletions Source/Core/Core/Src/HW/WiimoteReal/WiimoteReal.h
Expand Up @@ -33,8 +33,7 @@

#include "../../InputCommon/Src/InputConfig.h"

// Pointer to data, and size of data
typedef std::pair<u8*,u8> Report;
typedef std::vector<u8> Report;

namespace WiimoteReal
{
Expand All @@ -50,7 +49,7 @@ friend class WiimoteEmu::Wiimote;
void InterruptChannel(const u16 channel, const void* const data, const u32 size);
void Update();

Report ProcessReadQueue();
const Report& ProcessReadQueue();

bool Read();
bool Write();
Expand Down Expand Up @@ -99,7 +98,7 @@ friend class WiimoteEmu::Wiimote;
#endif

protected:
Report m_last_data_report;
Report m_last_input_report;
u16 m_channel;

private:
Expand Down
18 changes: 2 additions & 16 deletions Source/Core/Core/Src/HW/WiimoteReal/WiimoteRealBase.h
Expand Up @@ -41,20 +41,6 @@
#define WM_SET_REPORT 0xA0
#endif

// TODO: duplicated in WiimoteHid.h
// Commands
#define WM_CMD_RUMBLE 0x10
#define WM_CMD_LED 0x11
#define WM_CMD_REPORT_TYPE 0x12
#define WM_CMD_IR 0x13
#define WM_CMD_SPEAKER_ENABLE 0x14
#define WM_CMD_CTRL_STATUS 0x15
#define WM_CMD_WRITE_DATA 0x16
#define WM_CMD_READ_DATA 0x17
#define WM_CMD_SPEAKER_DATA 0x18
#define WM_CMD_SPEAKER_MUTE 0x19
#define WM_CMD_IR_2 0x1A

#define WM_BT_INPUT 0x01
#define WM_BT_OUTPUT 0x02

Expand All @@ -73,12 +59,12 @@

#ifdef _WIN32
// Available bluetooth stacks for Windows.
typedef enum win_bt_stack_t
enum win_bt_stack_t
{
MSBT_STACK_UNKNOWN,
MSBT_STACK_MS,
MSBT_STACK_BLUESOLEIL
} win_bt_stack_t;
};
#endif

#endif // WIIMOTE_COMM_H