mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 22:01:11 +00:00
Validate 7-bit I2C addresses (#3343)
* Validate 7-bit I2C addresses * add test * CHANGELOG.md * Docs * Include address in `Error::AddressInvalid` * Fix * CHANGELOG.md * Revert CHANGELOG change --------- Co-authored-by: Dániel Buga <bugadani@gmail.com>
This commit is contained in:
parent
c22797c694
commit
523bd381eb
@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Fix PCNT counter not keeping the peripheral enabled (#3334)
|
- Fix PCNT counter not keeping the peripheral enabled (#3334)
|
||||||
- Fixed an issue where inverting a pin via the interconnect matrix was ineffective (#3312)
|
- Fixed an issue where inverting a pin via the interconnect matrix was ineffective (#3312)
|
||||||
- The half-duplex SPI APIs should accept more valid line width combinations (#3325)
|
- The half-duplex SPI APIs should accept more valid line width combinations (#3325)
|
||||||
|
- Passing an invalid seven bit I2C address is now rejected (#3343)
|
||||||
- PARL_IO: Use correct max transfer size (#3346)
|
- PARL_IO: Use correct max transfer size (#3346)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
@ -79,17 +79,33 @@ const MAX_ITERATIONS: u32 = 1_000_000;
|
|||||||
pub enum I2cAddress {
|
pub enum I2cAddress {
|
||||||
/// 7-bit address mode type.
|
/// 7-bit address mode type.
|
||||||
///
|
///
|
||||||
/// Note that 7-bit addresses defined by drivers should be specified in
|
/// Note that 7-bit addresses are specified in **right-aligned** form, e.g.
|
||||||
/// **right-aligned** form, e.g. in the range `0x00..=0x7F`.
|
/// in the range `0x00..=0x7F`.
|
||||||
///
|
///
|
||||||
/// For example, a device that has the seven bit address of `0b011_0010`,
|
/// For example, a device that has the seven bit address of `0b011_0010`,
|
||||||
/// and therefore is addressed on the wire using:
|
/// and therefore is addressed on the wire using:
|
||||||
///
|
///
|
||||||
/// * `0b0110010_0` or `0x64` for *writes*
|
/// * `0b0110010_0` or `0x64` for *writes*
|
||||||
/// * `0b0110010_1` or `0x65` for *reads*
|
/// * `0b0110010_1` or `0x65` for *reads*
|
||||||
|
///
|
||||||
|
/// The above address is specified as 0b0011_0010 or 0x32, NOT 0x64 or 0x65.
|
||||||
SevenBit(u8),
|
SevenBit(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl I2cAddress {
|
||||||
|
fn validate(&self) -> Result<(), Error> {
|
||||||
|
match self {
|
||||||
|
I2cAddress::SevenBit(addr) => {
|
||||||
|
if *addr > 0x7F {
|
||||||
|
return Err(Error::AddressInvalid(*self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u8> for I2cAddress {
|
impl From<u8> for I2cAddress {
|
||||||
fn from(value: u8) -> Self {
|
fn from(value: u8) -> Self {
|
||||||
I2cAddress::SevenBit(value)
|
I2cAddress::SevenBit(value)
|
||||||
@ -168,6 +184,8 @@ pub enum Error {
|
|||||||
CommandNumberExceeded,
|
CommandNumberExceeded,
|
||||||
/// Zero length read or write operation.
|
/// Zero length read or write operation.
|
||||||
ZeroLengthInvalid,
|
ZeroLengthInvalid,
|
||||||
|
/// The given address is invalid.
|
||||||
|
AddressInvalid(I2cAddress),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// I2C no acknowledge error reason.
|
/// I2C no acknowledge error reason.
|
||||||
@ -231,6 +249,9 @@ impl core::fmt::Display for Error {
|
|||||||
write!(f, "The number of commands issued exceeded the limit")
|
write!(f, "The number of commands issued exceeded the limit")
|
||||||
}
|
}
|
||||||
Error::ZeroLengthInvalid => write!(f, "Zero length read or write operation"),
|
Error::ZeroLengthInvalid => write!(f, "Zero length read or write operation"),
|
||||||
|
Error::AddressInvalid(address) => {
|
||||||
|
write!(f, "The given address ({:?}) is invalid", address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,6 +815,8 @@ impl<'d> I2c<'d, Async> {
|
|||||||
address: I2cAddress,
|
address: I2cAddress,
|
||||||
operations: impl Iterator<Item = Operation<'a>>,
|
operations: impl Iterator<Item = Operation<'a>>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
|
|
||||||
let mut last_op: Option<OpKind> = None;
|
let mut last_op: Option<OpKind> = None;
|
||||||
// filter out 0 length read operations
|
// filter out 0 length read operations
|
||||||
let mut op_iter = operations
|
let mut op_iter = operations
|
||||||
@ -866,6 +889,8 @@ where
|
|||||||
address: I2cAddress,
|
address: I2cAddress,
|
||||||
operations: impl Iterator<Item = Operation<'a>>,
|
operations: impl Iterator<Item = Operation<'a>>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
|
|
||||||
let mut last_op: Option<OpKind> = None;
|
let mut last_op: Option<OpKind> = None;
|
||||||
// filter out 0 length read operations
|
// filter out 0 length read operations
|
||||||
let mut op_iter = operations
|
let mut op_iter = operations
|
||||||
@ -2211,6 +2236,7 @@ impl Driver<'_> {
|
|||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
start: bool,
|
start: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.reset_fifo();
|
self.reset_fifo();
|
||||||
self.reset_command_list();
|
self.reset_command_list();
|
||||||
let cmd_iterator = &mut self.regs().comd_iter();
|
let cmd_iterator = &mut self.regs().comd_iter();
|
||||||
@ -2244,6 +2270,7 @@ impl Driver<'_> {
|
|||||||
start: bool,
|
start: bool,
|
||||||
will_continue: bool,
|
will_continue: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.reset_fifo();
|
self.reset_fifo();
|
||||||
self.reset_command_list();
|
self.reset_command_list();
|
||||||
|
|
||||||
@ -2275,6 +2302,7 @@ impl Driver<'_> {
|
|||||||
start: bool,
|
start: bool,
|
||||||
stop: bool,
|
stop: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.clear_all_interrupts();
|
self.clear_all_interrupts();
|
||||||
|
|
||||||
// Short circuit for zero length writes without start or end as that would be an
|
// Short circuit for zero length writes without start or end as that would be an
|
||||||
@ -2311,6 +2339,7 @@ impl Driver<'_> {
|
|||||||
stop: bool,
|
stop: bool,
|
||||||
will_continue: bool,
|
will_continue: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.clear_all_interrupts();
|
self.clear_all_interrupts();
|
||||||
|
|
||||||
// Short circuit for zero length reads as that would be an invalid operation
|
// Short circuit for zero length reads as that would be an invalid operation
|
||||||
@ -2364,6 +2393,7 @@ impl Driver<'_> {
|
|||||||
start: bool,
|
start: bool,
|
||||||
stop: bool,
|
stop: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.clear_all_interrupts();
|
self.clear_all_interrupts();
|
||||||
|
|
||||||
// Short circuit for zero length writes without start or end as that would be an
|
// Short circuit for zero length writes without start or end as that would be an
|
||||||
@ -2400,6 +2430,7 @@ impl Driver<'_> {
|
|||||||
stop: bool,
|
stop: bool,
|
||||||
will_continue: bool,
|
will_continue: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
address.validate()?;
|
||||||
self.clear_all_interrupts();
|
self.clear_all_interrupts();
|
||||||
|
|
||||||
// Short circuit for zero length reads as that would be an invalid operation
|
// Short circuit for zero length reads as that would be an invalid operation
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
i2c::master::{AcknowledgeCheckFailedReason, Config, Error, I2c, Operation},
|
i2c::master::{AcknowledgeCheckFailedReason, Config, Error, I2c, I2cAddress, Operation},
|
||||||
Async,
|
Async,
|
||||||
Blocking,
|
Blocking,
|
||||||
};
|
};
|
||||||
@ -49,6 +49,22 @@ mod tests {
|
|||||||
Context { i2c }
|
Context { i2c }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_address_returns_error(mut ctx: Context) {
|
||||||
|
assert_eq!(
|
||||||
|
ctx.i2c.write(0x80, &[]),
|
||||||
|
Err(Error::AddressInvalid(I2cAddress::SevenBit(0x80)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ctx.i2c.read(0x80, &mut [0; 1]),
|
||||||
|
Err(Error::AddressInvalid(I2cAddress::SevenBit(0x80)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ctx.i2c.write_read(0x80, &[0x77], &mut [0; 1]),
|
||||||
|
Err(Error::AddressInvalid(I2cAddress::SevenBit(0x80)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_write_returns_ack_error_for_unknown_address(mut ctx: Context) {
|
fn empty_write_returns_ack_error_for_unknown_address(mut ctx: Context) {
|
||||||
// on some chips we can determine the ack-check-failed reason but not on all
|
// on some chips we can determine the ack-check-failed reason but not on all
|
||||||
|
Loading…
x
Reference in New Issue
Block a user