New Wiimote Plugin: Added a "Hybrid Wiimote" input source type. This allows a real wiimote to be used with an emulated extension.(and in the future, real wiimote with emulated motion plus) If the emulated extension is set to "None", a real extension can be used. Real/Emulated buttons are combined. Real acceleration data is used. Currently, emulated IR data is used.(might change this to allow both) Switching between an emulated and a real extension in-game is a bit testy right now, but if you switch the emu-extension to "None" before connecting a real extension, it usually works.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6082 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Jordan Woyak 2010-08-10 04:12:32 +00:00
parent a1daa636c2
commit 2b45e87b3e
9 changed files with 603 additions and 417 deletions

View File

@ -5,6 +5,8 @@
// a simple lockless thread-safe,
// single reader, single writer queue
#include "Atomic.h"
namespace Common
{
@ -12,7 +14,7 @@ template <typename T>
class FifoQueue
{
public:
FifoQueue()
FifoQueue() : m_size(0)
{
m_write_ptr = m_read_ptr = new ElementPtr();
}
@ -23,9 +25,20 @@ public:
delete m_read_ptr;
}
bool Empty() const // true if the queue is empty
u32 Size() const
{
return (m_read_ptr == m_write_ptr);
return m_size;
}
bool Empty() const
{
//return (m_read_ptr == m_write_ptr);
return (0 == m_size);
}
const T& Front() const
{
return *m_read_ptr->current;
}
void Push(const T& t)
@ -35,42 +48,41 @@ public:
// set the next pointer to a new element ptr
// then advance the write pointer
m_write_ptr = m_write_ptr->next = new ElementPtr();
Common::AtomicIncrement(m_size);
}
void Pop()
{
Common::AtomicDecrement(m_size);
ElementPtr *const tmpptr = m_read_ptr;
// advance the read pointer
m_read_ptr = m_read_ptr->next;
// set the next element to NULL to stop the recursive deletion
tmpptr->next = NULL;
delete tmpptr; // this also deletes the element
}
bool Pop(T& t)
{
// if write pointer points to the same element, queue is empty
if (m_read_ptr == m_write_ptr)
if (Empty())
return false;
// get the element from out of the queue
t = *m_read_ptr->current;
ElementPtr *const tmpptr = m_read_ptr;
// advance the read pointer
m_read_ptr = m_read_ptr->next;
// set the next element to NULL to stop the recursive deletion
tmpptr->next = NULL;
delete tmpptr; // this also deletes the element
t = Front();
Pop();
return true;
}
bool Peek(T& t)
// not thread-safe
void Clear()
{
// if write pointer points to the same element, queue is empty
if (m_read_ptr == m_write_ptr)
return false;
// get the element from out of the queue
t = *m_read_ptr->current;
return true;
m_size = 0;
delete m_read_ptr;
m_write_ptr = m_read_ptr = new ElementPtr();
}
private:
// stores a pointer to element at front of queue
// stores a pointer to element
// and a pointer to the next ElementPtr
class ElementPtr
{
@ -94,6 +106,7 @@ private:
ElementPtr *volatile m_write_ptr;
ElementPtr *volatile m_read_ptr;
volatile u32 m_size;
};
}

View File

@ -13,6 +13,7 @@ WiimoteConfigPage::WiimoteConfigPage(wxWindow* const parent, const int index)
m_input_src_choice->Append(wxT("None"));
m_input_src_choice->Append(wxT("Emulated Wiimote"));
m_input_src_choice->Append(wxT("Real Wiimote"));
m_input_src_choice->Append(wxT("Hybrid Wiimote"));
m_input_src_choice->Select(g_wiimote_sources[m_index]);
_connect_macro_(m_input_src_choice, WiimoteConfigPage::SelectSource, wxEVT_COMMAND_CHOICE_SELECTED, this);

View File

@ -38,23 +38,21 @@
#include "pluginspecs_wiimote.h"
#include "WiimoteEmu.h"
#include "WiimoteHid.h"
#include "../WiimoteReal/WiimoteReal.h"
#include "Attachment/Attachment.h"
/* Bit shift conversions */
u32 convert24bit(const u8* src)
u32 swap24(const u8* src)
{
return (src[0] << 16) | (src[1] << 8) | src[2];
}
u16 convert16bit(const u8* src)
{
return (src[0] << 8) | src[1];
}
namespace WiimoteEmu
{
void Wiimote::ReportMode(const u16 _channelID, wm_report_mode* dr)
void Wiimote::ReportMode(const wm_report_mode* const dr)
{
//INFO_LOG(WIIMOTE, "Set data report mode");
//DEBUG_LOG(WIIMOTE, " Rumble: %x", dr->rumble);
@ -92,9 +90,9 @@ void Wiimote::ReportMode(const u16 _channelID, wm_report_mode* dr)
The IR enable/disable and speaker enable/disable and mute/unmute values are
bit2: 0 = Disable (0x02), 1 = Enable (0x06)
*/
void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
void Wiimote::HidOutputReport(const wm_report* const sr, const bool send_ack)
{
INFO_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, _channelID, sr->wm);
INFO_LOG(WIIMOTE, "HidOutputReport (page: %i, cid: 0x%02x, wm: 0x%02x)", m_index, m_reporting_channel, sr->wm);
// wiibrew:
// In every single Output Report, bit 0 (0x01) of the first byte controls the Rumble feature.
@ -113,7 +111,7 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
break;
case WM_REPORT_MODE : // 0x12
ReportMode(_channelID, (wm_report_mode*)sr->data);
ReportMode((wm_report_mode*)sr->data);
break;
case WM_IR_PIXEL_CLOCK : // 0x13
@ -128,16 +126,16 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
break;
case WM_REQUEST_STATUS : // 0x15
RequestStatus(_channelID, (wm_request_status*)sr->data);
RequestStatus((wm_request_status*)sr->data);
return; // sends its own ack
break;
case WM_WRITE_DATA : // 0x16
WriteData(_channelID, (wm_write_data*)sr->data);
WriteData((wm_write_data*)sr->data);
break;
case WM_READ_DATA : // 0x17
ReadData(_channelID, (wm_read_data*)sr->data);
ReadData((wm_read_data*)sr->data);
return; // sends its own ack
break;
@ -175,7 +173,8 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
}
// send ack
SendAck(_channelID, sr->wm);
if (send_ack)
SendAck(sr->wm);
}
/* This will generate the 0x22 acknowledgement for most Input reports.
@ -183,7 +182,7 @@ void Wiimote::HidOutputReport(const u16 _channelID, wm_report* sr)
The first two bytes are the core buttons data,
00 00 means nothing is pressed.
The last byte is the success code 00. */
void Wiimote::SendAck(const u16 _channelID, u8 _reportID)
void Wiimote::SendAck(u8 _reportID)
{
u8 data[6];
@ -196,23 +195,16 @@ void Wiimote::SendAck(const u16 _channelID, u8 _reportID)
ack->reportID = _reportID;
ack->errorID = 0;
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, _channelID, data, sizeof(data));
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, m_reporting_channel, data, sizeof(data));
}
// old comment
/* Here we produce a 0x20 status report to send to the Wii. We currently ignore
the status request rs and all its eventual instructions it may include (for
example turn off rumble or something else) and just send the status
report. */
void Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
void Wiimote::HandleExtensionSwap()
{
//if (rs)
// handle switch extension
if ( m_extension->active_extension != m_extension->switch_extension )
if (m_extension->active_extension != m_extension->switch_extension)
{
// if an extension is currently connected and we want to switch to a different extension
if ( (m_extension->active_extension > 0) && m_extension->switch_extension )
if ((m_extension->active_extension > 0) && m_extension->switch_extension)
// detach extension first, wait til next Update() or RequestStatus() call to change to the new extension
m_extension->active_extension = 0;
else
@ -223,8 +215,18 @@ void Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
m_status.extension = m_extension->active_extension ? 1 : 0;
// set register, I hate this line
m_register[ 0xa40000 ] = ((WiimoteEmu::Attachment*)m_extension->attachments[ m_extension->active_extension ])->reg;
m_register[0xa40000] = ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension])->reg;
}
}
// old comment
/* Here we produce a 0x20 status report to send to the Wii. We currently ignore
the status request rs and all its eventual instructions it may include (for
example turn off rumble or something else) and just send the status
report. */
void Wiimote::RequestStatus(const wm_request_status* const rs)
{
HandleExtensionSwap();
// set up report
u8 data[8];
@ -234,14 +236,31 @@ void Wiimote::RequestStatus(const u16 _channelID, wm_request_status* rs)
// status values
*(wm_status_report*)(data + 2) = m_status;
// hybrid wiimote stuff
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index] && (m_extension->switch_extension <= 0))
{
using namespace WiimoteReal;
g_refresh_critsec.Enter();
if (g_wiimotes[m_index])
{
wm_request_status rpt;
rpt.rumble = 0;
g_wiimotes[m_index]->SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
}
g_refresh_critsec.Leave();
return;
}
// send report
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, data, sizeof(data));
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
}
/* Write data to Wiimote and Extensions registers. */
void Wiimote::WriteData(const u16 _channelID, wm_write_data* wd)
void Wiimote::WriteData(const wm_write_data* const wd)
{
u32 address = convert24bit(wd->address);
u32 address = swap24(wd->address);
// ignore the 0x010000 bit
address &= 0xFEFFFF;
@ -324,14 +343,24 @@ void Wiimote::WriteData(const u16 _channelID, wm_write_data* wd)
}
/* Read data from Wiimote and Extensions registers. */
void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
void Wiimote::ReadData(const wm_read_data* const rd)
{
u32 address = convert24bit(rd->address);
u16 size = convert16bit(rd->size);
u32 address = swap24(rd->address);
u16 size = Common::swap16(rd->size);
// ignore the 0x010000 bit
address &= 0xFEFFFF;
// hybrid wiimote stuff
// relay the read data request to real-wiimote
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index] && ((0xA4 != (address >> 16)) || (m_extension->switch_extension <= 0)))
{
WiimoteReal::InterruptChannel(m_index, m_reporting_channel, ((u8*)rd) - 2, sizeof(wm_read_data) + 2); // hacky
// don't want emu-wiimote to send reply
return;
}
ReadRequest rr;
u8* block = new u8[size];
@ -416,14 +445,14 @@ void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
}
// want the requested address, not the above modified one
rr.address = convert24bit(rd->address);
rr.address = swap24(rd->address);
rr.size = size;
//rr.channel = _channelID;
rr.position = 0;
rr.data = block;
// send up to 16 bytes
SendReadDataReply( _channelID, rr );
SendReadDataReply(rr);
// if there is more data to be sent, add it to the queue
if (rr.size)
@ -440,7 +469,7 @@ void Wiimote::ReadData(const u16 _channelID, wm_read_data* rd)
bytes in the message, the 0 means no error, the 00 20 means that the message
is at the 00 20 offest in the registry that was read.
*/
void Wiimote::SendReadDataReply(const u16 _channelID, ReadRequest& _request)
void Wiimote::SendReadDataReply(ReadRequest& _request)
{
u8 data[23];
data[0] = 0xA1;
@ -488,7 +517,7 @@ void Wiimote::SendReadDataReply(const u16 _channelID, ReadRequest& _request)
}
// Send a piece
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, data, sizeof(data));
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, sizeof(data));
}
void Wiimote::DoState(PointerWrap& p)

View File

@ -7,8 +7,10 @@
#include "WiimoteEmu.h"
#include "WiimoteHid.h"
#include <Timer.h>
#include <Common.h>
#include "../WiimoteReal/WiimoteReal.h"
#include "Timer.h"
#include "Common.h"
#include "UDPTLayer.h"
@ -38,10 +40,7 @@ static const u8 eeprom_data_16D0[] = {
#define SWING_INTENSITY 0x40
static struct ReportFeatures
{
u8 core, accel, ir, ext, size;
} const reporting_mode_features[] =
const ReportFeatures reporting_mode_features[] =
{
//0x30: Core Buttons
{ 2, 0, 0, 0, 4 },
@ -324,19 +323,215 @@ std::string Wiimote::GetName() const
return std::string("Wiimote") + char('1'+m_index);
}
void Wiimote::Update()
{
const bool is_sideways = m_options->settings[1]->value > 0;
const bool is_upright = m_options->settings[2]->value > 0;
// if windows is focused or background input is enabled
#define HAS_FOCUS (g_WiimoteInitialize.pRendererHasFocus() || (m_options->settings[0]->value != 0))
// if windows is focused or background input is enabled
const bool is_focus = g_WiimoteInitialize.pRendererHasFocus() || (m_options->settings[0]->value != 0);
bool Wiimote::Step()
{
const bool has_focus = HAS_FOCUS;
const bool is_sideways = m_options->settings[1]->value != 0;
// no rumble if no focus
if (false == is_focus)
if (false == has_focus)
m_rumble_on = false;
m_rumble->controls[0]->control_ref->State(m_rumble_on);
// update buttons in status struct
m_status.buttons = 0;
if (has_focus)
{
m_buttons->GetState(&m_status.buttons, button_bitmasks);
m_dpad->GetState(&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
UDPTLayer::GetButtons(m_udp, &m_status.buttons);
}
// check if there is a read data request
if (m_read_requests.size())
{
ReadRequest& rr = m_read_requests.front();
// send up to 16 bytes to the wii
SendReadDataReply(rr);
//SendReadDataReply(rr.channel, rr);
// if there is no more data, remove from queue
if (0 == rr.size)
{
delete[] rr.data;
m_read_requests.pop();
}
// dont send any other reports
return true;
}
// check if a status report needs to be sent
// this happens on wiimote sync and when extensions are switched
if (m_extension->active_extension != m_extension->switch_extension)
{
RequestStatus();
// Wiibrew: Following a connection or disconnection event on the Extension Port,
// data reporting is disabled and the Data Reporting Mode must be reset before new data can arrive.
// after a game receives an unrequested status report,
// it expects data reports to stop until it sets the reporting mode again
m_reporting_auto = false;
return true;
}
return false;
}
void Wiimote::GetCoreData(u8* const data)
{
*(wm_core*)data |= m_status.buttons;
}
void Wiimote::GetAccelData(u8* const data)
{
const bool has_focus = HAS_FOCUS;
const bool is_sideways = m_options->settings[1]->value != 0;
const bool is_upright = m_options->settings[2]->value != 0;
// ----TILT----
EmulateTilt((wm_accel*)data, m_tilt, (accel_cal*)&m_eeprom[0x16], has_focus, is_sideways, is_upright);
// ----SWING----
// ----SHAKE----
if (has_focus)
{
EmulateSwing((wm_accel*)data, m_swing, (accel_cal*)&m_eeprom[0x16], is_sideways, is_upright);
EmulateShake(data, m_shake, m_shake_step);
// UDP Wiimote
UDPTLayer::GetAcceleration(m_udp, (wm_accel*)data, (accel_cal*)&m_eeprom[0x16]);
}
}
void Wiimote::GetIRData(u8* const data)
{
const bool has_focus = HAS_FOCUS;
u16 x[4], y[4];
memset(x, 0xFF, sizeof(x));
if (has_focus)
{
float xx = 10000, yy = 0, zz = 0;
float tx, ty;
m_ir->GetState(&xx, &yy, &zz, true);
UDPTLayer::GetIR(m_udp, &xx, &yy, &zz);
m_tilt->GetState(&tx, &ty, 0, PI/2, false);
// disabled cause my math still fails
const float rsin = 0;//sin(tx);
const float rcos = 1;//cos(tx);
{
const float xxx = (xx * -256 * 0.95f);
const float yyy = (yy * -256 * 0.90f);
xx = 512 + xxx * rcos + (144 + yyy) * rsin;
yy = 384 + (108 + yyy) * rcos - xxx * rsin;
}
// dot distance of 25 is too little
const unsigned int distance = (unsigned int)(150 + 100 * zz);
x[0] = (unsigned int)(xx - distance * rcos);
x[1] = (unsigned int)(xx + distance * rcos);
x[2] = (unsigned int)(xx - 1.2f * distance * rcos);
x[3] = (unsigned int)(xx + 1.2f * distance * rcos);
y[0] = (unsigned int)(yy - 0.75 * distance * rsin);
y[1] = (unsigned int)(yy + 0.75 * distance * rsin);
y[2] = (unsigned int)(yy - 0.75 * 1.2f * distance * rsin);
y[3] = (unsigned int)(yy + 0.75 * 1.2f * distance * rsin);
}
// Fill report with valid data when full handshake was done
if (m_reg_ir->data[0x30])
// ir mode
switch (m_reg_ir->mode)
{
// basic
case 1 :
{
memset(data, 0xFF, 10);
wm_ir_basic* const irdata = (wm_ir_basic*)data;
for (unsigned int i=0; i<2; ++i)
{
if (x[i*2] < 1024 && y[i*2] < 768)
{
irdata[i].x1 = u8(x[i*2]);
irdata[i].x1hi = x[i*2] >> 8;
irdata[i].y1 = u8(y[i*2]);
irdata[i].y1hi = y[i*2] >> 8;
}
if (x[i*2+1] < 1024 && y[i*2+1] < 768)
{
irdata[i].x2 = u8(x[i*2+1]);
irdata[i].x2hi = x[i*2+1] >> 8;
irdata[i].y2 = u8(y[i*2+1]);
irdata[i].y2hi = y[i*2+1] >> 8;
}
}
}
break;
// extended
case 3 :
{
memset(data, 0xFF, 12);
wm_ir_extended* const irdata = (wm_ir_extended*)data;
for (unsigned int i=0; i<4; ++i)
if (x[i] < 1024 && y[i] < 768)
{
irdata[i].x = u8(x[i]);
irdata[i].xhi = x[i] >> 8;
irdata[i].y = u8(y[i]);
irdata[i].yhi = y[i] >> 8;
irdata[i].size = 10;
}
}
break;
// full
case 5 :
// UNSUPPORTED
break;
}
}
void Wiimote::GetExtData(u8* const data)
{
const bool has_focus = HAS_FOCUS;
m_extension->GetState(data, HAS_FOCUS);
// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
// i think it should be unencrpyted in the register, encrypted when read.
memcpy(m_reg_ext->controller_data, data, sizeof(wm_extension));
if (0xAA == m_reg_ext->encryption)
wiimote_encrypt(&m_ext_key, data, 0x00, sizeof(wm_extension));
}
void Wiimote::Update()
{
// no channel == not connected i guess
if (0 == m_reporting_channel)
return;
// returns true if a report was sent
if (Step())
return;
// ----speaker----
#ifdef USE_WIIMOTE_EMU_SPEAKER
@ -367,208 +562,119 @@ void Wiimote::Update()
//}
#endif
// update buttons in status struct
m_status.buttons = 0;
if (is_focus)
{
m_buttons->GetState(&m_status.buttons, button_bitmasks);
m_dpad->GetState(&m_status.buttons, is_sideways ? dpad_sideways_bitmasks : dpad_bitmasks);
UDPTLayer::GetButtons(m_udp, &m_status.buttons);
}
// no channel == not connected i guess
if (0 == m_reporting_channel)
return;
// check if there is a read data request
if (m_read_requests.size())
{
ReadRequest& rr = m_read_requests.front();
// send up to 16 bytes to the wii
SendReadDataReply(m_reporting_channel, rr);
//SendReadDataReply(rr.channel, rr);
// if there is no more data, remove from queue
if (0 == rr.size)
{
delete[] rr.data;
m_read_requests.pop();
}
// dont send any other reports
return;
}
// -- maybe this should happen before the read request stuff?
// check if a status report needs to be sent
// this happens on wiimote sync and when extensions are switched
if (m_extension->active_extension != m_extension->switch_extension)
{
RequestStatus(m_reporting_channel);
// Wiibrew: Following a connection or disconnection event on the Extension Port,
// data reporting is disabled and the Data Reporting Mode must be reset before new data can arrive.
// after a game receives an unrequested status report,
// it expects data reports to stop until it sets the reporting mode again
m_reporting_auto = false;
}
if (false == m_reporting_auto)
return;
// figure out what data we need
const ReportFeatures& rpt = reporting_mode_features[m_reporting_mode - WM_REPORT_CORE];
// what does the real wiimote do when put in a reporting mode with extension data,
// but with no extension attached? should i just send zeros? sure
//if (rpt.ext && (m_extension->active_extension <= 0))
//{
// m_reporting_auto = false;
// return;
//}
// set up output report
// made data bigger than needed in case the wii specifies the wrong ir mode for a reporting mode
u8 data[46];
memset( data, 0, sizeof(data) );
u8 data[MAX_PAYLOAD];
memset(data, 0, sizeof(data));
data[0] = 0xA1;
data[1] = m_reporting_mode;
// figure out what data we need
const ReportFeatures& rptf = reporting_mode_features[m_reporting_mode - WM_REPORT_CORE];
s8 rptf_size = rptf.size;
// core buttons
if (rpt.core)
*(wm_core*)(data + rpt.core) = m_status.buttons;
if (rptf.core)
GetCoreData(data + rptf.core);
// ----accelerometer----
if (rpt.accel)
// acceleration
if (rptf.accel)
GetAccelData(data + rptf.accel);
// IR
if (rptf.ir)
GetIRData(data + rptf.ir);
// extension
if (rptf.ext)
GetExtData(data + rptf.ext);
// hybrid wiimote stuff
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index])
{
// ----TILT----
EmulateTilt((wm_accel*)&data[rpt.accel], m_tilt, (accel_cal*)&m_eeprom[0x16], is_focus, is_sideways, is_upright);
using namespace WiimoteReal;
// ----SWING----
// ----SHAKE----
if (is_focus)
g_refresh_critsec.Enter();
if (g_wiimotes[m_index])
{
EmulateSwing((wm_accel*)&data[rpt.accel], m_swing, (accel_cal*)&m_eeprom[0x16], is_sideways, is_upright);
EmulateShake(data + rpt.accel, m_shake, m_shake_step);
// UDP Wiimote
UDPTLayer::GetAcceleration(m_udp, (wm_accel*)&data[rpt.accel], (accel_cal*)&m_eeprom[0x16]);
u8* const real_data = g_wiimotes[m_index]->ProcessReadQueue();
if (real_data)
{
switch (real_data[1])
{
// use data reports
default:
if (real_data[1] >= WM_REPORT_CORE)
{
const ReportFeatures& real_rptf = reporting_mode_features[real_data[1] - WM_REPORT_CORE];
// force same report type from real-wiimote
if (&real_rptf != &rptf)
rptf_size = 0;
// core
// mix real-buttons with emu-buttons in the status struct, and in the report
if (real_rptf.core && rptf.core)
{
m_status.buttons |= *(wm_core*)(real_data + real_rptf.core);
*(wm_core*)(data + rptf.core) = m_status.buttons;
}
// accel
// use real-accel data always i guess
if (real_rptf.accel && rptf.accel)
memcpy(data + rptf.accel, real_data + real_rptf.accel, sizeof(wm_accel));
// ir
// TODO
// ext
// use real-ext data if an emu-extention isn't chosen
if (real_rptf.ext && rptf.ext && (0 == m_extension->switch_extension))
memcpy(data + rptf.ext, real_data + real_rptf.ext, sizeof(wm_extension));
}
else if (WM_ACK_DATA != real_data[1] || m_extension->active_extension > 0)
rptf_size = 0;
else
// use real-acks if an emu-extension isn't chosen
rptf_size = -1;
break;
// use all status reports, after modification of the extension bit
case WM_STATUS_REPORT :
//if (m_extension->switch_extension)
//((wm_status_report*)(real_data + 2))->extension = (m_extension->active_extension > 0);
if (m_extension->active_extension)
((wm_status_report*)(real_data + 2))->extension = 1;
rptf_size = -1;
break;
// use all read-data replies
case WM_READ_DATA_REPLY:
rptf_size = -1;
break;
}
// copy over report from real-wiimote
if (-1 == rptf_size)
{
memcpy(data, real_data, MAX_PAYLOAD);
rptf_size = MAX_PAYLOAD;
}
if (real_data != g_wiimotes[m_index]->m_last_data_report)
delete[] real_data;
}
}
g_refresh_critsec.Leave();
}
// ----ir----
if (rpt.ir)
{
float xx = 10000, yy = 0, zz = 0;
unsigned int x[4], y[4];
// don't send a data report if auto reporting is off
if (false == m_reporting_auto && data[2] >= WM_REPORT_CORE)
return;
if (is_focus)
{
m_ir->GetState(&xx, &yy, &zz, true);
UDPTLayer::GetIR(m_udp, &xx, &yy, &zz);
float tx, ty;
m_tilt->GetState(&tx, &ty, 0, 1, false);
// TODO: fix tilt math stuff
const float rtan = tan(0.0f/*tx*/); // disabled cause my math fails
const float rsin = sin(rtan);
const float rcos = cos(rtan);
{
const float xxx = (xx * -256 * 0.95f);
const float yyy = (yy * -256 * 0.90f);
xx = 512 + xxx * rcos + yyy * rsin;
yy = 490 + yyy * rcos + xxx * rsin;
}
const unsigned int distance = (unsigned int)(200 + 100 * zz);
x[0] = (unsigned int)(xx - distance * rcos);
x[1] = (unsigned int)(xx + distance * rcos);
x[2] = (unsigned int)(xx - 1.2f * distance * rcos);
x[3] = (unsigned int)(xx + 1.2f * distance * rcos);
y[0] = (unsigned int)(yy - 0.75 * distance * rsin);
y[1] = (unsigned int)(yy + 0.75 * distance * rsin);
y[2] = (unsigned int)(yy - 0.75 * 1.2f * distance * rsin);
y[3] = (unsigned int)(yy + 0.75 * 1.2f * distance * rsin);
}
// Fill report with valid data when full handshake was done
if (m_reg_ir->data[0x30])
// ir mode
switch (m_reg_ir->mode)
{
// basic
case 1 :
{
memset(data + rpt.ir, 0xFF, 10);
wm_ir_basic* const irdata = (wm_ir_basic*)(data + rpt.ir);
for (unsigned int i=0; i<2; ++i)
{
if (x[i*2] < 1024 && y[i*2] < 768)
{
irdata[i].x1 = u8(x[i*2]);
irdata[i].x1hi = x[i*2] >> 8;
irdata[i].y1 = u8(y[i*2]);
irdata[i].y1hi = y[i*2] >> 8;
}
if (x[i*2+1] < 1024 && y[i*2+1] < 768)
{
irdata[i].x2 = u8(x[i*2+1]);
irdata[i].x2hi = x[i*2+1] >> 8;
irdata[i].y2 = u8(y[i*2+1]);
irdata[i].y2hi = y[i*2+1] >> 8;
}
}
}
break;
// extended
case 3 :
{
memset(data + rpt.ir, 0xFF, 12);
wm_ir_extended* const irdata = (wm_ir_extended*)(data + rpt.ir);
for (unsigned int i=0; i<4; ++i)
if (x[i] < 1024 && y[i] < 768)
{
irdata[i].x = u8(x[i]);
irdata[i].xhi = x[i] >> 8;
irdata[i].y = u8(y[i]);
irdata[i].yhi = y[i] >> 8;
irdata[i].size = 10;
}
}
break;
// full
case 5 :
// UNSUPPORTED
break;
}
}
// ----extension----
if (rpt.ext)
{
m_extension->GetState(data + rpt.ext, is_focus);
// i dont think anything accesses the extension data like this, but ill support it. Indeed, commercial games don't do this.
// i think it should be unencrpyted in the register, encrypted when read.
memcpy(m_reg_ext->controller_data, data + rpt.ext, sizeof(wm_extension));
if (0xAA == m_reg_ext->encryption) {
wiimote_encrypt(&m_ext_key, data + rpt.ext, 0x00, sizeof(wm_extension));
}
}
// send data report
g_WiimoteInitialize.pWiimoteInterruptChannel( m_index, m_reporting_channel, data, rpt.size );
if (rptf_size)
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_reporting_channel, data, rptf_size);
}
void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size)
@ -587,7 +693,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
// this all good?
m_reporting_channel = _channelID;
hid_packet* hidp = (hid_packet*)_pData;
const hid_packet* const hidp = (hid_packet*)_pData;
INFO_LOG(WIIMOTE, "Emu ControlChannel (page: %i, type: 0x%02x, param: 0x%02x)", m_index, hidp->type, hidp->param);
@ -606,7 +712,7 @@ void Wiimote::ControlChannel(const u16 _channelID, const void* _pData, u32 _Size
{
// AyuanX: My experiment shows Control Channel is never used
// shuffle2: but homebrew uses this, so we'll do what we must :)
HidOutputReport(_channelID, (wm_report*)hidp->data);
HidOutputReport((wm_report*)hidp->data);
u8 handshake = HID_HANDSHAKE_SUCCESS;
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, _channelID, &handshake, 1);
@ -631,7 +737,7 @@ void Wiimote::InterruptChannel(const u16 _channelID, const void* _pData, u32 _Si
// this all good?
m_reporting_channel = _channelID;
hid_packet* hidp = (hid_packet*)_pData;
const hid_packet* const hidp = (hid_packet*)_pData;
switch (hidp->type)
{
@ -640,8 +746,26 @@ void Wiimote::InterruptChannel(const u16 _channelID, const void* _pData, u32 _Si
{
case HID_PARAM_OUTPUT :
{
wm_report* sr = (wm_report*)hidp->data;
HidOutputReport(_channelID, sr);
const wm_report* const sr = (wm_report*)hidp->data;
if (WIIMOTE_SRC_HYBRID == g_wiimote_sources[m_index])
{
switch (sr->wm)
{
// these two types are handled in RequestStatus() & ReadData()
case WM_REQUEST_STATUS :
case WM_READ_DATA :
break;
default :
WiimoteReal::InterruptChannel(m_index, _channelID, _pData, _Size);
break;
}
HidOutputReport(sr, m_extension->switch_extension > 0);
}
else
HidOutputReport(sr);
}
break;
@ -762,7 +886,7 @@ void Wiimote::Register::Read( size_t address, void* dst, size_t length )
}
// TODO: i need to test this
void Wiimote::Register::Write( size_t address, void* src, size_t length )
void Wiimote::Register::Write( size_t address, const void* src, size_t length )
{
iterator i = begin();
const const_iterator e = end();

View File

@ -8,7 +8,7 @@
// just used to get the OpenAL includes :p
//#include <OpenALStream.h>
#include <ControllerEmu.h>
#include "ControllerEmu.h"
#include "ChunkFile.h"
#include "WiimoteHid.h"
@ -32,6 +32,13 @@ extern SWiimoteInitialize g_WiimoteInitialize;
namespace WiimoteEmu
{
struct ReportFeatures
{
u8 core, accel, ir, ext, size;
};
extern const ReportFeatures reporting_mode_features[];
void EmulateShake(u8* const accel_data
, ControllerEmu::Buttons* const buttons_group
, unsigned int* const shake_step);
@ -77,6 +84,19 @@ public:
void LoadDefaults(const ControllerInterface& ciface);
protected:
bool Step();
void HidOutputReport(const wm_report* const sr, const bool send_ack = true);
void HandleExtensionSwap();
void GetCoreData(u8* const data);
void GetAccelData(u8* const data);
void GetIRData(u8* const data);
void GetExtData(u8* const data);
bool HaveExtension() const { return m_extension->active_extension > 0; }
bool WantExtension() const { return m_extension->switch_extension != 0; }
private:
struct ReadRequest
{
@ -87,14 +107,12 @@ private:
void Reset();
void ReportMode(const u16 _channelID, wm_report_mode* dr);
void HidOutputReport(const u16 _channelID, wm_report* sr);
void SendAck(const u16 _channelID, u8 _reportID);
void RequestStatus(const u16 _channelID, wm_request_status* rs = NULL);
void WriteData(const u16 _channelID, wm_write_data* wd);
void ReadData(const u16 _channelID, wm_read_data* rd);
void SendReadDataReply(const u16 _channelID, ReadRequest& _request);
void ReportMode(const wm_report_mode* const dr);
void SendAck(const u8 _reportID);
void RequestStatus(const wm_request_status* const rs = NULL);
void ReadData(const wm_read_data* const rd);
void WriteData(const wm_write_data* const wd);
void SendReadDataReply(ReadRequest& _request);
#ifdef USE_WIIMOTE_EMU_SPEAKER
void SpeakerData(wm_speaker_data* sd);
@ -131,7 +149,7 @@ private:
class Register : public std::map< size_t, std::vector<u8> >
{
public:
void Write( size_t address, void* src, size_t length );
void Write( size_t address, const void* src, size_t length );
void Read( size_t address, void* dst, size_t length );
} m_register;

View File

@ -205,7 +205,7 @@ struct wm_read_data {
u8 space : 2; //see WM_SPACE_*
u8 : 5;
u8 address[3];
u8 size[2];
u16 size;
};
#define WM_SPACE_EEPROM 0

View File

@ -3,9 +3,9 @@
#include "pluginspecs_wiimote.h"
#include "WiimoteReal/WiimoteReal.h"
#include "WiimoteEmu/WiimoteEmu.h"
#include "ControllerInterface/ControllerInterface.h"
#include "WiimoteEmu/WiimoteEmu.h"
#if defined(HAVE_WX) && HAVE_WX
#include "WiimoteConfigDiag.h"
@ -127,15 +127,11 @@ void InitPlugin( void* const hwnd )
//
void Wiimote_ControlChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
switch (g_wiimote_sources[_number])
{
case WIIMOTE_SRC_REAL :
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->ControlChannel(_channelID, _pData, _Size);
if (WIIMOTE_SRC_REAL & g_wiimote_sources[_number])
WiimoteReal::ControlChannel(_number, _channelID, _pData, _Size);
break;
case WIIMOTE_SRC_EMU :
((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->ControlChannel( _channelID, _pData, _Size );
break;
}
}
// __________________________________________________________________________________________________
@ -169,15 +165,10 @@ unsigned int Wiimote_UnPairWiimotes(void)
//
void Wiimote_InterruptChannel(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
switch (g_wiimote_sources[_number])
{
case WIIMOTE_SRC_REAL :
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->InterruptChannel(_channelID, _pData, _Size);
else
WiimoteReal::InterruptChannel(_number, _channelID, _pData, _Size);
break;
case WIIMOTE_SRC_EMU :
((WiimoteEmu::Wiimote*)g_plugin.controllers[ _number ])->InterruptChannel( _channelID, _pData, _Size );
break;
}
}
// __________________________________________________________________________________________________
@ -201,15 +192,10 @@ void Wiimote_Update(int _number)
}
_last_number = _number;
switch (g_wiimote_sources[_number])
{
case WIIMOTE_SRC_REAL :
WiimoteReal::Update(_number);
break;
case WIIMOTE_SRC_EMU :
if (WIIMOTE_SRC_EMU & g_wiimote_sources[_number])
((WiimoteEmu::Wiimote*)g_plugin.controllers[_number])->Update();
break;
}
else
WiimoteReal::Update(_number);
g_plugin.controls_crit.Leave();
}

View File

@ -19,10 +19,8 @@
#include "Common.h"
#include "IniFile.h"
#include "Thread.h"
#include "StringUtil.h"
#include "Timer.h"
#include "FifoQueue.h"
#include "pluginspecs_wiimote.h"
#include "wiiuse.h"
@ -46,45 +44,13 @@ Common::CriticalSection g_refresh_critsec, g_wiimote_critsec;
THREAD_RETURN WiimoteThreadFunc(void* arg);
class Wiimote
{
public:
Wiimote(wiimote_t* const wm, const unsigned int index);
~Wiimote();
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();
void Read();
void Write();
void Disconnect();
void DisableDataReporting();
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
// pointer to data, and size of data
typedef std::pair<u8*,u8> Report;
private:
void ClearReports();
wiimote_t* const m_wiimote;
const unsigned int m_index;
u16 m_channel;
u8 m_last_data_report[MAX_PAYLOAD];
bool m_last_data_report_valid;
Common::FifoQueue<u8*> m_read_reports;
Common::FifoQueue<Report> m_write_reports;
};
Wiimote *g_wiimotes[4];
Wiimote::Wiimote(wiimote_t* const wm, const unsigned int index)
: m_wiimote(wm)
, m_index(index)
, m_channel(0)
, m_last_data_report_valid(false)
, m_last_data_report(NULL)
{
// disable reporting
DisableDataReporting();
@ -100,7 +66,7 @@ Wiimote::Wiimote(wiimote_t* const wm, const unsigned int index)
//SendPacket(g_wiimotes_from_wiiuse[i], WM_LEDS, &rpt, sizeof(rpt));
//}
// Rumble briefly
// Rumble briefly, this is a bad spot for the rumble
wiiuse_rumble(m_wiimote, 1);
SLEEP(200);
wiiuse_rumble(m_wiimote, 0);
@ -145,19 +111,26 @@ void Wiimote::SendPacket(const u8 rpt_id, const void* const data, const unsigned
void Wiimote::DisableDataReporting()
{
wm_report_mode rpt = wm_report_mode();
wm_report_mode rpt;
rpt.mode = WM_REPORT_CORE;
rpt.all_the_time = 0;
rpt.continuous = 0;
rpt.rumble = 0;
SendPacket(WM_REPORT_MODE, &rpt, sizeof(rpt));
}
void Wiimote::ClearReports()
{
m_last_data_report_valid = false;
if (m_last_data_report)
{
delete[] m_last_data_report;
m_last_data_report = NULL;
}
Report rpt;
while (m_read_reports.Pop(rpt.first))
delete[] rpt.first;
while (m_write_reports.Pop(rpt))
delete[] rpt.first;
delete rpt.first;
while (m_read_reports.Pop(rpt.first))
delete rpt.first;
}
void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
@ -177,7 +150,8 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
while (wiiuse_io_read(m_wiimote)) {};
// request status
wm_request_status rpt = wm_request_status();
wm_request_status rpt;
rpt.rumble = 0;
SendPacket(WM_REQUEST_STATUS, &rpt, sizeof(rpt));
}
@ -187,6 +161,24 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const data, const
rpt.first = new u8[size];
rpt.second = (u8)size;
memcpy(rpt.first, (u8*)data, size);
// some hax, since we just send the last data report to Dolphin on each Update() call
// , make the wiimote only send updated data reports when data changes
// == less bt traffic, eliminates some unneeded packets
if (WM_REPORT_MODE == ((u8*)data)[1])
{
// also delete the last data report
if (m_last_data_report)
{
delete[] m_last_data_report;
m_last_data_report = NULL;
}
// nice var names :p, this seems to be this one
((wm_report_mode*)(rpt.first + 2))->all_the_time = false;
//((wm_report_mode*)(data + 2))->continuous = false;
}
m_write_reports.Push(rpt);
}
@ -198,19 +190,10 @@ void Wiimote::Read()
if (wiiuse_io_read(m_wiimote))
{
// a data report, save it
if (m_wiimote->event_buf[1] >= 0x30)
{
memcpy(m_last_data_report, m_wiimote->event_buf, MAX_PAYLOAD);
m_last_data_report_valid = true;
}
else
{
// some other report, add it to queue
u8* const rpt = new u8[MAX_PAYLOAD];
memcpy(rpt, m_wiimote->event_buf, MAX_PAYLOAD);
m_read_reports.Push(rpt);
}
// add it to queue
u8* const rpt = new u8[MAX_PAYLOAD];
memcpy(rpt, m_wiimote->event_buf, MAX_PAYLOAD);
m_read_reports.Push(rpt);
}
}
@ -224,20 +207,37 @@ void Wiimote::Write()
}
}
// returns the next report that should be sent
u8* Wiimote::ProcessReadQueue()
{
// pop through the queued reports
u8* rpt = m_last_data_report;
while (m_read_reports.Pop(rpt))
{
// a data report
if (rpt[1] >= WM_REPORT_CORE)
m_last_data_report = rpt;
// some other kind of report
else
return rpt;
}
// the queue was empty, or there were only data reports
return rpt;
}
void Wiimote::Update()
{
// do we have some queued reports
u8* rpt;
if (m_read_reports.Pop(rpt))
{
// pop through the queued reports
u8* const rpt = ProcessReadQueue();
// send the report
if (rpt)
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, rpt, MAX_PAYLOAD);
// delete the data if it isn't also the last data rpt
if (rpt != m_last_data_report)
delete[] rpt;
}
else if (m_last_data_report_valid)
{
// otherwise send the last data report, if there is one
g_WiimoteInitialize.pWiimoteInterruptChannel(m_index, m_channel, m_last_data_report, MAX_PAYLOAD);
}
}
void Wiimote::Disconnect()
@ -248,14 +248,14 @@ void Wiimote::Disconnect()
DisableDataReporting();
// clear queue
ClearReports();
u8 *rpt;
while (m_read_reports.Pop(rpt))
delete rpt;
// clear out wiiuse queue, or maybe not, silly? idk
while (wiiuse_io_read(m_wiimote)) {};
}
Wiimote* g_wiimotes[4];
void LoadSettings()
{
std::string ini_filename = (std::string(File::GetUserPath(D_CONFIG_IDX)) + g_plugin.ini_name + ".ini" );
@ -284,7 +284,7 @@ unsigned int Initialize()
// only call wiiuse_find with the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0;
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
if (WIIMOTE_SRC_REAL == g_wiimote_sources[i])
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
++wanted_wiimotes;
// don't bother initializing wiiuse if we don't want any real wiimotes
@ -320,14 +320,11 @@ unsigned int Initialize()
g_wiimote_critsec.Enter(); // enter
// create real wiimote class instances, assign wiimotes
for (unsigned int i = 0, w = 0; i<MAX_WIIMOTES && w<g_wiimotes_found; ++i)
{
if (WIIMOTE_SRC_REAL != g_wiimote_sources[i])
continue;
// create/assign wiimote
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
}
g_wiimote_critsec.Leave(); // leave
@ -383,29 +380,21 @@ void Shutdown(void)
void Refresh() // this gets called from the GUI thread
{
g_refresh_critsec.Enter();
#ifdef __linux__
// make sure real wiimotes have been initialized
if (!g_real_wiimotes_initialized)
{
g_refresh_critsec.Leave();
Initialize();
return;
}
// find the number of slots configured for real wiimotes
unsigned int wanted_wiimotes = 0;
for (unsigned int i = 0; i < MAX_WIIMOTES; ++i)
if (g_wiimote_sources[i] == WIIMOTE_SRC_REAL)
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i])
++wanted_wiimotes;
// don't scan for wiimotes if we don't want any more
if (wanted_wiimotes <= g_wiimotes_found)
{
g_refresh_critsec.Leave();
return;
}
// scan for wiimotes
unsigned int num_wiimotes = wiiuse_find(g_wiimotes_from_wiiuse, wanted_wiimotes, 5);
@ -416,58 +405,41 @@ void Refresh() // this gets called from the GUI thread
DEBUG_LOG(WIIMOTE, "Connected to %i additional Real Wiimotes", num_new_wiimotes);
g_refresh_critsec.Enter();
g_wiimote_critsec.Enter(); // enter
// create real wiimote class instances, and assign wiimotes for the new wiimotes
for (unsigned int i = g_wiimotes_found, w = g_wiimotes_found;
i < MAX_WIIMOTES && w < num_wiimotes; ++i)
{
if (g_wiimote_sources[i] != WIIMOTE_SRC_REAL || g_wiimotes[i] != NULL)
continue;
// create/assign wiimote
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
if (WIIMOTE_SRC_REAL & g_wiimote_sources[i] && NULL == g_wiimotes[i])
g_wiimotes[i] = new Wiimote(g_wiimotes_from_wiiuse[w++], i);
}
g_wiimotes_found = num_wiimotes;
g_wiimote_critsec.Leave(); // leave
g_refresh_critsec.Leave();
#else // windows/ OSX
g_refresh_critsec.Enter();
// should be fine i think
Shutdown();
Initialize();
#endif
g_refresh_critsec.Leave();
#endif
}
void InterruptChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)
{
u8* data = (u8*)_pData;
// some hax, since we just send the last data report to Dolphin on each Update() call
// , make the wiimote only send updated data reports when data changes
// == less bt traffic, eliminates some unneeded packets
if (WM_REPORT_MODE == ((u8*)_pData)[1])
{
// I dont wanna write on the const *_pData
data = new u8[_Size];
memcpy(data, _pData, _Size);
// nice var names :p, this seems to be this one
((wm_report_mode*)(data + 2))->all_the_time = false;
//((wm_report_mode*)(data + 2))->continuous = false;
}
g_refresh_critsec.Enter();
if (g_wiimotes[_WiimoteNumber])
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, data, _Size);
g_wiimotes[_WiimoteNumber]->InterruptChannel(_channelID, _pData, _Size);
g_refresh_critsec.Leave();
if (data != _pData)
delete[] data;
}
void ControlChannel(int _WiimoteNumber, u16 _channelID, const void* _pData, u32 _Size)

View File

@ -21,6 +21,9 @@
#include "wiiuse.h"
#include "ChunkFile.h"
#include "Thread.h"
#include "FifoQueue.h"
#include "../WiimoteEmu/WiimoteEmu.h"
#include "../../InputCommon/Src/InputConfig.h"
@ -33,13 +36,53 @@ extern SWiimoteInitialize g_WiimoteInitialize;
enum
{
WIIMOTE_SRC_NONE = 0,
WIIMOTE_SRC_EMU,
WIIMOTE_SRC_REAL,
WIIMOTE_SRC_EMU = 1,
WIIMOTE_SRC_REAL = 2,
WIIMOTE_SRC_HYBRID = 3, // emu + real
};
namespace WiimoteReal
{
class Wiimote
{
friend class WiimoteEmu::Wiimote;
public:
Wiimote(wiimote_t* const wm, const unsigned int index);
~Wiimote();
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();
u8* ProcessReadQueue();
void Read();
void Write();
void Disconnect();
void DisableDataReporting();
void SendPacket(const u8 rpt_id, const void* const data, const unsigned int size);
// pointer to data, and size of data
typedef std::pair<u8*,u8> Report;
protected:
u8 *m_last_data_report;
u16 m_channel;
private:
void ClearReports();
wiimote_t* const m_wiimote;
const unsigned int m_index;
Common::FifoQueue<u8*> m_read_reports;
Common::FifoQueue<Report> m_write_reports;
};
extern Common::CriticalSection g_refresh_critsec;
extern Wiimote *g_wiimotes[4];
unsigned int Initialize();
void Shutdown();
void Refresh();