mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-10 01:59:02 +00:00
Mostly refactored again
This commit is contained in:
parent
fb274281b1
commit
5893ea9660
2 changed files with 167 additions and 84 deletions
|
@ -32,44 +32,154 @@ void I2CBusBase::Reset()
|
||||||
m_slaves.clear();
|
m_slaves.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int I2CBusSimple::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
bool I2CBusBase::StartWrite(u8 slave_addr)
|
||||||
{
|
{
|
||||||
if (slave != nullptr)
|
bool got_ack = false;
|
||||||
|
for (I2CSlave* slave : m_slaves)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
if (slave->StartWrite(slave_addr))
|
||||||
{
|
{
|
||||||
// TODO: Does this make sense? The transmitter can't NACK a read... it's the receiver that
|
if (got_ack)
|
||||||
// does that
|
|
||||||
auto byte = slave->ReadByte();
|
|
||||||
if (byte.has_value())
|
|
||||||
data_out[i] = byte.value();
|
|
||||||
else
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return 0;
|
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed starting write for I2C addr {:02x}",
|
||||||
|
slave_addr);
|
||||||
}
|
}
|
||||||
|
got_ack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return got_ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2CBusBase::StartRead(u8 slave_addr)
|
||||||
|
{
|
||||||
|
bool got_ack = false;
|
||||||
|
for (I2CSlave* slave : m_slaves)
|
||||||
|
{
|
||||||
|
if (slave->StartRead(slave_addr))
|
||||||
|
{
|
||||||
|
if (got_ack)
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed starting read for I2C addr {:02x}",
|
||||||
|
slave_addr);
|
||||||
|
}
|
||||||
|
got_ack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return got_ack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I2CBusBase::Stop()
|
||||||
|
{
|
||||||
|
for (I2CSlave* slave : m_slaves)
|
||||||
|
slave->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> I2CBusBase::ReadByte()
|
||||||
|
{
|
||||||
|
std::optional<u8> byte = std::nullopt;
|
||||||
|
for (I2CSlave* slave : m_slaves)
|
||||||
|
{
|
||||||
|
std::optional<u8> byte2 = slave->ReadByte();
|
||||||
|
if (byte.has_value() && byte2.has_value())
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to read: {:02x} vs {:02x}", *byte, *byte2);
|
||||||
|
}
|
||||||
|
else if (byte2.has_value())
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(WII_IPC, "I2C: Read {:02x}", byte2.value());
|
||||||
|
byte = byte2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I2CBusBase::WriteByte(u8 value)
|
||||||
|
{
|
||||||
|
bool got_ack = false;
|
||||||
|
for (I2CSlave* slave : m_slaves)
|
||||||
|
{
|
||||||
|
if (slave->WriteByte(value))
|
||||||
|
{
|
||||||
|
if (got_ack)
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed write of {:02x}", value);
|
||||||
|
}
|
||||||
|
got_ack = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return got_ack;
|
||||||
}
|
}
|
||||||
|
|
||||||
int I2CBusSimple::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
int I2CBusSimple::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
|
||||||
{
|
{
|
||||||
I2CSlave* slave = GetSlave(slave_addr);
|
if (!StartWrite(slave_addr))
|
||||||
if (slave != nullptr)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
if (!slave->WriteByte(data_in[i]))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start write to {:02x} ({:02x}, {:02x})", slave_addr, addr,
|
||||||
|
count);
|
||||||
|
Stop();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (!WriteByte(addr))
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: Failed to write device address {:02x} to {:02x} ({:02x})", addr,
|
||||||
|
slave_addr, count);
|
||||||
|
Stop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!WriteByte(data_in[i]))
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC,
|
||||||
|
"I2C: Failed to byte {} of {} starting at device address {:02x} to {:02x}", i,
|
||||||
|
count, addr, slave_addr);
|
||||||
|
Stop();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stop();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int I2CBusSimple::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
|
||||||
|
{
|
||||||
|
if (!StartWrite(slave_addr))
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start write for read from {:02x} ({:02x}, {:02x})",
|
||||||
|
slave_addr, addr, count);
|
||||||
|
Stop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!WriteByte(addr))
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: Failed to write device address {:02x} to {:02x} ({:02x})", addr,
|
||||||
|
slave_addr, count);
|
||||||
|
Stop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// Note: No Stop() call before StartRead.
|
||||||
|
if (!StartRead(slave_addr))
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start read from {:02x} ({:02x}, {:02x})",
|
||||||
|
slave_addr, addr, count);
|
||||||
|
Stop();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
const std::optional<u8> byte = ReadByte();
|
||||||
|
if (!byte.has_value())
|
||||||
|
{
|
||||||
|
WARN_LOG_FMT(WII_IPC,
|
||||||
|
"I2C: Failed to byte {} of {} starting at device address {:02x} from {:02x}", i,
|
||||||
|
count, addr, slave_addr);
|
||||||
|
Stop();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
data_out[i] = byte.value();
|
||||||
|
}
|
||||||
|
Stop();
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool I2CBus::GetSCL() const
|
bool I2CBus::GetSCL() const
|
||||||
|
@ -120,10 +230,7 @@ void I2CBus::Start()
|
||||||
void I2CBus::Stop()
|
void I2CBus::Stop()
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "AVE: Stop I2C");
|
INFO_LOG_FMT(WII_IPC, "AVE: Stop I2C");
|
||||||
for (I2CSlave* slave : m_slaves)
|
I2CBusBase::Stop();
|
||||||
{
|
|
||||||
slave->Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
state = State::Inactive;
|
state = State::Inactive;
|
||||||
bit_counter = 0;
|
bit_counter = 0;
|
||||||
|
@ -178,19 +285,7 @@ void I2CBus::SCLRisingEdge(const bool sda)
|
||||||
if (state == State::ReadFromDevice && bit_counter == 0)
|
if (state == State::ReadFromDevice && bit_counter == 0)
|
||||||
{
|
{
|
||||||
// Start of a read.
|
// Start of a read.
|
||||||
std::optional<u8> byte = std::nullopt;
|
const std::optional<u8> byte = ReadByte();
|
||||||
for (I2CSlave* slave : m_slaves)
|
|
||||||
{
|
|
||||||
std::optional<u8> byte2 = slave->ReadByte();
|
|
||||||
if (byte.has_value() && byte2.has_value())
|
|
||||||
{
|
|
||||||
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to read: {:02x} vs {:02x}", *byte, *byte2);
|
|
||||||
}
|
|
||||||
else if (byte2.has_value())
|
|
||||||
{
|
|
||||||
byte = byte2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!byte.has_value())
|
if (!byte.has_value())
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(WII_IPC, "No slaves responded to I2C read");
|
WARN_LOG_FMT(WII_IPC, "No slaves responded to I2C read");
|
||||||
|
@ -233,61 +328,42 @@ void I2CBus::SCLFallingEdge(const bool sda)
|
||||||
// Write finished.
|
// Write finished.
|
||||||
if (state == State::SetI2CAddress)
|
if (state == State::SetI2CAddress)
|
||||||
{
|
{
|
||||||
bool got_ack = false;
|
const u8 slave_addr = current_byte >> 1;
|
||||||
for (I2CSlave* slave : m_slaves)
|
|
||||||
{
|
|
||||||
if (current_byte & 1)
|
if (current_byte & 1)
|
||||||
{
|
{
|
||||||
if (slave->StartRead(current_byte >> 1))
|
if (StartRead(slave_addr))
|
||||||
{
|
{
|
||||||
if (got_ack)
|
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
|
||||||
{
|
INFO_LOG_FMT(WII_IPC, "I2C: Start read from {:02x}", slave_addr);
|
||||||
WARN_LOG_FMT(WII_IPC, "Multiple slaves ACKed starting read for I2C addr {:02x}",
|
|
||||||
current_byte);
|
|
||||||
}
|
|
||||||
got_ack = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // (current_byte & 1) == 0
|
|
||||||
{
|
|
||||||
if (slave->StartWrite(current_byte >> 1))
|
|
||||||
{
|
|
||||||
if (got_ack)
|
|
||||||
{
|
|
||||||
WARN_LOG_FMT(WII_IPC, "Multiple slaves ACKed starting write for I2C addr {:02x}",
|
|
||||||
current_byte);
|
|
||||||
}
|
|
||||||
got_ack = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!got_ack)
|
|
||||||
{
|
|
||||||
state = State::Inactive; // NACK
|
|
||||||
WARN_LOG_FMT(WII_IPC, "AVE: Unknown I2C address {:02x}", current_byte);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
state = State::Inactive; // NACK
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: No device responded to read from {:02x}", current_byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (StartWrite(slave_addr))
|
||||||
{
|
{
|
||||||
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
|
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
|
||||||
INFO_LOG_FMT(WII_IPC, "AVE: I2C address is {:02x}", current_byte);
|
INFO_LOG_FMT(WII_IPC, "I2C: Start write to {:02x}", slave_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = State::Inactive; // NACK
|
||||||
|
WARN_LOG_FMT(WII_IPC, "I2C: No device responded to write to {:02x}", current_byte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Actual write
|
// Actual write
|
||||||
ASSERT(state == State::WriteToDevice);
|
ASSERT(state == State::WriteToDevice);
|
||||||
bool got_ack = false;
|
if (!WriteByte(current_byte))
|
||||||
for (I2CSlave* slave : m_slaves)
|
|
||||||
{
|
{
|
||||||
if (slave->WriteByte(current_byte))
|
state = State::Inactive;
|
||||||
{
|
WARN_LOG_FMT(WII_IPC, "I2C: Write of {:02x} NACKed", current_byte);
|
||||||
if (got_ack)
|
|
||||||
{
|
|
||||||
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to write of {:02x}", current_byte);
|
|
||||||
}
|
|
||||||
got_ack = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ public:
|
||||||
virtual bool StartWrite(u8 slave_addr) = 0;
|
virtual bool StartWrite(u8 slave_addr) = 0;
|
||||||
virtual bool StartRead(u8 slave_addr) = 0;
|
virtual bool StartRead(u8 slave_addr) = 0;
|
||||||
virtual void Stop() = 0;
|
virtual void Stop() = 0;
|
||||||
|
// NOTE: std::optional is for ease of implementation. I2C does not provide a way for the
|
||||||
|
// transmitting device to NACK a read; only the receiver can NACK.
|
||||||
virtual std::optional<u8> ReadByte() = 0;
|
virtual std::optional<u8> ReadByte() = 0;
|
||||||
virtual bool WriteByte(u8 value) = 0;
|
virtual bool WriteByte(u8 value) = 0;
|
||||||
};
|
};
|
||||||
|
@ -61,9 +63,14 @@ public:
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Returns nullptr if there is no match
|
// Signals all slaves on the bus
|
||||||
I2CSlave* GetSlave(u8 slave_addr);
|
bool StartWrite(u8 slave_addr);
|
||||||
|
bool StartRead(u8 slave_addr);
|
||||||
|
void Stop();
|
||||||
|
std::optional<u8> ReadByte();
|
||||||
|
bool WriteByte(u8 value);
|
||||||
|
|
||||||
|
private:
|
||||||
// Pointers are unowned:
|
// Pointers are unowned:
|
||||||
std::vector<I2CSlave*> m_slaves;
|
std::vector<I2CSlave*> m_slaves;
|
||||||
};
|
};
|
||||||
|
@ -72,8 +79,8 @@ class I2CBusSimple : public I2CBusBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// TODO: change int to u16 or something
|
// 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);
|
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in);
|
||||||
|
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out);
|
||||||
};
|
};
|
||||||
|
|
||||||
// An I²C bus implementation accessed via bit-banging.
|
// An I²C bus implementation accessed via bit-banging.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue