changeset 136:f7b7bfe5485e

issue: https://bugs.openjdk.java.net/browse/DIO-20; https://bugs.openjdk.java.net/browse/DIO-21 Summary: i/o via SPI is completely broken.; Second call to DeviceManager.open() results in UDE, not IOE Reviewed-by:snazarki Contributed-by:bkvartsk
author onazarkina
date Mon, 29 Aug 2016 14:45:05 +0300
parents be55d80a4e8d
children 2f7da76bdc69
files src/share/classes/com/oracle/dio/spibus/impl/SPICompositeMessageImpl.java src/share/linux/native/com/oracle/dio/i2c/i2c.c
diffstat 2 files changed, 510 insertions(+), 201 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/oracle/dio/spibus/impl/SPICompositeMessageImpl.java	Fri Mar 18 16:51:03 2016 +0300
+++ b/src/share/classes/com/oracle/dio/spibus/impl/SPICompositeMessageImpl.java	Mon Aug 29 14:45:05 2016 +0300
@@ -24,12 +24,17 @@
  */
 
 package com.oracle.dio.spibus.impl;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 
 import com.oracle.dio.utils.ExceptionMessage;
 
+import java.io.InterruptedIOException;
+import java.nio.BufferOverflowException;
+import java.util.ConcurrentModificationException;
+
 import jdk.dio.ClosedDeviceException;
 import jdk.dio.UnavailableDeviceException;
 import jdk.dio.spibus.SPICompositeMessage;
@@ -38,19 +43,23 @@
 
 final class SPICompositeMessageImpl implements SPICompositeMessage {
 
-    private ArrayList<Message> messageList = new ArrayList<>();
+    private final ArrayList<Message> messageList = new ArrayList<>();
 
     private boolean isAlreadyTransferedOnce;
 
     /* Owner of the message */
-    private SPISlaveImpl device;
+    private final SPISlaveImpl device;
 
     // delay between operations
     private int delay;
 
+    // number of messages to read
+    private int rxMsgs;
+
     private class Message {
-        ByteBuffer tx, rx;
+        ByteBuffer tx, rx, newTx, newRx;
         int skip, delay;
+
         public Message(ByteBuffer tx, int skip, ByteBuffer rx, int delay) {
             this.rx = rx;
             this.skip = skip;
@@ -59,12 +68,15 @@
         }
     }
 
-    private void checkStatus() {
+    private void checkStatus() throws ClosedDeviceException {
         if (isAlreadyTransferedOnce) {
             throw new IllegalStateException(
-                ExceptionMessage.format(ExceptionMessage.I2CBUS_ALREADY_TRANSFERRED_MESSAGE)
+                    ExceptionMessage.format(ExceptionMessage.I2CBUS_ALREADY_TRANSFERRED_MESSAGE)
             );
         }
+        if (!device.isOpen()) {
+            throw new ClosedDeviceException();
+        }
     }
 
     private void check(Message message) throws ClosedDeviceException {
@@ -73,26 +85,26 @@
 
         if (0 > message.skip) {
             throw new IllegalArgumentException(
-                ExceptionMessage.format(ExceptionMessage.I2CBUS_NEGATIVE_SKIP_ARG)
+                    ExceptionMessage.format(ExceptionMessage.I2CBUS_NEGATIVE_SKIP_ARG)
             );
         }
 
-        if (!device.isOpen()) {
-            throw new ClosedDeviceException();
-        }
-
         for (int i = 0; i < messageList.size(); i++) {
             ByteBuffer tx = messageList.get(i).tx;
             ByteBuffer rx = messageList.get(i).rx;
-            if (    (null != tx && (tx == message.tx ||
-                                    tx == message.rx) )
-                 || (null != rx && (rx == message.tx ||
-                                    rx == message.rx ) ) ) {
+            if ((null != tx && (tx == message.tx ||
+                    tx == message.rx))
+                    || (null != rx && (rx == message.tx ||
+                    rx == message.rx))) {
                 throw new IllegalArgumentException(
-                    ExceptionMessage.format(ExceptionMessage.I2CBUS_BUFFER_GIVEN_TWICE)
+                        ExceptionMessage.format(ExceptionMessage.I2CBUS_BUFFER_GIVEN_TWICE)
                 );
             }
         }
+
+        if (null != message.rx) {
+            rxMsgs++;
+        }
     }
 
     /**
@@ -103,20 +115,22 @@
     }
 
     @Override
-    public SPICompositeMessage appendRead(ByteBuffer rxBuf)  throws IOException, ClosedDeviceException {
+    public SPICompositeMessage appendRead(ByteBuffer rxBuf) throws IOException, ClosedDeviceException {
         return appendRead(0, rxBuf);
     }
 
     @Override
     public SPICompositeMessage appendRead(int rxSkip, ByteBuffer rxBuf) throws IOException, ClosedDeviceException {
-        device.checkBuffer(rxBuf);
+        //null check
+        rxBuf.limit();
         return append(null, rxSkip, rxBuf);
     }
 
     @Override
-    public SPICompositeMessage appendWrite( ByteBuffer txBuf) throws IOException,
+    public SPICompositeMessage appendWrite(ByteBuffer txBuf) throws IOException,
             ClosedDeviceException {
-        device.checkBuffer(txBuf);
+        //null check
+        txBuf.limit();
         return append(txBuf, 0, null);
     }
 
@@ -126,13 +140,14 @@
     }
 
     @Override
-    public SPICompositeMessage appendWriteAndRead(ByteBuffer src, int skip, ByteBuffer dst) throws IOException, ClosedDeviceException{
-        device.checkBuffer(src);
-        device.checkBuffer(dst);
-        return append(src,skip,dst);
+    public SPICompositeMessage appendWriteAndRead(ByteBuffer src, int skip, ByteBuffer dst) throws IOException, ClosedDeviceException {
+        //null check
+        src.limit();
+        dst.limit();
+        return append(src, skip, dst);
     }
 
-    private SPICompositeMessage append(ByteBuffer src, int skip, ByteBuffer dst)  throws IOException, ClosedDeviceException {
+    private synchronized SPICompositeMessage append(ByteBuffer src, int skip, ByteBuffer dst) throws IOException, ClosedDeviceException {
         Message message = new Message(src, skip, dst, delay);
         check(message);
         messageList.add(message);
@@ -140,7 +155,7 @@
     }
 
     @Override
-    public synchronized SPICompositeMessage appendDelay(int delay) {
+    public synchronized SPICompositeMessage appendDelay(int delay) throws ClosedDeviceException {
 
         checkStatus();
 
@@ -153,37 +168,115 @@
         return device;
     }
 
+    /**
+     * Returns buffers are suitable for low level SPI operations
+     * New src buffer is located at index 0, dst buffer is at index 1
+     * The both buffers are direct to avoid native resource allocations in low levels
+     * The both buffers are the same length
+     *
+     * @param originalSrc original array to be sent
+     * @param originalDst The buffer into which bytes are to be transferred
+     * @return New buffers in the array. newSrcBuf = array[0], newDstBuf = array[1]
+     */
+    private ByteBuffer[] getBuffersForTransfer(ByteBuffer originalSrc, int skip, ByteBuffer originalDst) {
+        int bytesInSrc = originalSrc == null ? 0 : originalSrc.remaining();
+        int bytesInDst = originalDst == null ? 0 : originalDst.remaining();
+
+        int newRequiredSizeOfBuffers = bytesInSrc < (skip + bytesInDst) ?
+                skip + bytesInDst :
+                bytesInSrc;
+
+        ByteBuffer[] array = new ByteBuffer[2];
+
+        if (originalSrc == null || !originalSrc.isDirect() || originalSrc.remaining() < newRequiredSizeOfBuffers) {
+            array[0] = (ByteBuffer) ByteBuffer.allocateDirect(newRequiredSizeOfBuffers);
+            if (originalSrc != null) {
+                array[0].put(originalSrc);
+            }
+            array[0].rewind();
+        } else {
+            //Can not use originalSrc as is, because caller code can change position and limit
+            //after calling read/write/append operations
+            array[0] = originalSrc.slice();
+        }
+
+        if (originalDst == null || !originalDst.isDirect() || originalDst.remaining() < newRequiredSizeOfBuffers) {
+            array[1] = ByteBuffer.allocateDirect(newRequiredSizeOfBuffers);
+        } else {
+            //Can not use originalDst as is, because caller code can change position and limit
+            //after calling read/write/append operations
+            array[1] = originalDst.slice();
+        }
+
+        return array;
+    }
+
     @Override
     public int[] transfer() throws IOException, UnavailableDeviceException, ClosedDeviceException {
-        int bytesRead[];
-
         // global handle lock to prevent access from other threads.
         synchronized (device.getHandle()) {
             /* Forbid adding more messages to this combined message */
             isAlreadyTransferedOnce = true;
             if (0 == messageList.size()) {
-                return null;
+                return new int[0];
             }
 
-            int transaction = device.beginTransaction();
+            device.beginTransaction();
 
             try {
                 final int size = messageList.size();
-                bytesRead = new int[size];
-
                 for (int i = 0; i < size; i++) {
                     Message message = messageList.get(i);
-                    int res = device.transfer(message.tx, message.skip, message.rx, transaction);
-                    if (null != message.rx) {
-                        bytesRead[i] = res;
+                    ByteBuffer[] newBuffers = getBuffersForTransfer(message.tx, message.skip, message.rx);
+                    message.newTx = newBuffers[0];
+                    message.newRx = newBuffers[1];
+
+                    //New buffers have the same length and to avoid a dummy native call
+                    //just make tranfers of non-empty buffers
+                    if (message.newRx.remaining() > 0) {
+                        device.transferWithLock(message.newTx, message.newRx);
+                    }
+
+                    Thread.currentThread().sleep((message.delay / 1000), (message.delay % 1000) * 1000);
+                }
+
+            } catch (InterruptedException ex) {
+                throw new InterruptedIOException(ExceptionMessage.format(
+                        ExceptionMessage.SPIBUS_TRANSFER_INTERRUPTED));
+            } finally {
+                device.endTransaction();
+            }
+
+            int j = 0;
+            int[] bytesRead = new int[rxMsgs];
+
+            for (Message message : messageList) {
+                if (message.tx != null) {
+                    message.tx.position(message.tx.limit());
+                }
+
+                if (message.rx != null) {
+                    message.newRx.position(message.skip);
+                    try {
+                        message.newRx.limit(message.skip + message.rx.remaining());
+
+                        bytesRead[j++] = message.newRx.remaining();
+
+                        message.rx.put(message.newRx);
+                    } catch (BufferOverflowException | IllegalArgumentException ex) {
+                        //IAE for newRx.limit and BOE is for rx.put
+                        throw new ConcurrentModificationException(
+                                ExceptionMessage.format(
+                                        ExceptionMessage.SPIBUS_BYTE_BUFFER_MODIFICATION));
+
                     }
                 }
 
-            } finally {
-                device.endTransaction(transaction);
+                message.newRx = null;
+                message.newTx = null;
             }
 
+            return bytesRead;
         }
-        return bytesRead;
     }
 }
--- a/src/share/linux/native/com/oracle/dio/i2c/i2c.c	Fri Mar 18 16:51:03 2016 +0300
+++ b/src/share/linux/native/com/oracle/dio/i2c/i2c.c	Mon Aug 29 14:45:05 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,96 +341,97 @@
 
 
 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) {
+    javacall_dio_result ret;
+    
     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 */
+    ret = i2c_attach_slave_to_bus(slave, busNum);
+    if (JAVACALL_DIO_OK != ret) {
+        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);
-        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;
+        /* dealloc descriptor */
+        javacall_free(slave);
+        return ret;
     }
 
     *pHandle = (javacall_handle) slave;
@@ -314,40 +439,95 @@
     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 +535,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 +626,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 joint 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 +650,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);
 }
 
 /**