// Copyright 2017 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland // www.source-code.biz, www.inventec.ch/chdh // // This module is multi-licensed and may be used under the terms of any of the following licenses: // // LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html // EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal // // Please contact the author if you need another license. // This module is provided "as is", without warranties of any kind. // // Home page: http://www.source-code.biz/snippets/java/I2cBusDriver package biz.source_code.utils; import com.sun.jna.IntegerType; import com.sun.jna.Library; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.Pointer; import java.io.IOException; import java.util.Arrays; // An I2C bus API class for Linux / Android. public class I2cBusDriver { private static final int ioBufSize = 64; private int fileHandle; private Memory ioBuf; private int currentSlaveAddr; public I2cBusDriver (String busNoOrFileName) throws IOException { staticInit(); fileHandle = -1; currentSlaveAddr = -1; String fileName = (busNoOrFileName.length() > 2) ? busNoOrFileName : "/dev/i2c-" + busNoOrFileName; fileHandle = libc.open(fileName, LibcDefs.O_RDWR); if (fileHandle == -1) { throw new IOException("Open of \"" + fileName + "\" failed, errno=" + Native.getLastError() + "."); } ioBuf = new Memory(ioBufSize); } public void close() throws IOException { if (fileHandle != -1) { int rc = libc.close(fileHandle); fileHandle = -1; if (rc != 0) { throw new IOException("Close failed, errno=" + Native.getLastError() + "."); }}} private void verifyIsOpen() { if (fileHandle == -1) { throw new RuntimeException("The I2C bus driver is closed."); }} private void setSlaveAddr (int slaveAddr) throws IOException { if (currentSlaveAddr == slaveAddr) { return; } int rc = libc.ioctl(fileHandle, LibcDefs.I2C_SLAVE, new Pointer(slaveAddr)); if (rc == -1) { throw new IOException("IOCTL for I2C_SLAVE failed, errno=" + Native.getLastError() + "."); } currentSlaveAddr = slaveAddr; } // Sends data bytes to an I2C device. // Returns the number of bytes written or -1 on error. public int send (int slaveAddr, byte[] buf, int len) throws IOException { verifyIsOpen(); if (len > ioBufSize) { throw new IOException("Internal i/o buffer too small."); } if (len <= 0) { return 0; } setSlaveAddr(slaveAddr); ioBuf.write(0, buf, 0, len); NativeSizeT trLen0 = libc.write(fileHandle, ioBuf, new NativeSizeT(len)); return trLen0.intValue(); } // Receives data bytes from an I2C device. // Returns the number of bytes read or -1 on error. public int receive (int slaveAddr, byte[] buf, int len) throws IOException { verifyIsOpen(); if (len > ioBufSize) { throw new IOException("Internal i/o buffer too small."); } if (len <= 0) { return 0; } setSlaveAddr(slaveAddr); NativeSizeT trLen0 = libc.read(fileHandle, ioBuf, new NativeSizeT(len)); int trLen = trLen0.intValue(); if (trLen <= 0) { return trLen; } ioBuf.read(0, buf, 0, trLen); return trLen; } // Sends a 0 byte to probe the presence of a device at a specific slave address. // Returns true if an I2C device responds at the specified slave address. public boolean probeSlaveAddress (int slaveAddr) throws IOException { byte[] buf = new byte[1]; int i = send(slaveAddr, buf, 1); return i == 1; } // Sets multiple contiguous registers of an I2C device. public void setRegs (int slaveAddr, int regAddr, byte[] regValues) throws IOException { if (regValues.length == 0) { return; } byte[] buf = new byte[regValues.length + 1]; buf[0] = (byte)regAddr; System.arraycopy(regValues, 0, buf, 1, regValues.length); int trLen = send(slaveAddr, buf, buf.length); if (trLen == -1) { throw new IOException("I2C register output failed, errno=" + Native.getLastError() + "."); } if (trLen != buf.length) { throw new IOException("I2C register output failed, bufLen=" + buf.length + ", trLen=" + trLen + "."); }} // Sets a single register of an I2C device. public void setReg (int slaveAddr, int regAddr, int regValue) throws IOException { byte[] regValues = new byte[]{(byte)regValue}; setRegs(slaveAddr, regAddr, regValues); } // Retrieves the values of multiple contiguous registers of an I2C device. public byte[] getRegs (int slaveAddr, int regAddr, int len) throws IOException { byte[] txBuf = new byte[]{(byte)regAddr}; int trLen = send(slaveAddr, txBuf, 1); if (trLen == -1) { throw new IOException("I2C register input failed while sending register address, errno=" + Native.getLastError() + "."); } if (trLen != 1) { throw new IOException("I2C register input failed while sending register address, trLen=" + trLen + "."); } byte[] buf = new byte[len]; trLen = receive(slaveAddr, buf, len); if (trLen == -1) { throw new IOException("I2C register input failed, errno=" + Native.getLastError() + "."); } if (trLen != len) { throw new IOException("I2C register input failed, len=" + len + ", trLen=" + trLen + "."); } return buf; } // Retrieves the value of a single register of an I2C device. public int getReg (int slaveAddr, int regAddr) throws IOException { byte[] buf = getRegs(slaveAddr, regAddr, 1); return buf[0] & 0xFF; } //------------------------------------------------------------------------------ private static Libc libc; private static boolean staticInitDone; private static interface Libc extends Library { // fcntl.h int open(String path, int mode); // unistd.h int close (int fileHandle); int ioctl (int fileHandle, int request, Pointer addr); NativeSizeT read (int fileHandle, Pointer addr, NativeSizeT length); NativeSizeT write (int fileHandle, Pointer addr, NativeSizeT length); } private static class LibcDefs { // fcntl.h static final int O_RDWR = 00000002; // i2c.h static final int I2C_SLAVE = 0x0703; } public static class NativeSizeT extends IntegerType { // must be public for JNA public NativeSizeT() { this(0); } public NativeSizeT (long value) { super(Native.SIZE_T_SIZE, value); }} private static synchronized void staticInit() { if (staticInitDone) { return; } libc = (Libc)Native.loadLibrary("c", Libc.class); staticInitDone = true; } }