WiimoteEmu: Motion plus now works half of the time in Wii Sports Resort.

This commit is contained in:
Jordan Woyak
2018-11-29 17:42:59 -06:00
parent 0b4329e077
commit 86c94b8b22
4 changed files with 205 additions and 78 deletions

View File

@ -63,6 +63,8 @@ union wm_buttons // also just called "core data"
u8 down : 1; u8 down : 1;
u8 up : 1; u8 up : 1;
u8 plus : 1; u8 plus : 1;
// For most input reports this is the 2 LSbs of accel.x:
// For interleaved reports this is alternating bits of accel.z:
u8 acc_bits : 2; u8 acc_bits : 2;
u8 unknown : 1; u8 unknown : 1;
@ -71,6 +73,8 @@ union wm_buttons // also just called "core data"
u8 b : 1; u8 b : 1;
u8 a : 1; u8 a : 1;
u8 minus : 1; u8 minus : 1;
// For most input reports this is bits of accel.y/z:
// For interleaved reports this is alternating bits of accel.z:
u8 acc_bits2 : 2; u8 acc_bits2 : 2;
u8 home : 1; u8 home : 1;
}; };
@ -425,8 +429,10 @@ struct wm_write_data
u8 rumble : 1; u8 rumble : 1;
u8 space : 2; // see WM_SPACE_* u8 space : 2; // see WM_SPACE_*
u8 : 5; u8 : 5;
// used only for register space (i2c bus) // A real wiimote ignores the i2c read/write bit.
u8 slave_address; u8 i2c_rw_ignored : 1;
// Used only for register space (i2c bus) (7-bits):
u8 slave_address : 7;
// big endian: // big endian:
u8 address[2]; u8 address[2];
u8 size; u8 size;
@ -447,8 +453,10 @@ struct wm_read_data
u8 rumble : 1; u8 rumble : 1;
u8 space : 2; // see WM_SPACE_* u8 space : 2; // see WM_SPACE_*
u8 : 5; u8 : 5;
// used only for register space (i2c bus) // A real wiimote ignores the i2c read/write bit.
u8 slave_address; u8 i2c_rw_ignored : 1;
// Used only for register space (i2c bus) (7-bits):
u8 slave_address : 7;
// big endian: // big endian:
u8 address[2]; u8 address[2];
u8 size[2]; u8 size[2];
@ -460,6 +468,7 @@ struct wm_read_data_reply
wm_buttons buttons; wm_buttons buttons;
u8 error : 4; // see WM_RDERR_* u8 error : 4; // see WM_RDERR_*
u8 size_minus_one : 4; u8 size_minus_one : 4;
// big endian:
u16 address; u16 address;
u8 data[16]; u8 data[16];
}; };

View File

@ -40,7 +40,7 @@ namespace WiimoteEmu
void Wiimote::ReportMode(const wm_report_mode* const dr) void Wiimote::ReportMode(const wm_report_mode* const dr)
{ {
if (dr->mode < RT_REPORT_CORE || dr->mode > RT_REPORT_INTERLEAVE2 || if (dr->mode < RT_REPORT_CORE || dr->mode > RT_REPORT_INTERLEAVE2 ||
(dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21)) (dr->mode > RT_REPORT_CORE_ACCEL_IR10_EXT6 && dr->mode < RT_REPORT_EXT21))
{ {
// A real wiimote ignores the entire message if the mode is invalid. // A real wiimote ignores the entire message if the mode is invalid.
WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode); WARN_LOG(WIIMOTE, "Game requested invalid report mode: 0x%02x", dr->mode);
@ -177,11 +177,11 @@ void Wiimote::SendAck(u8 report_id, u8 error_code)
rpt.param = HID_PARAM_INPUT; rpt.param = HID_PARAM_INPUT;
rpt.report_id = RT_ACK_DATA; rpt.report_id = RT_ACK_DATA;
auto ack = &rpt.data; auto& ack = rpt.data;
ack->buttons = m_status.buttons; ack.buttons = m_status.buttons;
ack->reportID = report_id; ack.reportID = report_id;
ack->errorID = error_code; ack.errorID = error_code;
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(),
rpt.GetSize()); rpt.GetSize());
@ -194,12 +194,22 @@ void Wiimote::HandleExtensionSwap()
{ {
// if an extension is currently connected and we want to switch to a different 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 // detach extension first, wait til next Update() or RequestStatus() call to change to the new
// extension // extension
m_extension->active_extension = 0; m_extension->active_extension = 0;
}
else else
{
// set the wanted extension // set the wanted extension
m_extension->active_extension = m_extension->switch_extension; m_extension->active_extension = m_extension->switch_extension;
}
// TODO: this is a bit ugly:
if (m_extension->active_extension != 0)
m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic);
else
m_motion_plus_logic.extension_port.SetAttachment(nullptr);
// reset register // reset register
((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension].get()) ((WiimoteEmu::Attachment*)m_extension->attachments[m_extension->active_extension].get())
@ -209,7 +219,7 @@ void Wiimote::HandleExtensionSwap()
void Wiimote::RequestStatus(const wm_request_status* const rs) void Wiimote::RequestStatus(const wm_request_status* const rs)
{ {
INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); // INFO_LOG(WIIMOTE, "Wiimote::RequestStatus");
// update status struct // update status struct
m_status.extension = m_extension_port.IsDeviceConnected(); m_status.extension = m_extension_port.IsDeviceConnected();
@ -247,7 +257,8 @@ void Wiimote::WriteData(const wm_write_data* const wd)
return; return;
} }
INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space, wd->slave_address, address, wd->size); INFO_LOG(WIIMOTE, "Wiimote::WriteData: 0x%02x @ 0x%02x @ 0x%02x (%d)", wd->space,
wd->slave_address, address, wd->size);
if (wd->size > 16) if (wd->size > 16)
{ {
@ -290,7 +301,8 @@ void Wiimote::WriteData(const wm_write_data* const wd)
// Write to Control Register // Write to Control Register
// Top byte of address is ignored on the bus. // Top byte of address is ignored on the bus.
auto const bytes_written = m_i2c_bus.BusWrite(wd->slave_address >> 1, (u8)address, wd->size, wd->data); auto const bytes_written =
m_i2c_bus.BusWrite(wd->slave_address, (u8)address, wd->size, wd->data);
if (bytes_written != wd->size) if (bytes_written != wd->size)
{ {
// A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave) // A real wiimote gives error 7 for failed write to i2c bus (mainly a non-existant slave)
@ -325,7 +337,7 @@ void Wiimote::ReadData(const wm_read_data* const rd)
m_read_request.size = Common::swap16(rd->size); m_read_request.size = Common::swap16(rd->size);
INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space, INFO_LOG(WIIMOTE, "Wiimote::ReadData: %d @ 0x%02x @ 0x%02x (%d)", m_read_request.space,
m_read_request.slave_address, m_read_request.address, m_read_request.size); m_read_request.slave_address, m_read_request.address, m_read_request.size);
// Send up to one read-data-reply. // Send up to one read-data-reply.
// If more data needs to be sent it will happen on the next "Update()" // If more data needs to be sent it will happen on the next "Update()"
@ -336,7 +348,7 @@ bool Wiimote::ProcessReadDataRequest()
{ {
// Limit the amt to 16 bytes // Limit the amt to 16 bytes
// AyuanX: the MTU is 640B though... what a waste! // AyuanX: the MTU is 640B though... what a waste!
u16 const bytes_to_read = std::min((u16)16, m_read_request.size); const u16 bytes_to_read = std::min<u16>(16, m_read_request.size);
if (0 == bytes_to_read) if (0 == bytes_to_read)
{ {
@ -404,28 +416,40 @@ bool Wiimote::ProcessReadDataRequest()
// Read from Control Register // Read from Control Register
// Top byte of address is ignored on the bus, but it IS maintained in the read-reply. // Top byte of address is ignored on the bus, but it IS maintained in the read-reply.
auto const bytes_read = m_i2c_bus.BusRead(m_read_request.slave_address >> 1, auto const bytes_read = m_i2c_bus.BusRead(
(u8)m_read_request.address, bytes_to_read, reply->data); m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply->data);
reply->size_minus_one = bytes_read - 1; reply->size_minus_one = bytes_read - 1;
if (bytes_read != bytes_to_read) if (bytes_read != bytes_to_read)
{ {
// generate read error, 7 == no such slave (no ack) // generate read error, 7 == no such slave (no ack)
INFO_LOG(WIIMOTE, "Responding with read error 7."); INFO_LOG(WIIMOTE, "Responding with read error 7 @ 0x%x @ 0x%x (%d)",
m_read_request.slave_address, m_read_request.address, m_read_request.size);
reply->error = 0x07; reply->error = 0x07;
} }
} }
break; break;
default: default:
PanicAlert("Wiimote::ReadData: unimplemented address space (space: 0x%x)!", m_read_request.space); PanicAlert("Wiimote::ReadData: invalid address space (space: 0x%x)!", m_read_request.space);
break; break;
} }
// Modify the read request, zero size == complete if (reply->error)
m_read_request.address += bytes_to_read; {
m_read_request.size -= bytes_to_read; // Stop processing request on read error:
m_read_request.size = 0;
// TODO: what size does a real wiimote return on read error?
// it's 10 minus one (9) for some reason??
// reply->size_minus_one = 0;
}
else
{
// Modify the read request, zero size == complete
m_read_request.address += bytes_to_read;
m_read_request.size -= bytes_to_read;
}
// Send the data // Send the data
Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(),

View File

@ -101,7 +101,7 @@ static const ReportFeatures reporting_mode_features[] = {
{2, 0, 10, 9, 23}, {2, 0, 10, 9, 23},
// 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes
{2, 3, 10, 6, 23}, {2, 3, 10, 6, 23},
// Ugly padding members so 0x3d,3e,3f are properly placed in the array: // 0x38 - 0x3c: Nothing
{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0},
@ -109,7 +109,8 @@ static const ReportFeatures reporting_mode_features[] = {
{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0},
// 0x3d: 21 Extension Bytes // 0x3d: 21 Extension Bytes
{0, 0, 0, 21, 23}, {0, 0, 0, 21, 23},
// 0x3e / 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes // 0x3e - 0x3f: Interleaved Core Buttons and Accelerometer with 36 IR bytes
{2, 1, 0, 18, 23},
{2, 1, 0, 18, 23}, {2, 1, 0, 18, 23},
}; };
@ -313,6 +314,7 @@ static const char* const named_buttons[] = {
void Wiimote::Reset() void Wiimote::Reset()
{ {
// TODO: Is a wiimote in CORE or DISABLED reporting mode on sync?
m_reporting_mode = RT_REPORT_DISABLED; m_reporting_mode = RT_REPORT_DISABLED;
m_reporting_channel = 0; m_reporting_channel = 0;
m_reporting_auto = false; m_reporting_auto = false;
@ -345,8 +347,26 @@ void Wiimote::Reset()
static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60,
0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d}; 0xc9, 0xf5, 0x37, 0xc1, 0x2d, 0xe9, 0x15, 0x8d};
static const u8 mp_cert[64] = {
//0x50, 0xc3, 0x0c, 0xab, 0x16, 0x07, 0xf6, 0x89, 0x51, 0x93, 0xbe, 0xa5, 0xb2,
//0xbb, 0xbb, 0x35, 0x49, 0x32, 0x04, 0xfd, 0x29, 0x1d, 0xc1, 0xb7, 0x5a, 0x7c,
//0x85, 0xb9, 0x78, 0x14, 0xf4, 0xfe, 0x21, 0x30, 0xa2, 0x5f, 0xb2, 0xc2, 0x8b,
//0x72, 0x02, 0xf8, 0x60, 0xdf, 0x03, 0x30, 0xdc, 0xb6, 0x86, 0xa4, 0x41, 0xdd,
//0x49, 0x01, 0x7b, 0x2f, 0xb2, 0xc8, 0x5b, 0x12, 0x92, 0x47, 0xb8, 0x23
//0xf0, 0x89, 0x3b, 0xf7, 0x1b, 0x6c, 0x92, 0x95, 0xa0, 0x05, 0xd4, 0x03, 0x82,
//0x8e, 0xae, 0x73, 0x15, 0xc7, 0x95, 0xfb, 0xae, 0xee, 0xc0, 0x68, 0xbd, 0x49,
//0xf5, 0x32, 0x48, 0x8d, 0x33, 0x00, 0x94, 0x32, 0xf5, 0xf1, 0x30, 0x66, 0x68,
//0x9e, 0xf3, 0xe5, 0xfa, 0x9b, 0xb6, 0xe3, 0x0b, 0xa8, 0x07, 0xd5, 0x25, 0x38,
0x99, 0x1a, 0x07, 0x1b, 0x97, 0xf1, 0x11, 0x78, 0x0c, 0x42, 0x2b, 0x68, 0xdf, 0x44, 0x38, 0x0d,
0x2b, 0x7e, 0xd6, 0x84, 0x84, 0x58, 0x65, 0xc9, 0xf2, 0x95, 0xd9, 0xaf, 0xb6, 0xc4, 0x87, 0xd5,
0x18, 0xdb, 0x67, 0x3a, 0xc0, 0x71, 0xec, 0x3e, 0xf4, 0xe6, 0x7e, 0x35, 0xa3, 0x29, 0xf8, 0x1f,
0xc5, 0x7c, 0x3d, 0xb9, 0x56, 0x22, 0x95, 0x98, 0x8f, 0xfb, 0x66, 0x3e, 0x9a, 0xdd, 0xeb, 0x7e,
};
// std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data); // std::copy(std::begin(c1), std::end(c1), m_motion_plus_logic.reg_data.calibration_data);
// std::copy(std::begin(c2), std::end(c2), m_motion_plus_logic.reg_data.calibration_data + 0x10); std::copy(std::begin(mp_cert), std::end(mp_cert), m_motion_plus_logic.reg_data.cert_data);
// status // status
memset(&m_status, 0, sizeof(m_status)); memset(&m_status, 0, sizeof(m_status));
@ -379,7 +399,7 @@ void Wiimote::Reset()
// TODO: only add to bus when connected: // TODO: only add to bus when connected:
// Address 0x52 (when motion plus is not activated) // Address 0x52 (when motion plus is not activated)
// Connected to motion plus i2c_bus (with passthrough by default) // Connected to motion plus i2c_bus (with passthrough by default)
m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic); //m_motion_plus_logic.extension_port.SetAttachment(&m_ext_logic);
} }
Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1) Wiimote::Wiimote(const unsigned int index) : m_index(index), ir_sin(0), ir_cos(1)
@ -563,19 +583,12 @@ bool Wiimote::Step()
UpdateButtonsStatus(); UpdateButtonsStatus();
} }
if (ProcessReadDataRequest())
{
// Read requests suppress normal input reports
// Don't send any other reports
return true;
}
// If an extension change is requested in the GUI it will first be disconnected here. // If an extension change is requested in the GUI it will first be disconnected here.
// causing IsDeviceConnected() to return false below: // causing IsDeviceConnected() to return false below:
HandleExtensionSwap(); HandleExtensionSwap();
// check if a status report needs to be sent // Check if a status report needs to be sent. This happens when extensions are switched.
// this happens when extensions are switched // ..even during read requests which continue after the status report is sent.
if (m_status.extension != m_extension_port.IsDeviceConnected()) if (m_status.extension != m_extension_port.IsDeviceConnected())
{ {
// WiiBrew: Following a connection or disconnection event on the Extension Port, // WiiBrew: Following a connection or disconnection event on the Extension Port,
@ -583,11 +596,20 @@ bool Wiimote::Step()
// arrive. // arrive.
m_reporting_mode = RT_REPORT_DISABLED; m_reporting_mode = RT_REPORT_DISABLED;
INFO_LOG(WIIMOTE, "Sending status report due to extension status change.");
RequestStatus(); RequestStatus();
return true; return true;
} }
if (ProcessReadDataRequest())
{
// Read requests suppress normal input reports
// Don't send any other reports
return true;
}
return false; return false;
} }
@ -878,7 +900,7 @@ void Wiimote::Update()
if (RT_REPORT_DISABLED == m_reporting_mode) if (RT_REPORT_DISABLED == m_reporting_mode)
{ {
// The wiimote is in this disabled state on boot and after an extension change. // The wiimote is in this disabled after an extension change.
// Input reports are not sent, even on button change. // Input reports are not sent, even on button change.
return; return;
} }
@ -932,14 +954,14 @@ void Wiimote::Update()
if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) if (RT_REPORT_INTERLEAVE1 == m_reporting_mode)
{ {
*feature_ptr = (x >> 2) & 0xff; *feature_ptr = (x >> 2) & 0xff;
core.acc_bits = (z >> 4) & 0b11; core.acc_bits = (z >> 6) & 0b11;
core.acc_bits2 = (z >> 6) & 0b11; core.acc_bits2 = (z >> 8) & 0b11;
} }
if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) if (RT_REPORT_INTERLEAVE2 == m_reporting_mode)
{ {
*feature_ptr = (y >> 2) & 0xff; *feature_ptr = (y >> 2) & 0xff;
core.acc_bits = (z >> 0) & 0b11; core.acc_bits = (z >> 2) & 0b11;
core.acc_bits2 = (z >> 2) & 0b11; core.acc_bits2 = (z >> 4) & 0b11;
} }
else else
{ {
@ -999,6 +1021,7 @@ void Wiimote::Update()
Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension,
m_ext_logic.ext_key); m_ext_logic.ext_key);
} }
if (NetPlay::IsNetPlayRunning()) if (NetPlay::IsNetPlayRunning())
{ {
NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode); NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode);
@ -1006,7 +1029,6 @@ void Wiimote::Update()
m_status.buttons = *reinterpret_cast<wm_buttons*>(data + rptf.core_size); m_status.buttons = *reinterpret_cast<wm_buttons*>(data + rptf.core_size);
} }
// TODO: need to fix usage of rptf probably
Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension,
m_ext_logic.ext_key); m_ext_logic.ext_key);
@ -1180,7 +1202,7 @@ int Wiimote::CurrentExtension() const
bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() bool Wiimote::ExtensionLogic::ReadDeviceDetectPin()
{ {
return extension->active_extension ? true : false; return extension->active_extension != 0;
} }
void Wiimote::ExtensionLogic::Update() void Wiimote::ExtensionLogic::Update()
@ -1204,25 +1226,54 @@ void Wiimote::MotionPlusLogic::Update()
return; return;
} }
// TODO: clean up this hackery: auto& data = reg_data.controller_data;
// the value seems to increase based on time starting after the first read of 0x00 auto& mplus_data = *reinterpret_cast<wm_motionplus_data*>(data);
if (IsActive() && times_updated_since_activation < 0xff)
{
++times_updated_since_activation;
// TODO: wtf is this value actually.. if (0x0 == reg_data.cert_ready)
if (times_updated_since_activation == 9) {
reg_data.initialization_status = 0x4;
else if (times_updated_since_activation == 10) // Without sending this nonsense, inputs are unresponsive.. even regular buttons
reg_data.initialization_status = 0x8; // Device still operates when changing the data slightly so its not any sort of encrpytion
else if (times_updated_since_activation == 18) // It even works when removing the is_mp_data bit in the last byte
reg_data.initialization_status = 0xc; static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc, 0x02};
else if (times_updated_since_activation == 53) //std::copy(std::begin(init_data), std::end(init_data), data);
reg_data.initialization_status = 0xe; reg_data.cert_ready = 0x2;
//return;
} }
auto& mplus_data = *reinterpret_cast<wm_motionplus_data*>(reg_data.controller_data); if (0x2 == reg_data.cert_ready)
auto& data = reg_data.controller_data; {
// A real wiimote takes about 2 seconds to reach this state:
reg_data.cert_ready = 0xe;
}
if (0x18 == reg_data.cert_ready)
{
const u8 mp_cert2[64] = {
//0x39, 0x7c, 0xe9, 0x79, 0x15, 0x52, 0x0e, 0x4f, 0x28, 0x4d, 0x9d, 0x2c, 0xd3,
//0x2a, 0x1a, 0x28, 0xa1, 0x25, 0x55, 0xb4, 0x4e, 0xb1, 0xd5, 0xae, 0x9d, 0x99,
//0x96, 0x96, 0x1d, 0x94, 0xd1, 0x22, 0xca, 0x1f, 0x51, 0x1d, 0x55, 0xee, 0x4d,
//0x58, 0x97, 0xd4, 0xb9, 0x3f, 0x0d, 0x0a, 0x04, 0xd8, 0x01, 0x21, 0xf9, 0x17,
//0x45, 0xe4, 0x42, 0x58, 0x3f, 0x7c, 0x3c, 0x2c, 0x3a, 0xcd, 0xbd, 0x27, 0x3b,
//0xea, 0x7c, 0x25, 0xaf, 0xcc, 0xc8, 0xef, 0x22, 0x99, 0xb3, 0x79, 0x72, 0x60,
//0xe8, 0x16, 0x4f, 0x5a, 0x47, 0x07, 0x04, 0x02, 0x14, 0x7b, 0xd0, 0xf6, 0xc9,
//0x77, 0x28, 0x9f, 0x77, 0x78, 0xce, 0x19, 0x74, 0x89, 0xe3, 0x56, 0x3a, 0x23,
//0x13, 0x63, 0xbb, 0x86, 0xf9, 0x13, 0x0e, 0x62, 0xfb, 0x61, 0xf5, 0x42, 0x65,
//0x48, 0x8e, 0xed, 0xc2, 0xc4, 0xc1, 0x18, 0xd0, 0x19, 0x9c, 0xe5, 0x1e
0xa5, 0x84, 0x1f, 0xd6, 0xbd, 0xdc, 0x7a, 0x4c, 0xf3, 0xc0, 0x24, 0xe0, 0x92, 0xef, 0x19, 0x28,
0x65, 0xe0, 0x62, 0x7c, 0x9b, 0x41, 0x6f, 0x12, 0xc3, 0xac, 0x78, 0xe4, 0xfc, 0x6b, 0x7b, 0x0a,
0xb4, 0x50, 0xd6, 0xf2, 0x45, 0xf7, 0x93, 0x04, 0xaf, 0xf2, 0xb7, 0x26, 0x94, 0xee, 0xad, 0x92,
0x05, 0x6d, 0xe5, 0xc6, 0xd6, 0x36, 0xdc, 0xa5, 0x69, 0x0f, 0xc8, 0x99, 0xf2, 0x1c, 0x4e, 0x0d,
};
std::copy(std::begin(mp_cert2), std::end(mp_cert2), reg_data.cert_data);
// A real wiimote takes about 2 seconds to reach this state:
reg_data.cert_ready = 0x1a;
INFO_LOG(WIIMOTE, "M+ cert 2 ready!", reg_data.cert_ready);
}
// TODO: make sure a motion plus report is sent first after init // TODO: make sure a motion plus report is sent first after init
@ -1268,6 +1319,7 @@ void Wiimote::MotionPlusLogic::Update()
// Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it
SetBit(data[5], 2, Common::ExtractBit(data[5], 0)); SetBit(data[5], 2, Common::ExtractBit(data[5], 0));
// Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
mplus_data.is_mp_data = false; mplus_data.is_mp_data = false;
} }
} }
@ -1284,9 +1336,11 @@ void Wiimote::MotionPlusLogic::Update()
// Data passing through drops the least significant bit of the axes of the left (or only) // Data passing through drops the least significant bit of the axes of the left (or only)
// joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1 // joystick Bit 0 of Byte 4 is overwritten [by the 'extension_connected' flag] Bits 0 and 1
// of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before // of Byte 5 are moved to bit 0 of Bytes 0 and 1, overwriting what was there before
SetBit(data[0], 0, Common::ExtractBit(data[5], 0)); SetBit(data[0], 0, Common::ExtractBit(data[5], 0));
SetBit(data[1], 0, Common::ExtractBit(data[5], 1)); SetBit(data[1], 0, Common::ExtractBit(data[5], 1));
// Bit 0 and 1 of byte 5 contain a M+ flag and a zero bit which is set below.
mplus_data.is_mp_data = false; mplus_data.is_mp_data = false;
} }
} }
@ -1315,9 +1369,17 @@ void Wiimote::MotionPlusLogic::Update()
mplus_data.pitch1 = pitch_value & 0xff; mplus_data.pitch1 = pitch_value & 0xff;
// Bits 8-13 // Bits 8-13
mplus_data.yaw1 = yaw_value >> 8; mplus_data.yaw2 = yaw_value >> 8;
mplus_data.roll1 = roll_value >> 8; mplus_data.roll2 = roll_value >> 8;
mplus_data.pitch1 = pitch_value >> 8; mplus_data.pitch2 = pitch_value >> 8;
// hax:
//data[0] = 0x6d;
//data[1] = 0xfa;
//data[2] = 0x13;
//data[3] = 0x7f;
//data[4] = 0x83;
//data[5] = 0x7e;
} }
mplus_data.extension_connected = extension_port.IsDeviceConnected(); mplus_data.extension_connected = extension_port.IsDeviceConnected();

View File

@ -306,6 +306,7 @@ public:
} }
private: private:
// Pointers are unowned:
std::vector<I2CSlave*> m_slaves; std::vector<I2CSlave*> m_slaves;
}; };
@ -334,7 +335,10 @@ public:
void SetAttachment(ExtensionAttachment* dev) void SetAttachment(ExtensionAttachment* dev)
{ {
m_i2c_bus.RemoveSlave(m_attachment); m_i2c_bus.RemoveSlave(m_attachment);
m_i2c_bus.AddSlave(m_attachment = dev); m_attachment = dev;
if (dev)
m_i2c_bus.AddSlave(dev);
} }
private: private:
@ -470,12 +474,12 @@ private:
// Check if encrypted reads is on // Check if encrypted reads is on
if (0xaa == reg_data.encryption) if (0xaa == reg_data.encryption)
{ {
INFO_LOG(WIIMOTE, "Encrypted read."); //INFO_LOG(WIIMOTE, "Encrypted read.");
WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); WiimoteEncrypt(&ext_key, data_out, addr, (u8)count);
} }
else else
{ {
INFO_LOG(WIIMOTE, "Unencrypted read."); //INFO_LOG(WIIMOTE, "Unencrypted read.");
} }
return result; return result;
@ -561,9 +565,11 @@ private:
struct MotionPlusLogic : public ExtensionAttachment struct MotionPlusLogic : public ExtensionAttachment
{ {
private:
// The bus on the end of the motion plus: // The bus on the end of the motion plus:
I2CBus i2c_bus; I2CBus i2c_bus;
public:
// The port on the end of the motion plus: // The port on the end of the motion plus:
ExtensionPort extension_port{i2c_bus}; ExtensionPort extension_port{i2c_bus};
@ -573,24 +579,37 @@ private:
struct MotionPlusRegister struct MotionPlusRegister
{ {
u8 controller_data[21]; u8 controller_data[21];
u8 unknown[11]; u8 unknown_0x15[11];
// address 0x20 // address 0x20
u8 calibration_data[0x20]; u8 calibration_data[0x20];
u8 unknown2[0xb0];
u8 unknown_0x40[0x10];
// address 0x50
u8 cert_data[0x40];
u8 unknown_0x90[0x60];
// address 0xF0 // address 0xF0
u8 initialized; u8 initialized;
u8 unknown3[6]; // address 0xF1
u8 cert_enable;
u8 unknown_0xf2[5];
// address 0xf7 // address 0xf7
// Wii Sports Resort reads regularly and claims mplus is disconnected if not to its liking // Wii Sports Resort reads regularly
// Value starts at 0x00 and goes up after activation (not initialization) // Value starts at 0x00 and goes up after activation (not initialization)
// Immediately returns 0x02, even still after 15 and 30 seconds // Immediately returns 0x02, even still after 15 and 30 seconds
u8 initialization_status; // After the first data read the value seems to progress to 0x4,0x8,0xc,0xe
// More typical seems to be 2,8,c,e
// A value of 0xe triggers the game to read 64 bytes from 0x50
// The game claims M+ is disconnected after this read of unsatisfactory data
u8 cert_ready;
u8 unknown4[2]; u8 unknown_0xf8[2];
// address 0xFA // address 0xFA
u8 ext_identifier[6]; u8 ext_identifier[6];
@ -610,9 +629,6 @@ private:
PASSTHROUGH_CLASSIC = 0x07, PASSTHROUGH_CLASSIC = 0x07,
}; };
// TODO: savestate
u8 times_updated_since_activation = 0;
bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; }
PassthroughMode GetPassthroughMode() const PassthroughMode GetPassthroughMode() const
@ -659,15 +675,29 @@ private:
if (ACTIVE_DEVICE_ADDR == slave_addr) if (ACTIVE_DEVICE_ADDR == slave_addr)
{ {
auto const result = RawWrite(&reg_data, addr, count, data_in); auto const result = RawWrite(&reg_data, addr, count, data_in);
return result;
// It seems a write of any value triggers deactivation. // It seems a write of any value triggers deactivation.
// TODO: kill magic number
if (0xf0 == addr) if (0xf0 == addr)
{ {
// Deactivate motion plus: // Deactivate motion plus:
reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1; reg_data.ext_identifier[2] = INACTIVE_DEVICE_ADDR << 1;
times_updated_since_activation = 0; reg_data.cert_ready = 0x0;
// Pass through the activation write to the attached extension:
// The M+ deactivation signal is cleverly the same as EXT activation:
i2c_bus.BusWrite(slave_addr, addr, count, data_in);
} }
// TODO: kill magic number
else if (0xf1 == addr)
{
INFO_LOG(WIIMOTE, "M+ cert activation: 0x%x", reg_data.cert_enable);
// 0x14,0x18 is also a valid value
// 0x1a is final value
reg_data.cert_ready = 0x18;
}
return result;
} }
else else
{ {
@ -684,13 +714,15 @@ private:
// It seems a write of any value triggers activation. // It seems a write of any value triggers activation.
if (0xfe == addr) if (0xfe == addr)
{ {
INFO_LOG(WIIMOTE, "Motion Plus has been activated with value: %d", data_in[0]); INFO_LOG(WIIMOTE, "M+ has been activated: %d", data_in[0]);
// Activate motion plus: // Activate motion plus:
reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1; reg_data.ext_identifier[2] = ACTIVE_DEVICE_ADDR << 1;
reg_data.initialization_status = 0x2; // TODO: kill magic number
//reg_data.cert_ready = 0x2;
// Some hax to disable encryption: // TODO: activate extension and disable encrption
// also do this if an extension is attached after activation.
std::array<u8, 1> data = {0x55}; std::array<u8, 1> data = {0x55};
i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data());
} }
@ -710,11 +742,11 @@ private:
{ {
if (IsActive()) if (IsActive())
{ {
// TODO: logic for when motion plus deactivates
return true; return true;
} }
else else
{ {
// When inactive the device detect pin reads from ext port:
return extension_port.IsDeviceConnected(); return extension_port.IsDeviceConnected();
} }
} }