i2cdev2/
linux.rs

1// Copyright 2015, Paul Osborne <osbpau@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option.  This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use ffi;
10use core::{I2CDevice, I2CBus};
11use std::error::Error;
12use std::path::Path;
13use std::fs::File;
14use std::fmt;
15use nix;
16use std::io;
17use std::fs::OpenOptions;
18use std::io::prelude::*;
19use std::os::unix::prelude::*;
20
21// Expose these core structs from this module
22pub use core::{I2CMsgFlags, I2CMsg};
23
24pub struct LinuxI2CDevice {
25    devfile: File,
26    slave_address: u16,
27}
28
29pub struct LinuxI2CBus {
30    devfile: File,
31}
32
33#[derive(Debug)]
34pub enum LinuxI2CError {
35    Nix(nix::Error),
36    Io(io::Error),
37}
38
39impl From<nix::Error> for LinuxI2CError {
40    fn from(e: nix::Error) -> Self {
41        LinuxI2CError::Nix(e)
42    }
43}
44
45impl From<io::Error> for LinuxI2CError {
46    fn from(e: io::Error) -> Self {
47        LinuxI2CError::Io(e)
48    }
49}
50
51impl From<LinuxI2CError> for io::Error {
52    fn from(e: LinuxI2CError) -> io::Error {
53        match e {
54            LinuxI2CError::Io(e) => e,
55            LinuxI2CError::Nix(e) => {
56                match e {
57                    nix::Error::Sys(e) => io::Error::from_raw_os_error(e as i32),
58                    e => {
59                        io::Error::new(io::ErrorKind::InvalidInput, format!("{:?}", e))
60                    }
61                }
62            }
63        }
64    }
65}
66
67impl fmt::Display for LinuxI2CError {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        match *self {
70            LinuxI2CError::Nix(ref e) => fmt::Display::fmt(e, f),
71            LinuxI2CError::Io(ref e) => fmt::Display::fmt(e, f),
72        }
73    }
74}
75
76impl Error for LinuxI2CError {
77    fn description(&self) -> &str {
78        match *self {
79            LinuxI2CError::Io(ref e) => e.description(),
80            LinuxI2CError::Nix(ref e) => e.description(),
81        }
82    }
83
84    fn cause(&self) -> Option<&Error> {
85        match *self {
86            LinuxI2CError::Io(ref e) => Some(e),
87            LinuxI2CError::Nix(ref e) => Some(e),
88        }
89    }
90}
91
92impl AsRawFd for LinuxI2CDevice {
93    fn as_raw_fd(&self) -> RawFd {
94        self.devfile.as_raw_fd()
95    }
96}
97
98impl AsRawFd for LinuxI2CBus {
99    fn as_raw_fd(&self) -> RawFd {
100        self.devfile.as_raw_fd()
101    }
102}
103
104impl LinuxI2CDevice {
105    /// Create a new I2CDevice for the specified path
106    pub fn new<P: AsRef<Path>>(path: P,
107                               slave_address: u16)
108                               -> Result<LinuxI2CDevice, LinuxI2CError> {
109        let file = OpenOptions::new()
110                            .read(true)
111                            .write(true)
112                            .open(path)?;
113        let mut device = LinuxI2CDevice {
114            devfile: file,
115            slave_address: 0, // will be set later
116        };
117        device.set_slave_address(slave_address)?;
118        Ok(device)
119    }
120
121    /// Set the slave address for this device
122    ///
123    /// Typically the address is expected to be 7-bits but 10-bit addresses
124    /// may be supported by the kernel driver in some cases.  Little validation
125    /// is done in Rust as the kernel is good at making sure things are valid.
126    ///
127    /// Note that if you have created a device using
128    /// `I2Device::new(...)` it is not necesasry to call this method
129    /// (it is done internally).  Calling this method is only
130    /// necessary if you need to change the slave device and you do
131    /// not want to create a new device.
132    pub fn set_slave_address(&mut self, slave_address: u16) -> Result<(), LinuxI2CError> {
133        ffi::i2c_set_slave_address(self.as_raw_fd(), slave_address)?;
134        self.slave_address = slave_address;
135        Ok(())
136    }
137}
138
139impl I2CDevice for LinuxI2CDevice {
140    type Error = LinuxI2CError;
141
142    /// Read data from the device to fill the provided slice
143    fn read(&mut self, data: &mut [u8]) -> Result<(), LinuxI2CError> {
144        self.devfile.read(data).map_err(From::from).map(drop)
145    }
146
147    /// Write the provided buffer to the device
148    fn write(&mut self, data: &[u8]) -> Result<(), LinuxI2CError> {
149        self.devfile.write(data).map_err(From::from).map(drop)
150    }
151
152    /// This sends a single bit to the device, at the place of the Rd/Wr bit
153    fn smbus_write_quick(&mut self, bit: bool) -> Result<(), LinuxI2CError> {
154        ffi::i2c_smbus_write_quick(self.as_raw_fd(), bit).map_err(From::from)
155    }
156
157    /// Read a single byte from a device, without specifying a device register
158    ///
159    /// Some devices are so simple that this interface is enough; for
160    /// others, it is a shorthand if you want to read the same register as in
161    /// the previous SMBus command.
162    fn smbus_read_byte(&mut self) -> Result<u8, LinuxI2CError> {
163        ffi::i2c_smbus_read_byte(self.as_raw_fd()).map_err(From::from)
164    }
165
166    /// Write a single byte to a sdevice, without specifying a device register
167    ///
168    /// This is the opposite operation as smbus_read_byte.  As with read_byte,
169    /// no register is specified.
170    fn smbus_write_byte(&mut self, value: u8) -> Result<(), LinuxI2CError> {
171        ffi::i2c_smbus_write_byte(self.as_raw_fd(), value).map_err(From::from)
172    }
173
174    /// Read a single byte from a device, from a designated register
175    ///
176    /// The register is specified through the Comm byte.
177    fn smbus_read_byte_data(&mut self, register: u8) -> Result<u8, LinuxI2CError> {
178        ffi::i2c_smbus_read_byte_data(self.as_raw_fd(), register).map_err(From::from)
179    }
180
181    /// Write a single byte to a specific register on a device
182    ///
183    /// The register is specified through the Comm byte.
184    fn smbus_write_byte_data(&mut self, register: u8, value: u8) -> Result<(), LinuxI2CError> {
185        ffi::i2c_smbus_write_byte_data(self.as_raw_fd(), register, value).map_err(From::from)
186    }
187
188    /// Read 2 bytes form a given register on a device
189    fn smbus_read_word_data(&mut self, register: u8) -> Result<u16, LinuxI2CError> {
190        ffi::i2c_smbus_read_word_data(self.as_raw_fd(), register).map_err(From::from)
191    }
192
193    /// Write 2 bytes to a given register on a device
194    fn smbus_write_word_data(&mut self, register: u8, value: u16) -> Result<(), LinuxI2CError> {
195        ffi::i2c_smbus_write_word_data(self.as_raw_fd(), register, value).map_err(From::from)
196    }
197
198    /// Select a register, send 16 bits of data to it, and read 16 bits of data
199    fn smbus_process_word(&mut self, register: u8, value: u16) -> Result<u16, LinuxI2CError> {
200        ffi::i2c_smbus_process_call(self.as_raw_fd(), register, value).map_err(From::from)
201    }
202
203    /// Read a block of up to 32 bytes from a device
204    ///
205    /// The actual number of bytes available to read is returned in the count
206    /// byte.  This code returns a correctly sized vector containing the
207    /// count bytes read from the device.
208    fn smbus_read_block_data(&mut self, register: u8) -> Result<Vec<u8>, LinuxI2CError> {
209        ffi::i2c_smbus_read_block_data(self.as_raw_fd(), register).map_err(From::from)
210    }
211
212    /// Read a block of up to 32 bytes from a device via i2c_smbus_i2c_read_block_data
213    fn smbus_read_i2c_block_data(&mut self, register: u8, len: u8) -> Result<Vec<u8>, LinuxI2CError> {
214        ffi::i2c_smbus_read_i2c_block_data(self.as_raw_fd(), register, len).map_err(From::from)
215    }
216
217    /// Write a block of up to 32 bytes to a device
218    ///
219    /// The opposite of the Block Read command, this writes up to 32 bytes to
220    /// a device, to a designated register that is specified through the
221    /// Comm byte. The amount of data is specified in the Count byte.
222    fn smbus_write_block_data(&mut self, register: u8, values: &[u8]) -> Result<(), LinuxI2CError> {
223        ffi::i2c_smbus_write_block_data(self.as_raw_fd(), register, values).map_err(From::from)
224    }
225
226    /// Write a block of up to 32 bytes from a device via i2c_smbus_i2c_write_block_data
227    fn smbus_write_i2c_block_data(&mut self, register: u8, values: &[u8]) -> Result<(), LinuxI2CError> {
228        ffi::i2c_smbus_write_i2c_block_data(self.as_raw_fd(), register, values).map_err(From::from)
229    }
230
231    /// Select a register, send 1 to 31 bytes of data to it, and reads
232    /// 1 to 31 bytes of data from it.
233    fn smbus_process_block(&mut self, register: u8, values: &[u8]) -> Result<Vec<u8>, LinuxI2CError> {
234        ffi::i2c_smbus_process_call_block(self.as_raw_fd(), register, values).map_err(From::from)
235    }
236}
237
238impl LinuxI2CBus {
239    /// Create a new LinuxI2CBus for the specified path
240    pub fn new<P: AsRef<Path>>(path: P)
241                               -> Result<LinuxI2CBus, LinuxI2CError> {
242        let file = OpenOptions::new()
243                            .read(true)
244                            .write(true)
245                            .open(path)?;
246        let bus = LinuxI2CBus {
247            devfile: file,
248        };
249        Ok(bus)
250    }
251}
252
253impl I2CBus for LinuxI2CBus {
254    type Error = LinuxI2CError;
255
256    /// Issue the provided sequence of I2C transactions
257    fn rdwr(&mut self, msgs: &mut Vec<I2CMsg>) -> Result<i32, LinuxI2CError> {
258        ffi::i2c_rdwr_read_write(self.as_raw_fd(), msgs).map_err(From::from)
259    }
260}