mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-03 14:49:22 +00:00
It builds!
This commit is contained in:
parent
28b082f46d
commit
e2defde893
16 changed files with 408 additions and 318 deletions
|
@ -412,7 +412,7 @@ void I2CBus::DoState(PointerWrap& p)
|
||||||
|
|
||||||
bool I2CSlaveAutoIncrementing::StartWrite(u8 slave_addr)
|
bool I2CSlaveAutoIncrementing::StartWrite(u8 slave_addr)
|
||||||
{
|
{
|
||||||
if (slave_addr == m_slave_addr)
|
if (DeviceEnabled() && slave_addr == m_slave_addr)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} write started, previously active: {}", m_slave_addr,
|
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} write started, previously active: {}", m_slave_addr,
|
||||||
m_active);
|
m_active);
|
||||||
|
@ -428,7 +428,7 @@ bool I2CSlaveAutoIncrementing::StartWrite(u8 slave_addr)
|
||||||
|
|
||||||
bool I2CSlaveAutoIncrementing::StartRead(u8 slave_addr)
|
bool I2CSlaveAutoIncrementing::StartRead(u8 slave_addr)
|
||||||
{
|
{
|
||||||
if (slave_addr == m_slave_addr)
|
if (DeviceEnabled() && slave_addr == m_slave_addr)
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} read started, previously active: {}", m_slave_addr,
|
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} read started, previously active: {}", m_slave_addr,
|
||||||
m_active);
|
m_active);
|
||||||
|
@ -508,8 +508,6 @@ void I2CSlaveAutoIncrementing::DoState(PointerWrap& p)
|
||||||
}
|
}
|
||||||
p.Do(m_active);
|
p.Do(m_active);
|
||||||
p.Do(m_device_address);
|
p.Do(m_device_address);
|
||||||
|
|
||||||
DoDeviceState(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // namespace Common
|
}; // namespace Common
|
||||||
|
|
|
@ -47,7 +47,7 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class I2CSlaveAutoIncrementing : public I2CSlave
|
class I2CSlaveAutoIncrementing : public virtual I2CSlave
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
I2CSlaveAutoIncrementing(u8 slave_addr) : m_slave_addr(slave_addr) {}
|
I2CSlaveAutoIncrementing(u8 slave_addr) : m_slave_addr(slave_addr) {}
|
||||||
|
@ -59,14 +59,14 @@ public:
|
||||||
std::optional<u8> ReadByte() override;
|
std::optional<u8> ReadByte() override;
|
||||||
bool WriteByte(u8 value) override;
|
bool WriteByte(u8 value) override;
|
||||||
|
|
||||||
void DoState(PointerWrap& p);
|
virtual void DoState(PointerWrap& p);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// i.e. should the device respond on the bus
|
||||||
|
virtual bool DeviceEnabled() { return true; }
|
||||||
virtual u8 ReadByte(u8 addr) = 0;
|
virtual u8 ReadByte(u8 addr) = 0;
|
||||||
virtual void WriteByte(u8 addr, u8 value) = 0;
|
virtual void WriteByte(u8 addr, u8 value) = 0;
|
||||||
|
|
||||||
virtual void DoDeviceState(PointerWrap& p) = 0;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const u8 m_slave_addr;
|
const u8 m_slave_addr;
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
|
@ -104,6 +104,16 @@ public:
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out);
|
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class I2CBusForwarding : public I2CBusSimple
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using I2CBusBase::ReadByte;
|
||||||
|
using I2CBusBase::StartRead;
|
||||||
|
using I2CBusBase::StartWrite;
|
||||||
|
using I2CBusBase::Stop;
|
||||||
|
using I2CBusBase::WriteByte;
|
||||||
|
};
|
||||||
|
|
||||||
// An I²C bus implementation accessed via bit-banging.
|
// An I²C bus implementation accessed via bit-banging.
|
||||||
// A few assumptions and limitations exist:
|
// A few assumptions and limitations exist:
|
||||||
// - All devices support both writes and reads.
|
// - All devices support both writes and reads.
|
||||||
|
|
|
@ -176,6 +176,12 @@ public:
|
||||||
ave_ever_logged = {};
|
ave_ever_logged = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DoState(PointerWrap& p) override
|
||||||
|
{
|
||||||
|
I2CSlaveAutoIncrementing::DoState(p);
|
||||||
|
p.Do(m_registers);
|
||||||
|
}
|
||||||
|
|
||||||
AVEState m_registers{};
|
AVEState m_registers{};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -203,7 +209,6 @@ protected:
|
||||||
value);
|
value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void DoDeviceState(PointerWrap& p) override { p.Do(m_registers); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::bitset<sizeof(AVEState)> ave_ever_logged{}; // logging only, not saved
|
std::bitset<sizeof(AVEState)> ave_ever_logged{}; // logging only, not saved
|
||||||
|
|
|
@ -24,31 +24,26 @@ void CameraLogic::Reset()
|
||||||
|
|
||||||
void CameraLogic::DoState(PointerWrap& p)
|
void CameraLogic::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
|
I2CSlaveAutoIncrementing::DoState(p);
|
||||||
|
|
||||||
p.Do(m_reg_data);
|
p.Do(m_reg_data);
|
||||||
|
|
||||||
// FYI: m_is_enabled is handled elsewhere.
|
// FYI: m_is_enabled is handled elsewhere.
|
||||||
}
|
}
|
||||||
|
|
||||||
int CameraLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
bool CameraLogic::DeviceEnabled()
|
||||||
{
|
{
|
||||||
if (I2C_ADDR != slave_addr)
|
return m_is_enabled;
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!m_is_enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return RawRead(&m_reg_data, addr, count, data_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CameraLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
u8 CameraLogic::ReadByte(u8 addr)
|
||||||
{
|
{
|
||||||
if (I2C_ADDR != slave_addr)
|
return RawRead(&m_reg_data, addr);
|
||||||
return 0;
|
}
|
||||||
|
|
||||||
if (!m_is_enabled)
|
void CameraLogic::WriteByte(u8 addr, u8 value)
|
||||||
return 0;
|
{
|
||||||
|
RawWrite(&m_reg_data, addr, value);
|
||||||
return RawWrite(&m_reg_data, addr, count, data_in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<CameraPoint, CameraLogic::NUM_POINTS>
|
std::array<CameraPoint, CameraLogic::NUM_POINTS>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/I2C.h"
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
|
@ -101,9 +101,11 @@ struct IRFull : IRExtended
|
||||||
};
|
};
|
||||||
static_assert(sizeof(IRFull) == 9, "Wrong size");
|
static_assert(sizeof(IRFull) == 9, "Wrong size");
|
||||||
|
|
||||||
class CameraLogic : public I2CSlave
|
class CameraLogic : public Common::I2CSlaveAutoIncrementing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
CameraLogic() : I2CSlaveAutoIncrementing(I2C_ADDR) {}
|
||||||
|
|
||||||
// OEM sensor bar distance between LED clusters in meters.
|
// OEM sensor bar distance between LED clusters in meters.
|
||||||
static constexpr float SENSOR_BAR_LED_SEPARATION = 0.2f;
|
static constexpr float SENSOR_BAR_LED_SEPARATION = 0.2f;
|
||||||
|
|
||||||
|
@ -132,7 +134,7 @@ public:
|
||||||
static constexpr int MAX_POINT_SIZE = 15;
|
static constexpr int MAX_POINT_SIZE = 15;
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
void DoState(PointerWrap& p);
|
void DoState(PointerWrap& p) override;
|
||||||
static std::array<CameraPoint, NUM_POINTS> GetCameraPoints(const Common::Matrix44& transform,
|
static std::array<CameraPoint, NUM_POINTS> GetCameraPoints(const Common::Matrix44& transform,
|
||||||
Common::Vec2 field_of_view);
|
Common::Vec2 field_of_view);
|
||||||
void Update(const std::array<CameraPoint, NUM_POINTS>& camera_points);
|
void Update(const std::array<CameraPoint, NUM_POINTS>& camera_points);
|
||||||
|
@ -176,8 +178,9 @@ public:
|
||||||
static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data);
|
static const u8 REPORT_DATA_OFFSET = offsetof(Register, camera_data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
bool DeviceEnabled() override;
|
||||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
u8 ReadByte(u8 addr) override;
|
||||||
|
void WriteByte(u8 addr, u8 value) override;
|
||||||
|
|
||||||
Register m_reg_data{};
|
Register m_reg_data{};
|
||||||
|
|
||||||
|
|
|
@ -71,14 +71,29 @@ void None::DoState(PointerWrap& p)
|
||||||
// Nothing needed.
|
// Nothing needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
int None::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
bool None::StartWrite(u8 slave_addr)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int None::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
bool None::StartRead(u8 slave_addr)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void None::Stop()
|
||||||
|
{
|
||||||
|
// Nothing needed.
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> None::ReadByte()
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool None::WriteByte(u8 value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncryptedExtension::ReadDeviceDetectPin() const
|
bool EncryptedExtension::ReadDeviceDetectPin() const
|
||||||
|
@ -86,11 +101,8 @@ bool EncryptedExtension::ReadDeviceDetectPin() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EncryptedExtension::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
u8 EncryptedExtension::ReadByte(u8 addr)
|
||||||
{
|
{
|
||||||
if (I2C_ADDR != slave_addr)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (0x00 == addr)
|
if (0x00 == addr)
|
||||||
{
|
{
|
||||||
// This is where real hardware would update controller data
|
// This is where real hardware would update controller data
|
||||||
|
@ -98,7 +110,7 @@ int EncryptedExtension::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
||||||
// TAS code fails to sync data reads and such..
|
// TAS code fails to sync data reads and such..
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const result = RawRead(&m_reg, addr, count, data_out);
|
u8 result = RawRead(&m_reg, addr);
|
||||||
|
|
||||||
// Encrypt data read from extension register.
|
// Encrypt data read from extension register.
|
||||||
if (ENCRYPTION_ENABLED == m_reg.encryption)
|
if (ENCRYPTION_ENABLED == m_reg.encryption)
|
||||||
|
@ -109,30 +121,25 @@ int EncryptedExtension::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
||||||
m_is_key_dirty = false;
|
m_is_key_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ext_key.Encrypt(data_out, addr, count);
|
ext_key.Encrypt(&result, addr, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EncryptedExtension::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
void EncryptedExtension::WriteByte(u8 addr, u8 value)
|
||||||
{
|
{
|
||||||
if (I2C_ADDR != slave_addr)
|
RawWrite(&m_reg, addr, value);
|
||||||
return 0;
|
|
||||||
|
|
||||||
auto const result = RawWrite(&m_reg, addr, count, data_in);
|
|
||||||
|
|
||||||
constexpr u8 ENCRYPTION_KEY_DATA_BEGIN = offsetof(Register, encryption_key_data);
|
constexpr u8 ENCRYPTION_KEY_DATA_BEGIN = offsetof(Register, encryption_key_data);
|
||||||
constexpr u8 ENCRYPTION_KEY_DATA_END = ENCRYPTION_KEY_DATA_BEGIN + 0x10;
|
constexpr u8 ENCRYPTION_KEY_DATA_END = ENCRYPTION_KEY_DATA_BEGIN + 0x10;
|
||||||
|
|
||||||
if (addr + count > ENCRYPTION_KEY_DATA_BEGIN && addr < ENCRYPTION_KEY_DATA_END)
|
if (addr >= ENCRYPTION_KEY_DATA_BEGIN && addr < ENCRYPTION_KEY_DATA_END)
|
||||||
{
|
{
|
||||||
// FYI: Real extensions seem to require the key data written in specifically sized chunks.
|
// FYI: Real extensions seem to require the key data written in specifically sized chunks.
|
||||||
// We just run the key generation on all writes to the key area.
|
// We just run the key generation on all writes to the key area.
|
||||||
m_is_key_dirty = true;
|
m_is_key_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncryptedExtension::Reset()
|
void EncryptedExtension::Reset()
|
||||||
|
@ -147,6 +154,8 @@ void EncryptedExtension::Reset()
|
||||||
|
|
||||||
void EncryptedExtension::DoState(PointerWrap& p)
|
void EncryptedExtension::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
|
I2CSlaveAutoIncrementing::DoState(p);
|
||||||
|
|
||||||
p.Do(m_reg);
|
p.Do(m_reg);
|
||||||
|
|
||||||
if (p.IsReadMode())
|
if (p.IsReadMode())
|
||||||
|
|
|
@ -10,15 +10,19 @@
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/I2C.h"
|
||||||
#include "Core/HW/WiimoteEmu/Encryption.h"
|
#include "Core/HW/WiimoteEmu/Encryption.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
#include "InputCommon/ControllerEmu/ControllerEmu.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4250) // C4250 inherits via dominance - intended behavior here
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
struct DesiredExtensionState;
|
struct DesiredExtensionState;
|
||||||
|
|
||||||
class Extension : public ControllerEmu::EmulatedController, public I2CSlave
|
class Extension : public ControllerEmu::EmulatedController, public virtual Common::I2CSlave
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Extension(const char* name);
|
explicit Extension(const char* name);
|
||||||
|
@ -56,23 +60,39 @@ private:
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
bool StartWrite(u8 slave_addr) override;
|
||||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
bool StartRead(u8 slave_addr) override;
|
||||||
|
void Stop() override;
|
||||||
|
std::optional<u8> ReadByte() override;
|
||||||
|
bool WriteByte(u8 value) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class provides the encryption and initialization behavior of most extensions.
|
// This class provides the encryption and initialization behavior of most extensions.
|
||||||
class EncryptedExtension : public Extension
|
class EncryptedExtension : public Extension, public Common::I2CSlaveAutoIncrementing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr u8 I2C_ADDR = 0x52;
|
static constexpr u8 I2C_ADDR = 0x52;
|
||||||
static constexpr int CONTROLLER_DATA_BYTES = 21;
|
static constexpr int CONTROLLER_DATA_BYTES = 21;
|
||||||
|
|
||||||
using Extension::Extension;
|
explicit EncryptedExtension(const char* name)
|
||||||
|
: Extension(name), I2CSlaveAutoIncrementing(I2C_ADDR)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
EncryptedExtension(const char* config_name, const char* display_name)
|
||||||
|
: Extension(config_name, display_name), I2CSlaveAutoIncrementing(I2C_ADDR)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This is public for TAS reasons.
|
// TODO: This is public for TAS reasons.
|
||||||
// TODO: TAS handles encryption poorly.
|
// TODO: TAS handles encryption poorly.
|
||||||
EncryptionKey ext_key;
|
EncryptionKey ext_key;
|
||||||
|
|
||||||
|
using I2CSlaveAutoIncrementing::ReadByte;
|
||||||
|
using I2CSlaveAutoIncrementing::StartRead;
|
||||||
|
using I2CSlaveAutoIncrementing::StartWrite;
|
||||||
|
using I2CSlaveAutoIncrementing::Stop;
|
||||||
|
using I2CSlaveAutoIncrementing::WriteByte;
|
||||||
|
|
||||||
static constexpr int CALIBRATION_CHECKSUM_BYTES = 2;
|
static constexpr int CALIBRATION_CHECKSUM_BYTES = 2;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
@ -117,8 +137,8 @@ private:
|
||||||
|
|
||||||
bool ReadDeviceDetectPin() const override;
|
bool ReadDeviceDetectPin() const override;
|
||||||
|
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
u8 ReadByte(u8 addr) override;
|
||||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
void WriteByte(u8 addr, u8 value) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Extension1stParty : public EncryptedExtension
|
class Extension1stParty : public EncryptedExtension
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
ExtensionPort::ExtensionPort(I2CBus* i2c_bus) : m_i2c_bus(*i2c_bus)
|
ExtensionPort::ExtensionPort(Common::I2CBusBase* i2c_bus) : m_i2c_bus(*i2c_bus)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
|
#include "Common/I2C.h"
|
||||||
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
|
@ -35,14 +35,14 @@ public:
|
||||||
static constexpr u8 REPORT_I2C_SLAVE = 0x52;
|
static constexpr u8 REPORT_I2C_SLAVE = 0x52;
|
||||||
static constexpr u8 REPORT_I2C_ADDR = 0x00;
|
static constexpr u8 REPORT_I2C_ADDR = 0x00;
|
||||||
|
|
||||||
explicit ExtensionPort(I2CBus* i2c_bus);
|
explicit ExtensionPort(Common::I2CBusBase* i2c_bus);
|
||||||
|
|
||||||
bool IsDeviceConnected() const;
|
bool IsDeviceConnected() const;
|
||||||
void AttachExtension(Extension* dev);
|
void AttachExtension(Extension* dev);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Extension* m_extension = nullptr;
|
Extension* m_extension = nullptr;
|
||||||
I2CBus& m_i2c_bus;
|
Common::I2CBusBase& m_i2c_bus;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Copyright 2019 Dolphin Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,75 +0,0 @@
|
||||||
// Copyright 2019 Dolphin Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#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 <typename T>
|
|
||||||
static int RawRead(T* reg_data, u8 addr, int count, u8* data_out)
|
|
||||||
{
|
|
||||||
static_assert(std::is_standard_layout_v<T> && std::is_trivially_copyable_v<T>);
|
|
||||||
static_assert(0x100 == sizeof(T));
|
|
||||||
|
|
||||||
// TODO: addr wraps around after 0xff
|
|
||||||
|
|
||||||
u8* src = reinterpret_cast<u8*>(reg_data) + addr;
|
|
||||||
count = std::min(count, int(reinterpret_cast<u8*>(reg_data + 1) - src));
|
|
||||||
|
|
||||||
std::copy_n(src, count, data_out);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static int RawWrite(T* reg_data, u8 addr, int count, const u8* data_in)
|
|
||||||
{
|
|
||||||
static_assert(std::is_standard_layout_v<T> && std::is_trivially_copyable_v<T>);
|
|
||||||
static_assert(0x100 == sizeof(T));
|
|
||||||
|
|
||||||
// TODO: addr wraps around after 0xff
|
|
||||||
|
|
||||||
u8* dst = reinterpret_cast<u8*>(reg_data) + addr;
|
|
||||||
count = std::min(count, int(reinterpret_cast<u8*>(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<I2CSlave*> m_slaves;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace WiimoteEmu
|
|
|
@ -189,105 +189,166 @@ ExtensionPort& MotionPlus::GetExtPort()
|
||||||
return m_extension_port;
|
return m_extension_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MotionPlus::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
bool MotionPlus::StartWrite(u8 slave_addr)
|
||||||
{
|
{
|
||||||
switch (GetActivationStatus())
|
switch (GetActivationStatus())
|
||||||
{
|
{
|
||||||
case ActivationStatus::Inactive:
|
case ActivationStatus::Inactive:
|
||||||
if (INACTIVE_DEVICE_ADDR != slave_addr)
|
// m_inactive_wrapper is connected to the bus, so it will respond to 0x53 when inactive.
|
||||||
{
|
return m_i2c_bus.StartWrite(slave_addr);
|
||||||
// Passthrough to the connected extension. (if any)
|
|
||||||
return m_i2c_bus.BusRead(slave_addr, addr, count, data_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a normal read of the M+ register.
|
|
||||||
return RawRead(&m_reg_data, addr, count, data_out);
|
|
||||||
|
|
||||||
case ActivationStatus::Active:
|
case ActivationStatus::Active:
|
||||||
// FYI: Motion plus does not respond to 0x53 when activated.
|
// FYI: Motion plus does not respond to 0x53 when activated.
|
||||||
if (ACTIVE_DEVICE_ADDR != slave_addr)
|
// No i2c passthrough when activated, so *only* target 0x52.
|
||||||
{
|
return m_active_wrapper.StartWrite(slave_addr);
|
||||||
// No i2c passthrough when activated.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a normal read of the M+ register.
|
|
||||||
return RawRead(&m_reg_data, addr, count, data_out);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
case ActivationStatus::Activating:
|
case ActivationStatus::Activating:
|
||||||
case ActivationStatus::Deactivating:
|
case ActivationStatus::Deactivating:
|
||||||
// The extension port is completely unresponsive here.
|
// The extension port is completely unresponsive here.
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int MotionPlus::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
bool MotionPlus::StartRead(u8 slave_addr)
|
||||||
{
|
{
|
||||||
switch (GetActivationStatus())
|
switch (GetActivationStatus())
|
||||||
{
|
{
|
||||||
case ActivationStatus::Inactive:
|
case ActivationStatus::Inactive:
|
||||||
{
|
// m_inactive_wrapper is connected to the bus, so it will respond to 0x53 when inactive.
|
||||||
if (INACTIVE_DEVICE_ADDR != slave_addr)
|
return m_i2c_bus.StartRead(slave_addr);
|
||||||
{
|
|
||||||
// Passthrough to the connected extension. (if any)
|
case ActivationStatus::Active:
|
||||||
return m_i2c_bus.BusWrite(slave_addr, addr, count, data_in);
|
// FYI: Motion plus does not respond to 0x53 when activated.
|
||||||
|
// No i2c passthrough when activated, so *only* target 0x52.
|
||||||
|
return m_active_wrapper.StartRead(slave_addr);
|
||||||
|
|
||||||
|
default:
|
||||||
|
case ActivationStatus::Activating:
|
||||||
|
case ActivationStatus::Deactivating:
|
||||||
|
// The extension port is completely unresponsive here.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DEBUG_LOG_FMT(WIIMOTE, "Inactive M+ write {:#x} : {}", addr, ArrayToString(data_in, count));
|
void MotionPlus::Stop()
|
||||||
|
{
|
||||||
|
switch (GetActivationStatus())
|
||||||
|
{
|
||||||
|
case ActivationStatus::Inactive:
|
||||||
|
// m_inactive_wrapper is connected to the bus, so it will respond to 0x53 when inactive.
|
||||||
|
m_i2c_bus.Stop();
|
||||||
|
break;
|
||||||
|
|
||||||
auto const result = RawWrite(&m_reg_data, addr, count, data_in);
|
case ActivationStatus::Active:
|
||||||
|
// FYI: Motion plus does not respond to 0x53 when activated.
|
||||||
|
// No i2c passthrough when activated, so *only* target 0x52.
|
||||||
|
m_active_wrapper.Stop();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case ActivationStatus::Activating:
|
||||||
|
case ActivationStatus::Deactivating:
|
||||||
|
// The extension port is completely unresponsive here.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> MotionPlus::ReadByte()
|
||||||
|
{
|
||||||
|
switch (GetActivationStatus())
|
||||||
|
{
|
||||||
|
case ActivationStatus::Inactive:
|
||||||
|
// m_inactive_wrapper is connected to the bus, so it will respond to 0x53 when inactive.
|
||||||
|
return m_i2c_bus.ReadByte();
|
||||||
|
|
||||||
|
case ActivationStatus::Active:
|
||||||
|
// FYI: Motion plus does not respond to 0x53 when activated.
|
||||||
|
// No i2c passthrough when activated, so *only* target 0x52.
|
||||||
|
return m_active_wrapper.ReadByte();
|
||||||
|
|
||||||
|
default:
|
||||||
|
case ActivationStatus::Activating:
|
||||||
|
case ActivationStatus::Deactivating:
|
||||||
|
// The extension port is completely unresponsive here.
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MotionPlus::WriteByte(u8 value)
|
||||||
|
{
|
||||||
|
switch (GetActivationStatus())
|
||||||
|
{
|
||||||
|
case ActivationStatus::Inactive:
|
||||||
|
// m_inactive_wrapper is connected to the bus, so it will respond to 0x53 when inactive.
|
||||||
|
return m_i2c_bus.WriteByte(value);
|
||||||
|
|
||||||
|
case ActivationStatus::Active:
|
||||||
|
// FYI: Motion plus does not respond to 0x53 when activated.
|
||||||
|
// No i2c passthrough when activated, so *only* target 0x52.
|
||||||
|
return m_active_wrapper.WriteByte(value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
case ActivationStatus::Activating:
|
||||||
|
case ActivationStatus::Deactivating:
|
||||||
|
// The extension port is completely unresponsive here.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 MotionPlus::RegisterWrapper::ReadByte(u8 addr)
|
||||||
|
{
|
||||||
|
return RawRead(&m_owner->m_reg_data, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MotionPlus::InactiveRegisterWrapper::WriteByte(u8 addr, u8 value)
|
||||||
|
{
|
||||||
|
DEBUG_LOG_FMT(WIIMOTE, "Inactive M+ write {:02x} = {:02x}", addr, value);
|
||||||
|
|
||||||
|
RawWrite(&m_owner->m_reg_data, addr, value);
|
||||||
|
|
||||||
if (PASSTHROUGH_MODE_OFFSET == addr)
|
if (PASSTHROUGH_MODE_OFFSET == addr)
|
||||||
{
|
{
|
||||||
OnPassthroughModeWrite();
|
m_owner->OnPassthroughModeWrite();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
void MotionPlus::ActiveRegisterWrapper::WriteByte(u8 addr, u8 value)
|
||||||
}
|
{
|
||||||
|
DEBUG_LOG_FMT(WIIMOTE, "Inactive M+ write {:02x} = {:02x}", addr, value);
|
||||||
|
|
||||||
case ActivationStatus::Active:
|
RawWrite(&m_owner->m_reg_data, addr, value);
|
||||||
{
|
|
||||||
// FYI: Motion plus does not respond to 0x53 when activated.
|
|
||||||
if (ACTIVE_DEVICE_ADDR != slave_addr)
|
|
||||||
{
|
|
||||||
// No i2c passthrough when activated.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG_FMT(WIIMOTE, "Active M+ write {:#x} : {}", addr, ArrayToString(data_in, count));
|
|
||||||
|
|
||||||
auto const result = RawWrite(&m_reg_data, addr, count, data_in);
|
|
||||||
|
|
||||||
switch (addr)
|
switch (addr)
|
||||||
{
|
{
|
||||||
case offsetof(Register, init_trigger):
|
case offsetof(Register, init_trigger):
|
||||||
// It seems a write of any value here triggers deactivation on a real M+.
|
// It seems a write of any value here triggers deactivation on a real M+.
|
||||||
Deactivate();
|
m_owner->Deactivate();
|
||||||
|
|
||||||
// Passthrough the write to the attached extension.
|
// Passthrough the write to the attached extension.
|
||||||
// The M+ deactivation signal is cleverly the same as EXT initialization.
|
// The M+ deactivation signal is cleverly the same as EXT initialization.
|
||||||
m_i2c_bus.BusWrite(slave_addr, addr, count, data_in);
|
// TODO
|
||||||
|
// m_i2c_bus.BusWrite(slave_addr, addr, count, data_in);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case offsetof(Register, challenge_type):
|
case offsetof(Register, challenge_type):
|
||||||
if (ChallengeState::ParameterXReady == m_reg_data.challenge_state)
|
if (ChallengeState::ParameterXReady == m_owner->m_reg_data.challenge_state)
|
||||||
{
|
{
|
||||||
DEBUG_LOG_FMT(WIIMOTE, "M+ challenge: {:#x}", m_reg_data.challenge_type);
|
DEBUG_LOG_FMT(WIIMOTE, "M+ challenge: {:#x}", m_owner->m_reg_data.challenge_type);
|
||||||
|
|
||||||
// After games read parameter x they write here to request y0 or y1.
|
// After games read parameter x they write here to request y0 or y1.
|
||||||
if (0 == m_reg_data.challenge_type)
|
if (0 == m_owner->m_reg_data.challenge_type)
|
||||||
{
|
{
|
||||||
// Preparing y0 on the real M+ is almost instant (30ms maybe).
|
// Preparing y0 on the real M+ is almost instant (30ms maybe).
|
||||||
constexpr int PREPARE_Y0_MS = 30;
|
constexpr int PREPARE_Y0_MS = 30;
|
||||||
m_progress_timer = ::Wiimote::UPDATE_FREQ * PREPARE_Y0_MS / 1000;
|
m_owner->m_progress_timer = ::Wiimote::UPDATE_FREQ * PREPARE_Y0_MS / 1000;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// A real M+ takes about 1200ms to prepare y1.
|
// A real M+ takes about 1200ms to prepare y1.
|
||||||
// Games seem to not care that we don't take that long.
|
// Games seem to not care that we don't take that long.
|
||||||
constexpr int PREPARE_Y1_MS = 500;
|
constexpr int PREPARE_Y1_MS = 500;
|
||||||
m_progress_timer = ::Wiimote::UPDATE_FREQ * PREPARE_Y1_MS / 1000;
|
m_owner->m_progress_timer = ::Wiimote::UPDATE_FREQ * PREPARE_Y1_MS / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Games give the M+ a bit of time to compute the value.
|
// Games give the M+ a bit of time to compute the value.
|
||||||
|
@ -295,30 +356,21 @@ int MotionPlus::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
||||||
// y1 gets at about 9.5 seconds.
|
// y1 gets at about 9.5 seconds.
|
||||||
// After this the M+ will fail the "challenge".
|
// After this the M+ will fail the "challenge".
|
||||||
|
|
||||||
m_reg_data.challenge_state = ChallengeState::PreparingY;
|
m_owner->m_reg_data.challenge_state = ChallengeState::PreparingY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case offsetof(Register, calibration_trigger):
|
case offsetof(Register, calibration_trigger):
|
||||||
// Games seem to invoke this to start and stop calibration. Exact consequences unknown.
|
// Games seem to invoke this to start and stop calibration. Exact consequences unknown.
|
||||||
DEBUG_LOG_FMT(WIIMOTE, "M+ calibration trigger: {:#x}", m_reg_data.calibration_trigger);
|
DEBUG_LOG_FMT(WIIMOTE, "M+ calibration trigger: {:#x}",
|
||||||
|
m_owner->m_reg_data.calibration_trigger);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PASSTHROUGH_MODE_OFFSET:
|
case PASSTHROUGH_MODE_OFFSET:
|
||||||
// Games sometimes (not often) write zero here to deactivate the M+.
|
// Games sometimes (not often) write zero here to deactivate the M+.
|
||||||
OnPassthroughModeWrite();
|
m_owner->OnPassthroughModeWrite();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
case ActivationStatus::Activating:
|
|
||||||
case ActivationStatus::Deactivating:
|
|
||||||
// The extension port is completely unresponsive here.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotionPlus::OnPassthroughModeWrite()
|
void MotionPlus::OnPassthroughModeWrite()
|
||||||
|
|
|
@ -6,15 +6,15 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/I2C.h"
|
||||||
#include "Common/MathUtil.h"
|
#include "Common/MathUtil.h"
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
{
|
{
|
||||||
struct MotionPlus : public Extension
|
class MotionPlus : public Extension
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class PassthroughMode : u8
|
enum class PassthroughMode : u8
|
||||||
|
@ -241,6 +241,43 @@ private:
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
static_assert(0x100 == sizeof(Register), "Wrong size");
|
static_assert(0x100 == sizeof(Register), "Wrong size");
|
||||||
|
|
||||||
|
class RegisterWrapper : public Common::I2CSlaveAutoIncrementing
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
RegisterWrapper(MotionPlus* owner, u8 slave_addr)
|
||||||
|
: I2CSlaveAutoIncrementing(slave_addr), m_owner(owner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 ReadByte(u8 addr) override;
|
||||||
|
|
||||||
|
MotionPlus* const m_owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InactiveRegisterWrapper : public RegisterWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InactiveRegisterWrapper(MotionPlus* owner) : RegisterWrapper(owner, INACTIVE_DEVICE_ADDR) {}
|
||||||
|
|
||||||
|
using Common::I2CSlaveAutoIncrementing::ReadByte;
|
||||||
|
using Common::I2CSlaveAutoIncrementing::WriteByte;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void WriteByte(u8 addr, u8 value) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ActiveRegisterWrapper : public RegisterWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ActiveRegisterWrapper(MotionPlus* owner) : RegisterWrapper(owner, ACTIVE_DEVICE_ADDR) {}
|
||||||
|
|
||||||
|
using Common::I2CSlaveAutoIncrementing::ReadByte;
|
||||||
|
using Common::I2CSlaveAutoIncrementing::WriteByte;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void WriteByte(u8 addr, u8 value) override;
|
||||||
|
};
|
||||||
|
|
||||||
void Activate();
|
void Activate();
|
||||||
void Deactivate();
|
void Deactivate();
|
||||||
void OnPassthroughModeWrite();
|
void OnPassthroughModeWrite();
|
||||||
|
@ -248,8 +285,11 @@ private:
|
||||||
ActivationStatus GetActivationStatus() const;
|
ActivationStatus GetActivationStatus() const;
|
||||||
PassthroughMode GetPassthroughMode() const;
|
PassthroughMode GetPassthroughMode() const;
|
||||||
|
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
bool StartWrite(u8 slave_addr) override;
|
||||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
bool StartRead(u8 slave_addr) override;
|
||||||
|
void Stop() override;
|
||||||
|
std::optional<u8> ReadByte() override;
|
||||||
|
bool WriteByte(u8 value) override;
|
||||||
|
|
||||||
bool ReadDeviceDetectPin() const override;
|
bool ReadDeviceDetectPin() const override;
|
||||||
|
|
||||||
|
@ -259,7 +299,10 @@ private:
|
||||||
u8 m_progress_timer = {};
|
u8 m_progress_timer = {};
|
||||||
|
|
||||||
// The port on the end of the motion plus:
|
// The port on the end of the motion plus:
|
||||||
I2CBus m_i2c_bus;
|
Common::I2CBusForwarding m_i2c_bus;
|
||||||
ExtensionPort m_extension_port{&m_i2c_bus};
|
ExtensionPort m_extension_port{&m_i2c_bus};
|
||||||
|
|
||||||
|
InactiveRegisterWrapper m_inactive_wrapper{this}; // connected to m_i2c_bus
|
||||||
|
ActiveRegisterWrapper m_active_wrapper{this}; // *not* connected to m_i2c_bus
|
||||||
};
|
};
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -146,6 +146,8 @@ void SpeakerLogic::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(adpcm_state);
|
p.Do(adpcm_state);
|
||||||
p.Do(reg_data);
|
p.Do(reg_data);
|
||||||
|
p.Do(m_i2c_active);
|
||||||
|
p.Do(m_device_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeakerLogic::SetSpeakerEnabled(bool enabled)
|
void SpeakerLogic::SetSpeakerEnabled(bool enabled)
|
||||||
|
@ -153,29 +155,104 @@ void SpeakerLogic::SetSpeakerEnabled(bool enabled)
|
||||||
m_speaker_enabled = enabled;
|
m_speaker_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpeakerLogic::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
bool SpeakerLogic::StartWrite(u8 slave_addr)
|
||||||
{
|
{
|
||||||
if (I2C_ADDR != slave_addr)
|
if (slave_addr == I2C_ADDR)
|
||||||
return 0;
|
|
||||||
|
|
||||||
return RawRead(®_data, addr, count, data_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
int SpeakerLogic::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
|
||||||
{
|
|
||||||
if (I2C_ADDR != slave_addr)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (0x00 == addr)
|
|
||||||
{
|
{
|
||||||
SpeakerData(data_in, count, m_speaker_pan_setting.GetValue() / 100);
|
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} write started, previously active: {}", I2C_ADDR,
|
||||||
return count;
|
m_i2c_active);
|
||||||
|
m_i2c_active = true;
|
||||||
|
m_device_address.reset();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpeakerLogic::StartRead(u8 slave_addr)
|
||||||
|
{
|
||||||
|
if (slave_addr == I2C_ADDR)
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(WII_IPC, "I2C Device {:02x} read started, previously active: {}", I2C_ADDR,
|
||||||
|
m_i2c_active);
|
||||||
|
if (m_device_address.has_value())
|
||||||
|
{
|
||||||
|
m_i2c_active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC,
|
||||||
|
"I2C Device {:02x}: read attempted without having written device address",
|
||||||
|
I2C_ADDR);
|
||||||
|
m_i2c_active = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeakerLogic::Stop()
|
||||||
|
{
|
||||||
|
m_i2c_active = false;
|
||||||
|
m_device_address.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> SpeakerLogic::ReadByte()
|
||||||
|
{
|
||||||
|
if (m_i2c_active)
|
||||||
|
{
|
||||||
|
// TODO: It seems reading address 0x00 should always return 0xff.
|
||||||
|
|
||||||
|
ASSERT(m_device_address.has_value()); // enforced by StartRead
|
||||||
|
const u8 cur_addr = m_device_address.value();
|
||||||
|
// Wrapping from 255 to 0 is the assumed behavior; this may not make sense here
|
||||||
|
m_device_address = cur_addr + 1;
|
||||||
|
return RawRead(®_data, cur_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpeakerLogic::WriteByte(u8 value)
|
||||||
|
{
|
||||||
|
if (m_i2c_active)
|
||||||
|
{
|
||||||
|
if (m_device_address.has_value())
|
||||||
|
{
|
||||||
|
const u8 cur_addr = m_device_address.value();
|
||||||
|
|
||||||
|
if (cur_addr == SPEAKER_DATA_OFFSET) // == 0
|
||||||
|
{
|
||||||
|
// Note: No auto-incrementation in this case
|
||||||
|
SpeakerData(&value, 1, m_speaker_pan_setting.GetValue() / 100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Wrapping from 255 to 0 is the assumed behavior; this may not make sense here
|
||||||
|
m_device_address = cur_addr + 1;
|
||||||
|
|
||||||
// TODO: Does writing immediately change the decoder config even when active
|
// TODO: Does writing immediately change the decoder config even when active
|
||||||
// or does a write to 0x08 activate the new configuration or something?
|
// or does a write to 0x08 activate the new configuration or something?
|
||||||
return RawWrite(®_data, addr, count, data_in);
|
RawWrite(®_data, cur_addr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_device_address = value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
#include "Common/I2C.h"
|
||||||
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
||||||
|
|
||||||
namespace WiimoteEmu
|
namespace WiimoteEmu
|
||||||
|
@ -17,7 +17,7 @@ struct ADPCMState
|
||||||
|
|
||||||
class Wiimote;
|
class Wiimote;
|
||||||
|
|
||||||
class SpeakerLogic : public I2CSlave
|
class SpeakerLogic : public Common::I2CSlave
|
||||||
{
|
{
|
||||||
friend class Wiimote;
|
friend class Wiimote;
|
||||||
|
|
||||||
|
@ -63,8 +63,11 @@ private:
|
||||||
|
|
||||||
static_assert(0x100 == sizeof(Register));
|
static_assert(0x100 == sizeof(Register));
|
||||||
|
|
||||||
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out) override;
|
bool StartWrite(u8 slave_addr) override;
|
||||||
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in) override;
|
bool StartRead(u8 slave_addr) override;
|
||||||
|
void Stop() override;
|
||||||
|
std::optional<u8> ReadByte() override;
|
||||||
|
bool WriteByte(u8 value) override;
|
||||||
|
|
||||||
Register reg_data{};
|
Register reg_data{};
|
||||||
|
|
||||||
|
@ -75,6 +78,9 @@ private:
|
||||||
ControllerEmu::SettingValue<double> m_speaker_pan_setting;
|
ControllerEmu::SettingValue<double> m_speaker_pan_setting;
|
||||||
|
|
||||||
bool m_speaker_enabled = false;
|
bool m_speaker_enabled = false;
|
||||||
|
|
||||||
|
bool m_i2c_active = false;
|
||||||
|
std::optional<u8> m_device_address = std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace WiimoteEmu
|
} // namespace WiimoteEmu
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/Config/Config.h"
|
#include "Common/Config/Config.h"
|
||||||
|
#include "Common/I2C.h"
|
||||||
|
|
||||||
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
|
||||||
|
|
||||||
|
@ -17,7 +18,6 @@
|
||||||
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
#include "Core/HW/WiimoteEmu/Dynamics.h"
|
||||||
#include "Core/HW/WiimoteEmu/Encryption.h"
|
#include "Core/HW/WiimoteEmu/Encryption.h"
|
||||||
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
|
||||||
#include "Core/HW/WiimoteEmu/I2CBus.h"
|
|
||||||
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
#include "Core/HW/WiimoteEmu/MotionPlus.h"
|
||||||
#include "Core/HW/WiimoteEmu/Speaker.h"
|
#include "Core/HW/WiimoteEmu/Speaker.h"
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ private:
|
||||||
MotionPlus m_motion_plus;
|
MotionPlus m_motion_plus;
|
||||||
CameraLogic m_camera_logic;
|
CameraLogic m_camera_logic;
|
||||||
|
|
||||||
I2CBus m_i2c_bus;
|
Common::I2CBusSimple m_i2c_bus;
|
||||||
|
|
||||||
ExtensionPort m_extension_port{&m_i2c_bus};
|
ExtensionPort m_extension_port{&m_i2c_bus};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue