// I2C bus controller services using IO.Objects.SimpleIO // Copyright (C)2017-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. using System; namespace IO.Objects.SimpleIO.I2C { /// /// Encapsulates Linux I2C bus controllers using /// libsimpleio. /// public class Bus : IO.Interfaces.I2C.Bus { // If you have system with more than 64 I2C buses, my condolences. // You will need to increase the size of fdtable and rebuild the // library. private static int[] fdtable = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; private readonly int myfd; /// /// Constructor for a single I2C bus controller. /// /// I2 bus designator. public Bus(IO.Objects.SimpleIO.Device.Designator desg) { // Validate the I2C bus designator if (desg.Equals(IO.Objects.SimpleIO.Device.Designator.Unavailable)) { throw new Exception("Invalid designator"); } if (desg.chip != 0) { throw new Exception("Invalid designator"); } // Unlike almost any Linux I/O resource, an I2C bus can be shared // among two or more I2C slave devices. We save open file // descriptors in fdtable so we can reuse them if the program // attempts to create multiple instances of the same I2C bus. // An important use case is a mikroBUS shield with multiple sockets // all sharing the same I2C bus. We want to avoid opening a new // I2C bus file descriptor for each socket. if (desg.chan > fdtable.Length - 1) { throw new Exception("Too many I2C buses"); } string devname = String.Format("/dev/i2c-{0}", desg.chan); System.Threading.Monitor.Enter(fdtable); // Resuse an existing open file descriptor, if possible if (fdtable[desg.chan] >= 0) { this.myfd = fdtable[desg.chan]; } else { IO.Bindings.libsimpleio.I2C_open(devname, out this.myfd, out int error); if (error != 0) { System.Threading.Monitor.Exit(fdtable); throw new Exception("I2C_open() failed, " + errno.strerror(error)); } // Save the new open file descriptor to fdtable, if possible fdtable[desg.chan] = this.myfd; } System.Threading.Monitor.Exit(fdtable); } /// /// Read bytes from an I2C device. /// /// Slave device address. /// Response buffer. /// Number of bytes to read. public void Read(int slaveaddr, byte[] resp, int resplen) { if ((slaveaddr < 0) || (slaveaddr > 127)) { throw new Exception("Invalid slave address"); } if ((resplen < 0) || (resplen > resp.Length)) { throw new Exception("Invalid response length"); } IO.Bindings.libsimpleio.I2C_transaction(this.myfd, slaveaddr, null, 0, resp, resplen, out int error); if (error != 0) { throw new Exception("I2C_transaction() failed, " + errno.strerror(error)); } } /// /// Write bytes to an I2C device. /// /// Slave device address. /// Command buffer. /// Number of bytes to write. public void Write(int slaveaddr, byte[] cmd, int cmdlen) { if ((slaveaddr < 0) || (slaveaddr > 127)) { throw new Exception("Invalid slave address"); } if ((cmdlen < 0) || (cmdlen > cmd.Length)) { throw new Exception("Invalid command length"); } IO.Bindings.libsimpleio.I2C_transaction(this.myfd, slaveaddr, cmd, cmdlen, null, 0, out int error); if (error != 0) { throw new Exception("I2C_transaction() failed, " + errno.strerror(error)); } } /// /// Write and receive bytes to/from an I2C device. /// /// Device slave address. /// Command buffer. /// Number of bytes to write. /// Response buffer. /// Number of bytes to read. /// Delay in microseconds between the I2C /// write and read cycles. Allowed values are 0 to 65535 microseconds. public void Transaction(int slaveaddr, byte[] cmd, int cmdlen, byte[] resp, int resplen, int delayus = 0) { // Validate parameters if ((slaveaddr < 0) || (slaveaddr > 127)) throw new Exception("Invalid I2C slave address parameter"); if ((cmd == null) && (resp == null)) throw new Exception("Command buffer and response buffer are both null"); if ((cmdlen == 0) && (resplen == 0)) throw new Exception("Command length and response length are both zero"); if ((cmd == null) && (cmdlen != 0)) throw new Exception("Command buffer is null but command length is nonzero"); if ((cmd != null) && (cmdlen == 0)) throw new Exception("Command buffer is not null but command length is zero"); if ((resp == null) && (resplen != 0)) throw new Exception("Response buffer is null but response length is nonzero"); if ((resp != null) && (resplen == 0)) throw new Exception("Response buffer is not null but response length is zero"); if (cmd != null) if ((cmdlen < 1) || (cmdlen > 56) || (cmd.Length < cmdlen)) throw new Exception("Invalid command length parameter"); if (resp != null) if ((resplen < 1) || (resplen > 60) || (resp.Length < resplen)) throw new Exception("Invalid response length parameter"); if ((delayus < 0) || (delayus > 65535)) throw new Exception("Invalid delay parameter"); int error; if (delayus == 0) { IO.Bindings.libsimpleio.I2C_transaction(this.myfd, slaveaddr, cmd, cmdlen, resp, resplen, out error); if (error != 0) { throw new Exception("I2C_transaction() failed, " + errno.strerror(error)); } } else { IO.Bindings.libsimpleio.I2C_transaction(this.myfd, slaveaddr, cmd, cmdlen, null, 0, out error); if (error != 0) { throw new Exception("I2C_transaction() failed, " + errno.strerror(error)); } IO.Bindings.libsimpleio.LINUX_usleep(delayus, out error); if (error != 0) { throw new Exception("LINUX_usleep() failed, " + errno.strerror(error)); } IO.Bindings.libsimpleio.I2C_transaction(this.myfd, slaveaddr, null, 0, resp, resplen, out error); if (error != 0) { throw new Exception("I2C_transaction() failed, " + errno.strerror(error)); } } } /// /// Read-only property returning the Linux file descriptor for the /// I2C bus controller. /// public int fd { get { return this.myfd; } } } }