Merge branch 'real-wiimote-minor-fixes'

This commit is contained in:
Jordan Woyak 2013-04-06 16:57:55 -05:00
commit 53368823cb
6 changed files with 107 additions and 151 deletions

View File

@ -36,15 +36,16 @@ public:
return (0 == m_size); return (0 == m_size);
} }
const T& Front() const T& Front() const
{ {
return *m_read_ptr->current; return *m_read_ptr->current;
} }
void Push(const T& t) template <typename Arg>
void Push(Arg&& t)
{ {
// create the element, add it to the queue // create the element, add it to the queue
m_write_ptr->current = new T(t); m_write_ptr->current = new T(std::forward<Arg>(t));
// set the next pointer to a new element ptr // set the next pointer to a new element ptr
// then advance the write pointer // then advance the write pointer
m_write_ptr = m_write_ptr->next = new ElementPtr(); m_write_ptr = m_write_ptr->next = new ElementPtr();
@ -67,7 +68,7 @@ public:
if (Empty()) if (Empty())
return false; return false;
t = Front(); t = std::move(Front());
Pop(); Pop();
return true; return true;

View File

@ -707,10 +707,10 @@ void Wiimote::Update()
std::lock_guard<std::recursive_mutex> lk(g_refresh_lock); std::lock_guard<std::recursive_mutex> lk(g_refresh_lock);
if (g_wiimotes[m_index]) if (g_wiimotes[m_index])
{ {
Report rpt = g_wiimotes[m_index]->ProcessReadQueue(); const Report& rpt = g_wiimotes[m_index]->ProcessReadQueue();
const u8 *real_data = rpt.first; if (!rpt.empty())
if (real_data)
{ {
const u8 *real_data = rpt.data();
switch (real_data[1]) switch (real_data[1])
{ {
// use data reports // use data reports
@ -770,13 +770,9 @@ void Wiimote::Update()
// copy over report from real-wiimote // copy over report from real-wiimote
if (-1 == rptf_size) if (-1 == rptf_size)
{ {
memcpy(data, real_data, rpt.second); std::copy(rpt.begin(), rpt.end(), data);
rptf_size = rpt.second; rptf_size = rpt.size();
} }
if (real_data != g_wiimotes[m_index]->\
m_last_data_report.first)
delete[] real_data;
} }
} }
} }

View File

@ -370,73 +370,55 @@ bool Wiimote::IsConnected() const
// zero = error // zero = error
int Wiimote::IORead(u8* buf) int Wiimote::IORead(u8* buf)
{ {
// used below for a warning // Add data report indicator byte (here, 0xa1)
*buf = 0; buf[0] = 0xa1;
// Used below for a warning
buf[1] = 0;
DWORD bytes; DWORD bytes = 0;
ResetEvent(hid_overlap_read.hEvent); ResetEvent(hid_overlap_read.hEvent);
if (!ReadFile(dev_handle, buf, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read)) if (!ReadFile(dev_handle, buf + 1, MAX_PAYLOAD - 1, &bytes, &hid_overlap_read))
{ {
auto const err = GetLastError(); auto const read_err = GetLastError();
if (ERROR_IO_PENDING == err) if (ERROR_IO_PENDING == read_err)
{ {
auto const r = WaitForSingleObject(hid_overlap_read.hEvent, WIIMOTE_DEFAULT_TIMEOUT); auto const wait_result = WaitForSingleObject(hid_overlap_read.hEvent, WIIMOTE_DEFAULT_TIMEOUT);
if (WAIT_TIMEOUT == r) if (WAIT_TIMEOUT == wait_result)
{ {
// Timeout - cancel and continue CancelIo(dev_handle);
if (*buf) }
else if (WAIT_FAILED == wait_result)
{
WARN_LOG(WIIMOTE, "A wait error occurred on reading from Wiimote %i.", index + 1);
CancelIo(dev_handle);
}
if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, TRUE))
{
auto const overlapped_err = GetLastError();
if (ERROR_OPERATION_ABORTED == overlapped_err)
{
if (buf[1] != 0)
WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).", WARN_LOG(WIIMOTE, "Packet ignored. This may indicate a problem (timeout is %i ms).",
WIIMOTE_DEFAULT_TIMEOUT); WIIMOTE_DEFAULT_TIMEOUT);
CancelIo(dev_handle); return -1;
bytes = -1;
} }
else if (WAIT_FAILED == r)
{ WARN_LOG(WIIMOTE, "GetOverlappedResult error %d on Wiimote %i.", overlapped_err, index + 1);
WARN_LOG(WIIMOTE, "A wait error occurred on reading from Wiimote %i.", index + 1); return 0;
bytes = 0;
}
else if (WAIT_OBJECT_0 == r)
{
if (!GetOverlappedResult(dev_handle, &hid_overlap_read, &bytes, TRUE))
{
WARN_LOG(WIIMOTE, "GetOverlappedResult failed on Wiimote %i.", index + 1);
bytes = 0;
} }
} }
else else
{ {
bytes = 0; WARN_LOG(WIIMOTE, "ReadFile error %d on Wiimote %i.", read_err, index + 1);
} return 0;
}
else if (ERROR_HANDLE_EOF == err)
{
// Remote disconnect
bytes = 0;
}
else if (ERROR_DEVICE_NOT_CONNECTED == err)
{
// Remote disconnect
bytes = 0;
}
else
{
bytes = 0;
} }
} }
if (bytes > 0) return bytes + 1;
{
// Move the data over one, so we can add back in data report indicator byte (here, 0xa1)
std::copy_n(buf, MAX_PAYLOAD - 1, buf + 1);
buf[0] = 0xa1;
// TODO: is this really needed?
bytes = MAX_PAYLOAD;
}
return bytes;
} }
int Wiimote::IOWrite(const u8* buf, int len) int Wiimote::IOWrite(const u8* buf, int len)

View File

@ -57,7 +57,7 @@ Wiimote::Wiimote()
#elif defined(_WIN32) #elif defined(_WIN32)
, dev_handle(0), stack(MSBT_STACK_UNKNOWN) , dev_handle(0), stack(MSBT_STACK_UNKNOWN)
#endif #endif
, m_last_data_report(Report((u8 *)NULL, 0)) , m_last_input_report()
, m_channel(0), m_run_thread(false) , m_channel(0), m_run_thread(false)
{ {
#if defined(__linux__) && HAVE_BLUEZ #if defined(__linux__) && HAVE_BLUEZ
@ -73,11 +73,7 @@ Wiimote::~Wiimote()
Disconnect(); Disconnect();
ClearReadQueue(); ClearReadQueue();
m_write_reports.Clear();
// clear write queue
Report rpt;
while (m_write_reports.Pop(rpt))
delete[] rpt.first;
} }
// to be called from CPU thread // to be called from CPU thread
@ -85,17 +81,18 @@ void Wiimote::QueueReport(u8 rpt_id, const void* _data, unsigned int size)
{ {
auto const data = static_cast<const u8*>(_data); auto const data = static_cast<const u8*>(_data);
Report rpt; Report rpt(size + 2);
rpt.second = size + 2; rpt[0] = WM_SET_REPORT | WM_BT_OUTPUT;
rpt.first = new u8[rpt.second]; rpt[1] = rpt_id;
rpt.first[0] = WM_SET_REPORT | WM_BT_OUTPUT; std::copy_n(data, size, rpt.begin() + 2);
rpt.first[1] = rpt_id; m_write_reports.Push(std::move(rpt));
std::copy(data, data + size, rpt.first + 2);
m_write_reports.Push(rpt);
} }
void Wiimote::DisableDataReporting() void Wiimote::DisableDataReporting()
{ {
m_last_input_report.clear();
// This probably accomplishes nothing.
wm_report_mode rpt = {}; wm_report_mode rpt = {};
rpt.mode = WM_REPORT_CORE; rpt.mode = WM_REPORT_CORE;
rpt.all_the_time = 0; rpt.all_the_time = 0;
@ -108,14 +105,9 @@ void Wiimote::ClearReadQueue()
{ {
Report rpt; Report rpt;
if (m_last_data_report.first) // The "Clear" function isn't thread-safe :/
{
delete[] m_last_data_report.first;
m_last_data_report.first = NULL;
}
while (m_read_reports.Pop(rpt)) while (m_read_reports.Pop(rpt))
delete[] rpt.first; {}
} }
void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size) void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
@ -148,65 +140,58 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const
} }
auto const data = static_cast<const u8*>(_data); auto const data = static_cast<const u8*>(_data);
Report rpt(data, data + size);
Report rpt;
rpt.first = new u8[size];
rpt.second = (u8)size;
std::copy(data, data + size, rpt.first);
// Convert output DATA packets to SET_REPORT packets. // Convert output DATA packets to SET_REPORT packets.
// Nintendo Wiimotes work without this translation, but 3rd // Nintendo Wiimotes work without this translation, but 3rd
// party ones don't. // 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. // Disallow games from turning off all of the LEDs.
// It makes Wiimote connection status confusing. // 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) if (0 == leds_rpt.leds)
{ {
// Turn on ALL of the LEDs. // Turn on ALL of the LEDs.
leds_rpt.leds = 0xf; 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) && !SConfig::GetInstance().m_WiimoteEnableSpeaker)
{ {
// Translate speaker data reports into rumble reports. // Translate speaker data reports into rumble reports.
rpt.first[1] = WM_CMD_RUMBLE; rpt[1] = WM_RUMBLE;
// Keep only the rumble bit. // Keep only the rumble bit.
rpt.first[2] &= 0x1; rpt[2] &= 0x1;
rpt.second = 3; rpt.resize(3);
} }
m_write_reports.Push(rpt); m_write_reports.Push(std::move(rpt));
} }
bool Wiimote::Read() bool Wiimote::Read()
{ {
Report rpt; Report rpt(MAX_PAYLOAD);
auto const result = IORead(rpt.data());
rpt.first = new unsigned char[MAX_PAYLOAD]; if (result > 0 && m_channel > 0)
rpt.second = IORead(rpt.first); {
// Add it to queue
if (0 == rpt.second) 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); WARN_LOG(WIIMOTE, "Wiimote::IORead failed. Disconnecting Wiimote %d.", index + 1);
Disconnect(); Disconnect();
} }
if (rpt.second > 0 && m_channel > 0)
{
// Add it to queue
m_read_reports.Push(rpt);
return true;
}
delete[] rpt.first;
return false; return false;
} }
@ -216,16 +201,15 @@ bool Wiimote::Write()
{ {
Report const& rpt = m_write_reports.Front(); 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) if (!is_speaker_data || m_last_audio_report.GetTimeDifference() > 5)
{ {
IOWrite(rpt.first, rpt.second); IOWrite(rpt.data(), rpt.size());
if (is_speaker_data) if (is_speaker_data)
m_last_audio_report.Update(); m_last_audio_report.Update();
delete[] rpt.first;
m_write_reports.Pop(); m_write_reports.Pop();
return true; return true;
} }
@ -234,23 +218,35 @@ bool Wiimote::Write()
return false; return false;
} }
// Returns the next report that should be sent bool IsDataReport(const Report& rpt)
Report Wiimote::ProcessReadQueue()
{ {
// Pop through the queued reports return rpt.size() >= 2 && rpt[1] >= WM_REPORT_CORE;
Report rpt = m_last_data_report;
while (m_read_reports.Pop(rpt))
{
if (rpt.first[1] >= WM_REPORT_CORE)
// A data report
m_last_data_report = rpt;
else
// Some other kind of report
return rpt;
} }
// The queue was empty, or there were only data reports // Returns the next report that should be sent
return rpt; const Report& Wiimote::ProcessReadQueue()
{
// Pop through the queued reports
while (m_read_reports.Pop(m_last_input_report))
{
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.
}
}
// 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() void Wiimote::Update()
@ -262,16 +258,12 @@ void Wiimote::Update()
} }
// Pop through the queued reports // Pop through the queued reports
Report const rpt = ProcessReadQueue(); const Report& rpt = ProcessReadQueue();
// Send the report // Send the report
if (rpt.first != NULL && m_channel > 0) if (!rpt.empty() && m_channel > 0)
Core::Callback_WiimoteInterruptChannel(index, m_channel, Core::Callback_WiimoteInterruptChannel(index, m_channel,
rpt.first, rpt.second); rpt.data(), rpt.size());
// Delete the data if it isn't also the last data rpt
if (rpt != m_last_data_report)
delete[] rpt.first;
} }
bool Wiimote::Prepare(int _index) bool Wiimote::Prepare(int _index)
@ -279,13 +271,13 @@ bool Wiimote::Prepare(int _index)
index = _index; index = _index;
// core buttons, no continuous reporting // 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. // 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 // 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 // Request status report
u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0}; u8 const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};

View File

@ -33,8 +33,7 @@
#include "../../InputCommon/Src/InputConfig.h" #include "../../InputCommon/Src/InputConfig.h"
// Pointer to data, and size of data typedef std::vector<u8> Report;
typedef std::pair<u8*,u8> Report;
namespace WiimoteReal namespace WiimoteReal
{ {
@ -50,7 +49,7 @@ public:
void InterruptChannel(const u16 channel, const void* const data, const u32 size); void InterruptChannel(const u16 channel, const void* const data, const u32 size);
void Update(); void Update();
Report ProcessReadQueue(); const Report& ProcessReadQueue();
bool Read(); bool Read();
bool Write(); bool Write();
@ -99,7 +98,7 @@ public:
#endif #endif
protected: protected:
Report m_last_data_report; Report m_last_input_report;
u16 m_channel; u16 m_channel;
private: private:

View File

@ -41,20 +41,6 @@
#define WM_SET_REPORT 0xA0 #define WM_SET_REPORT 0xA0
#endif #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_INPUT 0x01
#define WM_BT_OUTPUT 0x02 #define WM_BT_OUTPUT 0x02
@ -73,12 +59,12 @@
#ifdef _WIN32 #ifdef _WIN32
// Available bluetooth stacks for Windows. // Available bluetooth stacks for Windows.
typedef enum win_bt_stack_t enum win_bt_stack_t
{ {
MSBT_STACK_UNKNOWN, MSBT_STACK_UNKNOWN,
MSBT_STACK_MS, MSBT_STACK_MS,
MSBT_STACK_BLUESOLEIL MSBT_STACK_BLUESOLEIL
} win_bt_stack_t; };
#endif #endif
#endif // WIIMOTE_COMM_H #endif // WIIMOTE_COMM_H