1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
//! The I2C module. //! //! The BeagleBone Black has 2 I2C interfaces available (I2C1 and I2C2). //! These can be enabled by using the bone_capemgr module. //! //! As an example, use the following command to enable UART4: //! `sudo sh -c "echo 'BB-I2C1' > /sys/devices/platform/bone_capemgr/slots"` //! This command works for recent kernel versions. //! If you wish to enable another I2C, substitute its number for 1 in the //! command above. use errors::*; use std::fs::{File, OpenOptions}; use std::mem; use std::os::unix::io::AsRawFd; /// Magic I2C numbers const I2C_SMBUS_BLOCK_MAX: u8 = 32; const I2C_SLAVE: u16 = 0x0703; const I2C_SMBUS: u16 = 0x0720; /// Where we store data that's read off an I2C device // TODO: switch to an untagged union once that feature is released. // GitHub issue: https://github.com/rust-lang/rust/issues/32836. #[repr(C)] struct i2c_data { block: [u8; (I2C_SMBUS_BLOCK_MAX + 2) as usize], } /// The structure used by i2c_call to interact with the i2cdev system. #[repr(C)] struct i2c_ioctl_data { read_write: u8, // __u8 read_write; command: u8, // __u8 command; size: u32, // __u32 size; data: *mut i2c_data, // union i2c_smbus_data __user *data; } // These macros expand to the nice IOCTL wrapper functions needed to work with // the i2cdev system. ioctl!(ioctl_set_i2c_slave_addr with I2C_SLAVE); ioctl!(ioctl_i2c_smbus with I2C_SMBUS); /// Represents and I2C interface. #[derive(Debug)] pub struct I2C { i2c_num: u8, i2c_file: File, } impl I2C { /// Creates a new I2C device. /// /// # Examples /// /// ```no_run /// use libbeaglebone::prelude::*; /// /// // Create a new I2C interface using BB_I2C1. /// // Don't forget to enable the I2C beforehand using bone_capemgr. /// // Consult the module documentation for more information! /// let i2c = I2C::new(1).unwrap(); /// ``` /// /// # Errors /// /// Method fails if `i2c_num` is an invalid I2C port (i.e. isn't within 1-2) /// or if the kernel fails to open the port for some other reason. pub fn new(i2c_num: u8) -> Result<(I2C)> { let i2c_file_path = format!("/dev/i2c-{}", i2c_num); Ok(I2C { i2c_num: i2c_num, i2c_file: OpenOptions::new() .read(true) .write(true) .open(i2c_file_path) .chain_err(|| format!("Failed to create new I2C #{}.", i2c_num))?, }) } /// Sets the address of the I2C slave device. /// /// # Examples /// /// ```no_run /// use libbeaglebone::prelude::*; /// /// // Create a new I2C interface using BB_I2C1. /// let i2c = I2C::new(1).unwrap(); /// /// // Set the slave address to 0x45. /// i2c.set_slave_address(0x45).unwrap(); /// ``` /// /// # Errors /// /// Fails if the kernel is unable to set the slave device address to the /// chosen value. pub fn set_slave_address(&self, slave_addr: u16) -> Result<()> { unsafe { let _ = ioctl_set_i2c_slave_addr(self.i2c_file.as_raw_fd(), slave_addr as *mut u8) .chain_err(|| format!("Failed to set I2C slave device address to {}.", slave_addr))?; Ok(()) } } /// Writes a single byte to an I2C slave. /// /// # Examples /// /// ```no_run /// use libbeaglebone::prelude::*; /// /// // Create a new I2C interface using BB_I2C1. /// let i2c = I2C::new(1).unwrap(); /// /// // Set the slave address to 0x45. /// i2c.set_slave_address(0x45).unwrap(); /// /// // Write a 1 to the I2C slave /// i2c.write(1).unwrap(); /// ``` /// /// # Errors /// /// Fails if the kernel is unable to write the chosen value to the device. pub fn write(&self, data: u8) -> Result<()> { unsafe { self.i2c_call(0 /* 0 => write */, data, 1, ::std::ptr::null_mut()) .chain_err(|| format!("Failed to write {} to the I2C device.", data))?; Ok(()) } } /// Reads a single byte from an I2C slave and returns it. /// /// # Examples /// /// ```no_run /// use libbeaglebone::prelude::*; /// /// // Create a new I2C interface using BB_I2C1. /// let mut i2c = I2C::new(1).unwrap(); /// /// // Set the slave address to 0x45. /// i2c.set_slave_address(0x45).unwrap(); /// /// // Read some data from the I2C device and display it. /// println!("Read {} from the I2C slave!", i2c.read().unwrap()); /// ``` /// /// # Errors /// /// Fails if the kernel is unable to read from the device. pub fn read(&mut self) -> Result<(u8)> { let mut data = i2c_data { block: unsafe { mem::zeroed() } }; unsafe { // TODO: fix this ugly temporary hack to avoid the "unused result" warning let _ = self.i2c_call(1, 0, 1, &mut data) .chain_err(|| "Failed to read from the I2C device.")?; }; Ok(data.block[0]) } /// Wrapper function around the IOCTL FFI that does all of the IOCTL dirty /// work. unsafe fn i2c_call(&self, read_write: u8, command: u8, // can be address or something else size: u32, data: *mut i2c_data) -> Result<()> { let mut args = i2c_ioctl_data { read_write: read_write, command: command, size: size, data: data, }; // Transmuting is ugly, but it's needed to remove all of the type information // that Rust keeps in the struct. let p_args: *mut u8 = mem::transmute(&mut args); let _ = ioctl_i2c_smbus(self.i2c_file.as_raw_fd(), p_args) .chain_err(|| "failed to write to i2c bus")?; Ok(()) } }