#[cfg(feature = "alloc")]
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
#[cfg(feature = "std")]
use std::{print, println};
#[cfg(all(feature = "linux", any(target_os = "linux", target_os = "android")))]
fn print_header_diagnostics(buf: &[u8], buffer_len: usize) {
if buf.len() < 12 {
eprintln!(
"⚠️ Warning: Not enough data to read EEPROM header ({} bytes available)",
buf.len()
);
return;
}
let version = buf[4];
let reserved = buf[5];
let numatoms = u16::from_le_bytes([buf[6], buf[7]]);
let eeplen = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
println!("EEPROM header analysis:");
println!(" Version: {}", version);
println!(" Reserved: {}", reserved);
println!(" Number of atoms: {}", numatoms);
println!(" EEPROM length: {} bytes", eeplen);
if numatoms == 0 {
eprintln!("⚠️ Warning: Header indicates 0 atoms, which is invalid");
}
if eeplen < 12 {
eprintln!("❌ Invalid EEPROM length: {} (should be >= 12)", eeplen);
} else if eeplen as usize > buffer_len {
eprintln!(
"⚠️ Warning: Header indicates EEPROM length ({} bytes) is larger than read buffer ({} bytes)",
eeplen, buffer_len
);
}
}
#[cfg(all(feature = "linux", any(target_os = "linux", target_os = "android")))]
fn read_eeprom_with_dynamic_buffer(
dev_path: &str,
addr: u16,
initial_len: usize,
) -> Result<Vec<u8>, crate::EhatromError> {
use crate::read_from_eeprom_i2c;
const MIN_HEADER_LEN: usize = 12;
let mut header_buf = vec![0u8; MIN_HEADER_LEN];
read_from_eeprom_i2c(&mut header_buf, dev_path, addr, 0)?;
let eeplen = if &header_buf[0..4] == b"R-Pi" {
Some(
u32::from_le_bytes([header_buf[8], header_buf[9], header_buf[10], header_buf[11]])
as usize,
)
} else {
None
};
let desired_len = initial_len
.max(MIN_HEADER_LEN)
.max(eeplen.unwrap_or(MIN_HEADER_LEN));
if desired_len <= header_buf.len() {
return Ok(header_buf);
}
let mut buf = vec![0u8; desired_len];
read_from_eeprom_i2c(&mut buf, dev_path, addr, 0)?;
Ok(buf)
}
#[cfg(all(feature = "linux", any(target_os = "linux", target_os = "android")))]
pub fn detect_and_show_eeprom_info(
dev_path: &str,
read_len: usize,
) -> Result<(), crate::EhatromError> {
use crate::Eeprom;
const HAT_EEPROM_ADDR: u16 = 0x50;
println!("Scanning I2C bus {} for HAT EEPROM...", dev_path);
println!("Using address: 0x{:02X}", HAT_EEPROM_ADDR);
print!("Trying 0x{:02X}... ", HAT_EEPROM_ADDR);
let buf = match read_eeprom_with_dynamic_buffer(dev_path, HAT_EEPROM_ADDR, read_len) {
Ok(buf) => buf,
Err(e) => {
eprintln!("read error: {}", e);
println!("No valid Raspberry Pi HAT EEPROM found on bus {}", dev_path);
return Ok(());
}
};
if buf.len() >= 4 && &buf[0..4] == b"R-Pi" {
println!("Found HAT EEPROM!");
println!("First 16 bytes: {:02X?}", &buf[0..16.min(buf.len())]);
print_header_diagnostics(&buf, buf.len());
match Eeprom::from_bytes(&buf) {
Ok(eeprom) => {
println!("EEPROM found at 0x{:02X} on {}", HAT_EEPROM_ADDR, dev_path);
println!("{eeprom}");
return Ok(());
}
Err(e) => {
eprintln!(
"EEPROM found at 0x{:02X} but failed to parse: {}",
HAT_EEPROM_ADDR, e
);
if buf.len() >= 64 {
println!("Raw data (first 64 bytes): {:02X?}", &buf[0..64]);
}
}
}
} else {
println!(
"no HAT signature (first 4 bytes: {:02X?})",
&buf[0..4.min(buf.len())]
);
}
println!("No valid Raspberry Pi HAT EEPROM found on bus {}", dev_path);
Ok(())
}
#[cfg(all(feature = "linux", any(target_os = "linux", target_os = "android")))]
pub fn find_i2c_devices() -> Vec<String> {
#[cfg(feature = "std")]
use std::fs;
let mut devices = Vec::new();
if let Ok(entries) = fs::read_dir("/dev") {
for entry in entries.flatten() {
let path = entry.path();
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.starts_with("i2c-") && name.len() > 4 {
if let Ok(_) = name[4..].parse::<u32>() {
devices.push(path.to_string_lossy().to_string());
}
}
}
}
}
devices.sort_by(|a, b| {
let num_a = a
.split('-')
.last()
.unwrap_or("0")
.parse::<u32>()
.unwrap_or(0);
let num_b = b
.split('-')
.last()
.unwrap_or("0")
.parse::<u32>()
.unwrap_or(0);
num_a.cmp(&num_b)
});
devices
}
#[cfg(all(feature = "linux", any(target_os = "linux", target_os = "android")))]
pub fn detect_all_i2c_devices() -> Result<(), crate::EhatromError> {
let devices = find_i2c_devices();
if devices.is_empty() {
println!("No I2C devices found in /dev");
println!("Make sure I2C is enabled and you have proper permissions.");
return Ok(());
}
println!("Found {} I2C device(s): {:?}", devices.len(), devices);
println!();
let read_len = match std::env::var("EHATROM_BUFFER_SIZE") {
Ok(size_str) => match size_str.parse::<usize>() {
Ok(size) => {
if size < 1024 {
eprintln!("Warning: Buffer size too small, using minimum 1KB");
1024
} else {
println!("Using custom buffer size: {} bytes", size);
size
}
}
Err(_) => {
eprintln!("Warning: Failed to parse EHATROM_BUFFER_SIZE, using 32KB");
32 * 1024
}
},
Err(_) => 32 * 1024, };
let mut found_any = false;
for device in &devices {
println!("=== Scanning {} ===", device);
match detect_and_show_eeprom_info(device, read_len) {
Ok(_) => {
found_any = true;
println!();
}
Err(e) => {
println!("Error scanning {}: {}", device, e);
println!();
}
}
}
if !found_any {
println!("No HAT EEPROM found on any I2C device.");
println!("This could mean:");
println!(" • No HAT is connected");
println!(" • HAT EEPROM is not programmed");
println!(" • HAT uses a different I2C address");
println!(" • Permissions issue (try running with sudo)");
}
Ok(())
}
#[cfg(not(all(feature = "linux", any(target_os = "linux", target_os = "android"))))]
pub fn find_i2c_devices() -> Vec<String> {
Vec::new()
}
#[cfg(not(all(feature = "linux", any(target_os = "linux", target_os = "android"))))]
pub fn detect_all_i2c_devices() -> Result<(), crate::EhatromError> {
#[cfg(feature = "std")]
{
eprintln!("I2C device detection is only supported on Linux with --features=linux");
std::process::exit(1);
}
#[cfg(not(feature = "std"))]
Err(crate::EhatromError::I2cError)
}
#[cfg(not(all(feature = "linux", any(target_os = "linux", target_os = "android"))))]
pub fn detect_and_show_eeprom_info(
_dev_path: &str,
_read_len: usize,
) -> Result<(), crate::EhatromError> {
#[cfg(feature = "std")]
{
eprintln!("EEPROM detection is only supported on Linux with --features=linux");
std::process::exit(1);
}
#[cfg(not(feature = "std"))]
Err(crate::EhatromError::I2cError)
}