rppal/i2c.rs
1//! Interface for the I2C peripheral.
2//!
3//! The Broadcom Serial Controller (BSC) peripheral controls a proprietary bus
4//! compliant with the I2C bus/interface. RPPAL communicates with the BSC
5//! using the `i2cdev` device interface.
6//!
7//! ## I2C buses
8//!
9//! The Raspberry Pi 3 B+ and earlier models support three hardware I2C buses,
10//! however only the I2C bus on physical pins 3 and 5 should be used to communicate
11//! with slave devices. The other two buses are used internally as an HDMI
12//! interface, and for HAT identification.
13//!
14//! On the Raspberry Pi 4 B, 400 and 5, three additional I2C buses are available,
15//! depending on your configuration, as explained below.
16//!
17//! ### I2C0 / I2C1
18//!
19//! On the Raspberry Pi B Rev 1, physical pins 3 (SDA) and 5 (SCL) are tied to I2C0.
20//! On every other Raspberry Pi model, they're connected to I2C1.
21//!
22//! The I2C bus connected to these pins is disabled by
23//! default. You can enable it through `sudo raspi-config`, or by manually
24//! adding `dtparam=i2c_arm=on` to `/boot/firmware/config.txt`. Remember to reboot
25//! the Raspberry Pi afterwards.
26//!
27//! * SDA: BCM GPIO 2 (physical pin 3)
28//! * SCL: BCM GPIO 3 (physical pin 5)
29//!
30//! ### I2C3
31//!
32//! I2C3 can be enabled by adding `dtoverlay=i2c3` to `/boot/firmware/config.txt`.
33//!
34//! * SDA: BCM GPIO 4 (physical pin 7)
35//! * SCL: BCM GPIO 5 (physical pin 29)
36//!
37//! ### I2C4
38//!
39//! I2C4 can be enabled by adding `dtoverlay=i2c4` to `/boot/firmware/config.txt`.
40//!
41//! * SDA: BCM GPIO 8 (physical pin 24)
42//! * SCL: BCM GPIO 9 (physical pin 21)
43//!
44//! ### I2C5
45//!
46//! I2C5 can be enabled by adding `dtoverlay=i2c5` to `/boot/firmware/config.txt`.
47//!
48//! * SDA: BCM GPIO 12 (physical pin 32)
49//! * SCL: BCM GPIO 13 (physical pin 33)
50//!
51//! ### I2C6
52//!
53//! I2C6 can be enabled by adding `dtoverlay=i2c6` to `/boot/firmware/config.txt`.
54//!
55//! * SDA: BCM GPIO 22 (physical pin 15)
56//! * SCL: BCM GPIO 23 (physical pin 16)
57//!
58//! ### Alternative pins
59//!
60//! The GPIO pin numbers mentioned above are part of the default configuration.
61//! Some of their functionality can be moved to different pins. Read
62//! `/boot/overlays/README` for more information.
63//!
64//! ### Software I2C
65//!
66//! In addition to the hardware I2C buses, it's possible to configure a
67//! bit-banged software I2C bus on any available GPIO pins through the `i2c-gpio`
68//! device tree overlay. More details on enabling and configuring `i2c-gpio`
69//! can be found in `/boot/overlays/README`.
70//!
71//! ## Transmission speed
72//!
73//! The BSC supports I2C data transfer rates up to 400 kbit/s (Fast-mode).
74//!
75//! By default, the I2C bus clock speed is set to 100 kHz. Transferring
76//! 1 bit takes 1 clock cycle. You can change the
77//! transfer rate by adding `dtparam=i2c_arm_baudrate=X` to
78//! `/boot/firmware/config.txt`, where `X` should be replaced with the
79//! clock frequency in hertz (Hz). Remember to reboot
80//! the Raspberry Pi afterwards.
81//!
82//! ## Not supported
83//!
84//! Some I2C and SMBus features aren't fully supported by the `i2cdev` interface, the underlying driver or
85//! the BCM283x SoC: 10-bit slave addresses, SMBus Block Read, SMBus Block Process Call, SMBus Host Notify,
86//! SMBus Read/Write 32/64, and the SMBus Address Resolution Protocol.
87//!
88//! While clock stretching is supported, a bug exists in the implementation on the BCM283x SoC that will result
89//! in corrupted data when a slave device uses clock stretching at arbitrary points during the transfer.
90//! Clock stretching only works properly during read operations, directly after the ACK phase, when the additional
91//! delay is longer than half of a clock period. More information can be found [here](https://elinux.org/BCM2835_datasheet_errata#p35_I2C_clock_stretching).
92//!
93//! A possible workaround for slave devices that require clock stretching at other points during the transfer is
94//! to use a bit-banged software I2C bus by configuring the `i2c-gpio` device tree overlay as described in `/boot/overlays/README`.
95//!
96//! ## Troubleshooting
97//!
98//! ### Permission denied
99//!
100//! If [`new`] or [`with_bus`] returns an `io::ErrorKind::PermissionDenied`
101//! error, make sure the file permissions for `/dev/i2c-1` or `/dev/i2c-0`
102//! are correct, and the current user is a member of the `i2c` group.
103//!
104//! ### Timed out
105//!
106//! Transactions return an `io::ErrorKind::TimedOut` error when their duration
107//! exceeds the timeout value. You can change the timeout using [`set_timeout`].
108//!
109//! [`new`]: struct.I2c.html#method.new
110//! [`with_bus`]: struct.I2c.html#method.with_bus
111//! [`set_timeout`]: struct.I2c.html#method.set_timeout
112
113#![allow(dead_code)]
114
115use std::error;
116use std::fmt;
117use std::fs::{File, OpenOptions};
118use std::io;
119use std::io::{Read, Write};
120use std::marker::PhantomData;
121use std::os::unix::io::AsRawFd;
122use std::result;
123
124use libc::c_ulong;
125
126use crate::system;
127use crate::system::{DeviceInfo, Model};
128
129#[cfg(any(
130 feature = "embedded-hal-0",
131 feature = "embedded-hal",
132 feature = "embedded-hal-nb"
133))]
134mod hal;
135mod ioctl;
136
137pub use self::ioctl::Capabilities;
138
139/// Errors that can occur when accessing the I2C peripheral.
140#[derive(Debug)]
141pub enum Error {
142 /// I/O error.
143 Io(io::Error),
144 /// Invalid slave address.
145 ///
146 /// I2C supports 7-bit and 10-bit addresses. Several 7-bit addresses
147 /// can't be used as slave addresses.
148 InvalidSlaveAddress(u16),
149 /// I2C/SMBus feature not supported.
150 ///
151 /// The underlying drivers don't support the selected I2C feature or SMBus protocol.
152 FeatureNotSupported,
153 /// Unknown model.
154 ///
155 /// The Raspberry Pi model or SoC can't be identified. Support for
156 /// new models is usually added shortly after they are officially
157 /// announced and available to the public. Make sure you're using
158 /// the latest release of RPPAL.
159 ///
160 /// You may also encounter this error if your Linux distribution
161 /// doesn't provide any of the common user-accessible system files
162 /// that are used to identify the model and SoC.
163 UnknownModel,
164}
165
166impl fmt::Display for Error {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 match *self {
169 Error::Io(ref err) => write!(f, "I/O error: {}", err),
170 Error::InvalidSlaveAddress(address) => write!(f, "Invalid slave address: {}", address),
171 Error::FeatureNotSupported => write!(f, "I2C/SMBus feature not supported"),
172 Error::UnknownModel => write!(f, "Unknown Raspberry Pi model"),
173 }
174 }
175}
176
177impl error::Error for Error {}
178
179impl From<io::Error> for Error {
180 fn from(err: io::Error) -> Error {
181 Error::Io(err)
182 }
183}
184
185impl From<system::Error> for Error {
186 fn from(_err: system::Error) -> Error {
187 Error::UnknownModel
188 }
189}
190
191/// Result type returned from methods that can have `i2c::Error`s.
192pub type Result<T> = result::Result<T, Error>;
193
194/// Provides access to the Raspberry Pi's I2C peripheral.
195///
196/// Before using `I2c`, make sure your Raspberry Pi has the necessary I2C buses
197/// enabled. More information can be found [here].
198///
199/// Besides basic I2C communication through buffer reads and writes, `I2c` can
200/// also be used with devices that require SMBus (System Management Bus) support.
201/// SMBus is based on I2C, and defines more structured message transactions
202/// through its various protocols. More details can be found in the latest SMBus
203/// [specification].
204///
205/// The `embedded-hal` trait implementations for `I2c` can be enabled by specifying
206/// the optional `hal` feature in the dependency declaration for the `rppal` crate.
207///
208/// [here]: index.html#i2c-buses
209/// [specification]: http://smbus.org/specs/SMBus_3_1_20180319.pdf
210#[derive(Debug)]
211pub struct I2c {
212 bus: u8,
213 funcs: Capabilities,
214 i2cdev: File,
215 addr_10bit: bool,
216 address: u16,
217 // The not_sync field is a workaround to force !Sync. I2c isn't safe for
218 // Sync because of ioctl() and the underlying drivers. This avoids needing
219 // #![feature(optin_builtin_traits)] to manually add impl !Sync for I2c.
220 not_sync: PhantomData<*const ()>,
221}
222
223impl I2c {
224 /// Constructs a new `I2c`.
225 ///
226 /// `new` attempts to identify which I2C bus is bound to physical pins 3 (SDA)
227 /// and 5 (SCL) based on the Raspberry Pi model.
228 ///
229 /// More information on configuring the I2C buses can be found [here].
230 ///
231 /// [here]: index.html#i2c-buses
232 pub fn new() -> Result<I2c> {
233 match DeviceInfo::new()?.model() {
234 // Pi B Rev 1 uses I2C0
235 Model::RaspberryPiBRev1 => I2c::with_bus(0),
236 Model::RaspberryPi4B
237 | Model::RaspberryPi400
238 | Model::RaspberryPiComputeModule4
239 | Model::RaspberryPiComputeModule4S
240 | Model::RaspberryPi5
241 | Model::RaspberryPi500
242 | Model::RaspberryPiComputeModule5
243 | Model::RaspberryPiComputeModule5Lite => {
244 // Pi 4B/400 could have I2C3 enabled on pins 3 and 5
245 I2c::with_bus(1).or_else(|_| I2c::with_bus(3))
246 }
247 // Everything else should be using I2C1
248 _ => I2c::with_bus(1),
249 }
250 }
251
252 /// Constructs a new `I2c` using the specified bus.
253 ///
254 /// `bus` indicates the selected I2C bus. You'll typically want to select the
255 /// bus that's bound to physical pins 3 (SDA) and 5 (SCL). On the Raspberry
256 /// Pi B Rev 1, those pins are tied to bus 0. On every other Raspberry
257 /// Pi model, they're connected to bus 1. Additional I2C buses are available
258 /// on the Raspberry Pi 4 B, 400 and 5.
259 ///
260 /// More information on configuring the I2C buses can be found [here].
261 ///
262 /// [here]: index.html#i2c-buses
263 pub fn with_bus(bus: u8) -> Result<I2c> {
264 // bus is a u8, because any 8-bit bus ID could potentially
265 // be configured for bit banging I2C using i2c-gpio.
266 let i2cdev = OpenOptions::new()
267 .read(true)
268 .write(true)
269 .open(format!("/dev/i2c-{}", bus))?;
270
271 let capabilities = ioctl::funcs(i2cdev.as_raw_fd())?;
272
273 // Disable 10-bit addressing if it's supported
274 if capabilities.addr_10bit() {
275 ioctl::set_addr_10bit(i2cdev.as_raw_fd(), 0)?;
276 }
277
278 // Disable PEC if it's supported
279 if capabilities.smbus_pec() {
280 ioctl::set_pec(i2cdev.as_raw_fd(), 0)?;
281 }
282
283 Ok(I2c {
284 bus,
285 funcs: capabilities,
286 i2cdev,
287 addr_10bit: false,
288 address: 0,
289 not_sync: PhantomData,
290 })
291 }
292
293 /// Returns information on the functionality supported by the underlying drivers.
294 ///
295 /// The returned [`Capabilities`] instance lists the available
296 /// I2C and SMBus features.
297 ///
298 /// [`Capabilities`]: struct.Capabilities.html
299 pub fn capabilities(&self) -> Capabilities {
300 self.funcs
301 }
302
303 /// Returns the I2C bus ID.
304 pub fn bus(&self) -> u8 {
305 self.bus
306 }
307
308 /// Returns the clock frequency in hertz (Hz).
309 pub fn clock_speed(&self) -> Result<u32> {
310 let mut buffer = [0u8; 4];
311
312 File::open(format!(
313 "/sys/class/i2c-adapter/i2c-{}/of_node/clock-frequency",
314 self.bus
315 ))?
316 .read_exact(&mut buffer)?;
317
318 Ok(u32::from(buffer[3])
319 | (u32::from(buffer[2]) << 8)
320 | (u32::from(buffer[1]) << 16)
321 | (u32::from(buffer[0]) << 24))
322 }
323
324 /// Sets a 7-bit or 10-bit slave address.
325 ///
326 /// `slave_address` refers to the slave device you're communicating with.
327 /// The specified address shouldn't include the R/W bit.
328 ///
329 /// By default, 10-bit addressing is disabled, which means
330 /// `set_slave_address` only accepts 7-bit addresses. 10-bit addressing
331 /// can be enabled with [`set_addr_10bit`]. Note that setting a 7-bit
332 /// address when 10-bit addressing is enabled won't correctly target a
333 /// slave device that doesn't support 10-bit addresses.
334 ///
335 /// [`set_addr_10bit`]: #method.set_addr_10bit
336 pub fn set_slave_address(&mut self, slave_address: u16) -> Result<()> {
337 // Filter out invalid and unsupported addresses
338 if (!self.addr_10bit && ((slave_address >> 3) == 0b1111 || slave_address > 0x7F))
339 || (self.addr_10bit && slave_address > 0x03FF)
340 {
341 return Err(Error::InvalidSlaveAddress(slave_address));
342 }
343
344 ioctl::set_slave_address(self.i2cdev.as_raw_fd(), c_ulong::from(slave_address))?;
345
346 self.address = slave_address;
347
348 Ok(())
349 }
350
351 /// Sets the maximum duration of a transaction in milliseconds (ms).
352 ///
353 /// Transactions that take longer than `timeout` return an
354 /// `io::ErrorKind::TimedOut` error.
355 ///
356 /// `timeout` has a resolution of 10ms.
357 pub fn set_timeout(&self, timeout: u32) -> Result<()> {
358 // Contrary to the i2cdev documentation, this seems to
359 // be used as a timeout for (part of?) the I2C transaction.
360 ioctl::set_timeout(self.i2cdev.as_raw_fd(), timeout as c_ulong)?;
361
362 Ok(())
363 }
364
365 fn set_retries(&self, retries: u32) -> Result<()> {
366 // Set to private. While i2cdev implements retries, the underlying drivers don't.
367 ioctl::set_retries(self.i2cdev.as_raw_fd(), retries as c_ulong)?;
368
369 Ok(())
370 }
371
372 /// Enables or disables 10-bit addressing.
373 ///
374 /// 10-bit addressing currently isn't supported on the Raspberry Pi. `set_addr_10bit` returns
375 /// `Err(`[`Error::FeatureNotSupported`]`)` unless underlying driver support is detected.
376 ///
377 /// By default, `addr_10bit` is set to `false`.
378 ///
379 /// [`Error::FeatureNotSupported`]: enum.Error.html#variant.FeatureNotSupported
380 pub fn set_addr_10bit(&mut self, addr_10bit: bool) -> Result<()> {
381 if !self.capabilities().addr_10bit() {
382 return Err(Error::FeatureNotSupported);
383 }
384
385 ioctl::set_addr_10bit(self.i2cdev.as_raw_fd(), addr_10bit as c_ulong)?;
386
387 self.addr_10bit = addr_10bit;
388
389 Ok(())
390 }
391
392 /// Receives incoming data from the slave device and writes it to `buffer`.
393 ///
394 /// `read` reads as many bytes as can fit in `buffer`.
395 ///
396 /// Sequence: START → Address + Read Bit → Incoming Bytes → STOP
397 ///
398 /// Returns how many bytes were read.
399 pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
400 Ok(self.i2cdev.read(buffer)?)
401 }
402
403 /// Sends the outgoing data contained in `buffer` to the slave device.
404 ///
405 /// Sequence: START → Address + Write Bit → Outgoing Bytes → STOP
406 ///
407 /// Returns how many bytes were written.
408 pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
409 Ok(self.i2cdev.write(buffer)?)
410 }
411
412 /// Sends the outgoing data contained in `write_buffer` to the slave device, and
413 /// then fills `read_buffer` with incoming data.
414 ///
415 /// Compared to calling [`write`] and [`read`] separately, `write_read` doesn't
416 /// issue a STOP condition in between the write and read operation. A repeated
417 /// START is sent instead.
418 ///
419 /// `write_read` reads as many bytes as can fit in `read_buffer`. The maximum
420 /// number of bytes in either `write_buffer` or `read_buffer` can't exceed 8192.
421 ///
422 /// Sequence: START → Address + Write Bit → Outgoing Bytes → Repeated START →
423 /// Address + Read Bit → Incoming Bytes → STOP
424 ///
425 /// [`write`]: #method.write
426 /// [`read`]: #method.read
427 pub fn write_read(&self, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<()> {
428 ioctl::i2c_write_read(
429 self.i2cdev.as_raw_fd(),
430 self.address,
431 self.addr_10bit,
432 write_buffer,
433 read_buffer,
434 )?;
435
436 Ok(())
437 }
438
439 /// Sends an 8-bit `command`, and then fills a multi-byte `buffer` with
440 /// incoming data.
441 ///
442 /// `block_read` can read a maximum of 32 bytes.
443 ///
444 /// Although `block_read` isn't part of the SMBus protocol, it uses the
445 /// SMBus functionality to offer this commonly used I2C transaction format.
446 /// The difference between `block_read` and [`smbus_block_read`] is that the
447 /// latter also expects a byte count from the slave device.
448 ///
449 /// Sequence: START → Address + Write Bit → Command → Repeated START
450 /// → Address + Read Bit → Incoming Bytes → STOP
451 ///
452 /// [`smbus_block_read`]: #method.smbus_block_read
453 pub fn block_read(&self, command: u8, buffer: &mut [u8]) -> Result<()> {
454 ioctl::i2c_block_read(self.i2cdev.as_raw_fd(), command, buffer)?;
455
456 Ok(())
457 }
458
459 /// Sends an 8-bit `command` followed by a multi-byte `buffer`.
460 ///
461 /// `block_write` can write a maximum of 32 bytes. Any additional data contained
462 /// in `buffer` is ignored.
463 ///
464 /// Although `block_write` isn't part of the SMBus protocol, it uses the
465 /// SMBus functionality to offer this commonly used I2C transaction format. The
466 /// difference between `block_write` and [`smbus_block_write`] is that the latter
467 /// also sends a byte count to the slave device.
468 ///
469 /// Sequence: START → Address + Write Bit → Command → Outgoing Bytes → STOP
470 ///
471 /// [`smbus_block_write`]: #method.smbus_block_write
472 pub fn block_write(&self, command: u8, buffer: &[u8]) -> Result<()> {
473 ioctl::i2c_block_write(self.i2cdev.as_raw_fd(), command, buffer)?;
474
475 Ok(())
476 }
477
478 // Note: smbus_read/write_32/64 could theoretically be emulated using block_read/write
479 // provided the PEC value is calculated in software
480
481 /// Sends a 1-bit `command` in place of the R/W bit.
482 ///
483 /// Sequence: START → Address + Command Bit → STOP
484 pub fn smbus_quick_command(&self, command: bool) -> Result<()> {
485 ioctl::smbus_quick_command(self.i2cdev.as_raw_fd(), command)?;
486
487 Ok(())
488 }
489
490 /// Receives an 8-bit value.
491 ///
492 /// Sequence: START → Address + Read Bit → Incoming Byte → STOP
493 pub fn smbus_receive_byte(&self) -> Result<u8> {
494 Ok(ioctl::smbus_receive_byte(self.i2cdev.as_raw_fd())?)
495 }
496
497 /// Sends an 8-bit `value`.
498 ///
499 /// Sequence: START → Address + Write Bit → Outgoing Byte → STOP
500 pub fn smbus_send_byte(&self, value: u8) -> Result<()> {
501 ioctl::smbus_send_byte(self.i2cdev.as_raw_fd(), value)?;
502
503 Ok(())
504 }
505
506 /// Sends an 8-bit `command`, and receives an 8-bit value.
507 ///
508 /// Sequence: START → Address + Write Bit → Command → Repeated START
509 /// → Address + Read Bit → Incoming Byte → STOP
510 pub fn smbus_read_byte(&self, command: u8) -> Result<u8> {
511 Ok(ioctl::smbus_read_byte(self.i2cdev.as_raw_fd(), command)?)
512 }
513
514 /// Sends an 8-bit `command` and an 8-bit `value`.
515 ///
516 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte → STOP
517 pub fn smbus_write_byte(&self, command: u8, value: u8) -> Result<()> {
518 ioctl::smbus_write_byte(self.i2cdev.as_raw_fd(), command, value)?;
519
520 Ok(())
521 }
522
523 /// Sends an 8-bit `command`, and receives a 16-bit value.
524 ///
525 /// Based on the SMBus protocol definition, the first byte received is
526 /// stored as the low byte of the 16-bit value, and the second byte as
527 /// the high byte. Some devices may require you to swap these bytes. In those
528 /// cases you can use the convenience method [`smbus_read_word_swapped`] instead.
529 ///
530 /// Sequence: START → Address + Write Bit → Command → Repeated START
531 /// → Address + Read Bit → Incoming Byte Low → Incoming Byte High → STOP
532 ///
533 /// [`smbus_read_word_swapped`]: #method.smbus_read_word_swapped
534 pub fn smbus_read_word(&self, command: u8) -> Result<u16> {
535 Ok(ioctl::smbus_read_word(self.i2cdev.as_raw_fd(), command)?)
536 }
537
538 /// Sends an 8-bit `command`, and receives a 16-bit `value` in a non-standard swapped byte order.
539 ///
540 /// `smbus_read_word_swapped` is a convenience method that works similarly to [`smbus_read_word`],
541 /// but reverses the byte order of the incoming 16-bit value. The high byte is received first,
542 /// and the low byte second.
543 ///
544 /// Sequence: START → Address + Write Bit → Command → Repeated START
545 /// → Address + Read Bit → Incoming Byte High → Incoming Byte Low → STOP
546 ///
547 /// [`smbus_read_word`]: #method.smbus_read_word
548 pub fn smbus_read_word_swapped(&self, command: u8) -> Result<u16> {
549 let value = ioctl::smbus_read_word(self.i2cdev.as_raw_fd(), command)?;
550
551 Ok(((value & 0xFF00) >> 8) | ((value & 0xFF) << 8))
552 }
553
554 /// Sends an 8-bit `command` and a 16-bit `value`.
555 ///
556 /// Based on the SMBus protocol definition, the first byte sent is the low byte
557 /// of the 16-bit value, and the second byte is the high byte. Some devices may
558 /// require you to swap these bytes. In those cases you can use the convenience method
559 /// [`smbus_write_word_swapped`] instead.
560 ///
561 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte Low → Outgoing Byte High → STOP
562 ///
563 /// [`smbus_write_word_swapped`]: #method.smbus_write_word_swapped
564 pub fn smbus_write_word(&self, command: u8, value: u16) -> Result<()> {
565 ioctl::smbus_write_word(self.i2cdev.as_raw_fd(), command, value)?;
566
567 Ok(())
568 }
569
570 /// Sends an 8-bit `command` and a 16-bit `value` in a non-standard swapped byte order.
571 ///
572 /// `smbus_write_word_swapped` is a convenience method that works similarly to [`smbus_write_word`], but reverses the byte
573 /// order of the outgoing 16-bit value. The high byte is sent first, and the low byte second.
574 ///
575 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte High → Outgoing Byte Low → STOP
576 ///
577 /// [`smbus_write_word`]: #method.smbus_write_word
578 pub fn smbus_write_word_swapped(&self, command: u8, value: u16) -> Result<()> {
579 ioctl::smbus_write_word(
580 self.i2cdev.as_raw_fd(),
581 command,
582 ((value & 0xFF00) >> 8) | ((value & 0xFF) << 8),
583 )?;
584
585 Ok(())
586 }
587
588 /// Sends an 8-bit `command` and a 16-bit `value`, and then receives a 16-bit value in response.
589 ///
590 /// Based on the SMBus protocol definition, for both the outgoing and incoming 16-bit value,
591 /// the first byte transferred is the low byte of the 16-bit value, and the second byte is the
592 /// high byte. Some devices may require you to swap these bytes. In those cases you can use the
593 /// convenience method [`smbus_process_call_swapped`] instead.
594 ///
595 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte Low →
596 /// Outgoing Byte High → Repeated START → Address + Read Bit → Incoming Byte Low →
597 /// Incoming Byte High → STOP
598 ///
599 /// [`smbus_process_call_swapped`]: #method.smbus_process_call_swapped
600 pub fn smbus_process_call(&self, command: u8, value: u16) -> Result<u16> {
601 Ok(ioctl::smbus_process_call(
602 self.i2cdev.as_raw_fd(),
603 command,
604 value,
605 )?)
606 }
607
608 /// Sends an 8-bit `command` and a 16-bit `value`, and then receives a 16-bit value in response, in
609 /// a non-standard byte order.
610 ///
611 /// `smbus_process_call_swapped` is a convenience method that works similarly to [`smbus_process_call`],
612 /// but reverses the byte order of the outgoing and incoming 16-bit value. The high byte is transferred
613 /// first, and the low byte second.
614 ///
615 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte High →
616 /// Outgoing Byte Low → Repeated START → Address + Read Bit → Incoming Byte High →
617 /// Incoming Byte Low → STOP
618 ///
619 /// [`smbus_process_call`]: #method.smbus_process_call
620 pub fn smbus_process_call_swapped(&self, command: u8, value: u16) -> Result<u16> {
621 let response = ioctl::smbus_process_call(
622 self.i2cdev.as_raw_fd(),
623 command,
624 ((value & 0xFF00) >> 8) | ((value & 0xFF) << 8),
625 )?;
626
627 Ok(((response & 0xFF00) >> 8) | ((response & 0xFF) << 8))
628 }
629
630 /// Sends an 8-bit `command`, and then receives an 8-bit byte count along with a
631 /// multi-byte `buffer`.
632 ///
633 /// `smbus_block_read` currently isn't supported on the Raspberry Pi, and returns
634 /// `Err(`[`Error::FeatureNotSupported`]`)` unless underlying driver support is
635 /// detected. You might be able to emulate the `smbus_block_read` functionality
636 /// with [`write_read`], [`block_read`] or [`read`] provided the length of the
637 /// expected incoming data is known beforehand, or the slave device allows the
638 /// master to read more data than it needs to send.
639 ///
640 /// `smbus_block_read` can read a maximum of 32 bytes.
641 ///
642 /// Sequence: START → Address + Write Bit → Command → Repeated START →
643 /// Address + Read Bit → Incoming Byte Count → Incoming Bytes → STOP
644 ///
645 /// Returns how many bytes were read.
646 ///
647 /// [`Error::FeatureNotSupported`]: enum.Error.html#variant.FeatureNotSupported
648 /// [`write_read`]: #method.write_read
649 /// [`block_read`]: #method.block_read
650 /// [`read`]: #method.read
651 pub fn smbus_block_read(&self, command: u8, buffer: &mut [u8]) -> Result<usize> {
652 if !self.capabilities().smbus_block_read() {
653 return Err(Error::FeatureNotSupported);
654 }
655
656 Ok(ioctl::smbus_block_read(
657 self.i2cdev.as_raw_fd(),
658 command,
659 buffer,
660 )?)
661 }
662
663 /// Sends an 8-bit `command` and an 8-bit byte count along with a multi-byte `buffer`.
664 ///
665 /// `smbus_block_write` can write a maximum of 32 bytes. Any additional data contained
666 /// in `buffer` is ignored.
667 ///
668 /// Sequence: START → Address + Write Bit → Command → Outgoing Byte Count
669 /// → Outgoing Bytes → STOP
670 pub fn smbus_block_write(&self, command: u8, buffer: &[u8]) -> Result<()> {
671 ioctl::smbus_block_write(self.i2cdev.as_raw_fd(), command, buffer)?;
672
673 Ok(())
674 }
675
676 /// Enables or disables SMBus Packet Error Checking.
677 ///
678 /// Packet Error Checking inserts a CRC-8 Packet Error Code (PEC) byte before each STOP
679 /// condition for all SMBus protocols, except Quick Command and Host Notify.
680 ///
681 /// The PEC is calculated on all message bytes except the START, STOP, ACK and NACK bits.
682 ///
683 /// By default, `pec` is set to `false`.
684 pub fn set_smbus_pec(&self, pec: bool) -> Result<()> {
685 ioctl::set_pec(self.i2cdev.as_raw_fd(), pec as c_ulong)?;
686
687 Ok(())
688 }
689}
690
691// Send is safe for I2c, but we're marked !Send because of the dummy pointer that's
692// needed to force !Sync.
693unsafe impl Send for I2c {}