diff --git a/Source/Core/Common/I2C.cpp b/Source/Core/Common/I2C.cpp index 4490912814..419c0e2f1f 100644 --- a/Source/Core/Common/I2C.cpp +++ b/Source/Core/Common/I2C.cpp @@ -51,7 +51,11 @@ int I2CBusSimple::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { // TODO: Does this make sense? The transmitter can't NACK a read... it's the receiver that // does that - data_out[i] = slave->ReadByte(addr + i); + auto byte = slave->ReadByte(addr + i); + if (byte.has_value()) + data_out[i] = byte.value(); + else + return i; } return count; } @@ -203,7 +207,18 @@ void I2CBus::SCLRisingEdge(const bool sda) ASSERT_MSG(WII_IPC, slave != nullptr, "Expected device with ID {:02x} to be on the I2C bus as it was earlier", i2c_address.value()); - current_byte = slave->ReadByte(device_address.value()); + std::optional byte = slave->ReadByte(device_address.value()); + if (!byte.has_value()) + { + WARN_LOG_FMT(WII_IPC, "Failed to read from device {:02x} at address {:02x}", + i2c_address.value(), device_address.value()); + // TODO + current_byte = 0xff; + } + else + { + current_byte = byte.value(); + } // INFO_LOG_FMT(WII_IPC, "AVE: Read from {:02x} ({}) -> {:02x}", device_address.value(), // IOS::GetAVERegisterName(device_address.value()), current_byte); } diff --git a/Source/Core/Common/I2C.h b/Source/Core/Common/I2C.h index a98f63fda6..16fcd8dc06 100644 --- a/Source/Core/Common/I2C.h +++ b/Source/Core/Common/I2C.h @@ -17,27 +17,30 @@ class I2CSlave { public: virtual bool Matches(u8 slave_addr) = 0; - virtual u8 ReadByte(u8 addr) = 0; + virtual std::optional ReadByte(u8 addr) = 0; virtual bool WriteByte(u8 addr, u8 value) = 0; +}; -protected: - ~I2CSlave() = default; - - template - static u8 RawRead(T* reg_data, u8 addr) +template +class I2CSlaveSimple : I2CSlave +{ +public: + bool Matches(u8 slave_addr) override { return slave_addr == slave_addr_val; } + std::optional ReadByte(u8 addr) { return data_bytes[addr]; } + bool WriteByte(u8 addr, u8 value) { - static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v); - static_assert(sizeof(T) == 0x100); - return reinterpret_cast(reg_data)[addr]; + data_bytes[addr] = value; + return true; } - template - static void RawWrite(T* reg_data, u8 addr, u8 value) + union { - static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v); - static_assert(sizeof(T) == 0x100); - reinterpret_cast(reg_data)[addr] = value; - } + T data; + std::array data_bytes; + }; + + static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v); + static_assert(sizeof(T) == 0x100); }; class I2CBusBase diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp index b253042e68..1dcdb77952 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Camera.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.cpp @@ -29,20 +29,26 @@ void CameraLogic::DoState(PointerWrap& p) // FYI: m_is_enabled is handled elsewhere. } -bool CameraLogic::Matches(u8 slave_addr) +int CameraLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { - return I2C_ADDR == slave_addr && m_is_enabled; + if (I2C_ADDR != slave_addr) + return 0; + + if (!m_is_enabled) + return 0; + + return RawRead(&m_reg_data, addr, count, data_out); } -u8 CameraLogic::ReadByte(u8 addr) +int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { - return RawRead(&m_reg_data, addr); -} + if (I2C_ADDR != slave_addr) + return 0; -bool CameraLogic::WriteByte(u8 addr, u8 value) -{ - RawWrite(&m_reg_data, addr, value); - return true; + if (!m_is_enabled) + return 0; + + return RawWrite(&m_reg_data, addr, count, data_in); } std::array diff --git a/Source/Core/Core/HW/WiimoteEmu/Camera.h b/Source/Core/Core/HW/WiimoteEmu/Camera.h index c3b51598c7..23e21cc84e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Camera.h +++ b/Source/Core/Core/HW/WiimoteEmu/Camera.h @@ -5,8 +5,8 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/I2C.h" #include "Core/HW/WiimoteEmu/Dynamics.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" #include "InputCommon/ControllerEmu/ControlGroup/Cursor.h" namespace Common @@ -101,7 +101,7 @@ struct IRFull : IRExtended }; static_assert(sizeof(IRFull) == 9, "Wrong size"); -class CameraLogic : public Common::I2CSlave +class CameraLogic : public I2CSlave { public: // OEM sensor bar distance between LED clusters in meters. @@ -175,9 +175,9 @@ public: // The real wiimote reads camera data from the i2c bus at offset 0x37: static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data); - bool Matches(u8 slave_addr) override; - u8 ReadByte(u8 addr) override; - bool WriteByte(u8 addr, u8 value) override; +private: + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override; + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override; Register m_reg_data{}; diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp index ebc034ed74..fd2b185872 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.cpp @@ -71,19 +71,14 @@ void None::DoState(PointerWrap& p) // Nothing needed. } -bool None::Matches(u8 slave_addr) +int None::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { - return false; + return 0; } -std::optional None::ReadByte(u8 addr) +int None::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { - return std::nullopt; -} - -bool None::WriteByte(u8 addr, u8 value) -{ - return false; + return 0; } bool EncryptedExtension::ReadDeviceDetectPin() const diff --git a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h index e2fe729d50..4a055c597d 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h +++ b/Source/Core/Core/HW/WiimoteEmu/Extension/Extension.h @@ -10,15 +10,15 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/I2C.h" #include "Core/HW/WiimoteEmu/Encryption.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" #include "InputCommon/ControllerEmu/ControllerEmu.h" namespace WiimoteEmu { struct DesiredExtensionState; -class Extension : public ControllerEmu::EmulatedController, public Common::I2CSlave +class Extension : public ControllerEmu::EmulatedController, public I2CSlave { public: explicit Extension(const char* name); @@ -56,9 +56,8 @@ private: void Reset() override; void DoState(PointerWrap& p) override; - bool Matches(u8 slave_addr) override; - std::optional ReadByte(u8 addr) override; - bool WriteByte(u8 addr, u8 value) override; + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override; + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override; }; // This class provides the encryption and initialization behavior of most extensions. diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp index c5043298b8..97e73ea4bc 100644 --- a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.cpp @@ -3,9 +3,11 @@ #include "Core/HW/WiimoteEmu/ExtensionPort.h" +#include "Common/ChunkFile.h" + namespace WiimoteEmu { -ExtensionPort::ExtensionPort(Common::I2CBusBase* i2c_bus) : m_i2c_bus(*i2c_bus) +ExtensionPort::ExtensionPort(I2CBus* i2c_bus) : m_i2c_bus(*i2c_bus) { } diff --git a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h index 3c6c70e62a..86b1fd31f9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h +++ b/Source/Core/Core/HW/WiimoteEmu/ExtensionPort.h @@ -3,8 +3,9 @@ #pragma once -#include "Common/I2C.h" +#include "Common/ChunkFile.h" #include "Core/HW/WiimoteEmu/Extension/Extension.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" namespace WiimoteEmu { @@ -34,14 +35,14 @@ public: static constexpr u8 REPORT_I2C_SLAVE = 0x52; static constexpr u8 REPORT_I2C_ADDR = 0x00; - explicit ExtensionPort(Common::I2CBusBase* i2c_bus); + explicit ExtensionPort(I2CBus* i2c_bus); bool IsDeviceConnected() const; void AttachExtension(Extension* dev); private: Extension* m_extension = nullptr; - Common::I2CBusBase& m_i2c_bus; + I2CBus& m_i2c_bus; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp index e69de29bb2..19202350fd 100644 --- a/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.cpp @@ -0,0 +1,53 @@ +// Copyright 2019 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Core/HW/WiimoteEmu/I2CBus.h" + +#include + +namespace WiimoteEmu +{ +void I2CBus::AddSlave(I2CSlave* slave) +{ + m_slaves.emplace_back(slave); +} + +void I2CBus::RemoveSlave(I2CSlave* slave) +{ + m_slaves.erase(std::remove(m_slaves.begin(), m_slaves.end(), slave), m_slaves.end()); +} + +void I2CBus::Reset() +{ + m_slaves.clear(); +} + +int I2CBus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) +{ + for (auto& slave : m_slaves) + { + auto const bytes_read = slave->BusRead(slave_addr, addr, count, data_out); + + // A slave responded, we are done. + if (bytes_read) + return bytes_read; + } + + return 0; +} + +int I2CBus::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) +{ + for (auto& slave : m_slaves) + { + auto const bytes_written = slave->BusWrite(slave_addr, addr, count, data_in); + + // A slave responded, we are done. + if (bytes_written) + return bytes_written; + } + + return 0; +} + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/I2CBus.h b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h index e69de29bb2..3a1e0a67d3 100644 --- a/Source/Core/Core/HW/WiimoteEmu/I2CBus.h +++ b/Source/Core/Core/HW/WiimoteEmu/I2CBus.h @@ -0,0 +1,75 @@ +// Copyright 2019 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace WiimoteEmu +{ +class I2CBus; + +class I2CSlave +{ + friend I2CBus; + +protected: + virtual ~I2CSlave() = default; + + virtual int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) = 0; + virtual int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) = 0; + + template + static int RawRead(T* reg_data, u8 addr, int count, u8* data_out) + { + static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v); + static_assert(0x100 == sizeof(T)); + + // TODO: addr wraps around after 0xff + + u8* src = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - src)); + + std::copy_n(src, count, data_out); + + return count; + } + + template + static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in) + { + static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v); + static_assert(0x100 == sizeof(T)); + + // TODO: addr wraps around after 0xff + + u8* dst = reinterpret_cast(reg_data) + addr; + count = std::min(count, int(reinterpret_cast(reg_data + 1) - dst)); + + std::copy_n(data_in, count, dst); + + return count; + } +}; + +class I2CBus +{ +public: + void AddSlave(I2CSlave* slave); + void RemoveSlave(I2CSlave* slave); + + void Reset(); + + // TODO: change int to u16 or something + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out); + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in); + +private: + // Pointers are unowned: + std::vector m_slaves; +}; + +} // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h index ccbc0605c3..02ddfb7ec2 100644 --- a/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h +++ b/Source/Core/Core/HW/WiimoteEmu/MotionPlus.h @@ -248,9 +248,8 @@ private: ActivationStatus GetActivationStatus() const; PassthroughMode GetPassthroughMode() const; - bool Matches(u8 slave_addr) override; - u8 ReadByte(u8 addr) override; - bool WriteByte(u8 addr, u8 value) override; + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override; + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override; bool ReadDeviceDetectPin() const override; @@ -260,7 +259,7 @@ private: u8 m_progress_timer = {}; // The port on the end of the motion plus: - Common::I2CBusSimple m_i2c_bus; + I2CBus m_i2c_bus; ExtensionPort m_extension_port{&m_i2c_bus}; }; } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp index 99b9b82912..23cba6bfd7 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.cpp @@ -153,29 +153,30 @@ void SpeakerLogic::SetSpeakerEnabled(bool enabled) m_speaker_enabled = enabled; } -bool SpeakerLogic::Matches(u8 slave_addr) +int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) { - return I2C_ADDR == slave_addr; + if (I2C_ADDR != slave_addr) + return 0; + + return RawRead(®_data, addr, count, data_out); } -u8 SpeakerLogic::ReadByte(u8 addr) +int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) { - return RawRead(®_data, addr); -} + if (I2C_ADDR != slave_addr) + return 0; -bool SpeakerLogic::WriteByte(u8 addr, u8 value) -{ if (0x00 == addr) { SpeakerData(data_in, count, m_speaker_pan_setting.GetValue() / 100); - // TODO: Does writing immediately change the decoder config even when active - // or does a write to 0x08 activate the new configuration or something? + return count; } else { - RawWrite(®_data, addr, value); + // TODO: Does writing immediately change the decoder config even when active + // or does a write to 0x08 activate the new configuration or something? + return RawWrite(®_data, addr, count, data_in); } - return true; } } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/Speaker.h b/Source/Core/Core/HW/WiimoteEmu/Speaker.h index 995ccd2f0c..9a6a652bb1 100644 --- a/Source/Core/Core/HW/WiimoteEmu/Speaker.h +++ b/Source/Core/Core/HW/WiimoteEmu/Speaker.h @@ -5,7 +5,7 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/I2C.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" namespace WiimoteEmu @@ -17,7 +17,7 @@ struct ADPCMState class Wiimote; -class SpeakerLogic : public Common::I2CSlave +class SpeakerLogic : public I2CSlave { friend class Wiimote; @@ -63,9 +63,8 @@ private: static_assert(0x100 == sizeof(Register)); - bool Matches(u8 slave_addr) override; - u8 ReadByte(u8 addr) override; - bool WriteByte(u8 addr, u8 value) override; + int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override; + int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override; Register reg_data{}; diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index 8468e4dc60..e364ccdb6c 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -10,7 +10,6 @@ #include "Common/Common.h" #include "Common/Config/Config.h" -#include "Common/I2C.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h" @@ -18,6 +17,7 @@ #include "Core/HW/WiimoteEmu/Dynamics.h" #include "Core/HW/WiimoteEmu/Encryption.h" #include "Core/HW/WiimoteEmu/ExtensionPort.h" +#include "Core/HW/WiimoteEmu/I2CBus.h" #include "Core/HW/WiimoteEmu/MotionPlus.h" #include "Core/HW/WiimoteEmu/Speaker.h" @@ -316,7 +316,7 @@ private: MotionPlus m_motion_plus; CameraLogic m_camera_logic; - Common::I2CBusSimple m_i2c_bus; + I2CBus m_i2c_bus; ExtensionPort m_extension_port{&m_i2c_bus};