use embedded_hal::i2c::Operation;
use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTrans};
use xca9548a::{SlaveAddr, Xca9543a, Xca9545a, Xca9548a};
const DEV_ADDR: u8 = 0b111_0000;
const SLAVE_ADDR: u8 = 0b010_0000;
const SLAVE_WRITE_DATA: [u8; 2] = [0b0101_0101, 0b1010_1010];
const SLAVE_READ_DATA: [u8; 2] = [0b1001_1001, 0b0110_0110];
macro_rules! test_interrupt {
( $name:ident, $channels:expr ) => {
#[test]
fn can_get_interrupt_status() {
let transactions = [I2cTrans::read(
DEV_ADDR,
vec![0b1010_0000 & ($channels << 4)],
)];
let mut switch = new(&transactions);
let read_status = switch.get_interrupt_status().unwrap();
assert_eq!(0b0000_1010 & $channels, read_status);
switch.destroy().done();
}
};
}
macro_rules! test_ch_out_of_range {
( $name:ident, $channel:expr ) => {
#[test]
fn ignore_ch_out_of_range() {
let transactions = [I2cTrans::write(DEV_ADDR, vec![0x01])];
let mut switch = new(&transactions);
switch.select_channels(0b1000_0001).unwrap();
switch.destroy().done();
}
};
}
macro_rules! test_device {
( $name:ident, $channels:expr ) => {
fn new(transactions: &[I2cTrans]) -> $name<I2cMock> {
$name::new(I2cMock::new(transactions), SlaveAddr::default())
}
#[test]
fn can_select_channels() {
let transactions = [I2cTrans::write(DEV_ADDR, vec![0x01])];
let mut switch = new(&transactions);
switch.select_channels(0x01).unwrap();
switch.destroy().done();
}
#[test]
fn can_get_channel_status() {
let transactions = [I2cTrans::read(DEV_ADDR, vec![0b0101_0101 & $channels])];
let mut switch = new(&transactions);
let read_status = switch.get_channel_status().unwrap();
assert_eq!(0b0101_0101 & $channels, read_status);
switch.destroy().done();
}
#[test]
fn can_write_to_slave() {
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::write(SLAVE_ADDR, SLAVE_WRITE_DATA.to_vec()),
];
let mut switch = new(&transactions);
switch.select_channels(0b0000_0001).unwrap();
switch.write(SLAVE_ADDR, &SLAVE_WRITE_DATA).unwrap();
switch.destroy().done();
}
#[test]
fn can_read_from_slave() {
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::read(SLAVE_ADDR, SLAVE_READ_DATA.to_vec()),
];
let mut switch = new(&transactions);
switch.select_channels(0b0000_0001).unwrap();
let mut read_data = [0; 2];
switch.read(SLAVE_ADDR, &mut read_data).unwrap();
assert_eq!(read_data, SLAVE_READ_DATA);
switch.destroy().done();
}
#[test]
fn can_do_transaction_from_slave() {
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::transaction_start(SLAVE_ADDR),
I2cTrans::write(SLAVE_ADDR, SLAVE_WRITE_DATA.to_vec()),
I2cTrans::transaction_end(SLAVE_ADDR),
];
let mut switch = new(&transactions);
switch.select_channels(0b0000_0001).unwrap();
switch
.transaction(SLAVE_ADDR, &mut [Operation::Write(&SLAVE_WRITE_DATA)])
.unwrap();
switch.destroy().done();
}
#[test]
fn can_write_read_from_slave() {
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::write_read(
SLAVE_ADDR,
SLAVE_WRITE_DATA.to_vec(),
SLAVE_READ_DATA.to_vec(),
),
];
let mut switch = new(&transactions);
switch.select_channels(0b0000_0001).unwrap();
let mut read_data = [0; 2];
switch
.write_read(SLAVE_ADDR, &SLAVE_WRITE_DATA, &mut read_data)
.unwrap();
assert_eq!(read_data, SLAVE_READ_DATA);
switch.destroy().done();
}
#[test]
fn can_split_and_communicate_with_slave() {
let slave_read_data_2 = [0xAB, 0xCD];
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::write(SLAVE_ADDR, SLAVE_WRITE_DATA.to_vec()),
I2cTrans::write(DEV_ADDR, vec![0x02]),
I2cTrans::read(SLAVE_ADDR, SLAVE_READ_DATA.to_vec()),
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::write_read(
SLAVE_ADDR,
SLAVE_WRITE_DATA.to_vec(),
slave_read_data_2.to_vec(),
),
I2cTrans::transaction_start(SLAVE_ADDR),
I2cTrans::write(SLAVE_ADDR, SLAVE_WRITE_DATA.to_vec()),
I2cTrans::transaction_end(SLAVE_ADDR),
];
let switch = new(&transactions);
{
let mut read_data_1 = [0; 2];
let mut read_data_2 = [0; 2];
let mut parts = switch.split();
parts.i2c0.write(SLAVE_ADDR, &SLAVE_WRITE_DATA).unwrap();
parts.i2c1.read(SLAVE_ADDR, &mut read_data_1).unwrap();
parts
.i2c0
.write_read(SLAVE_ADDR, &SLAVE_WRITE_DATA, &mut read_data_2)
.unwrap();
parts
.i2c0
.transaction(SLAVE_ADDR, &mut [Operation::Write(&SLAVE_WRITE_DATA)])
.unwrap();
assert_eq!(read_data_1, SLAVE_READ_DATA);
assert_eq!(read_data_2, slave_read_data_2);
}
switch.destroy().done();
}
#[test]
fn when_split_only_change_channel_if_necessary() {
let transactions = [
I2cTrans::write(DEV_ADDR, vec![0x01]),
I2cTrans::write(SLAVE_ADDR, SLAVE_WRITE_DATA.to_vec()),
I2cTrans::read(SLAVE_ADDR, SLAVE_READ_DATA.to_vec()),
];
let switch = new(&transactions);
{
let mut read_data = [0; 2];
let mut parts = switch.split();
parts.i2c0.write(SLAVE_ADDR, &SLAVE_WRITE_DATA).unwrap();
parts.i2c0.read(SLAVE_ADDR, &mut read_data).unwrap();
assert_eq!(read_data, SLAVE_READ_DATA);
}
switch.destroy().done();
}
};
}
mod test_xca9548a {
use super::*;
use embedded_hal::i2c::I2c;
test_device!(Xca9548a, 0xff);
}
mod test_xca9545a {
use super::*;
use embedded_hal::i2c::I2c;
test_device!(Xca9545a, 0x0f);
test_interrupt!(Xca9545a, 0x0f);
test_ch_out_of_range!(Xca9545a, 0x0f);
}
mod test_xca9543a {
use super::*;
use embedded_hal::i2c::I2c;
test_device!(Xca9543a, 0x03);
test_interrupt!(Xca9543a, 0x03);
test_ch_out_of_range!(Xca9543a, 0x03);
}