changeset 250:ad12ae44e850

DIO-10:[I2C] Impl combined message support for Linux Summary: This change is adding combined i2c transactions support and resoves a number of related issues that revealed on the fix applied: https://bugs.openjdk.java.net/browse/DIO-10 [I2C] Impl combined message support for Linux https://bugs.openjdk.java.net/browse/DIO-12 [I2C] 10-bit i2c addressing is not supported https://bugs.openjdk.java.net/browse/DIO-13 [I2C] wrong handling of clock frequency https://bugs.openjdk.java.net/browse/DIO-14 [I2C] impossible to communicate to two slaves simultaneously (raspberry pi) https://bugs.openjdk.java.net/browse/DIO-15 Unable to simply skip some bytes. Unable to simply skip some bytes. Reviewed-by:snazarki Contributed-by:bkvartsk
author onazarkina
date Mon, 29 Feb 2016 19:06:11 +0300
parents 2ebc0471aa02
children 19eec3548675
files src/share/classes/com/oracle/dio/i2cbus/impl/I2CCombinedMessage.java src/share/classes/com/oracle/dio/i2cbus/impl/I2CSlaveImpl.java src/share/linux/native/com/oracle/dio/i2c/i2c.c
diffstat 3 files changed, 495 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/oracle/dio/i2cbus/impl/I2CCombinedMessage.java	Thu Feb 11 13:23:23 2016 +0300
+++ b/src/share/classes/com/oracle/dio/i2cbus/impl/I2CCombinedMessage.java	Mon Feb 29 19:06:11 2016 +0300
@@ -28,6 +28,8 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 import com.oracle.dio.utils.ExceptionMessage;
 import com.oracle.dio.utils.Logging;
@@ -91,7 +93,7 @@
             }
 
             // null check and mark for stopCondition.
-            if (message.buf.remaining() != 0) {
+            if (message.buf.remaining() != 0  || message.skip > 0) {
                 boolean alreadyAppended = false;
                 for (Message<I2CSlaveImpl> already : messageList) {
                     if (already.buf == message.buf) {
@@ -225,21 +227,29 @@
             } else {
                 int flag = I2CSlaveImpl.I2C_COMBINED_START;
                 Logging.reportInformation(ExceptionMessage.format(ExceptionMessage.I2CBUS_FIRST_MESSAGE));
+                Set<I2CSlaveImpl> slavesInUse = new HashSet<I2CSlaveImpl>();
+
                 for (int i = 0; i < messageList.size(); i++) {
                     Message message = messageList.get(i);
+                    I2CSlaveImpl slave = (I2CSlaveImpl) message.device;
+                    slavesInUse.add(slave);
                     int skip = (message.isRx) ? message.skip : -1;
                     if (i == stopConditionIndex) {
                         Logging.reportInformation(ExceptionMessage.format(ExceptionMessage.I2CBUS_LAST_MESSAGE));
                         flag = I2CSlaveImpl.I2C_COMBINED_END;
                     }
-                    int res = ((I2CSlaveImpl) message.device).transfer(flag, skip, message.buf);
+
+                    int res = slave.transfer(flag, skip, message.buf);
                     if (message.isRx) {
                         bytesRead[bytesReadIdx++] = res;
                     }
                     flag = I2CSlaveImpl.I2C_COMBINED_BODY;
                 }
+
+                for (I2CSlaveImpl usedSlave : slavesInUse) {
+                    usedSlave.applyDelayedConversions();
+                }
             }
-
         }
         return bytesRead;
     }
--- a/src/share/classes/com/oracle/dio/i2cbus/impl/I2CSlaveImpl.java	Thu Feb 11 13:23:23 2016 +0300
+++ b/src/share/classes/com/oracle/dio/i2cbus/impl/I2CSlaveImpl.java	Mon Feb 29 19:06:11 2016 +0300
@@ -28,6 +28,8 @@
 import java.io.IOException;
 import java.nio.*;
 import java.security.AccessController;
+import java.util.LinkedList;
+import java.util.List;
 
 import com.oracle.dio.power.impl.PowerManagedBase;
 import com.oracle.dio.utils.Constants;
@@ -58,7 +60,7 @@
 
         if (cfg.getControllerName() != null) {
             throw new InvalidDeviceConfigException(
-                ExceptionMessage.format(ExceptionMessage.DEVICE_OPEN_WITH_DEVICENAME_UNSUPPORTED)
+                    ExceptionMessage.format(ExceptionMessage.DEVICE_OPEN_WITH_DEVICENAME_UNSUPPORTED)
             );
         }
 
@@ -69,24 +71,24 @@
 
     public Bus getBus() throws IOException {
         return new Bus() {
-                public jdk.dio.i2cbus.I2CCombinedMessage createCombinedMessage() {
-                    return new I2CCombinedMessage();
-                }
-            };
+            public jdk.dio.i2cbus.I2CCombinedMessage createCombinedMessage() {
+                return new I2CCombinedMessage();
+            }
+        };
     }
 
     private String getSecurityName() {
         I2CDeviceConfig cfg = dscr.getConfiguration();
         String securityName = (DeviceConfig.DEFAULT == cfg.getControllerNumber()) ?
-            "" : String.valueOf(cfg.getControllerNumber());
+                "" : String.valueOf(cfg.getControllerNumber());
         securityName = (DeviceConfig.DEFAULT == cfg.getAddress()) ?
-            securityName : securityName + ":" + cfg.getAddress();
+                securityName : securityName + ":" + cfg.getAddress();
         return securityName;
     }
 
     protected void checkPowerPermission() {
         AccessController.checkPermission(new I2CPermission(getSecurityName(),
-                                         DevicePermission.POWER_MANAGE));
+                DevicePermission.POWER_MANAGE));
     }
 
     private void doCheck(int skip, ByteBuffer buf) {
@@ -110,7 +112,7 @@
     }
 
     @Override
-    public  int read() throws IOException,
+    public int read() throws IOException,
             UnavailableDeviceException, ClosedDeviceException {
         ByteBuffer dst = ByteBuffer.allocateDirect(1);
         read(dst);
@@ -118,13 +120,13 @@
     }
 
     @Override
-    public  int read(ByteBuffer dst) throws IOException,
+    public int read(ByteBuffer dst) throws IOException,
             UnavailableDeviceException, ClosedDeviceException {
         return read(0, dst);
     }
 
     @Override
-    public  int read(int skip, ByteBuffer dst) throws IOException,
+    public int read(int skip, ByteBuffer dst) throws IOException,
             UnavailableDeviceException, ClosedDeviceException {
 
         doCheck(skip, dst);
@@ -133,15 +135,15 @@
     }
 
     @Override
-    public  int read(int subaddress, int subaddressSize,
-            ByteBuffer dst) throws IOException, UnavailableDeviceException,
+    public int read(int subaddress, int subaddressSize,
+                    ByteBuffer dst) throws IOException, UnavailableDeviceException,
             ClosedDeviceException {
         return read(subaddress, subaddressSize, 0, dst);
     }
 
     @Override
-    public  int read(int subaddress, int subaddressSize, int skip,
-            ByteBuffer dst) throws IOException, UnavailableDeviceException,
+    public int read(int subaddress, int subaddressSize, int skip,
+                    ByteBuffer dst) throws IOException, UnavailableDeviceException,
             ClosedDeviceException {
 
         if (subaddressSize <= 0 || subaddressSize > 4 || subaddress < 0)
@@ -152,7 +154,7 @@
         ByteBuffer tmp = ByteBuffer.wrap(new byte[4/*sizeof(int)*/]);
         tmp.order(ByteOrder.BIG_ENDIAN);
         tmp.putInt(subaddress);
-        tmp.position(4-subaddressSize);
+        tmp.position(4 - subaddressSize);
         I2CCombinedMessage msg = new I2CCombinedMessage();
         msg.appendWrite(this, tmp);
         msg.appendRead(this, skip, dst);
@@ -171,14 +173,14 @@
     public void write(int srcData) throws IOException,
             UnavailableDeviceException, ClosedDeviceException {
         ByteBuffer dst = ByteBuffer.allocateDirect(1);
-        dst.put((byte)srcData);
+        dst.put((byte) srcData);
         dst.flip();
         write(dst);
     }
 
     @Override
     public int write(int subaddress, int subaddressSize,
-            ByteBuffer src) throws IOException, UnavailableDeviceException,
+                     ByteBuffer src) throws IOException, UnavailableDeviceException,
             ClosedDeviceException {
         boolean callEnd = false;
 
@@ -192,29 +194,24 @@
         tmp.order(ByteOrder.BIG_ENDIAN);
         tmp.putInt(subaddress);
         tmp.put(src);
-        tmp.position(4-subaddressSize);
+        tmp.position(4 - subaddressSize);
         return write(tmp) - subaddressSize;
     }
 
     /**
      * @throws IllegalStateException if the bus is occupied for
-     *        communication with other peripheral or with other
-     *        transaction
+     *                               communication with other peripheral or with other
+     *                               transaction
      */
-    int transfer(int flag, int skip, ByteBuffer buf)  throws
+    int transfer(int flag, int skip, ByteBuffer appBuffer) throws
             UnavailableDeviceException, ClosedDeviceException, IOException {
         int ret = 0;
-        ByteBuffer toSend;
 
-        if (!buf.hasRemaining()) {
+        if (!appBuffer.hasRemaining() && skip <= 0) {
             return 0;
         }
 
-        if (skip > 0) {
-            toSend = ByteBuffer.allocateDirect(buf.remaining() + skip);
-        } else {
-            toSend = convert(buf, null);
-        }
+        ByteBuffer direct = toDirect(skip, appBuffer);
 
         checkPowerState();
         // if driver supports combined message
@@ -222,21 +219,64 @@
         // immediately if single shot is trying to interrupt ongoing I2C combined
         // message.
         // if not, then it is pipelining transfer request.
-        synchronized(handle) {
+        synchronized (handle) {
             try {
                 conditionalLock();
-                ret = transfer0(skip < 0, toSend, flag);
+                ret = transfer0(skip < 0, direct, flag);
             } finally {
                 conditionalUnlock();
             }
         }
 
-        toSend.limit(ret);
+        direct.limit(ret);
 
         if (skip > 0) {
-            if (ret > skip) {
+            ret = (ret > skip) ? ret - skip : 0;
+        }
+
+        BuffersMap conversion = new BuffersMap(skip, appBuffer, ret, direct);
+
+        return flag == I2CSlaveImpl.I2C_REGULAR ?
+                restoreFromDirect(conversion) :
+                addDelayedConversion(conversion);
+    }
+
+    static class BuffersMap {
+        int skip, ret;
+        ByteBuffer direct, nonDirect;
+
+        BuffersMap(int skip, ByteBuffer nonDirect, int ret, ByteBuffer direct) {
+            this.skip = skip;
+            this.ret = ret;
+            this.direct = direct;
+            this.nonDirect = nonDirect;
+        }
+    }
+
+    private List<BuffersMap> delayedConversions = new LinkedList<BuffersMap>();
+
+    private int addDelayedConversion(BuffersMap conversion) {
+        delayedConversions.add(conversion);
+        return conversion.ret;
+    }
+
+    void applyDelayedConversions() {
+        for (BuffersMap delayedConversion : delayedConversions) {
+            restoreFromDirect(delayedConversion);
+        }
+
+        delayedConversions.clear();
+    }
+
+    private int restoreFromDirect(BuffersMap conversion) {
+        ByteBuffer toSend = conversion.direct;
+        int ret = conversion.ret;
+        int skip = conversion.skip;
+        ByteBuffer buf = conversion.nonDirect;
+
+        if (skip > 0) {
+            if (ret > 0) {
                 // need to count only bytes transfered to recv buffer
-                ret -= skip;
                 toSend.position(skip);
                 try {
                     buf.put(toSend);
@@ -246,12 +286,10 @@
                     // position throws IllegalArgumentException
                     Logging.reportError(ExceptionMessage.format(ExceptionMessage.BUFFER_IS_MODIFIED));
                 }
-            } else {
-                ret = 0;
             }
         } else {
-            try{
-                // if write just update buffer postion
+            try {
+                // if write just update buffer position
                 if (skip < 0) {
                     buf.position(buf.position() + ret);
                 } else {
@@ -269,25 +307,35 @@
         return ret;
     }
 
+    private ByteBuffer toDirect(int skip, ByteBuffer buf) {
+        ByteBuffer toSend;
+        if (skip > 0) {
+            toSend = ByteBuffer.allocateDirect(buf.remaining() + skip);
+        } else {
+            toSend = convert(buf, null);
+        }
+        return toSend;
+    }
+
     protected synchronized int getGrpID() {
         return getGrpID0();
     }
 
     @Override
     public ByteBuffer prepareBuffer(ByteBuffer buffer, int size) throws IOException, ClosedDeviceException {
-        return (ByteBuffer)super.prepareBufferInt(buffer,size);
+        return (ByteBuffer) super.prepareBufferInt(buffer, size);
     }
 
     @Local(DontRemoveFields = {
-        "jdk.dio.i2cbus.I2CDeviceConfig.controllerNumber",
-        "jdk.dio.i2cbus.I2CDeviceConfig.clockFrequency",
-        "jdk.dio.i2cbus.I2CDeviceConfig.addressSize",
-        "jdk.dio.i2cbus.I2CDeviceConfig.address",
-        "com.oracle.dio.impl.Handle.unlock_func_ptr",
-        "com.oracle.dio.impl.Handle.native_handle",
-        "com.oracle.dio.impl.Handle.lock_func_ptr",
-        "com.oracle.dio.impl.Handle.close_func_ptr",
-        "com.oracle.dio.impl.AbstractPeripheral.handle",
+            "jdk.dio.i2cbus.I2CDeviceConfig.controllerNumber",
+            "jdk.dio.i2cbus.I2CDeviceConfig.clockFrequency",
+            "jdk.dio.i2cbus.I2CDeviceConfig.addressSize",
+            "jdk.dio.i2cbus.I2CDeviceConfig.address",
+            "com.oracle.dio.impl.Handle.unlock_func_ptr",
+            "com.oracle.dio.impl.Handle.native_handle",
+            "com.oracle.dio.impl.Handle.lock_func_ptr",
+            "com.oracle.dio.impl.Handle.close_func_ptr",
+            "com.oracle.dio.impl.AbstractPeripheral.handle",
     })
     private native void open0(Object config, boolean isExclusive);
 
@@ -301,23 +349,22 @@
      *              {@code false} for read
      * @param dst   the buffer that holds the data or to store data
      *              to.
-     *
      * @return number of bytes was transfered over the bus.
      */
     @Local(DontRemoveFields = {
-        "java.nio.Buffer.position",
-        "java.nio.Buffer.limit",
-        "java.nio.Buffer.flags",
-        "java.nio.Buffer.data",
-        "java.nio.Buffer.arrayOffset",
-        "com.oracle.dio.impl.Handle.native_handle",
-        "com.oracle.dio.impl.AbstractPeripheral.handle",
+            "java.nio.Buffer.position",
+            "java.nio.Buffer.limit",
+            "java.nio.Buffer.flags",
+            "java.nio.Buffer.data",
+            "java.nio.Buffer.arrayOffset",
+            "com.oracle.dio.impl.Handle.native_handle",
+            "com.oracle.dio.impl.AbstractPeripheral.handle",
     })
     private native int transfer0(boolean write, ByteBuffer dst, int flag);
 
     @Local(DontRemoveFields = {
-        "com.oracle.dio.impl.Handle.native_handle",
-        "com.oracle.dio.impl.AbstractPeripheral.handle",
+            "com.oracle.dio.impl.Handle.native_handle",
+            "com.oracle.dio.impl.AbstractPeripheral.handle",
     })
     private native int getGrpID0();
 }
--- a/src/share/linux/native/com/oracle/dio/i2c/i2c.c	Thu Feb 11 13:23:23 2016 +0300
+++ b/src/share/linux/native/com/oracle/dio/i2c/i2c.c	Mon Feb 29 19:06:11 2016 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
 #include <stdio.h>
 #include <errno.h>
 
+#include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 #include <pthread.h>
 
@@ -37,6 +38,44 @@
 #include "javacall_logging.h"
 #include "javacall_memory.h"
 
+
+#if ENABLE_DEBUG    
+    #define BUS_WR_LOCK(bus) \
+      JAVACALL_REPORT_ERROR3(JC_DIO, "going to wrlock on bus 0x%x (file %s, line %d) ", (bus),__FILE__,__LINE__); \
+      pthread_rwlock_wrlock(&(bus)->lock); \
+      JAVACALL_REPORT_ERROR3(JC_DIO, "set wrlock on bus 0x%x (file %s, line %d) ", (bus),__FILE__,__LINE__)
+
+    #define BUS_RD_LOCK(bus) \
+      JAVACALL_REPORT_ERROR3(JC_DIO, "going to rdlock on bus 0x%x (file %s, line %d) ", (bus),__FILE__,__LINE__); \
+      pthread_rwlock_rdlock(&(bus)->lock); \
+      JAVACALL_REPORT_ERROR3(JC_DIO, "set rdlock on bus 0x%x (file %s, line %d) ", (bus),__FILE__,__LINE__)
+
+    #define BUS_UNLOCK(bus) \
+      pthread_rwlock_unlock(&(bus)->lock); \
+      JAVACALL_REPORT_ERROR3(JC_DIO, "unlocked bus 0x%x (file %s, line %d) ", (bus),__FILE__,__LINE__)
+
+    #define BUSLIST_UNLOCK \
+      pthread_rwlock_unlock(&_i2c_buses_lock); \
+      JAVACALL_REPORT_ERROR2(JC_DIO, "unlocked bus list (file %s, line %d) ", __FILE__,__LINE__)
+
+    #define BUSLIST_WR_LOCK \
+      JAVACALL_REPORT_ERROR2(JC_DIO, "Going to wrlock bus list (file %s, line %d) ", __FILE__,__LINE__); \
+      pthread_rwlock_wrlock(&_i2c_buses_lock); \
+      JAVACALL_REPORT_ERROR2(JC_DIO, "Set wrlock on bus list (file %s, line %d) ", __FILE__,__LINE__)
+
+    #define BUSLIST_RD_LOCK \
+      JAVACALL_REPORT_ERROR2(JC_DIO, "Going to rdlock bus list (file %s, line %d) ", __FILE__,__LINE__); \
+      pthread_rwlock_rdlock(&_i2c_buses_lock); \
+      JAVACALL_REPORT_ERROR2(JC_DIO, "Set rdlock on bus list (file %s, line %d) ", __FILE__,__LINE__)
+#else
+    #define BUS_UNLOCK(bus) pthread_rwlock_unlock(&(bus)->lock)
+    #define BUS_WR_LOCK(bus) pthread_rwlock_wrlock(&(bus)->lock)
+    #define BUS_RD_LOCK(bus) pthread_rwlock_rdlock(&(bus)->lock)
+    #define BUSLIST_UNLOCK pthread_rwlock_unlock(&_i2c_buses_lock)
+    #define BUSLIST_WR_LOCK pthread_rwlock_wrlock(&_i2c_buses_lock)
+    #define BUSLIST_RD_LOCK pthread_rwlock_rdlock(&_i2c_buses_lock)
+#endif
+
 enum {
     READ = 0,
     WRITE = 1
@@ -44,93 +83,133 @@
 
 typedef struct {
     // I2C javacall handle to notify java thread
+    javacall_handle pBus;
     javacall_handle pDev;
-    // I2C device handle to avoid pDev dereferencing that may be closed at the end of native operation
-    int fd;
-    // operation type
-    javacall_int8 type;
-    // next data chunk len
-    javacall_int32 len;
-    // src/dst buffer pointer
-    javacall_int8* buf;
-    // temp buffer if any
-    javacall_int8 tmp[0];
+    struct i2c_rdwr_ioctl_data rdwr_data;
 } i2c_io_context;
 
 typedef struct i2c_bus_t i2c_bus;
-
 typedef struct i2c_slave_str i2c_slave;
+typedef struct i2c_frame_t i2c_frame;
+typedef struct i2c_msg i2c_message;
 
 struct i2c_bus_t {
     i2c_bus *next;
     unsigned int busId;
-    javacall_bool isBusy;
-    javacall_bool lastMessage;
+    //javacall_bool isBusy;
+    //javacall_bool lastMessage;
     /* list of opened I2C slaves on the bus */
     i2c_slave *slaves;
+    i2c_frame* currentTransaction;
+    int fd;
+    pthread_rwlock_t lock;
 };
 
 struct i2c_slave_str {
-    uint8_t address;  /* 7 bits long */
-    int fd;
+    long address;  /* 7 or 10 bits long */
+    uint8_t addressSize;  /* 7 or 10 */
+    long clockFrequency;
     pthread_t context;
     i2c_bus* bus;
     i2c_slave* next;
 };
 
-/* global list of opened I2C busses */
+struct i2c_frame_t {
+    i2c_frame* next;
+    i2c_message msg;
+};
+
+/* global list of opened I2C buses */
 static i2c_bus *_i2c_buses = NULL;
+static pthread_rwlock_t _i2c_buses_lock = PTHREAD_RWLOCK_INITIALIZER;
 
 static inline javacall_dio_result i2c_create_bus(unsigned int busId, i2c_bus** pBus) {
     i2c_bus *tmpBus;
 
     /* /dev/i2c-X file has been opened successfully,
      * created new 'i2c_bus' */
-    tmpBus = (i2c_bus*) javacall_malloc(sizeof(i2c_bus));
+    tmpBus = (i2c_bus*) javacall_calloc(1, sizeof(i2c_bus));
+
     if (tmpBus == NULL ) {
-        JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] cannot alloc i2c_bus");
+        JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Memory allocation error, i2c_create_bus");
         return JAVACALL_DIO_OUT_OF_MEMORY;
     }
 
     tmpBus->busId = busId;
     tmpBus->slaves = NULL;
-    tmpBus->isBusy = 0;
+    tmpBus->currentTransaction = NULL;
+
+    if (pthread_rwlock_init(&tmpBus->lock, NULL) != 0) {
+        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Unable to create a rwlock for bus number %d", busId);
+        javacall_free(tmpBus);
+        return JAVACALL_DIO_FAIL;
+    }
 
     /* Add newly created bus to 'g_i2c_buses' list */
+
     tmpBus->next = _i2c_buses;
     _i2c_buses = tmpBus;
+    *pBus = tmpBus;
 
-    *pBus = tmpBus;
     return JAVACALL_DIO_OK;
 }
 
 /* find the bus with a given id or create one if it's not exist */
 static inline javacall_dio_result i2c_get_bus(unsigned int busId, i2c_bus** pBus) {
+    BUSLIST_WR_LOCK;
+    i2c_bus *bus = _i2c_buses;
 
-    i2c_bus *bus = _i2c_buses;
     while(bus) {
         if (bus->busId == busId) {
             *pBus = bus;
+            BUSLIST_UNLOCK;
             return JAVACALL_DIO_OK;
         }
         bus = bus->next;
     }
 
     /* No currently opened i2c bus with 'busId' id. Create the new one */
-    return i2c_create_bus(busId, pBus);
+    javacall_dio_result rv =  i2c_create_bus(busId, pBus);
+    BUSLIST_UNLOCK;
+
+    return rv;
+}
+
+static javacall_dio_result close_i2c_bus_file(i2c_bus * bus) {
+   int rv;
+   
+   JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] Closing bus file /dev/i2c-%d", bus->busId);
+   /* If i2c device fd is opened */
+    if (bus->fd >= 0) {
+        /* close device fd */
+        while ((rv = close(bus->fd)) < 0 && EINTR == errno);
+
+        if (rv != 0)
+            JAVACALL_REPORT_ERROR2(JC_DIO, "[I2C] Could not close bus file, errno=%d (%s)", errno, strerror(errno));
+    }
+
+    return (rv == 0)?
+          JAVACALL_DIO_OK:
+          JAVACALL_DIO_FAIL;
 }
 
 /* release bus if it has no opened slaves */
 static javacall_dio_result i2c_release_bus(i2c_bus* bus) {
-
     i2c_bus *busPrev, *busCur;
 
-    if (NULL != bus->slaves)
+    BUS_WR_LOCK(bus);
+
+    if (NULL != bus->slaves) {
+        JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] There is no need to close bus file /dev/i2c-%d, there are open slaves in use", bus->busId);
+        BUS_UNLOCK(bus);
         return JAVACALL_DIO_OK;
+    }
 
     /* find the previous bus in the bus' list */
+    BUSLIST_WR_LOCK;
     busPrev = NULL;
     busCur = _i2c_buses;
+
     while(busCur) {
         /* Check if it's a bus to remove */
         if (busCur == bus) {
@@ -140,8 +219,18 @@
             } else {
                 busPrev->next = bus->next;
             }
+
+            BUSLIST_UNLOCK;
+
+            javacall_dio_result rv = close_i2c_bus_file(bus);
+            BUS_UNLOCK(bus);
+
+            if (pthread_rwlock_destroy(&bus->lock) != 0) {
+                JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] unable to destroy bus access rwlock, bus id %d", bus->busId);
+            }
+
             javacall_free(bus);
-            return JAVACALL_DIO_OK;
+            return rv;
         }
         busPrev = busCur;
         busCur = busCur->next;
@@ -149,25 +238,53 @@
 
     JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] inconsistency in bus list, failed to release %d", bus->busId);
 
-    /* 'bus' bus hasn't been found */
+    BUSLIST_UNLOCK;
+    BUS_UNLOCK(bus);    
     return JAVACALL_DIO_FAIL;
 }
 
+
+static inline javacall_dio_result open_setup_i2c_bus_file(i2c_slave* slave, unsigned int busId) {
+    if (slave->next != NULL)  {
+        JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] There is no need to open bus file /dev/i2c-%d, it is already open", busId);
+    }
+
+    JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] Opening bus file /dev/i2c-%d", busId);
+    char filename[20];
+
+    snprintf(filename, 19, "/dev/i2c-%d", busId);
+    restore_privileges();
+    slave->bus->fd = open(filename, O_RDWR);
+    drop_privileges();
+   
+    if (slave->bus->fd < 0) {
+        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Could not open bus file %s ", filename);
+        return JAVACALL_DIO_FAIL;
+    }
+    
+    return JAVACALL_DIO_OK;
+}
+
+
 static inline javacall_dio_result i2c_attach_slave_to_bus(i2c_slave *slave, unsigned int busId) {
     javacall_dio_result rv;
     i2c_bus *bus;
     i2c_slave *tmpSlave;
 
-    /* get bus descriptor */
-    if (JAVACALL_DIO_OK != (rv = i2c_get_bus(busId, &bus))) {
+    /* get bus descriptor */    
+    rv = i2c_get_bus(busId, &bus);
+
+    if (JAVACALL_DIO_OK != rv) {
         return rv;
     }
 
+    BUS_WR_LOCK(bus);
+
     /* check if a slave with the same address is already opened */
     tmpSlave = bus->slaves;
     while (tmpSlave) {
         if (tmpSlave->address == slave->address) {
-            i2c_release_bus(bus);
+            BUS_UNLOCK(bus);
             return JAVACALL_DIO_BUSY;
         }
         tmpSlave = tmpSlave->next;
@@ -177,8 +294,11 @@
     slave->bus = bus;
     slave->next = bus->slaves;
     bus->slaves = slave;
-
-    return JAVACALL_DIO_OK;
+    
+    /*  open bus file is needed */
+    javacall_dio_result ret =  open_setup_i2c_bus_file(slave, busId);
+    BUS_UNLOCK(bus);
+    return ret;
 }
 
 static inline javacall_dio_result i2c_detach_slave_from_bus(i2c_slave *slave) {
@@ -186,6 +306,8 @@
 
     /* find the previous slave in the slave's list */
     slavePrev = NULL;
+
+    BUS_WR_LOCK(slave->bus);
     slaveCur = slave->bus->slaves;
     while(slaveCur) {
         if (slaveCur == slave) {
@@ -197,19 +319,21 @@
 
     /* Check if there are no given slave in the list */
     if (NULL == slaveCur) {
-        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] inconsistency in slave list, failed to release %d", slave->address);
-        javacall_free(slave);
+        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Inconsistency in the list of slaves, failed to release slave with address %d", slave->address);
+        BUS_UNLOCK(slave->bus);
         return JAVACALL_DIO_FAIL;
     }
 
     /* Check if it is about to remove first slave from the slave's list */
     if (NULL == slavePrev) {
         slave->bus->slaves = slave->next;
+        BUS_UNLOCK(slave->bus);
         i2c_release_bus(slave->bus);
     } else {
         /* There is at least one opened slave in the list before 'the slave to remove'.
            It means that there is not necessary to release bus struct. */
         slavePrev->next = slave->next;
+        BUS_UNLOCK(slave->bus);
     }
 
     return JAVACALL_DIO_OK;
@@ -217,137 +341,191 @@
 
 
 static void i2c_close_slave(i2c_slave *slave) {
-    int rv;
-
-    /* if device is attached to a bus */
-    if (slave->bus) {
-        i2c_detach_slave_from_bus(slave);
-    }
-
-    /* If i2c device fd is opened */
-    if (slave->fd >= 0) {
-        /* close device fd */
-        while ((rv = close(slave->fd)) < 0 && EINTR == errno);
-
-        if (rv != 0)
-            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] cannot close bus fd, errno=%d", errno);
-    }
-
-    /* dealloc descriptor */
+    i2c_detach_slave_from_bus(slave);
     javacall_free(slave);
 }
 
 
+#if ENABLE_DEBUG    
+static void dumpBuf(i2c_message* msg) {
+    int i; 
+
+    printf("[I2C] (address = 0x%X)", msg->addr);
+    
+    if (msg->flags & I2C_M_RD) {
+        printf("--->");
+    } else {
+        printf("<---");
+    }
+
+    printf("[");
+
+    for(i=0; i < msg->len - 1; i++) {
+        printf("0x%X, ", msg->buf[i]);
+    }
+
+    if (msg->len > 0) {
+       printf("0x%X", msg->buf[msg->len-1]);
+    }
+
+    printf("]\n");
+}
+#endif
+
 javacall_dio_result javacall_i2c_open_slave_with_config(javacall_int32 busNum,
         javacall_int32 devAddr, javacall_int32 addrSize,
         javacall_int32 clockFrequency,
         const javacall_bool exclusive,
-        /*OUT*/javacall_handle* pHandle)
-{
-    javacall_uint32 reg = 0xFFFFFFFF;
-    i2c_slave *slave;
-    javacall_dio_result rv;
-    char filename[20];
-
-    (void) clockFrequency;
-
-    *pHandle = NULL;
-
+        /*OUT*/javacall_handle* pHandle) {
+    
     if (JAVACALL_TRUE != exclusive) {
         JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Shared mode is unsupported for I2C device");
         return JAVACALL_DIO_UNSUPPORTED_ACCESS_MODE;
     }
-
+    
+    if (clockFrequency != PERIPHERAL_CONFIG_DEFAULT) {
+        JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Clock frequency == -1 is supported only.");
+        return JAVACALL_DIO_INVALID_CONFIG;
+    }
+    
     if (addrSize == PERIPHERAL_CONFIG_DEFAULT)
         addrSize = 7;
-
+    
     if (busNum == PERIPHERAL_CONFIG_DEFAULT)
         busNum = 1;
-
-    if (addrSize != 7) {
-        JAVACALL_REPORT_ERROR(JC_DIO, "i2c config error: addrSize != 7");
+    
+    if (addrSize != 7 && addrSize != 10) {
+        JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Configuration error: addrSize != 7 or 10");
         return JAVACALL_DIO_INVALID_CONFIG;
     }
-
+    
     if (busNum < 0) {
         JAVACALL_REPORT_ERROR(JC_DIO,
-                "i2c config error: busNum should not be less than zero");
+                "[I2C] Configuration error: busNum should not be less than zero");
         return JAVACALL_DIO_FAIL;
     }
 
+    i2c_slave *slave;
+    *pHandle = NULL;
+   
     slave = (i2c_slave*) javacall_calloc(1, sizeof(i2c_slave));
+
     if (slave == NULL) {
         JAVACALL_REPORT_ERROR(JC_DIO,
-                "malloc error in javacall_i2c_open_slave_with_config");
+                "[I2C] Memory allocation error, javacall_i2c_open_slave_with_config");
         return JAVACALL_DIO_OUT_OF_MEMORY;
     }
+    
+    
+    slave->address = (long)devAddr;
+    slave->addressSize = (long)addrSize;
+    slave->clockFrequency = clockFrequency;
+    
+    /* Attach device to a bus */
+    if (JAVACALL_DIO_OK != i2c_attach_slave_to_bus(slave, busNum)) {
+        if (slave->bus != NULL) {
+            i2c_detach_slave_from_bus(slave);
+        }
 
-    slave->address = devAddr;
-
-    /* Try to open corresponding /dev/i2c-X file first */
-    snprintf(filename, 19, "/dev/i2c-%d", busNum);
-    restore_privileges();
-    slave->fd = open(filename, O_RDWR);
-    drop_privileges();
-
-    if (slave->fd < 0) {
-        i2c_close_slave(slave);
-        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] cannot open %s bus file", filename);
+        /* dealloc descriptor */
+        javacall_free(slave);
         return JAVACALL_DIO_FAIL;
     }
 
-    /* Set target address */
-    if (ioctl(slave->fd, I2C_SLAVE, slave->address) < 0) {
-        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] cannot set %d address", slave->address);
-        i2c_close_slave(slave);
-        return JAVACALL_DIO_FAIL;
-    }
-
-    /* Attach device to a bus */
-    if (JAVACALL_DIO_OK != (rv = i2c_attach_slave_to_bus(slave, busNum))) {
-        i2c_close_slave(slave);
-        return rv;
-    }
-
     *pHandle = (javacall_handle) slave;
 
     return JAVACALL_DIO_OK;
 }
 
+void cleanupTransaction(i2c_bus *bus) {
+    i2c_frame* transaction = bus->currentTransaction;
+    i2c_frame* nextFrame = transaction;
+    i2c_frame* curFrame = transaction;
+
+    while(nextFrame != NULL) {
+        curFrame=nextFrame;
+        nextFrame = nextFrame->next;
+        javacall_free(curFrame);
+    }
+
+    bus->currentTransaction = NULL;
+}
+
+static javacall_dio_result transaction_to_i2c_rdwr_ioctl_data(i2c_bus* bus, struct i2c_rdwr_ioctl_data* rdwr_data) {
+    int transactionLength = 0;
+    i2c_frame* nextFrame;
+    i2c_frame* curFrame;
+    int i;
+
+    i2c_frame* transaction = bus->currentTransaction;
+    
+    if (transaction == NULL) {
+        JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Transaction is NULL for bus %d", bus->busId);
+        return JAVACALL_DIO_FAIL;
+    } else {
+        nextFrame = transaction;
+        
+        while(nextFrame != NULL) {
+            nextFrame = nextFrame->next; transactionLength++;
+        }
+    
+        i2c_message* messages = (i2c_message*)javacall_malloc(sizeof(i2c_message)*transactionLength);
+
+        if (messages == NULL) {
+            JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Could not allocate memory for messages array");
+            return JAVACALL_DIO_OUT_OF_MEMORY;
+        } else {
+           nextFrame = transaction;
+
+           for(i = 0; i < transactionLength; i++) {
+               *(messages + transactionLength - i - 1) = nextFrame->msg;
+               nextFrame = nextFrame->next;
+           }
+
+           rdwr_data->msgs = messages;
+           rdwr_data->nmsgs = transactionLength;
+        }
+    }
+    return JAVACALL_DIO_OK;
+}
+
 static void* io_thread(void* arg) {
     i2c_io_context* ctx = (i2c_io_context*)arg;
-    void* pData = (void*)ctx->buf;
-    size_t len = (size_t)ctx->len;
-    int type = ctx->type;
+
+    i2c_bus *pBus = (i2c_bus*)ctx->pBus;
+    struct i2c_rdwr_ioctl_data* rdwr_data = &ctx->rdwr_data;
     int rv;
 
-    JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] Transfer start (%d)", type);
+    int transactionLength = 0;
+    i2c_frame* nextFrame;
+    i2c_frame* curFrame;
+    int i;
+    
+    rv=ioctl(pBus->fd, I2C_RDWR, rdwr_data);
 
-    if (WRITE == type) {
-        rv = (int)write(ctx->fd, pData, len);
+#ifdef ENABLE_DEBUG
+    for(i=0; i < rdwr_data->nmsgs; i++) {
+        dumpBuf(rdwr_data->msgs + i);
+    }
+#endif
+    
+    if ( rv < 0 ) {
+        JAVACALL_REPORT_ERROR2(JC_DIO, "[I2C] rdwr ioctl error: %d (%s)", errno, strerror(errno));
+    } 
 
-        if (-1 == rv) {
-            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] failed to write, errno=%d", errno);
-        }
-    } else {
-        rv = (int)read(ctx->fd, pData, len);
-
-        if (-1 == rv) {
-            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] failed to read, errno=%d", errno);
-        }
+    if (rv >= 0) {
+        rv = (rdwr_data->msgs + rdwr_data->nmsgs - 1)->len;
     }
 
     if (-1 == rv) {
-        // hope will get here when device is closed
         javanotify_i2c_event(JAVACALL_I2C_SEND_SIGNAL, ctx->pDev, JAVACALL_DIO_FAIL);
     } else {
-        ctx->len = rv;
         javanotify_i2c_event(JAVACALL_I2C_SEND_SIGNAL, ctx->pDev, JAVACALL_DIO_OK);
     }
 
+    javacall_free(rdwr_data->msgs);
     javacall_free(ctx);
 
-    JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] Transfer end. rv=%d", rv);
     return (void*)rv;
 }
 
@@ -355,43 +533,88 @@
                                                 const javacall_i2c_message_type type,
                                                 const javacall_bool write,
                                                 char* pData, int len,
-                                                javacall_int32 *const pBytes){
+                                                javacall_int32 *const pBytes) {
+
+    javacall_dio_result operationResult = JAVACALL_DIO_OK;
+    i2c_slave* pDev = (i2c_slave*)handle;
+        
     pthread_attr_t attr;
-    i2c_slave* pDev = (i2c_slave*)handle;
-    int flag = write ? WRITE : READ;
-
-    if (pDev->bus->isBusy &&
-        (JAVACALL_I2C_COMBINED_START == type || JAVACALL_I2C_REGULAR == type)) {
-        return JAVACALL_DIO_INVALID_STATE;
+    
+    if (JAVACALL_I2C_COMBINED_START == type || JAVACALL_I2C_REGULAR == type) {
+        BUS_WR_LOCK(pDev->bus);
+        if (NULL != pDev->bus->currentTransaction) {
+            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Could not start transaction for bus id %d", pDev->bus->busId);
+            BUS_UNLOCK(pDev->bus);
+            return JAVACALL_DIO_INVALID_STATE;
+        } else {
+            JAVACALL_REPORT_INFO1(JC_DIO, "[I2C] Successfully started transaction for bus id %d", pDev->bus->busId);
+        }
+        BUS_UNLOCK(pDev->bus);
     }
 
-    pDev->bus->isBusy = JAVACALL_TRUE;
+    do {
+        i2c_frame* nextFrame = (i2c_frame*) javacall_malloc(sizeof(i2c_frame));
+    
+        if (NULL == nextFrame) {
+            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Could not allocate i2c message structure, errno=%d", errno);
+            operationResult =  JAVACALL_DIO_OUT_OF_MEMORY;
+            break;
+        }
+        
+        nextFrame->msg.addr = pDev->address;
+        nextFrame->msg.len = len;
+        nextFrame->msg.buf = pData;
+    
+        if (write == JAVACALL_TRUE) {
+           nextFrame->msg.flags = 0;
+        } else {
+           nextFrame->msg.flags = I2C_M_RD;
+        }
+    
+        if (pDev->addressSize == 10) {
+            nextFrame->msg.flags |= I2C_M_TEN;
+        }
+        
+        nextFrame->next = pDev->bus->currentTransaction;
+        pDev->bus->currentTransaction = nextFrame;
+    
+        if (JAVACALL_I2C_COMBINED_START == type || JAVACALL_I2C_COMBINED_BODY == type) {
+            *pBytes = len;
+            return JAVACALL_DIO_OK;
+        }
 
-    if (JAVACALL_I2C_COMBINED_END == type || JAVACALL_I2C_REGULAR == type) {
-        pDev->bus->lastMessage = JAVACALL_TRUE;
-    } else {
-        pDev->bus->lastMessage = JAVACALL_FALSE;
-    }
+        
+        i2c_io_context *ctx = javacall_malloc(sizeof(i2c_io_context));
+        
+        if (NULL == ctx) {
+            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Could not allocate temporary buffer, errno=%d", errno);
+            operationResult =  JAVACALL_DIO_OUT_OF_MEMORY;
+            break;
+        }
 
-    i2c_io_context *ctx = javacall_malloc(sizeof(i2c_io_context));
-    if (NULL == ctx) {
-        JAVACALL_REPORT_ERROR1(JC_DIO,
-                               "[I2C] cannot allocate temp buffer: errno=%d", errno);
-        return JAVACALL_DIO_OUT_OF_MEMORY;
-    }
+        ctx->pDev = pDev;       
+        ctx->pBus = pDev->bus;
 
-    ctx->pDev = handle;
-    ctx->len = len;
-    ctx->type = flag;
-    ctx->buf = (javacall_int8*)pData;
-    ctx->fd = pDev->fd;
+        if (JAVACALL_DIO_OK != (operationResult = transaction_to_i2c_rdwr_ioctl_data(ctx->pBus, &ctx->rdwr_data))) {
+            JAVACALL_REPORT_ERROR(JC_DIO, "[I2C] Failed to copy transaction to i2c_rdwr_ioctl_data structure");
+            break;
+        }
 
-    if (pthread_create(&pDev->context, NULL, io_thread, ctx) != 0) {
-        javacall_free(ctx);
-        JAVACALL_REPORT_ERROR1(JC_DIO,
-                               "[I2C] failed to start read operation: errno=%d", errno);
-        return JAVACALL_DIO_FAIL;
-    }
+        if (pthread_create(&pDev->context, NULL, io_thread, ctx) != 0) {
+            JAVACALL_REPORT_ERROR2(JC_DIO, "[I2C] failed to start i/o operation: errno=%d (%s)", errno, strerror(errno));
+            javacall_free(ctx->rdwr_data.msgs);
+            javacall_free(ctx);
+            operationResult =  JAVACALL_DIO_FAIL;
+            break;
+        }
+    } while (0);
+    
+    if (operationResult != JAVACALL_DIO_OK) {
+        BUS_WR_LOCK(pDev->bus);
+        cleanupTransaction(pDev->bus);        
+        BUS_UNLOCK(pDev->bus);
+        return operationResult;
+    } 
 
     return JAVACALL_DIO_WOULD_BLOCK;
 }
@@ -401,21 +624,23 @@
                                                  char* pData, int len,
                                                  javacall_int32* const pBytes) {
     i2c_slave* pDev = (i2c_slave*)handle;
-    // if not interrupt
-    if (cancel) {
-        pthread_detach(pDev->context);
+
+    BUS_WR_LOCK(pDev->bus);
+    cleanupTransaction(pDev->bus);        
+    BUS_UNLOCK(pDev->bus);
+    
+    if (cancel == JAVACALL_TRUE) {
+        if (pDev->context != NULL) {
+            pthread_detach(pDev->context);
+        }
     } else {
         if (0 != pthread_join(pDev->context, (void**)pBytes)) {
-            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Can't join thread: %d", errno);
+            JAVACALL_REPORT_ERROR1(JC_DIO, "[I2C] Could not join io_thread: %d", errno);
+            return JAVACALL_DIO_FAIL;
         }
     }
 
-    if(pDev->bus->lastMessage == JAVACALL_TRUE || cancel) {
-        pDev->bus->isBusy = JAVACALL_FALSE;
-    }
-
     return JAVACALL_DIO_OK;
-
 }
 
 
@@ -423,18 +648,7 @@
  * See javacall_i2c.h for definition
  */
 void javacall_i2c_close(javacall_handle handle) {
-
-    i2c_slave *pDev = (i2c_slave*)handle;
-
-    pthread_detach(pDev->context);
-
-    // if this was regular or last combined message then release bus
-    if(pDev->bus->lastMessage == JAVACALL_TRUE) {
-        pDev->bus->isBusy = JAVACALL_FALSE;
-    }
-
-    i2c_close_slave(pDev);
-
+    i2c_close_slave((i2c_slave*)handle);
 }
 
 /**