i2cdev/
ffi.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
9#![allow(dead_code)]
10#![allow(non_camel_case_types)]
11
12use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
13use nix;
14use std::io::Cursor;
15use std::marker::PhantomData;
16use std::mem;
17use std::os::unix::prelude::*;
18use std::ptr;
19
20pub type I2CError = nix::Error;
21
22/// Linux I2C message
23#[repr(C)]
24pub struct i2c_msg<'a> {
25    /// slave address
26    pub(crate) addr: u16,
27    /// serialized I2CMsgFlags
28    pub(crate) flags: u16,
29    /// msg length
30    pub(crate) len: u16,
31    /// pointer to msg data
32    pub(crate) buf: *const u8,
33
34    pub(crate) _p: PhantomData<&'a mut [u8]>,
35}
36
37bitflags! {
38    struct I2CFunctions: u32 {
39        const I2C_FUNC_I2C = 0x0000_0001;
40        const I2C_FUNC_10BIT_ADDR = 0x0000_0002;
41        const I2C_FUNC_PROTOCOL_MANGLING = 0x0000_0004; /* I2C_M_IGNORE_NAK etc. */
42        const I2C_FUNC_SMBUS_PEC = 0x0000_0008;
43        const I2C_FUNC_NOSTART = 0x0000_0010; /* I2C_M_NOSTART */
44        const I2C_FUNC_SMBUS_BLOCK_PROC_CALL = 0x0000_8000; /* SMBus 2.0 */
45        const I2C_FUNC_SMBUS_QUICK = 0x0001_0000;
46        const I2C_FUNC_SMBUS_READ_BYTE = 0x0002_0000;
47        const I2C_FUNC_SMBUS_WRITE_BYTE = 0x0004_0000;
48        const I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x0008_0000;
49        const I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x0010_0000;
50        const I2C_FUNC_SMBUS_READ_WORD_DATA = 0x0020_0000;
51        const I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x0040_0000;
52        const I2C_FUNC_SMBUS_PROC_CALL = 0x0080_0000;
53        const I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x0100_0000;
54        const I2C_FUNC_SMBUS_WRITE_BLOCK_DATA  = 0x0200_0000;
55        const I2C_FUNC_SMBUS_READ_I2C_BLOCK = 0x0400_0000; /* I2C-like block xfer  */
56        const I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = 0x0800_0000; /* w/ 1-byte reg. addr. */
57
58        const I2C_FUNC_SMBUS_BYTE = (I2CFunctions::I2C_FUNC_SMBUS_READ_BYTE.bits() |
59                                     I2CFunctions::I2C_FUNC_SMBUS_WRITE_BYTE.bits());
60        const I2C_FUNC_SMBUS_BYTE_DATA = (I2CFunctions::I2C_FUNC_SMBUS_READ_BYTE_DATA.bits() |
61                                          I2CFunctions::I2C_FUNC_SMBUS_WRITE_BYTE_DATA.bits());
62        const I2C_FUNC_SMBUS_WORD_DATA = (I2CFunctions::I2C_FUNC_SMBUS_READ_WORD_DATA.bits() |
63                                          I2CFunctions::I2C_FUNC_SMBUS_WRITE_WORD_DATA.bits());
64        const I2C_FUNC_SMBUS_BLOCK_DATA = (I2CFunctions::I2C_FUNC_SMBUS_READ_BLOCK_DATA.bits() |
65                                           I2CFunctions::I2C_FUNC_SMBUS_WRITE_BLOCK_DATA.bits());
66        const I2C_FUNC_SMBUS_I2C_BLOCK = (I2CFunctions::I2C_FUNC_SMBUS_READ_I2C_BLOCK.bits() |
67                                          I2CFunctions::I2C_FUNC_SMBUS_WRITE_I2C_BLOCK.bits());
68        const I2C_FUNC_SMBUS_EMUL = (I2CFunctions::I2C_FUNC_SMBUS_QUICK.bits() |
69                                     I2CFunctions::I2C_FUNC_SMBUS_BYTE.bits() |
70                                     I2CFunctions::I2C_FUNC_SMBUS_BYTE_DATA.bits() |
71                                     I2CFunctions::I2C_FUNC_SMBUS_WORD_DATA.bits() |
72                                     I2CFunctions::I2C_FUNC_SMBUS_PROC_CALL.bits() |
73                                     I2CFunctions::I2C_FUNC_SMBUS_WRITE_BLOCK_DATA.bits() |
74                                     I2CFunctions::I2C_FUNC_SMBUS_I2C_BLOCK.bits() |
75                                     I2CFunctions::I2C_FUNC_SMBUS_PEC.bits());
76    }
77}
78
79/// As specified in SMBus standard
80const I2C_SMBUS_BLOCK_MAX: u8 = 32;
81
82// In C, this is a union, but the largest item is clearly
83// the largest.  Rust does not have unions at this time,
84// so we improvise.  See https://github.com/rust-lang/rust/issues/5492
85//
86// union i2c_smbus_data {
87//     __u8 byte;
88//     __u16 word;
89//     __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
90//                            /* and one more for user-space compatibility */
91// };
92#[repr(C)]
93struct i2c_smbus_data {
94    block: [u8; (I2C_SMBUS_BLOCK_MAX + 2) as usize],
95}
96
97impl i2c_smbus_data {
98    fn empty() -> i2c_smbus_data {
99        unsafe { mem::zeroed() }
100    }
101}
102
103#[repr(u8)]
104enum I2CSMBusReadWrite {
105    I2C_SMBUS_READ = 1,
106    I2C_SMBUS_WRITE = 0,
107}
108
109#[repr(u32)]
110enum I2CSMBusSize {
111    I2C_SMBUS_QUICK = 0,
112    I2C_SMBUS_BYTE = 1,
113    I2C_SMBUS_BYTE_DATA = 2,
114    I2C_SMBUS_WORD_DATA = 3,
115    I2C_SMBUS_PROC_CALL = 4,
116    I2C_SMBUS_BLOCK_DATA = 5,
117    I2C_SMBUS_I2C_BLOCK_BROKEN = 6,
118    I2C_SMBUS_BLOCK_PROC_CALL = 7, // SMBus 2.0
119    I2C_SMBUS_I2C_BLOCK_DATA = 8,
120}
121
122// from include/uapi/linux/i2c-dev.h
123const I2C_RETRIES: u16 = 0x0701;
124const I2C_TIMEOUT: u16 = 0x0702;
125const I2C_SLAVE: u16 = 0x0703;
126const I2C_SLAVE_FORCE: u16 = 0x0706;
127const I2C_TENBIT: u16 = 0x0704;
128const I2C_FUNCS: u16 = 0x0705;
129const I2C_RDWR: u16 = 0x0707;
130const I2C_PEC: u16 = 0x0708;
131const I2C_SMBUS: u16 = 0x0720;
132const I2C_RDRW_IOCTL_MAX_MSGS: u8 = 42;
133
134/// This is the structure as used in the I2C_SMBUS ioctl call
135#[repr(C)]
136pub struct i2c_smbus_ioctl_data {
137    // __u8 read_write;
138    read_write: u8,
139    // __u8 command;
140    command: u8,
141    // __u32 size;
142    size: u32,
143    // union i2c_smbus_data __user *data;
144    data: *mut i2c_smbus_data,
145}
146
147/// This is the structure as used in the I2C_RDWR ioctl call
148// see linux/i2c-dev.h
149#[repr(C)]
150pub struct i2c_rdwr_ioctl_data<'a> {
151    // struct i2c_msg __user *msgs;
152    msgs: *mut i2c_msg<'a>,
153    // __u32 nmsgs;
154    nmsgs: u32,
155}
156
157mod ioctl {
158    pub use super::i2c_rdwr_ioctl_data;
159    pub use super::i2c_smbus_ioctl_data;
160    use super::{I2C_PEC, I2C_RDWR, I2C_SLAVE, I2C_SLAVE_FORCE, I2C_SMBUS};
161
162    ioctl_write_int_bad!(set_i2c_slave_address, I2C_SLAVE);
163    ioctl_write_int_bad!(set_i2c_slave_address_force, I2C_SLAVE_FORCE);
164    ioctl_write_int_bad!(set_smbus_pec, I2C_PEC);
165    ioctl_write_ptr_bad!(i2c_smbus, I2C_SMBUS, i2c_smbus_ioctl_data);
166    ioctl_write_ptr_bad!(i2c_rdwr, I2C_RDWR, i2c_rdwr_ioctl_data);
167}
168
169pub fn i2c_set_slave_address(fd: RawFd, slave_address: u16) -> Result<(), nix::Error> {
170    unsafe {
171        ioctl::set_i2c_slave_address(fd, i32::from(slave_address))?;
172    }
173    Ok(())
174}
175
176pub fn i2c_set_slave_address_force(fd: RawFd, slave_address: u16) -> Result<(), nix::Error> {
177    unsafe {
178        ioctl::set_i2c_slave_address_force(fd, i32::from(slave_address))?;
179    }
180    Ok(())
181}
182
183pub fn i2c_set_smbus_pec(fd: RawFd, enable: bool) -> Result<(), nix::Error> {
184    unsafe {
185        ioctl::set_smbus_pec(fd, i32::from(enable))?;
186    }
187    Ok(())
188}
189
190unsafe fn i2c_smbus_access(
191    fd: RawFd,
192    read_write: I2CSMBusReadWrite,
193    command: u8, // can be address or something else
194    size: I2CSMBusSize,
195    data: *mut i2c_smbus_data,
196) -> Result<(), I2CError> {
197    let args = i2c_smbus_ioctl_data {
198        read_write: read_write as u8,
199        command,
200        size: size as u32,
201        data,
202    };
203
204    // remove type information
205    ioctl::i2c_smbus(fd, &args).map(drop)
206}
207
208#[inline]
209pub fn i2c_smbus_write_quick(fd: RawFd, bit: bool) -> Result<(), I2CError> {
210    let read_write = if bit {
211        I2CSMBusReadWrite::I2C_SMBUS_READ
212    } else {
213        I2CSMBusReadWrite::I2C_SMBUS_WRITE
214    };
215    unsafe {
216        i2c_smbus_access(
217            fd,
218            read_write,
219            0,
220            I2CSMBusSize::I2C_SMBUS_QUICK,
221            ptr::null_mut(),
222        )
223    }
224}
225
226#[inline]
227pub fn i2c_smbus_read_byte(fd: RawFd) -> Result<u8, I2CError> {
228    let mut data = i2c_smbus_data::empty();
229    unsafe {
230        i2c_smbus_access(
231            fd,
232            I2CSMBusReadWrite::I2C_SMBUS_READ,
233            0,
234            I2CSMBusSize::I2C_SMBUS_BYTE,
235            &mut data,
236        )?
237    }
238    Ok(data.block[0])
239}
240
241#[inline]
242pub fn i2c_smbus_write_byte(fd: RawFd, value: u8) -> Result<(), I2CError> {
243    unsafe {
244        i2c_smbus_access(
245            fd,
246            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
247            value,
248            I2CSMBusSize::I2C_SMBUS_BYTE,
249            ptr::null_mut(),
250        )
251    }
252}
253
254#[inline]
255pub fn i2c_smbus_read_byte_data(fd: RawFd, register: u8) -> Result<u8, I2CError> {
256    let mut data = i2c_smbus_data::empty();
257    unsafe {
258        i2c_smbus_access(
259            fd,
260            I2CSMBusReadWrite::I2C_SMBUS_READ,
261            register,
262            I2CSMBusSize::I2C_SMBUS_BYTE_DATA,
263            &mut data,
264        )?;
265    }
266    Ok(data.block[0])
267}
268
269#[inline]
270pub fn i2c_smbus_write_byte_data(fd: RawFd, register: u8, value: u8) -> Result<(), I2CError> {
271    let mut data = i2c_smbus_data::empty();
272    data.block[0] = value;
273    unsafe {
274        i2c_smbus_access(
275            fd,
276            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
277            register,
278            I2CSMBusSize::I2C_SMBUS_BYTE_DATA,
279            &mut data,
280        )
281    }
282}
283
284#[inline]
285pub fn i2c_smbus_read_word_data(fd: RawFd, register: u8) -> Result<u16, I2CError> {
286    let mut data = i2c_smbus_data::empty();
287    unsafe {
288        i2c_smbus_access(
289            fd,
290            I2CSMBusReadWrite::I2C_SMBUS_READ,
291            register,
292            I2CSMBusSize::I2C_SMBUS_WORD_DATA,
293            &mut data,
294        )?;
295    };
296
297    Ok(Cursor::new(&data.block[..])
298        .read_u16::<NativeEndian>()
299        .unwrap())
300}
301
302#[inline]
303pub fn i2c_smbus_write_word_data(fd: RawFd, register: u8, value: u16) -> Result<(), I2CError> {
304    let mut data = i2c_smbus_data::empty();
305    Cursor::new(&mut data.block[..])
306        .write_u16::<NativeEndian>(value)
307        .unwrap();
308
309    unsafe {
310        i2c_smbus_access(
311            fd,
312            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
313            register,
314            I2CSMBusSize::I2C_SMBUS_WORD_DATA,
315            &mut data,
316        )
317    }
318}
319
320#[inline]
321pub fn i2c_smbus_process_call(fd: RawFd, register: u8, value: u16) -> Result<u16, I2CError> {
322    let mut data = i2c_smbus_data::empty();
323    Cursor::new(&mut data.block[..])
324        .write_u16::<NativeEndian>(value)
325        .unwrap();
326
327    unsafe {
328        i2c_smbus_access(
329            fd,
330            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
331            register,
332            I2CSMBusSize::I2C_SMBUS_PROC_CALL,
333            &mut data,
334        )?;
335    }
336    Ok(Cursor::new(&data.block[..])
337        .read_u16::<NativeEndian>()
338        .unwrap())
339}
340
341#[inline]
342pub fn i2c_smbus_read_block_data(fd: RawFd, register: u8) -> Result<Vec<u8>, I2CError> {
343    let mut data = i2c_smbus_data::empty();
344    unsafe {
345        i2c_smbus_access(
346            fd,
347            I2CSMBusReadWrite::I2C_SMBUS_READ,
348            register,
349            I2CSMBusSize::I2C_SMBUS_BLOCK_DATA,
350            &mut data,
351        )?;
352    }
353
354    // create a vector from the data in the block starting at byte
355    // 1 and ending after count bytes after that
356    let count = data.block[0];
357    Ok((data.block[1..(count + 1) as usize]).to_vec())
358}
359
360pub fn i2c_smbus_read_i2c_block_data(
361    fd: RawFd,
362    register: u8,
363    len: u8,
364) -> Result<Vec<u8>, I2CError> {
365    let mut data = i2c_smbus_data::empty();
366    data.block[0] = len;
367    unsafe {
368        i2c_smbus_access(
369            fd,
370            I2CSMBusReadWrite::I2C_SMBUS_READ,
371            register,
372            I2CSMBusSize::I2C_SMBUS_I2C_BLOCK_DATA,
373            &mut data,
374        )?;
375    }
376
377    // create a vector from the data in the block starting at byte
378    // 1 and ending after count bytes after that
379    let count = data.block[0];
380    Ok((data.block[1..(count + 1) as usize]).to_vec())
381}
382
383#[inline]
384fn copy_to_i2c_block_data(values: &[u8], max_size: usize) -> i2c_smbus_data {
385    let mut data = i2c_smbus_data::empty();
386    let len: usize = if values.len() > max_size {
387        max_size
388    } else {
389        values.len()
390    };
391    data.block[0] = len as u8;
392    data.block[1..=len].copy_from_slice(&values[..len]);
393    data
394}
395
396#[inline]
397pub fn i2c_smbus_write_block_data(fd: RawFd, register: u8, values: &[u8]) -> Result<(), I2CError> {
398    let mut data = copy_to_i2c_block_data(values, 32);
399    unsafe {
400        i2c_smbus_access(
401            fd,
402            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
403            register,
404            I2CSMBusSize::I2C_SMBUS_BLOCK_DATA,
405            &mut data,
406        )
407    }
408}
409
410#[inline]
411pub fn i2c_smbus_write_i2c_block_data(
412    fd: RawFd,
413    register: u8,
414    values: &[u8],
415) -> Result<(), I2CError> {
416    let mut data = copy_to_i2c_block_data(values, 32);
417    unsafe {
418        i2c_smbus_access(
419            fd,
420            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
421            register,
422            I2CSMBusSize::I2C_SMBUS_I2C_BLOCK_DATA,
423            &mut data,
424        )
425    }
426}
427
428#[inline]
429pub fn i2c_smbus_process_call_block(
430    fd: RawFd,
431    register: u8,
432    values: &[u8],
433) -> Result<Vec<u8>, I2CError> {
434    let mut data = copy_to_i2c_block_data(values, 31);
435    unsafe {
436        i2c_smbus_access(
437            fd,
438            I2CSMBusReadWrite::I2C_SMBUS_WRITE,
439            register,
440            I2CSMBusSize::I2C_SMBUS_BLOCK_PROC_CALL,
441            &mut data,
442        )?;
443    };
444
445    // create a vector from the data in the block starting at byte
446    // 1 and ending after count bytes after that
447    let count = data.block[0];
448    Ok((data.block[1..(count + 1) as usize]).to_vec())
449}
450
451#[inline]
452pub fn i2c_rdwr(fd: RawFd, values: &mut [i2c_msg]) -> Result<u32, I2CError> {
453    let i2c_data = i2c_rdwr_ioctl_data {
454        msgs: values.as_mut_ptr(),
455        nmsgs: values.len() as u32,
456    };
457
458    let n;
459    unsafe {
460        n = ioctl::i2c_rdwr(fd, &i2c_data)?;
461    }
462    Ok(n as u32)
463}