From 86c94b8b22e547a9445683258e9f36a1a1b793ef Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 29 Nov 2018 17:42:59 -0600 Subject: [PATCH] WiimoteEmu: Motion plus now works half of the time in Wii Sports Resort. --- .../Core/HW/WiimoteCommon/WiimoteReport.h | 17 ++- .../Core/HW/WiimoteEmu/EmuSubroutines.cpp | 58 +++++--- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp | 140 +++++++++++++----- Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h | 68 ++++++--- 4 files changed, 205 insertions(+), 78 deletions(-) diff --git a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h index 5a3cd89ee0..51290a5c6a 100644 --- a/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h +++ b/Source/Core/Core/HW/WiimoteCommon/WiimoteReport.h @@ -63,6 +63,8 @@ union wm_buttons // also just called "core data" u8 down : 1; u8 up : 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 unknown : 1; @@ -71,6 +73,8 @@ union wm_buttons // also just called "core data" u8 b : 1; u8 a : 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 home : 1; }; @@ -425,8 +429,10 @@ struct wm_write_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - // used only for register space (i2c bus) - u8 slave_address; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; // big endian: u8 address[2]; u8 size; @@ -447,8 +453,10 @@ struct wm_read_data u8 rumble : 1; u8 space : 2; // see WM_SPACE_* u8 : 5; - // used only for register space (i2c bus) - u8 slave_address; + // A real wiimote ignores the i2c read/write bit. + u8 i2c_rw_ignored : 1; + // Used only for register space (i2c bus) (7-bits): + u8 slave_address : 7; // big endian: u8 address[2]; u8 size[2]; @@ -460,6 +468,7 @@ struct wm_read_data_reply wm_buttons buttons; u8 error : 4; // see WM_RDERR_* u8 size_minus_one : 4; + // big endian: u16 address; u8 data[16]; }; diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 9275eaeb46..8bc69c7141 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -40,7 +40,7 @@ namespace WiimoteEmu void Wiimote::ReportMode(const wm_report_mode* const dr) { 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. 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.report_id = RT_ACK_DATA; - auto ack = &rpt.data; + auto& ack = rpt.data; - ack->buttons = m_status.buttons; - ack->reportID = report_id; - ack->errorID = error_code; + ack.buttons = m_status.buttons; + ack.reportID = report_id; + ack.errorID = error_code; Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), 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 ((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 + { // set the wanted 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 ((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) { - INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); + // INFO_LOG(WIIMOTE, "Wiimote::RequestStatus"); // update status struct m_status.extension = m_extension_port.IsDeviceConnected(); @@ -247,7 +257,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) 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) { @@ -290,7 +301,8 @@ void Wiimote::WriteData(const wm_write_data* const wd) // Write to Control Register // 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) { // 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); 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. // 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 // 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(16, m_read_request.size); if (0 == bytes_to_read) { @@ -404,28 +416,40 @@ bool Wiimote::ProcessReadDataRequest() // Read from Control Register // 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, - (u8)m_read_request.address, bytes_to_read, reply->data); + auto const bytes_read = m_i2c_bus.BusRead( + m_read_request.slave_address, (u8)m_read_request.address, bytes_to_read, reply->data); reply->size_minus_one = bytes_read - 1; if (bytes_read != bytes_to_read) { // 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; } } break; 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; } - // Modify the read request, zero size == complete - m_read_request.address += bytes_to_read; - m_read_request.size -= bytes_to_read; + if (reply->error) + { + // 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 Core::Callback_WiimoteInterruptChannel(m_index, m_reporting_channel, rpt.GetData(), diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp index 461a5aef55..d6f8191291 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.cpp @@ -101,7 +101,7 @@ static const ReportFeatures reporting_mode_features[] = { {2, 0, 10, 9, 23}, // 0x37: Core Buttons and Accelerometer with 10 IR bytes and 6 Extension Bytes {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}, @@ -109,7 +109,8 @@ static const ReportFeatures reporting_mode_features[] = { {0, 0, 0, 0, 0}, // 0x3d: 21 Extension Bytes {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}, }; @@ -313,6 +314,7 @@ static const char* const named_buttons[] = { void Wiimote::Reset() { + // TODO: Is a wiimote in CORE or DISABLED reporting mode on sync? m_reporting_mode = RT_REPORT_DISABLED; m_reporting_channel = 0; m_reporting_auto = false; @@ -345,8 +347,26 @@ void Wiimote::Reset() static const u8 c2[16] = {0x6f, 0x81, 0x7b, 0x89, 0x78, 0x51, 0x33, 0x60, 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(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 memset(&m_status, 0, sizeof(m_status)); @@ -379,7 +399,7 @@ void Wiimote::Reset() // TODO: only add to bus when connected: // Address 0x52 (when motion plus is not activated) // 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) @@ -563,19 +583,12 @@ bool Wiimote::Step() 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. // causing IsDeviceConnected() to return false below: HandleExtensionSwap(); - // check if a status report needs to be sent - // this happens when extensions are switched + // Check if a status report needs to be sent. 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()) { // WiiBrew: Following a connection or disconnection event on the Extension Port, @@ -583,11 +596,20 @@ bool Wiimote::Step() // arrive. m_reporting_mode = RT_REPORT_DISABLED; + INFO_LOG(WIIMOTE, "Sending status report due to extension status change."); + RequestStatus(); return true; } + if (ProcessReadDataRequest()) + { + // Read requests suppress normal input reports + // Don't send any other reports + return true; + } + return false; } @@ -878,7 +900,7 @@ void Wiimote::Update() 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. return; } @@ -932,14 +954,14 @@ void Wiimote::Update() if (RT_REPORT_INTERLEAVE1 == m_reporting_mode) { *feature_ptr = (x >> 2) & 0xff; - core.acc_bits = (z >> 4) & 0b11; - core.acc_bits2 = (z >> 6) & 0b11; + core.acc_bits = (z >> 6) & 0b11; + core.acc_bits2 = (z >> 8) & 0b11; } if (RT_REPORT_INTERLEAVE2 == m_reporting_mode) { *feature_ptr = (y >> 2) & 0xff; - core.acc_bits = (z >> 0) & 0b11; - core.acc_bits2 = (z >> 2) & 0b11; + core.acc_bits = (z >> 2) & 0b11; + core.acc_bits2 = (z >> 4) & 0b11; } else { @@ -999,6 +1021,7 @@ void Wiimote::Update() Movie::CallWiiInputManip(data, rptf, m_index, m_extension->active_extension, m_ext_logic.ext_key); } + if (NetPlay::IsNetPlayRunning()) { NetPlay_GetWiimoteData(m_index, data, rptf.total_size, m_reporting_mode); @@ -1006,7 +1029,6 @@ void Wiimote::Update() m_status.buttons = *reinterpret_cast(data + rptf.core_size); } - // TODO: need to fix usage of rptf probably Movie::CheckWiimoteStatus(m_index, data, rptf, m_extension->active_extension, m_ext_logic.ext_key); @@ -1180,7 +1202,7 @@ int Wiimote::CurrentExtension() const bool Wiimote::ExtensionLogic::ReadDeviceDetectPin() { - return extension->active_extension ? true : false; + return extension->active_extension != 0; } void Wiimote::ExtensionLogic::Update() @@ -1204,25 +1226,54 @@ void Wiimote::MotionPlusLogic::Update() return; } - // TODO: clean up this hackery: - // the value seems to increase based on time starting after the first read of 0x00 - if (IsActive() && times_updated_since_activation < 0xff) - { - ++times_updated_since_activation; + auto& data = reg_data.controller_data; + auto& mplus_data = *reinterpret_cast(data); - // TODO: wtf is this value actually.. - if (times_updated_since_activation == 9) - reg_data.initialization_status = 0x4; - else if (times_updated_since_activation == 10) - reg_data.initialization_status = 0x8; - else if (times_updated_since_activation == 18) - reg_data.initialization_status = 0xc; - else if (times_updated_since_activation == 53) - reg_data.initialization_status = 0xe; + if (0x0 == reg_data.cert_ready) + { + + // Without sending this nonsense, inputs are unresponsive.. even regular buttons + // Device still operates when changing the data slightly so its not any sort of encrpytion + // It even works when removing the is_mp_data bit in the last byte + static const u8 init_data[6] = {0x8e, 0xb0, 0x4f, 0x5a, 0xfc, 0x02}; + //std::copy(std::begin(init_data), std::end(init_data), data); + reg_data.cert_ready = 0x2; + //return; } - auto& mplus_data = *reinterpret_cast(reg_data.controller_data); - auto& data = reg_data.controller_data; + if (0x2 == reg_data.cert_ready) + { + // 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 @@ -1268,6 +1319,7 @@ void Wiimote::MotionPlusLogic::Update() // Bit 0 of byte 5 is moved to bit 2 of byte 5, overwriting it 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; } } @@ -1284,9 +1336,11 @@ void Wiimote::MotionPlusLogic::Update() // 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 // 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[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; } } @@ -1315,9 +1369,17 @@ void Wiimote::MotionPlusLogic::Update() mplus_data.pitch1 = pitch_value & 0xff; // Bits 8-13 - mplus_data.yaw1 = yaw_value >> 8; - mplus_data.roll1 = roll_value >> 8; - mplus_data.pitch1 = pitch_value >> 8; + mplus_data.yaw2 = yaw_value >> 8; + mplus_data.roll2 = roll_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(); diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 573f1b6bc2..4b335af3de 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -306,6 +306,7 @@ public: } private: + // Pointers are unowned: std::vector m_slaves; }; @@ -334,7 +335,10 @@ public: void SetAttachment(ExtensionAttachment* dev) { m_i2c_bus.RemoveSlave(m_attachment); - m_i2c_bus.AddSlave(m_attachment = dev); + m_attachment = dev; + + if (dev) + m_i2c_bus.AddSlave(dev); } private: @@ -470,12 +474,12 @@ private: // Check if encrypted reads is on if (0xaa == reg_data.encryption) { - INFO_LOG(WIIMOTE, "Encrypted read."); + //INFO_LOG(WIIMOTE, "Encrypted read."); WiimoteEncrypt(&ext_key, data_out, addr, (u8)count); } else { - INFO_LOG(WIIMOTE, "Unencrypted read."); + //INFO_LOG(WIIMOTE, "Unencrypted read."); } return result; @@ -561,9 +565,11 @@ private: struct MotionPlusLogic : public ExtensionAttachment { + private: // The bus on the end of the motion plus: I2CBus i2c_bus; + public: // The port on the end of the motion plus: ExtensionPort extension_port{i2c_bus}; @@ -573,24 +579,37 @@ private: struct MotionPlusRegister { u8 controller_data[21]; - u8 unknown[11]; + u8 unknown_0x15[11]; // address 0x20 u8 calibration_data[0x20]; - u8 unknown2[0xb0]; + + u8 unknown_0x40[0x10]; + + // address 0x50 + u8 cert_data[0x40]; + + u8 unknown_0x90[0x60]; // address 0xF0 u8 initialized; - u8 unknown3[6]; + // address 0xF1 + u8 cert_enable; + + u8 unknown_0xf2[5]; // 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) // 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 u8 ext_identifier[6]; @@ -610,9 +629,6 @@ private: PASSTHROUGH_CLASSIC = 0x07, }; - // TODO: savestate - u8 times_updated_since_activation = 0; - bool IsActive() const { return ACTIVE_DEVICE_ADDR << 1 == reg_data.ext_identifier[2]; } PassthroughMode GetPassthroughMode() const @@ -659,15 +675,29 @@ private: if (ACTIVE_DEVICE_ADDR == slave_addr) { auto const result = RawWrite(®_data, addr, count, data_in); - return result; // It seems a write of any value triggers deactivation. + // TODO: kill magic number if (0xf0 == addr) { // Deactivate motion plus: 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 { @@ -684,13 +714,15 @@ private: // It seems a write of any value triggers activation. 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: 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 data = {0x55}; i2c_bus.BusWrite(ACTIVE_DEVICE_ADDR, 0xf0, (int)data.size(), data.data()); } @@ -710,11 +742,11 @@ private: { if (IsActive()) { - // TODO: logic for when motion plus deactivates return true; } else { + // When inactive the device detect pin reads from ext port: return extension_port.IsDeviceConnected(); } }