Skip to content

Conversation

@lxuxx
Copy link
Collaborator

@lxuxx lxuxx commented Jun 13, 2025

No description provided.

src/i2c.rs Outdated
#[repr(u8)]
pub enum CmdErr {
NoErr = 0,
ErrBusRecovery = 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run cargo fmt.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

src/common.rs Outdated
self.buf.as_mut_ptr()
}

pub fn len(&self) -> usize {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a const method.

@rusty1968
Copy link
Contributor

Can you add me as a reviewer?

@lxuxx lxuxx requested a review from rusty1968 June 24, 2025 16:59
@rusty1968
Copy link
Contributor

rusty1968 commented Jun 24, 2025

This is a very sophisticated I2C controller hardware with multiple modes (DMA, polling, interrupt). We need a more layered approach. We should formalize a I2CHardware trait and build the higher level embedded_hal::i2c , I2CTarget traits on top of it.

@lxuxx lxuxx changed the title i2c master function i2c master and slave Jul 2, 2025
@rusty1968
Copy link
Contributor

Seems like there are lots of clippy warnings. Do you want to convert this to a draft?

@lxuxx lxuxx marked this pull request as draft July 10, 2025 01:08
@lxuxx lxuxx marked this pull request as ready for review July 14, 2025 23:17
@lxuxx lxuxx marked this pull request as draft July 18, 2025 23:41
@lxuxx lxuxx marked this pull request as ready for review July 19, 2025 00:36
pub slave_target_addr: u8,
pub slave_target: Option<&'a mut I2CT>,
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using RAII (Resource Acquisition is Initialization) patterns for resource management

Suggested change
impl<I2C: Instance, I2CT: I2CTarget> Drop for I2cController<'_, I2C, I2CT> {
fn drop(&mut self) {
// Disable I2C controller
self.i2c.i2cc00().write(|w| unsafe { w.bits(0) });
// Clear any pending interrupts
self.i2c.i2cm14().write(|w| unsafe { w.bits(0xffffffff) });
self.i2c.i2cs24().write(|w| unsafe { w.bits(0xffffffff) });
}
}


impl<'a, I2CT: I2CTarget> I2cData<'a, I2CT> {
pub fn new(buf_idx: usize) -> Self {
assert!(buf_idx < I2C_TOTAL); // Prevent out-of-bounds access
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserts create panic. In embedded systems, panics can be problematic due to limited debugging capabilities and the need for high reliability. Instead of using assert!, it's often better to handle errors gracefully.

pub fn new(buf_idx: usize) -> Result<Self, AspeedI2cError> {
        if buf_idx < I2C_TOTAL {
            Ok(Self {
                // Initialize the struct here
            })
        } else {
            // Error
        }

}

src/i2c.rs Outdated
self.aspeed_i2c_master_irq();
}

pub fn i2c_wait_completion(&mut self) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn i2c_wait_completion(&mut self) {
pub fn i2c_wait_completion(&mut self) -> Result<(), CmdErr> {
let mut delay = DummyDelay {};
let mut timeout = 1_000_000;
while timeout > 0 && !self.i2c_data.completion {
self.aspeed_i2c_master_irq();
delay.delay_ns(100_000);
timeout -= 1;
}
if !self.i2c_data.completion {
self.i2c_data.cmd_err = CmdErr::ErrTimeout;
return Err(CmdErr::ErrTimeout);
}
Ok(())
}

src/i2c.rs Outdated
}

//master recover bus
pub fn aspeed_new_i2c_recover_bus(&mut self) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation of aspeed_new_i2c_recover_bus returns a boolean to indicate success or failure. While this approach is straightforward, it lacks the ability to provide detailed error information.

Suggestion: Consider using a Result type to return more descriptive errors, which is more idiomatic in Rust. This change will allow the caller to handle different error cases appropriately and improve overall error handling.

Example:

pub fn aspeed_new_i2c_recover_bus(&mut self) -> Result<(), ASpeedI2cError> {
    // Your implementation here
    // Return Ok(()) on success or Err(I2cError::SpecificError) on failure
}

Benefits:

  • Enhances code readability and maintainability by clearly communicating the possible outcomes of the function.
  • Provides more detailed error information, allowing for better error handling and debugging.

src/i2c.rs Outdated
self.i2c
.i2cm18()
.modify(|_, w| w.enbl_bus_recover_cmd().bit(true));
self.i2c_wait_completion();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With idiomatic error handling, we can simplify the code by using the ? operator here. This operator propagates errors, making the code cleaner and easier to read.

Suggestion: Refactor self.i2c_wait_completion() to return a Result type. This will allow us to use the ? operator to handle errors more idiomatically.

Example:

self.i2c_wait_completion()?;

Benefits:

  • Simplifies error handling by propagating errors automatically.
  • Enhances code readability and maintainability.
  • Aligns with Rust's idiomatic practices for error handling.

pub fn new(buf_idx: usize) -> Self {
assert!(buf_idx < I2C_TOTAL); // Prevent out-of-bounds access
unsafe {
let buf_ref: &'a mut [u8] = &mut I2C_BUF[buf_idx];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let buf_ref: &'a mut [u8] = &mut I2C_BUF[buf_idx];
let buf_ref = I2C_BUF.get_mut(buf_idx)
.ok_or(AspeedError::BufferIndexOutOfBounds)?;

This line of code ensures safety in the following ways:

Bounds Checking:

The method I2C_BUF.get_mut(buf_idx) is used to access the buffer at the specified index.
This method performs an internal check to ensure that buf_idx is within the valid range of indices for I2C_BUF.
If buf_idx is out of bounds, get_mut returns None, preventing any out-of-bounds memory access.

Error Handling:

The .ok_or(AspeedError::BufferIndexOutOfBounds)? part converts the Option returned by get_mut into a Result.
If get_mut returns None (indicating an out-of-bounds index), ok_or converts it to Err(AspeedError::BufferIndexOutOfBounds).
The ? operator then propagates this error, causing the function to return early with the appropriate error, thus preventing further execution with an invalid buffer reference.

Immutable Borrowing:

By using get_mut, the code ensures that the buffer is borrowed mutably only if the index is valid.
This prevents any undefined behavior that could arise from accessing invalid memory locations.

Clear Error Reporting:

The use of AspeedError::BufferIndexOutOfBounds provides a clear and specific error message, making it easier to diagnose and handle the error appropriately.

@@ -0,0 +1,56 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
trait I2cHardware {
type Config: Clone + core::fmt::Debug;
type Error;
fn reset(&mut self);
fn configure(&mut self, config: &Self::Config) -> Result<(), Self::Error>;
fn enable_interrupts(&mut self, mask: u32);
fn clear_interrupts(&mut self, mask: u32);
fn start_transfer(&mut self, state: &TransferState) -> Result<(), Self::Error>;
fn handle_interrupt(&mut self) -> InterruptStatus<Self::Error>;
fn is_bus_busy(&self) -> bool;
fn recover_bus(&mut self) -> Result<(), Self::Error>;
}

}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct I2cConfigBuilder {
xfer_mode: I2cXferMode,
multi_master: bool,
smbus_timeout: bool,
manual_scl_high: u8,
manual_scl_low: u8,
manual_sda_hold: u8,
smbus_alert: bool,
clk_src: u32,
mode: Mode,
}
impl I2cConfigBuilder {
/// Creates a new builder with default values
pub fn new() -> Self {
Self {
xfer_mode: I2cXferMode::DmaMode,
multi_master: false,
smbus_timeout: false,
manual_scl_high: 0,
manual_scl_low: 0,
manual_sda_hold: 0,
smbus_alert: false,
clk_src: 0,
mode: Mode::Standard,
}
}
/// Sets the transfer mode
pub fn xfer_mode(mut self, mode: I2cXferMode) -> Self {
self.xfer_mode = mode;
self
}
/// Sets multi-master mode
pub fn multi_master(mut self, enabled: bool) -> Self {
self.multi_master = enabled;
self
}
/// Sets SMBus timeout
pub fn smbus_timeout(mut self, enabled: bool) -> Self {
self.smbus_timeout = enabled;
self
}
/// Sets manual SCL high timing
pub fn manual_scl_high(mut self, value: u8) -> Self {
self.manual_scl_high = value;
self
}
/// Sets manual SCL low timing
pub fn manual_scl_low(mut self, value: u8) -> Self {
self.manual_scl_low = value;
self
}
/// Sets manual SDA hold timing
pub fn manual_sda_hold(mut self, value: u8) -> Self {
self.manual_sda_hold = value;
self
}
/// Sets SMBus alert
pub fn smbus_alert(mut self, enabled: bool) -> Self {
self.smbus_alert = enabled;
self
}
/// Sets the clock source
pub fn clk_src(mut self, value: u32) -> Self {
self.clk_src = value;
self
}
/// Sets the I2C mode (Standard/Fast/FastPlus)
pub fn mode(mut self, mode: Mode) -> Self {
self.mode = mode;
self
}
/// Builds and returns the I2cConfig
pub fn build(self) -> I2cConfig {
I2cConfig {
xfer_mode: self.xfer_mode,
multi_master: self.multi_master,
smbus_timeout: self.smbus_timeout,
manual_scl_high: self.manual_scl_high,
manual_scl_low: self.manual_scl_low,
manual_sda_hold: self.manual_sda_hold,
smbus_alert: self.smbus_alert,
clk_src: self.clk_src,
mode: self.mode,
}
}
/// Creates a configuration for standard I2C mode (100kHz)
pub fn standard() -> I2cConfig {
Self::new().mode(Mode::Standard).build()
}
/// Creates a configuration for fast I2C mode (400kHz)
pub fn fast() -> I2cConfig {
Self::new().mode(Mode::Fast).build()
}
/// Creates a configuration for fast plus I2C mode (1MHz)
pub fn fast_plus() -> I2cConfig {
Self::new().mode(Mode::FastPlus).build()
}
/// Creates a configuration with multi-master support enabled
pub fn multi_master_config() -> I2cConfig {
Self::new().multi_master(true).build()
}
/// Creates a configuration with SMBus features enabled
pub fn smbus_config() -> I2cConfig {
Self::new()
.smbus_timeout(true)
.smbus_alert(true)
.build()
}
}

}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the Builder pattern for I2C Configuration.

@lxuxx lxuxx merged commit 91bb9c6 into OpenPRoT:main Aug 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants