use std::ffi::c_void;
use wokwi_chip_ll::{i2cInit, I2CConfig};
use crate::pin::Pin;
pub struct I2CDeviceConfig {
pub address: u32,
pub scl: Pin,
pub sda: Pin,
pub connect_callback: Option<Box<dyn FnMut(u32, bool) -> bool + 'static>>,
pub read_callback: Option<Box<dyn FnMut() -> u8 + 'static>>,
pub write_callback: Option<Box<dyn FnMut(u8) -> bool + 'static>>,
pub disconnect_callback: Option<Box<dyn FnMut() + 'static>>,
}
static mut I2C_CONFIG_REGISTRY: Vec<*const I2CDeviceConfig> = Vec::new();
extern "C" fn i2c_connect_trampoline(user_data: *mut c_void, address: u32, write: bool) -> bool {
let i2c_device = unsafe { &mut *(user_data as *mut I2CDeviceConfig) };
if let Some(callback) = &mut i2c_device.connect_callback {
callback(address, write)
} else {
false
}
}
extern "C" fn i2c_read_trampoline(user_data: *mut c_void) -> u8 {
let i2c_device = unsafe { &mut *(user_data as *mut I2CDeviceConfig) };
if i2c_device.read_callback.is_some() {
i2c_device.read_callback.as_mut().unwrap()()
} else {
0
}
}
extern "C" fn i2c_write_trampoline(user_data: *mut c_void, data: u8) -> bool {
let i2c_device = unsafe { &mut *(user_data as *mut I2CDeviceConfig) };
if i2c_device.write_callback.is_some() {
i2c_device.write_callback.as_mut().unwrap()(data)
} else {
false
}
}
extern "C" fn i2c_disconnect_trampoline(user_data: *mut c_void) {
let i2c_device = unsafe { &mut *(user_data as *mut I2CDeviceConfig) };
if i2c_device.disconnect_callback.is_some() {
i2c_device.disconnect_callback.as_mut().unwrap()();
}
}
pub fn create(config: I2CDeviceConfig) {
let ll_config = I2CConfig {
user_data: &config as *const _ as *const c_void,
address: config.address,
scl: config.scl.get_id(),
sda: config.sda.get_id(),
connect: i2c_connect_trampoline,
read: i2c_read_trampoline,
write: i2c_write_trampoline,
disconnect: i2c_disconnect_trampoline,
};
unsafe {
i2cInit(&ll_config);
}
unsafe {
I2C_CONFIG_REGISTRY.push(&config);
}
}