-
Notifications
You must be signed in to change notification settings - Fork 8
Add SMBus HAL, dependent on embedded-hal-async::I2c
#7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Cargo Vet Audit Passed
|
| if use_pec { | ||
| let pec = Self::get_pec_calc(); | ||
| if let Some(mut pec) = pec { | ||
| pec.write_u8(address << 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to rewrite these functions in terms of a generic buffer read/write functions. I was thinking something like write_buffer(address: _ , reg: _, pec: bool, buffer: &[u8]). Then write_byte would be implemented as write_buffer(address, reg, pec, &[byte])`. This would allow implementers to override the core read/write functionality in a single place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already enforced as part of the associated type PecCalc having the core::hash::Hasher trait.
write() is defined in the Hasher trait to take in a bytestream and compute a hash on it, so that functionality is already captured.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm talking about the default implementation for the trait functions. There's duplication of the PEC calculation and duplication of I2C bus operations. I think you could create Smbus::read_buf/Smbus::write_buf functions that take a buffer and do the PEC calculation and the I2C operation. Then functions like Smbus::write_byte can be implemented in terms of Smbus::write_buf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noodled around with this for a while and could not find a nice way to calculate the PEC in a generic way. A PEC byte needs to be appended to the end of a write buffer, of which the size of a buffer is only known to the calling function.
Even worse, any i2c::transaction() call cannot append a PEC byte to a write operation in a generic way because Operation::Write()s contain immutable references to the underlying buffer. The slice of Operations is mutable, but a new Operation cannot be added without having some sort of allocated collection. The user could pass in storage for the PEC byte but i think that would make the API arguments confusing.
I tried defining my own Operation, but this breaks interop with i2c::transaction(). I cannot construct the slice of [i2c::Operation] from [smbus::Operation] because I would need to allocate a collection to pass along to i2c::transaction().
TBH, the user is not intended to override any of these methods. So long as the underlying I2C driver conforms to the embedded_hal_async::i2c::I2c trait, there is a guarantee that the default SMBus trait methods will work. I could make this whole trait a wrapper struct on a I2C driver, but I think it more ergonomic to tack a trait to existing I2C drivers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could make this whole trait a wrapper struct on a I2C driver, but I think it more ergonomic to tack a trait to existing I2C drivers.
I think a combination of both approaches might be best. We can keep trait Smbus: I2c and the current default implementation becomes the impl<B: I2c> Smbus for SwSmbusI2c<B>. There's still some de-duplication in the code that can be done with an auxiliary function or two which tells me we want the current default logic in its own struct.
| if use_pec { | ||
| let pec = Self::get_pec_calc(); | ||
| if let Some(mut pec) = pec { | ||
| pec.write_u8(address << 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm talking about the default implementation for the trait functions. There's duplication of the PEC calculation and duplication of I2C bus operations. I think you could create Smbus::read_buf/Smbus::write_buf functions that take a buffer and do the PEC calculation and the I2C operation. Then functions like Smbus::write_byte can be implemented in terms of Smbus::write_buf.
kurtjd
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Robert's comments are a good idea but otherwise looks good.
| crate::smbus::bus::ErrorKind::Pec, | ||
| ))?; | ||
|
|
||
| pec.write_u8(address << 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to use the same i2c::transaction-based logic as write_block so that the caller doesn't have to allocate memory for the PEC byte. This would also help simplify functions like send_byte.
| use_pec: bool, | ||
| read: &mut [u8], | ||
| ) -> impl core::future::Future<Output = Result<(), <Self as crate::smbus::bus::ErrorType>::Error>> { | ||
| async move { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to write_buf, can you use transactions here so the caller doesn't have to provide space for the PEC byte?
| let mut pec = Self::get_pec_calc().ok_or(<Self as crate::smbus::bus::ErrorType>::Error::to_kind( | ||
| crate::smbus::bus::ErrorKind::Pec, | ||
| ))?; | ||
| pec.write_u8(address << 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to break-out most of the logic in read_byte/read_word into a helper function.
Add SMBus support in the form of a helper trait built on top of an embedded-hal-async I2C implementation.
Based on the SMBus v3,3 spec.