diff --git a/libi2c/LICENSE b/libi2c/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..ddd1fc0be0c71c8be33b2aa2257a50e4890d089d
--- /dev/null
+++ b/libi2c/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Amaork
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/libi2c/Makefile b/libi2c/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f0d043b3b0b7403f194802ffa999f133dd5e56b6
--- /dev/null
+++ b/libi2c/Makefile
@@ -0,0 +1,53 @@
+PYTHON		= python
+CC		= $(CROSS)gcc
+AR		= $(CROSS)ar
+VERSION=$(shell head -n 1 VERSION)
+INCDIR = include
+CFLAGS		= -Wall -I$(INCDIR) -Wextra -g -fPIC -DLIBI2C_VERSION="$(VERSION)"
+LDSHFLAGS	= -rdynamic -shared 
+ARFLAGS		= rcv
+CODE_STYLE	= astyle --align-pointer=name --align-reference=name --suffix=none --break-blocks --pad-oper --pad-header --break-blocks --keep-one-line-blocks --indent-switches --indent=spaces
+
+SOURCES=$(filter-out src/pyi2c.c, $(wildcard src/*.c))
+HEADERS=$(wildcard $(INCDIR)/i2c/*.h)
+OBJECTS=$(SOURCES:.c=.o)
+TARGETS = libi2c.a libi2c.so pylibi2c.so
+
+.PHONY:all clean example test install help style
+.SILENT: clean
+
+all:$(TARGETS) example
+
+clean:
+	make -C example clean
+	find . -name "*.o" | xargs rm -f 
+	$(RM) *.o *.so *~ a.out depend $(TARGETS) build -rf
+
+help:
+	$(PYTHON) help.py
+
+test:
+	$(PYTHON) -m unittest discover tests 
+
+style:
+	@find -regex '.*/.*\.\(c\|cpp\|h\)$$' | xargs $(CODE_STYLE)
+
+install:
+	$(PYTHON) setup.py install
+
+example:$(TARGETS)
+	make -C $@
+
+libi2c.a:$(OBJECTS)
+	$(AR) $(ARFLAGS) $@ $^
+
+libi2c.so:$(OBJECTS)
+	$(CC) $(LDSHFLAGS) -o $@ $^
+
+pylibi2c.so:$(OBJECTS)
+	$(PYTHON) setup.py build_ext --inplace
+
+depend:$(SOURCES) $(HEADERS) src/pyi2c.c
+	$(CC) $(CFLAGS) -MM $^ > $@
+
+-include depend
diff --git a/libi2c/README.md b/libi2c/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d9752d5b051138ea83460cdf37051e45eae57094
--- /dev/null
+++ b/libi2c/README.md
@@ -0,0 +1,159 @@
+libi2c
+=======
+
+Linux userspace i2c library.
+
+
+## Features
+
+- Support C/C++ and Python.
+
+- Support Python2+, Python3+
+
+- Support multiple bus and devices.
+
+- Support 7-bit and 10-bit i2c slave address.
+
+- Support 1 - 4 byte internal address, auto convert.
+
+- Provide read/write/ioctl functions to operate i2c device.
+
+- Support 8/16/32/64/128/256 bytes page aligned write, read/write length are unlimited.
+
+- Using ioctl functions operate i2c can ignore i2c device ack signal and internal address.
+
+
+## Installation
+
+	pip install pylibi2c
+
+	or
+
+	sudo python setup.py install
+
+	or
+
+	sudo make install
+
+	or
+
+	sudo make install PYTHON=pythonX.X
+
+## Interface
+
+	i2c_ioctl_write (once max 16 bytes) are more efficient than i2c_write (once max 4 bytes).
+
+	/* Close i2c bus */
+	void i2c_close(int bus);
+
+	/* Open i2c bus, return i2c bus fd */
+	int i2c_open(const char *bus_name);
+
+	/* I2C file I/O read, write */
+	ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
+	ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);
+
+	/* I2c ioctl read, write can set i2c flags */
+	ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
+	ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);
+
+## Data structure
+
+**C/C++**
+
+	typedef struct i2c_device {
+		int bus;			/* I2C Bus fd, return from i2c_open */
+		unsigned short addr;		/* I2C device(slave) address */
+		unsigned char tenbit;		/* I2C is 10 bit device address */
+		unsigned char delay;		/* I2C internal operate delay, unit millisecond */
+		unsigned short flags;		/* I2C i2c_ioctl_read/write flags */
+		unsigned int page_bytes;    	/* I2C max number of bytes per page, 1K/2K 8, 4K/8K/16K 16, 32K/64K 32 etc */
+		unsigned int iaddr_bytes;	/* I2C device internal(word) address bytes, such as: 24C04 1 byte, 24C64 2 bytes */
+	}I2CDevice;
+
+**Python**
+
+	I2CDevice object
+	I2CDevice(bus, addr, tenbit=False, iaddr_bytes=1, page_bytes=8, delay=1, flags=0)
+	tenbit, delay, flags, page_bytes, iaddr_bytes are attributes can setter/getter after init
+
+	required args: bus, addr.
+	optional args: tenbit(defult False, 7-bit), delay(defualt 1ms), flags(defualt 0), iaddr_bytes(defualt 1 byte internal address), page_bytes(default 8 bytes per page).
+
+
+## C/C++ Usage
+
+**1. First call `i2c_open` open i2c bus.**
+
+	int bus;
+
+	/* Open i2c bus /dev/i2c-0 */
+	if ((bus = i2c_open("/dev/i2c-0")) == -1) {
+
+		/* Error process */
+	}
+
+**2. Second fill `I2CDevice` struct, prepare read or write.**
+
+	I2CDevice device;
+	memset(&device, 0, sizeof(device));
+
+	/* 24C04 */
+	device.bus = bus;	/* Bus 0 */
+	device.addr = 0x50;	/* Slave address is 0x50, 7-bit */
+	device.iaddr_bytes = 1;	/* Device internal address is 1 byte */
+	device.page_bytes = 16; /* Device are capable of 16 bytes per page */
+
+**3. Call `i2c_read/write` or `i2c_ioctl_read/write` read or write i2c device.**
+
+	unsigned char buffer[256];
+	ssize_t size = sizeof(buffer);
+	memset(buffer, 0, sizeof(buffer));
+
+	/* From i2c 0x0 address read 256 bytes data to buffer */
+	if ((i2c_read(&device, 0x0, buffer, size)) != size) {
+
+		/* Error process */
+	}
+
+**4. Close i2c bus `i2c_close(bus)`.**
+
+	i2c_close(bus);
+
+## Python Usage
+
+	import ctypes
+	import pylibi2c
+
+	# Open i2c device @/dev/i2c-0, addr 0x50.
+	i2c = pylibi2c.I2CDevice('/dev/i2c-0', 0x50)
+
+	# Open i2c device @/dev/i2c-0, addr 0x50, 16bits internal address
+	i2c = pylibi2c.I2CDevice('/dev/i2c-0', 0x50, iaddr_bytes=2)
+
+	# Set delay
+	i2c.delay = 10
+
+	# Set page_bytes
+	i2c.page_bytes = 16
+
+	# Set flags
+	i2c.flags = pylibi2c.I2C_M_IGNORE_NAK
+
+	# Python2
+	buf = bytes(bytearray(256))
+
+	# Python3
+	buf = bytes(256)
+
+	# Write data to i2c, buf must be read-only type
+	size = i2c.write(0x0, buf)
+
+	# From i2c 0x0(internal address) read 256 bytes data, using ioctl_read.
+	data = i2c.ioctl_read(0x0, 256)
+
+## Notice
+
+1. If i2c device do not have internal address, please use `i2c_ioctl_read/write` function for read/write, set`'iaddr_bytes=0`.
+
+2. If want ignore i2c device nak signal, please use `i2c_ioctl_read/write` function, set I2CDevice.falgs as `I2C_M_IGNORE_NAK`.
diff --git a/libi2c/VERSION b/libi2c/VERSION
new file mode 100644
index 0000000000000000000000000000000000000000..7dff5b8921122a487162febe3c8e32effb7acb35
--- /dev/null
+++ b/libi2c/VERSION
@@ -0,0 +1 @@
+0.2.1
\ No newline at end of file
diff --git a/libi2c/example/Makefile b/libi2c/example/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..d835bc346a533e13e16258dff42a90741f18d012
--- /dev/null
+++ b/libi2c/example/Makefile
@@ -0,0 +1,33 @@
+CC			= $(CROSS)gcc
+AR			= $(CROSS)ar
+CFLAGS		= -Wall -g
+LDSHFLAGS	= -rdynamic -shared 
+ARFLAGS		= rcv
+CFLAGS		+= -I../include
+LDFLAGS		= -L.. -li2c -Wl,-R -Wl,..
+
+OBJDIR=../objs
+SOURCES = $(wildcard *.c) $(wildcard *.cpp)
+TARGETS = $(foreach src, $(SOURCES), $(basename $(src))) 
+
+.PHONY:all clean objdir
+.SILENT:clean
+
+all:objdir $(TARGETS)
+
+objdir:
+	@mkdir -p $(OBJDIR)
+
+clean:
+	$(RM) *.o a.out depend $(TARGETS) $(OBJDIR) -rf
+
+i2c_tools: i2c_tools.o
+	$(CC) $(CFLAGS) -o $(OBJDIR)/$@ $^ $(LDFLAGS)
+
+i2c_without_internal_address: i2c_without_internal_address.o
+	$(CC) $(CFLAGS) -o $(OBJDIR)/$@ $^ $(LDFLAGS)
+
+depend:$(wildcard *.h *.c)
+	$(CC) $(CFLAGS) -MM $^ > $@
+
+-include depend
diff --git a/libi2c/example/i2c_tools.c b/libi2c/example/i2c_tools.c
new file mode 100644
index 0000000000000000000000000000000000000000..e67bd5aab665f6f33f7db8eda0f39d64481756c6
--- /dev/null
+++ b/libi2c/example/i2c_tools.c
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "i2c/i2c.h"
+
+void print_i2c_data(const unsigned char *data, size_t len)
+{
+    size_t i = 0;
+
+    for (i = 0; i < len; i++) {
+
+        if (i % 16 == 0) {
+
+            fprintf(stdout, "\n");
+        }
+
+        fprintf(stdout, "%02x ", data[i]);
+    }
+
+    fprintf(stdout, "\n");
+}
+
+
+int main(int argc, char **argv)
+{
+    char i2c_dev_desc[128];
+    I2C_READ_HANDLE i2c_read_handle = i2c_read;
+    I2C_WRITE_HANDLE i2c_write_handle = i2c_write;
+    unsigned int addr = 0, iaddr_bytes = 0, page_bytes = 0, bus_num = -1;
+
+    if (argc < 5) {
+
+        fprintf(stdout, "Usage:%s <bus_num> <dev_addr> <iaddr_bytes> <page_bytes> [ioctl]\n"
+                "Such as:\n"
+                "\t24c02 i2c_test 1 0x50 1 8\n"
+                "\t24c04 i2c_test 1 0x50 1 16\n"
+                "\t24c64 i2c_test 1 0x50 2 32\n"
+                "\t24c64 i2c_test 1 0x50 2 ioctl\n", argv[0]);
+        exit(0);
+    }
+
+    /* Get i2c bus number */
+    if (sscanf(argv[1], "%u", &bus_num) != 1) {
+
+        fprintf(stderr, "Can't parse i2c 'bus_num' [%s]\n", argv[1]);
+        exit(-1);
+    }
+
+    /* Get i2c device address */
+    if (sscanf(argv[2], "0x%x", &addr) != 1) {
+
+        fprintf(stderr, "Can't parse i2c 'dev_addr' [%s]\n", argv[2]);
+        exit(-1);
+    }
+
+    /* Get i2c internal address bytes */
+    if (sscanf(argv[3], "%u", &iaddr_bytes) != 1) {
+
+        fprintf(stderr, "Can't parse i2c 'iaddr_bytes' [%s]\n", argv[3]);
+        exit(-2);
+    }
+
+    /* Get i2c page bytes number */
+    if (sscanf(argv[4], "%u", &page_bytes) != 1) {
+
+        fprintf(stderr, "Can't parse i2c 'page_bytes' [%s]\n", argv[4]);
+        exit(-2);
+    }
+
+
+    /* If specify ioctl using ioctl r/w i2c */
+    if (argc == 6 && (memcmp(argv[5], "ioctl", strlen("ioctl")) == 0)) {
+
+        i2c_read_handle = i2c_ioctl_read;
+        i2c_write_handle = i2c_ioctl_write;
+        fprintf(stdout, "Using i2c_ioctl_oper r/w data\n");
+    }
+    else {
+
+        fprintf(stdout, "Using i2c_oper r/w data\n");
+    }
+
+    /* Open i2c bus */
+    int bus;
+    char bus_name[32];
+    memset(bus_name, 0, sizeof(bus_name));
+
+    if (snprintf(bus_name, sizeof(bus_name), "/dev/i2c-%u", bus_num) < 0) {
+
+        fprintf(stderr, "Format i2c bus name error!\n");
+        exit(-3);
+    }
+
+    if ((bus = i2c_open(bus_name)) == -1) {
+
+        fprintf(stderr, "Open i2c bus:%s error!\n", bus_name);
+        exit(-3);
+    }
+
+    /* Init i2c device */
+    I2CDevice device;
+    memset(&device, 0, sizeof(device));
+    i2c_init_device(&device);
+
+    device.bus = bus;
+    device.addr = addr & 0x3ff;
+    device.page_bytes = page_bytes;
+    device.iaddr_bytes = iaddr_bytes;
+
+    /* Print i2c device description */
+    fprintf(stdout, "%s\n", i2c_get_device_desc(&device, i2c_dev_desc, sizeof(i2c_dev_desc)));
+
+    size_t i = 0;
+    ssize_t ret = 0;
+    unsigned char buf[256];
+    size_t buf_size = sizeof(buf);
+    memset(buf, 0, buf_size);
+
+    /* I/O r/w 0x00 - 0xff */
+    if (i2c_read_handle == i2c_read) {
+
+        for (i = 0; i < buf_size; i++) {
+
+            buf[i] = i;
+        }
+    }
+    /* ioctl r/w 0xff - 0x0 */
+    else {
+
+        for (i = 0; i < buf_size; i++) {
+
+            buf[i] = 0xff - i;
+        }
+    }
+
+    /* Print before write */
+    fprintf(stdout, "Write data:\n");
+    print_i2c_data(buf, buf_size);
+
+    ret = i2c_write_handle(&device, 0x0, buf, buf_size);
+    if (ret != -1 || (size_t)ret != buf_size)
+    {
+
+        fprintf(stderr, "Write i2c error!\n");
+        exit(-4);
+    }
+
+    fprintf(stdout, "\nWrite success, prepare read....\n");
+
+    /* Read */
+    usleep(100000);
+    memset(buf, 0, buf_size);
+
+    ret = i2c_read_handle(&device, 0x0, buf, buf_size);
+    if (ret == -1 || (size_t)ret != buf_size)
+    {
+
+        fprintf(stderr, "Read i2c error!\n");
+        exit(-5);
+    }
+
+    /* Print read result */
+    fprintf(stdout, "Read data:\n");
+    print_i2c_data(buf, buf_size);
+
+    i2c_close(bus);
+    return 0;
+}
+
+
diff --git a/libi2c/example/i2c_without_internal_address.c b/libi2c/example/i2c_without_internal_address.c
new file mode 100644
index 0000000000000000000000000000000000000000..bd4306e0a6d2c1fae4d41f26e6d5fd09e412f477
--- /dev/null
+++ b/libi2c/example/i2c_without_internal_address.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "i2c/i2c.h"
+
+int main()
+{
+    int fd;
+    I2CDevice device;
+    const char *data = "9876543";
+    ssize_t ret;
+
+    /* First open i2c bus */
+    if ((fd = i2c_open("/dev/i2c-0")) == -1) {
+
+        perror("Open i2c bus error");
+        return -1;
+    }
+
+    /* Fill i2c device struct */
+    device.bus = fd;
+    device.addr = 0x12;
+    device.tenbit = 0;
+    device.delay = 10;
+    device.flags = 0;
+    device.page_bytes = 8;
+    device.iaddr_bytes = 0; /* Set this to zero, and using i2c_ioctl_xxxx API will ignore chip internal address */
+
+    /* Write data to i2c */
+    ret = i2c_ioctl_write(&device, 0x0, data, strlen(data));
+    if (ret == -1 || (size_t)ret != strlen(data))
+    {
+        /* Error process */
+    }
+
+    i2c_close(fd);
+    return 0;
+
+}
+
diff --git a/libi2c/example/meson.build b/libi2c/example/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..c44338f0001d9a5077a3cb644a7a23c770df34e9
--- /dev/null
+++ b/libi2c/example/meson.build
@@ -0,0 +1,15 @@
+examples = [
+  'i2c_tools',
+  'i2c_without_internal_address',
+]
+
+foreach example: examples
+  exe = executable(example, example + '.c',
+    link_with: libi2c,
+    include_directories: i2c_incdir,
+    c_args: [
+      cflags,
+      '-D_DEFAULT_SOURCE',
+    ],
+  )
+endforeach
\ No newline at end of file
diff --git a/libi2c/example/pylibi2c_tools.py b/libi2c/example/pylibi2c_tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d2d9e388581dc0ef1b41aa449fb4799c3441824
--- /dev/null
+++ b/libi2c/example/pylibi2c_tools.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+import argparse
+import pylibi2c
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+
+    # Required args
+    parser.add_argument('-b', '--bus', help='i2c bus, such as /dev/i2c-1', type=str, required=True)
+    parser.add_argument('-d', '--device', help='i2c device address, such as 0x56', type=str, required=True)
+
+    # Device option args
+    parser.add_argument('--iaddr', help='i2c internal address', type=str, default="0x0")
+    parser.add_argument('--delay', help='i2c r/w delay, unit is msec', type=int, default=1)
+    parser.add_argument('--iaddr_bytes', help='i2c internal address bytes', type=int, default=1)
+    parser.add_argument('--page_bytes', help='i2c per page max number of bytes', type=int, default=8)
+
+    # Read/write options
+    parser.add_argument('--data', help='write data', type=str)
+    parser.add_argument('--size', help='read data size', type=int)
+    parser.add_argument('--ioctl', help='using ioctl r/w i2c', type=bool, default=False)
+    args = vars(parser.parse_args())
+
+    try:
+
+        bus = args.get('bus')
+        device = int(args.get('device'), 16)
+
+        delay = args.get('delay')
+        iaddr = int(args.get('iaddr'), 16)
+        page_bytes = args.get('page_bytes')
+        iaddr_bytes = args.get('iaddr_bytes')
+
+        data = args.get('data')
+        size = args.get('size')
+        ioctl = args.get('ioctl')
+
+        if data is None and size is None:
+            raise RuntimeError("'data' or 'size' must specified one, 'data' for write, 'size' for read")
+
+        # Create a i2c device
+        i2c = pylibi2c.I2CDevice(bus=bus, addr=device, page_bytes=page_bytes, iaddr_bytes=iaddr_bytes, delay=delay)
+
+        if data:
+            write_handle = i2c.ioctl_write if ioctl else i2c.write
+            ret = write_handle(iaddr, bytes(data.encode("ascii")))
+            print("Write: '{0:s}' to address: 0x{1:x}".format(data, iaddr))
+            print("Result:{}".format(ret))
+        else:
+            read_handle = i2c.ioctl_read if ioctl else i2c.read
+            data = read_handle(iaddr, size)
+            print("Read: {0:d} bytes data from address: 0x{1:x}".format(size, iaddr))
+            print("Result:'{}'".format(data.decode("ascii")))
+    except (TypeError, IOError, ValueError, RuntimeError) as err:
+        print("I2C R/W error:{}".format(err))
+
diff --git a/libi2c/help.py b/libi2c/help.py
new file mode 100644
index 0000000000000000000000000000000000000000..596a03e5b6d055676ef0afcf90b1cce588ef86b8
--- /dev/null
+++ b/libi2c/help.py
@@ -0,0 +1,3 @@
+import pylibi2c
+help(pylibi2c)
+help(pylibi2c.I2CDevice)
diff --git a/libi2c/include/i2c/i2c.h b/libi2c/include/i2c/i2c.h
new file mode 100644
index 0000000000000000000000000000000000000000..b57f35eee65ec49f9a854dad3bf4640f7d03f78a
--- /dev/null
+++ b/libi2c/include/i2c/i2c.h
@@ -0,0 +1,58 @@
+#ifndef _LIB_I2C_H_
+#define _LIB_I2C_H_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+/* I2c device */
+typedef struct i2c_device {
+    int bus;			        /* I2C Bus fd, return from i2c_open */
+    unsigned short addr;		/* I2C device(slave) address */
+    unsigned char tenbit;		/* I2C is 10 bit device address */
+    unsigned char delay;		/* I2C internal operation delay, unit millisecond */
+    unsigned short flags;		/* I2C i2c_ioctl_read/write flags */
+    unsigned int page_bytes;    /* I2C max number of bytes per page, 1K/2K 8, 4K/8K/16K 16, 32K/64K 32 etc */
+    unsigned int iaddr_bytes;   /* I2C device internal(word) address bytes, such as: 24C04 1 byte, 24C64 2 bytes */
+} I2CDevice;
+
+/* Close i2c bus */
+void i2c_close(int bus);
+
+/* Open i2c bus, return i2c bus fd */
+int i2c_open(const char *bus_name);
+
+/* Initialize I2CDevice with default value */
+void i2c_init_device(I2CDevice *device);
+
+/* Get i2c device description */
+char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size);
+
+/* Select i2c device on i2c bus */
+int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit);
+
+/* I2C internal(word) address convert */
+void i2c_iaddr_convert(unsigned int int_addr, unsigned int iaddr_bytes, unsigned char *addr);
+
+/* I2C file I/O read, write */
+ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
+ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);
+
+/* I2c ioctl read, write can set i2c flags */
+ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
+ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);
+
+/* I2C read / write handle function */
+typedef ssize_t (*I2C_READ_HANDLE)(const I2CDevice *dev, unsigned int iaddr, void *buf, size_t len);
+typedef ssize_t (*I2C_WRITE_HANDLE)(const I2CDevice *dev, unsigned int iaddr, const void *buf, size_t len);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/libi2c/include/meson.build b/libi2c/include/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..d258ebd8def61947a63ff32f62da1c456bb7c71c
--- /dev/null
+++ b/libi2c/include/meson.build
@@ -0,0 +1,7 @@
+i2c_incdir = include_directories('.')
+
+# public headers
+install_headers(['i2c/i2c.h'],
+  # i2c.h can clash with linux/i2c.h, so we put it in a subdir
+  subdir: meson.project_name(),
+)
\ No newline at end of file
diff --git a/libi2c/libi2c.wrap b/libi2c/libi2c.wrap
new file mode 100644
index 0000000000000000000000000000000000000000..b611c4f44bc67b2cb0e96da5511061de23dd3cb1
--- /dev/null
+++ b/libi2c/libi2c.wrap
@@ -0,0 +1,6 @@
+[wrap-git]
+url = https://github.com/mdegans/libi2c.git
+revision = master
+
+[provide]
+i2c=i2c_dep
\ No newline at end of file
diff --git a/libi2c/meson.build b/libi2c/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..c6979f15298884f5ec8f722f807f5b322fad703e
--- /dev/null
+++ b/libi2c/meson.build
@@ -0,0 +1,44 @@
+project('i2c', ['c'],
+  version: run_command(
+    'head', '-n', '1', files('VERSION'),
+  ).stdout().strip(),
+  meson_version: '>= 0.56.0',
+  license: 'MIT',
+  default_options: [
+    'c_std=c11',
+    'warning_level=2',  # -Wall, -Wextra
+    'werror=true',  # -Werror
+  ],
+)
+
+# the c++ compiler object
+cc = meson.get_compiler('c')
+
+# global cflags (public, included in subprojects and pkg-config)
+cflags = ['-DLIBI2C_VERSION="' + meson.project_version() + '"']
+
+# per compiler cflags, if desired
+if cc.get_id() == 'clang'
+  cflags += []
+else
+  cflags += []
+endif
+
+# split the project version
+proj_ver = meson.project_version().split('.')
+# version minus patch revision
+proj_ver_short = proj_ver[0] + '.' + proj_ver[1]
+proj_ver_major = proj_ver[0]
+proj_ver_minor = proj_ver[1]
+proj_ver_patch = proj_ver[2]
+
+# all of this is used by configure_file on i2c_config.h.in
+proj_description = 'Linux userspace i2c operation library'
+proj_url = 'https://github.com/amaork/libi2c'
+binary_package = 'libi2c'
+origin = 'github'
+
+subdir('include')
+subdir('src')
+subdir('example')
+subdir('tests')
\ No newline at end of file
diff --git a/libi2c/pyproject.toml b/libi2c/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..46e825ca2f5dc6024d3b272549e8d671216a3d82
--- /dev/null
+++ b/libi2c/pyproject.toml
@@ -0,0 +1,34 @@
+[build-system]
+requires = ["setuptools", "setuptools-scm"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "pylibi2c"
+authors = [
+  { name="amaork", email="amaork@gmail.com" },
+]
+
+description = "Linux userspace i2c operation library"
+keywords = ["i2c", "raspberry-pi"]
+
+readme = "README.md"
+requires-python = ">=2.7"
+license = { file = "LICENSE" }
+
+dynamic = ["version"]
+
+classifiers = [
+    "License :: OSI Approved :: MIT License",
+    "Operating System :: POSIX :: Linux",
+    "Programming Language :: C",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 2",
+    "Programming Language :: Python :: 3",
+]
+
+[tool.setuptools.dynamic]
+version = { file = "VERSION" }
+
+[project.urls]
+"Homepage" = "https://github.com/amaork/libi2c"
+"Bug Tracker" = "https://github.com/amaork/libi2c/issues"
\ No newline at end of file
diff --git a/libi2c/setup.py b/libi2c/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab61535db3b141f624e669b3151f33a1fe29ae0a
--- /dev/null
+++ b/libi2c/setup.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+"""
+setup.py file for pylibi2c
+"""
+
+import os
+from distutils.core import setup, Extension
+
+THIS_DIR = os.path.dirname(os.path.abspath(__file__))
+INC_DIR = os.path.join(THIS_DIR, 'include')
+VERSION = open('VERSION').read().strip()
+
+pylibi2c_module = Extension('pylibi2c',
+  sources=['src/i2c.c', 'src/pyi2c.c'],
+  extra_compile_args=['-DLIBI2C_VERSION="' + VERSION + '"'],
+  include_dirs=[INC_DIR],
+)
+
+setup(
+    name='pylibi2c',
+    version=VERSION,
+    license='MIT',
+    author='Amaork',
+    author_email="amaork@gmail.com",
+    url='https://github.com/amaork/libi2c',
+    description="Linux userspace i2c operation library",
+    long_description=open('README.md').read(),
+    ext_modules=[pylibi2c_module],
+    py_modules=["pylibi2c"],
+    classifiers=[
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 3',
+    ],
+)
+
+
diff --git a/libi2c/src/i2c.c b/libi2c/src/i2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..c04efd8ebd907be6f47be5c2668e1196134f15e1
--- /dev/null
+++ b/libi2c/src/i2c.c
@@ -0,0 +1,371 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include "i2c/i2c.h"
+
+/* I2C default delay */
+#define I2C_DEFAULT_DELAY 1
+
+/* I2C internal address max length */
+#define INT_ADDR_MAX_BYTES 4
+
+/* I2C page max bytes */
+#define PAGE_MAX_BYTES 4096
+
+#define GET_I2C_DELAY(delay) ((delay) == 0 ? I2C_DEFAULT_DELAY : (delay))
+#define GET_I2C_FLAGS(tenbit, flags) ((tenbit) ? ((flags) | I2C_M_TEN) : (flags))
+#define GET_WRITE_SIZE(addr, remain, page_bytes) ((addr) + (remain) > (page_bytes) ? (page_bytes) - (addr) : remain)
+
+static void i2c_delay(unsigned char delay);
+
+/*
+**	@brief		:	Open i2c bus
+**	#bus_name	:	i2c bus name such as: /dev/i2c-1
+**	@return		:	failed return -1, success return i2c bus fd
+*/
+int i2c_open(const char *bus_name)
+{
+    int fd;
+
+    /* Open i2c-bus devcice */
+    if ((fd = open(bus_name, O_RDWR)) == -1) {
+
+        return -1;
+    }
+
+    return fd;
+}
+
+
+void i2c_close(int bus)
+{
+    close(bus);
+}
+
+
+/*
+**	@brief		:	Initialize I2CDevice with defualt value
+**	#device	    :	I2CDevice struct
+*/
+void i2c_init_device(I2CDevice *device)
+{
+    /* 7 bit device address */
+    device->tenbit = 0;
+
+    /* 1ms delay */
+    device->delay = 1;
+
+    /* 8 bytes per page */
+    device->page_bytes = 8;
+
+    /* 1 byte internal(word) address */
+    device->iaddr_bytes = 1;
+}
+
+
+/*
+**	@brief		:	Get I2CDevice struct desc
+**	#device	    :	I2CDevice struct
+**  #buf        :   Description message buffer
+**  #size       :   #buf size
+**	@return		:	return i2c device desc
+*/
+char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size)
+{
+    memset(buf, 0, size);
+    snprintf(buf, size, "Device address: 0x%x, tenbit: %s, internal(word) address: %d bytes, page max %d bytes, delay: %dms",
+             device->addr, device->tenbit ? "True" : "False", device->iaddr_bytes, device->page_bytes, device->delay);
+
+    return buf;
+}
+
+
+/*
+**	i2c_ioctl_read/write
+**	I2C bus top layer interface to operation different i2c devide
+**	This function will call XXX:ioctl system call and will be related
+**	i2c-dev.c i2cdev_ioctl to operation i2c device.
+**	1. it can choice ignore or not ignore i2c bus ack signal (flags set I2C_M_IGNORE_NAK)
+**	2. it can choice ignore or not ignore i2c internal address
+**
+*/
+ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
+{
+    struct i2c_msg ioctl_msg[2];
+    struct i2c_rdwr_ioctl_data ioctl_data;
+    unsigned char addr[INT_ADDR_MAX_BYTES];
+    unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);
+
+    memset(addr, 0, sizeof(addr));
+    memset(ioctl_msg, 0, sizeof(ioctl_msg));
+    memset(&ioctl_data, 0, sizeof(ioctl_data));
+
+    /* Target have internal address */
+    if (device->iaddr_bytes) {
+
+        i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
+
+        /* First message is write internal address */
+        ioctl_msg[0].len	=	device->iaddr_bytes;
+        ioctl_msg[0].addr	= 	device->addr;
+        ioctl_msg[0].buf	= 	addr;
+        ioctl_msg[0].flags	= 	flags;
+
+        /* Second message is read data */
+        ioctl_msg[1].len	= 	len;
+        ioctl_msg[1].addr	= 	device->addr;
+        ioctl_msg[1].buf	=	buf;
+        ioctl_msg[1].flags	=	flags | I2C_M_RD;
+
+        /* Package to i2c message to operation i2c device */
+        ioctl_data.nmsgs	=	2;
+        ioctl_data.msgs		=	ioctl_msg;
+    }
+    /* Target did not have internal address */
+    else {
+
+        /* Direct send read data message */
+        ioctl_msg[0].len	= 	len;
+        ioctl_msg[0].addr	= 	device->addr;
+        ioctl_msg[0].buf	=	buf;
+        ioctl_msg[0].flags	=	flags | I2C_M_RD;
+
+        /* Package to i2c message to operation i2c device */
+        ioctl_data.nmsgs	=	1;
+        ioctl_data.msgs		=	ioctl_msg;
+    }
+
+    /* Using ioctl interface operation i2c device */
+    if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1) {
+
+//        perror("Ioctl read i2c error:");
+        return -1;
+    }
+
+    return len;
+}
+
+
+ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
+{
+    ssize_t remain = len;
+    size_t size = 0, cnt = 0;
+    const unsigned char *buffer = buf;
+    unsigned char delay = GET_I2C_DELAY(device->delay);
+    unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);
+
+    struct i2c_msg ioctl_msg;
+    struct i2c_rdwr_ioctl_data ioctl_data;
+    unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
+
+    while (remain > 0) {
+
+        size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);
+
+        /* Convert i2c internal address */
+        memset(tmp_buf, 0, sizeof(tmp_buf));
+        i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);
+
+        /* Connect write data after device internal address */
+        memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
+
+        /* Fill kernel ioctl i2c_msg */
+        memset(&ioctl_msg, 0, sizeof(ioctl_msg));
+        memset(&ioctl_data, 0, sizeof(ioctl_data));
+
+        ioctl_msg.len	=	device->iaddr_bytes + size;
+        ioctl_msg.addr	=	device->addr;
+        ioctl_msg.buf	=	tmp_buf;
+        ioctl_msg.flags	=	flags;
+
+        ioctl_data.nmsgs =	1;
+        ioctl_data.msgs	=	&ioctl_msg;
+
+        if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1) {
+
+//            perror("Ioctl write i2c error:");
+            return -1;
+        }
+
+        /* XXX: Must have a little time delay */
+        i2c_delay(delay);
+
+        cnt += size;
+        iaddr += size;
+        buffer += size;
+        remain -= size;
+    }
+
+    return cnt;
+}
+
+
+/*
+**	@brief	:	read #len bytes data from #device #iaddr to #buf
+**	#device	:	I2CDevice struct, must call i2c_device_init first
+**	#iaddr	:	i2c_device internal address will read data from this address, no address set zero
+**	#buf	:	i2c data will read to here
+**	#len	:	how many data to read, lenght must less than or equal to buf size
+**	@return : 	success return read data length, failed -1
+*/
+ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
+{
+    ssize_t cnt;
+    unsigned char addr[INT_ADDR_MAX_BYTES];
+    unsigned char delay = GET_I2C_DELAY(device->delay);
+
+    /* Set i2c slave address */
+    if (i2c_select(device->bus, device->addr, device->tenbit) == -1) {
+
+        return -1;
+    }
+
+    /* Convert i2c internal address */
+    memset(addr, 0, sizeof(addr));
+    i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
+
+    /* Write internal address to devide  */
+    if (write(device->bus, addr, device->iaddr_bytes) != device->iaddr_bytes) {
+
+        perror("Write i2c internal address error");
+        return -1;
+    }
+
+    /* Wait a while */
+    i2c_delay(delay);
+
+    /* Read count bytes data from int_addr specify address */
+    if ((cnt = read(device->bus, buf, len)) == -1) {
+
+        perror("Read i2c data error");
+        return -1;
+    }
+
+    return cnt;
+}
+
+
+/*
+**	@brief	:	write #buf data to i2c #device #iaddr address
+**	#device	:	I2CDevice struct, must call i2c_device_init first
+**	#iaddr	: 	i2c_device internal address, no address set zero
+**	#buf	:	data will write to i2c device
+**	#len	:	buf data length without '/0'
+**	@return	: 	success return write data length, failed -1
+*/
+ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
+{
+    ssize_t remain = len;
+    ssize_t ret;
+    size_t cnt = 0, size = 0;
+    const unsigned char *buffer = buf;
+    unsigned char delay = GET_I2C_DELAY(device->delay);
+    unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];
+
+    /* Set i2c slave address */
+    if (i2c_select(device->bus, device->addr, device->tenbit) == -1) {
+
+        return -1;
+    }
+
+    /* Once only can write less than 4 byte */
+    while (remain > 0) {
+
+        size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);
+
+        /* Convert i2c internal address */
+        memset(tmp_buf, 0, sizeof(tmp_buf));
+        i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);
+
+        /* Copy data to tmp_buf */
+        memcpy(tmp_buf + device->iaddr_bytes, buffer, size);
+
+        /* Write to buf content to i2c device length  is address length and
+                write buffer length */
+        ret = write(device->bus, tmp_buf, device->iaddr_bytes + size);
+        if (ret == -1 || (size_t)ret != device->iaddr_bytes + size)
+        {
+            perror("I2C write error:");
+            return -1;
+        }
+
+        /* XXX: Must have a little time delay */
+        i2c_delay(delay);
+
+        /* Move to next #size bytes */
+        cnt += size;
+        iaddr += size;
+        buffer += size;
+        remain -= size;
+    }
+
+    return cnt;
+}
+
+
+/*
+**	@brief	:	i2c internal address convert
+**	#iaddr	:	i2c device internal address
+**	#len	:	i2c device internal address length
+**	#addr	:	save convert address
+*/
+void i2c_iaddr_convert(unsigned int iaddr, unsigned int len, unsigned char *addr)
+{
+    union {
+        unsigned int iaddr;
+        unsigned char caddr[INT_ADDR_MAX_BYTES];
+    } convert;
+
+    /* I2C internal address order is big-endian, same with network order */
+    convert.iaddr = htonl(iaddr);
+
+    /* Copy address to addr buffer */
+    int i = len - 1;
+    int j = INT_ADDR_MAX_BYTES - 1;
+
+    while (i >= 0 && j >= 0) {
+
+        addr[i--] = convert.caddr[j--];
+    }
+}
+
+
+/*
+**	@brief		:	Select i2c address @i2c bus
+**	#bus		:	i2c bus fd
+**	#dev_addr	:	i2c device address
+**	#tenbit		:	i2c device address is tenbit
+**	#return		:	success return 0, failed return -1
+*/
+int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit)
+{
+    /* Set i2c device address bit */
+    if (ioctl(bus, I2C_TENBIT, tenbit)) {
+
+        perror("Set I2C_TENBIT failed");
+        return -1;
+    }
+
+    /* Set i2c device as slave ans set it address */
+    if (ioctl(bus, I2C_SLAVE, dev_addr)) {
+
+        perror("Set i2c device address failed");
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+**	@brief	:	i2c delay
+**	#msec	:	milliscond to be delay
+*/
+static void i2c_delay(unsigned char msec)
+{
+    usleep(msec * 1e3);
+}
+
diff --git a/libi2c/src/meson.build b/libi2c/src/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..85b242834e9124e9dea688c7989967b76a3b4205
--- /dev/null
+++ b/libi2c/src/meson.build
@@ -0,0 +1,54 @@
+# source for core library
+i2c_src = [
+  'i2c.c',
+]
+
+# shared and/or static library
+libi2c = library(meson.project_name(), i2c_src,
+  c_args: [
+    cflags,
+    '-D_DEFAULT_SOURCE',
+  ],
+  include_directories: i2c_incdir,
+  install: true,
+)
+
+# dependency for use in subprojects (only)
+i2c_dep = declare_dependency(
+  compile_args: cflags,
+  include_directories: i2c_incdir,
+  link_with: libi2c,
+  version: meson.project_version(),
+)
+
+# pkg-config support
+pkg = import('pkgconfig')
+pkg.generate(libi2c,
+  extra_cflags: cflags,
+)
+
+# python module build follows
+pymod = import('python')
+python_versions = ['python2', 'python3']
+python_testme = []  # python execs and built libraries go in here for testing
+python_src = ['pyi2c.c']
+python_path = meson.current_build_dir()  # used for tests
+
+foreach python_version : python_versions
+  python = pymod.find_installation(python_version, required: false)
+  if python.found()
+    # we still need the python-dev package
+    python_dep = dependency(python_version)
+    message(python_version + ' module enabled')
+    # same arguments as `shared_module`, more or less
+    # https://mesonbuild.com/Python-module.html
+    pylib = python.extension_module('pylibi2c', python_src,
+      dependencies: python_dep,
+      include_directories: i2c_incdir,
+      link_with: libi2c,
+      install: true,
+      c_args: cflags,
+    )
+    python_testme += {'python': python, 'lib': pylib}
+  endif
+endforeach
\ No newline at end of file
diff --git a/libi2c/src/pyi2c.c b/libi2c/src/pyi2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ad71f036e76877b652f773e9f278291d306a78a
--- /dev/null
+++ b/libi2c/src/pyi2c.c
@@ -0,0 +1,557 @@
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include "i2c/i2c.h"
+
+#define _VERSION_ LIBI2C_VERSION
+#define _NAME_ "pylibi2c"
+#define _I2CDEV_MAX_SIZE_ 4096
+#define _I2CDEV_MAX_IADDR_BYTES_SIZE 4
+#define _I2CDEV_MAX_PAGE_BYTES_SIZE 1024
+PyDoc_STRVAR(I2CDevice_name, "I2CDevice");
+PyDoc_STRVAR(pylibi2c_doc, "Linux userspace i2c library.\n");
+
+
+/* Macros needed for Python 3 */
+#ifndef PyInt_Check
+#define PyInt_Check      PyLong_Check
+#define PyInt_FromLong   PyLong_FromLong
+#define PyInt_AsLong     PyLong_AsLong
+#define PyInt_Type       PyLong_Type
+#endif
+
+
+PyDoc_STRVAR(I2CDeviceObject_type_doc, "I2CDevice(bus, address, tenbit=False, iaddr_bytes=1, page_bytes=8, delay=1, flags=0) -> I2CDevice object.\n");
+typedef struct {
+    PyObject_HEAD;
+    I2CDevice dev;
+} I2CDeviceObject;
+
+
+static PyObject *I2CDevice_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
+    (void)args;
+    (void)kwds;
+
+    I2CDeviceObject *self;
+
+    if ((self = (I2CDeviceObject *)type->tp_alloc(type, 0)) == NULL) {
+
+        return NULL;
+    }
+
+    memset(&self->dev, 0, sizeof(self->dev));
+    i2c_init_device(&self->dev);
+
+    Py_INCREF(self);
+    return (PyObject *)self;
+}
+
+
+PyDoc_STRVAR(I2CDevice_close_doc, "close()\n\nClose i2c device.\n");
+static PyObject *I2CDevice_close(I2CDeviceObject *self) {
+
+    /* Close i2c bus */
+    if (self->dev.bus >= 0) {
+
+        i2c_close(self->dev.bus);
+    }
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+static void I2CDevice_free(I2CDeviceObject *self) {
+
+    PyObject *ref = I2CDevice_close(self);
+    Py_XDECREF(ref);
+
+    Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+
+/* I2CDevice(bus, addr, tenbit=0, iaddr_bytes=1, page_bytes=8, delay=1, flags=0) */
+static int I2CDevice_init(I2CDeviceObject *self, PyObject *args, PyObject *kwds) {
+
+    char *bus_name = NULL;
+    static char *kwlist[] = {"bus", "addr", "tenbit", "iaddr_bytes", "page_bytes", "delay", "flags", NULL};
+
+    /* Bus name and device address is required */
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "sH|BBHBH:__init__", kwlist,
+                                     &bus_name, &self->dev.addr,
+                                     &self->dev.tenbit, &self->dev.iaddr_bytes, &self->dev.page_bytes, &self->dev.delay, &self->dev.flags)) {
+
+        return -1;
+    }
+
+    /* Open i2c bus */
+    if ((self->dev.bus = i2c_open(bus_name)) == -1) {
+        PyErr_SetFromErrno(PyExc_IOError);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static PyObject *I2CDevice_enter(PyObject *self, PyObject *args) {
+
+    if (!PyArg_ParseTuple(args, ""))
+        return NULL;
+
+    Py_INCREF(self);
+    return self;
+}
+
+
+static PyObject *I2CDevice_exit(I2CDeviceObject *self, PyObject *args) {
+
+    PyObject *exc_type = 0;
+    PyObject *exc_value = 0;
+    PyObject *traceback = 0;
+
+    if (!PyArg_UnpackTuple(args, "__exit__", 3, 3, &exc_type, &exc_value, &traceback)) {
+
+        return 0;
+    }
+
+    /* Close i2c bus */
+    I2CDevice_close(self);
+    Py_RETURN_FALSE;
+}
+
+
+/* str */
+static PyObject *I2CDevice_str(PyObject *object) {
+
+    char desc[128];
+    PyObject *dev_desc = NULL;
+    I2CDeviceObject *self = (I2CDeviceObject *)object;
+    i2c_get_device_desc(&self->dev, desc, sizeof(desc));
+
+#if PY_MAJOR_VERSION >= 3
+    dev_desc = PyUnicode_FromString(desc);
+#else
+    dev_desc = PyString_FromString(desc);
+#endif
+
+    Py_INCREF(dev_desc);
+    return dev_desc;
+}
+
+
+/* i2c read device */
+static PyObject *i2c_read_device(I2CDeviceObject *self, PyObject *args, int ioctl) {
+
+    int result;
+    unsigned int len = 0;
+    unsigned int iaddr = 0;
+    PyObject *bytearray = NULL;
+    char buf[_I2CDEV_MAX_SIZE_];
+    memset(buf, 0, sizeof(buf));
+    I2C_READ_HANDLE read_handle = NULL;
+
+    if (!PyArg_ParseTuple(args, "II:read", &iaddr, &len)) {
+
+        return NULL;
+    }
+
+    len = len > sizeof(buf) ? sizeof(buf) : len;
+    read_handle = ioctl ? i2c_ioctl_read : i2c_read;
+    result = read_handle(&self->dev, iaddr, buf, len);
+
+    if (result < 0) {
+        PyErr_SetFromErrno(PyExc_IOError);
+        return NULL;
+    }
+
+    if ((uint)result != len)
+    {
+        perror("short read");
+        return NULL;
+    }
+
+    /* Copy data to bytearray and return */
+    bytearray = PyByteArray_FromStringAndSize(buf, len);
+    Py_INCREF(bytearray);
+    return bytearray;
+}
+
+
+/* i2c write device */
+static PyObject *i2c_write_device(I2CDeviceObject *self, PyObject *args, int ioctl) {
+
+    char *buf = NULL;
+    Py_ssize_t size = 0;
+    unsigned int iaddr = 0;
+    PyObject *result = NULL;
+    I2C_WRITE_HANDLE write_handle = NULL;
+
+    if (!PyArg_ParseTuple(args, "Is#::write", &iaddr, &buf, &size)) {
+        return NULL;
+    }
+
+    write_handle = ioctl ? i2c_ioctl_write : i2c_write;
+    result = Py_BuildValue("i", write_handle(&self->dev, iaddr, buf, size));
+
+    Py_INCREF(result);
+    return result;
+}
+
+
+/* file read */
+PyDoc_STRVAR(I2CDevice_read_doc, "read(iaddr, buf, size)\n\nRead #size bytes data from device #iaddress to #buf.\n");
+static PyObject *I2CDevice_read(I2CDeviceObject *self, PyObject *args) {
+
+    return i2c_read_device(self, args, 0);
+}
+
+
+/* file write */
+PyDoc_STRVAR(I2CDevice_write_doc, "write(iaddr, buf, size)\n\nWrite #size bytes data from #buf to device #iaddress.\n");
+static PyObject *I2CDevice_write(I2CDeviceObject *self, PyObject *args) {
+
+    return i2c_write_device(self, args, 0);
+}
+
+
+/* ioctl read */
+PyDoc_STRVAR(I2CDevice_ioctl_read_doc, "ioctl_read(iaddr, buf, size)\n\nIoctl read #size bytes data from device #iaddress to #buf.\n");
+static PyObject *I2CDevice_ioctl_read(I2CDeviceObject *self, PyObject *args) {
+
+    return i2c_read_device(self, args, 1);
+}
+
+
+/* ioctl write */
+PyDoc_STRVAR(I2CDevice_ioctl_write_doc, "ioctl_write(iaddr, buf, size)\n\nIoctl write #size bytes data from #buf to device #iaddress.\n");
+static PyObject *I2CDevice_ioctl_write(I2CDeviceObject *self, PyObject *args) {
+
+    return i2c_write_device(self, args, 1);
+}
+
+
+/* pylibi2c module methods */
+static PyMethodDef I2CDevice_methods[] = {
+
+    {"read", (PyCFunction)I2CDevice_read, METH_VARARGS, I2CDevice_read_doc},
+    {"write", (PyCFunction)I2CDevice_write, METH_VARARGS, I2CDevice_write_doc},
+    {"close", (PyCFunction)I2CDevice_close, METH_NOARGS, I2CDevice_close_doc},
+    {"ioctl_read", (PyCFunction)I2CDevice_ioctl_read, METH_VARARGS, I2CDevice_ioctl_read_doc},
+    {"ioctl_write", (PyCFunction)I2CDevice_ioctl_write, METH_VARARGS, I2CDevice_ioctl_write_doc},
+    {"__enter__", (PyCFunction)I2CDevice_enter, METH_NOARGS, NULL},
+    {"__exit__", (PyCFunction)I2CDevice_exit, METH_NOARGS, NULL},
+    {NULL},
+};
+
+
+/* Check */
+static int check_user_input(const char *name, PyObject *input, int min, int max) {
+
+    int value;
+
+    if (input == NULL) {
+        PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
+        return -1;
+    }
+
+    /* tenbit attribute, boolean type */
+    if (memcmp(name, "tenbit", strlen(name)) == 0) {
+        if (!PyBool_Check(input)) {
+            PyErr_SetString(PyExc_TypeError, "The last attribute value must be boolean");
+            return -1;
+        }
+
+        return 0;
+    }
+
+    /* Other attribute, integer type */
+    if (!PyInt_Check(input)) {
+        PyErr_SetString(PyExc_TypeError, "The last attribute value must be an integer");
+        return -1;
+    }
+
+    /* Convert value to int */
+    value = PyLong_AsLong(input);
+
+    /* Check input value range */
+    if (value < min || value > max) {
+        PyErr_Format(PyExc_ValueError, "invalid ' %s'(%d - %d)", name, min, max);
+        return -1;
+    }
+
+    return 0;
+}
+
+/* flags */
+PyDoc_STRVAR(I2CDevice_flags_doc, "ioctl_read/write i2c_msg flags\n\n"
+             "I_2C_M_NOSTART\n"
+             "In a combined transaction, no 'S Addr Wr/Rd [A]' is generated at some point."
+             "For example, setting I2C_M_NOSTART on the second partial message generates something like:\n\n"
+             "\t\t\tS Addr Rd [A] [Data] NA Data [A] P\n\n"
+             "If you set the I2C_M_NOSTART variable for the first partial message, we do not generate Addr,"
+             "but we do generate the startbit S. This will probably confuse all other clients on your bus, so don't try this.\n\n"
+             "I2C_M_IGNORE_NAK\n"
+             "Normally message is interrupted immediately if there is [NA] from the"
+             "client. Setting this flag treats any [NA] as [A], and all of message is sent.These messages may still fail to SCL lo->hi timeout.\n\n"
+             "I2C_M_NO_RD_ACK\n"
+             "In a read message, master A/NA bit is skipped.\n\n");
+static PyObject *I2CDevice_get_flags(I2CDeviceObject *self, void *closure) {
+    (void)closure;
+
+    PyObject *result = Py_BuildValue("H", self->dev.flags);
+    Py_INCREF(result);
+    return result;
+}
+
+static int I2CDevice_set_flags(I2CDeviceObject *self, PyObject *value, void *closure)
+{
+    (void)closure;
+
+    if (check_user_input("falgs", value, 0, I2C_M_NOSTART) != 0) {
+
+        return -1;
+    }
+
+    self->dev.flags = PyLong_AsLong(value);
+    return 0;
+}
+
+/* delay */
+PyDoc_STRVAR(I2CDevice_delay_doc, "i2c internal operate delay, unit milliscond.\n\n");
+static PyObject *I2CDevice_get_delay(I2CDeviceObject *self, void *closure) {
+    (void)closure;
+
+    PyObject *result = Py_BuildValue("b", self->dev.delay);
+    Py_INCREF(result);
+    return result;
+}
+
+static int I2CDevice_set_delay(I2CDeviceObject *self, PyObject *value, void *closure)
+{
+    (void)closure;
+
+    if (check_user_input("delay", value, 0, 100) != 0) {
+
+        return -1;
+    }
+
+    self->dev.delay = PyLong_AsLong(value);
+    return 0;
+}
+
+/* tenbit */
+PyDoc_STRVAR(I2CDevice_tenbit_doc, "True, Enable 10 bit addressing.\n\nFalse, 7 bit addressing(default).\n");
+static PyObject *I2CDevice_get_tenbit(I2CDeviceObject *self, void *closure) {
+    (void)closure;
+
+    PyObject *result = self->dev.tenbit ? Py_True : Py_False;
+    Py_INCREF(result);
+    return result;
+}
+
+static int I2CDevice_set_tenbit(I2CDeviceObject *self, PyObject *value, void *closure)
+{
+    (void)closure;
+
+    if (check_user_input("tenbit", value, 0, 1) != 0) {
+
+        return -1;
+    }
+
+    self->dev.tenbit = PyLong_AsLong(value);
+    return 0;
+}
+
+/* iaddr_bytes */
+PyDoc_STRVAR(I2CDevice_page_bytes_doc, "i2c EEPROM max number of bytes per page (must be divisible by 8)\n\n"
+             "8, 8 bytes per page, such as 24C01/24C02\n\n"
+             "16, 16 bytes per page, such as 24C04/24C08/24C16\n\n"
+             "32, 32 bytes per page, such as 24C32/24C64\n\n");
+static PyObject *I2CDevice_get_page_bytes(I2CDeviceObject *self, void *closure) {
+    (void)closure;
+
+    PyObject *result = Py_BuildValue("I", self->dev.page_bytes);
+    Py_INCREF(result);
+    return result;
+}
+
+static int I2CDevice_set_page_bytes(I2CDeviceObject *self, PyObject *value, void *closure)
+{
+    (void)closure;
+
+    int page_bytes = 0;
+
+    if (check_user_input("page_bytes", value, 8, _I2CDEV_MAX_PAGE_BYTES_SIZE) != 0) {
+
+        return -1;
+    }
+
+    page_bytes = PyLong_AsLong(value);
+
+    /* Divisible by 8 */
+    if (page_bytes % 8) {
+        PyErr_SetString(PyExc_ValueError, "The 'page_bytes' must be divisible by 8");
+        return -1;
+    }
+
+    self->dev.page_bytes = page_bytes;
+    return 0;
+}
+
+/* iaddr_bytes */
+PyDoc_STRVAR(I2CDevice_iaddr_bytes_doc, "I2C device internal(word) address bytes.\n\n"
+             "0, special device, without internal address\n\n"
+             "1, 1 byte internal address, such as 24C04\n\n"
+             "2, 2 byte internal address, such as 24C64\n\n"
+             "2, 3 byte internal address, such as 24C1024\n\n");
+static PyObject *I2CDevice_get_iaddr_bytes(I2CDeviceObject *self, void *closure) {
+    (void)closure;
+
+    PyObject *result = Py_BuildValue("b", self->dev.iaddr_bytes);
+    Py_INCREF(result);
+    return result;
+}
+
+static int I2CDevice_set_iaddr_bytes(I2CDeviceObject *self, PyObject *value, void *closure)
+{
+    (void)closure;
+
+    if (check_user_input("iaddr_bytes", value, 0, _I2CDEV_MAX_IADDR_BYTES_SIZE) != 0) {
+
+        return -1;
+    }
+
+    self->dev.iaddr_bytes = PyLong_AsLong(value);
+    return 0;
+}
+
+static PyGetSetDef I2CDevice_getseters[] = {
+
+    {"flags", (getter)I2CDevice_get_flags, (setter)I2CDevice_set_flags, I2CDevice_flags_doc, NULL},
+    {"delay", (getter)I2CDevice_get_delay, (setter)I2CDevice_set_delay, I2CDevice_delay_doc, NULL},
+    {"tenbit", (getter)I2CDevice_get_tenbit, (setter)I2CDevice_set_tenbit, I2CDevice_tenbit_doc, NULL},
+    {"page_bytes", (getter)I2CDevice_get_page_bytes, (setter)I2CDevice_set_page_bytes, I2CDevice_page_bytes_doc, NULL},
+    {"iaddr_bytes", (getter)I2CDevice_get_iaddr_bytes, (setter)I2CDevice_set_iaddr_bytes, I2CDevice_iaddr_bytes_doc, NULL},
+    {NULL},
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+
+static PyTypeObject I2CDeviceObjectType = {
+#if PY_MAJOR_VERSION >= 3
+    PyVarObject_HEAD_INIT(NULL, 0)
+#else
+    PyObject_HEAD_INIT(NULL) 0, /* ob_size */
+#endif
+    I2CDevice_name,		        /* tp_name */
+    sizeof(I2CDeviceObject),	/* tp_basicsize */
+    0,			        	    /* tp_itemsize */
+    (destructor)I2CDevice_free,/* tp_dealloc */
+    0,				            /* tp_print */
+    0,				            /* tp_getattr */
+    0,				            /* tp_setattr */
+    0,				            /* tp_compare */
+    0,				            /* tp_repr */
+    0,				            /* tp_as_number */
+    0,				            /* tp_as_sequence */
+    0,				            /* tp_as_mapping */
+    0,				            /* tp_hash */
+    0,				            /* tp_call */
+    I2CDevice_str,	            /* tp_str */
+    0,				            /* tp_getattro */
+    0,				            /* tp_setattro */
+    0,				            /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    I2CDeviceObject_type_doc,	/* tp_doc */
+    0,				            /* tp_traverse */
+    0,				            /* tp_clear */
+    0,				            /* tp_richcompare */
+    0,				            /* tp_weaklistoffset */
+    0,				            /* tp_iter */
+    0,				            /* tp_iternext */
+    I2CDevice_methods,		    /* tp_methods */
+    0,				            /* tp_members */
+    I2CDevice_getseters,        /* tp_getset */
+    0,				            /* tp_base */
+    0,				            /* tp_dict */
+    0,				            /* tp_descr_get */
+    0,				            /* tp_descr_set */
+    0,				            /* tp_dictoffset */
+    (initproc)I2CDevice_init,	/* tp_init */
+    0,				            /* tp_alloc */
+    I2CDevice_new,		        /* tp_new */
+};
+
+#pragma GCC diagnostic pop
+
+static PyMethodDef pylibi2c_methods[] = {
+    {NULL}
+};
+
+
+void define_constants(PyObject *module) {
+
+    PyModule_AddObject(module, "I2C_M_NOSTART", Py_BuildValue("H", I2C_M_NOSTART));
+    PyModule_AddObject(module, "I2C_M_NO_RD_ACK", Py_BuildValue("H", I2C_M_NO_RD_ACK));
+    PyModule_AddObject(module, "I2C_M_IGNORE_NAK", Py_BuildValue("H", I2C_M_IGNORE_NAK));
+}
+
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef pylibi2cmodule = {
+    PyModuleDef_HEAD_INIT,
+    _NAME_,       /* Module name */
+    pylibi2c_doc, /* Module pylibi2cMethods */
+    -1,           /* size of per-interpreter state of the module, size of per-interpreter state of the module,*/
+    pylibi2c_methods,
+    NULL,
+    0,
+    0,
+    0,
+};
+#endif
+
+
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC PyInit_pylibi2c(void)
+#else
+PyMODINIT_FUNC initpylibi2c(void)
+#endif
+{
+
+    PyObject *module;
+
+    if (PyType_Ready(&I2CDeviceObjectType) < 0) {
+#if PY_MAJOR_VERSION >= 3
+        return NULL;
+#else
+        return;
+#endif
+    }
+
+#if PY_MAJOR_VERSION >= 3
+    module = PyModule_Create(&pylibi2cmodule);
+    PyObject *version = PyUnicode_FromString(_VERSION_);
+#else
+    module = Py_InitModule3(_NAME_, pylibi2c_methods, pylibi2c_doc);
+    PyObject *version = PyString_FromString(_VERSION_);
+#endif
+
+    /* Constants */
+    define_constants(module);
+
+    /* Set module version */
+    PyObject *dict = PyModule_GetDict(module);
+    PyDict_SetItemString(dict, "__version__", version);
+    Py_DECREF(version);
+
+    /* Register I2CDeviceObject */
+    Py_INCREF(&I2CDeviceObjectType);
+    PyModule_AddObject(module, I2CDevice_name, (PyObject *)&I2CDeviceObjectType);
+
+#if PY_MAJOR_VERSION >= 3
+    return module;
+#endif
+}
+
diff --git a/libi2c/tests/meson.build b/libi2c/tests/meson.build
new file mode 100644
index 0000000000000000000000000000000000000000..3840021c4f7cd032c16072c2fa2817809367fce6
--- /dev/null
+++ b/libi2c/tests/meson.build
@@ -0,0 +1,14 @@
+foreach d : python_testme
+  python = d['python']
+  testname = 'python ' + python.language_version() + ' module'
+  test(testname, python,  # testname, executable
+    args: files('test_pylibi2c.py'),  # full path to test.py
+    depends: d['lib'],  # the library that must be built before
+    workdir: python_path,
+    protocol: 'exitcode',
+    env: [
+      'PYTHONPATH=' + python_path,
+    ],
+    is_parallel: false,
+  )
+endforeach
\ No newline at end of file
diff --git a/libi2c/tests/test_pylibi2c.py b/libi2c/tests/test_pylibi2c.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eb07e4607e16a43d46a9ff8b56e4c83ea2d6f2e
--- /dev/null
+++ b/libi2c/tests/test_pylibi2c.py
@@ -0,0 +1,235 @@
+import random
+import unittest
+import pylibi2c
+
+
+class Pylibi2cTest(unittest.TestCase):
+    def setUp(self):
+        self.i2c_size = 256
+        # 24C04 E2PROM test
+        self.i2c = pylibi2c.I2CDevice(bus="/dev/i2c-1", addr=0x56, page_bytes=16)
+
+    def test_init(self):
+        with self.assertRaises(TypeError):
+            pylibi2c.I2CDevice()
+
+        with self.assertRaises(TypeError):
+            pylibi2c.I2CDevice(1, 2)
+
+        with self.assertRaises(TypeError):
+            pylibi2c.I2CDevice("1", "2")
+
+        with self.assertRaises(TypeError):
+            pylibi2c.I2CDevice("/dev/i2c-1")
+
+        with self.assertRaises(IOError):
+            pylibi2c.I2CDevice("/dev/i2c-100", 0x56)
+
+    def test_getattr(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+
+        with self.assertRaises(AttributeError):
+            i2c.bus
+
+        with self.assertRaises(AttributeError):
+            i2c.addr
+
+    def test_setattr(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+
+        with self.assertRaises(AttributeError):
+            i2c.bus = ""
+
+        with self.assertRaises(AttributeError):
+            i2c.addr = ""
+
+    def test_flags(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+        self.assertEqual(i2c.flags, 0)
+
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56, flags=1)
+        self.assertEqual(i2c.flags, 1)
+
+        with self.assertRaises(TypeError):
+            i2c.flags = "100"
+
+        with self.assertRaises(TypeError):
+            i2c.flags = 0.1
+
+        with self.assertRaises(ValueError):
+            i2c.flags = -1
+
+        i2c.flags = 0
+        self.assertEqual(i2c.flags, 0)
+
+        i2c.flags = pylibi2c.I2C_M_NOSTART
+        self.assertEqual(i2c.flags, pylibi2c.I2C_M_NOSTART)
+
+        i2c.flags = pylibi2c.I2C_M_IGNORE_NAK
+        self.assertEqual(i2c.flags, pylibi2c.I2C_M_IGNORE_NAK)
+
+    def test_delay(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+        self.assertEqual(i2c.delay, 1)
+
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56, delay=0)
+        self.assertEqual(i2c.delay, 0)
+
+        with self.assertRaises(TypeError):
+            i2c.delay = "100"
+
+        with self.assertRaises(TypeError):
+            i2c.delay = 0.1
+
+        with self.assertRaises(ValueError):
+            i2c.delay = -1
+
+        with self.assertRaises(ValueError):
+            i2c.delay = 101
+
+        i2c.delay = 10
+        self.assertEqual(i2c.delay, 10)
+
+        i2c.delay = 100
+        self.assertEqual(i2c.delay, 100)
+
+    def test_tenbit(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+        self.assertEqual(i2c.tenbit, False)
+
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56, tenbit=1)
+        self.assertEqual(i2c.tenbit, True)
+
+        with self.assertRaises(TypeError):
+            i2c.tenbit = 0
+
+        with self.assertRaises(TypeError):
+            i2c.tenbit = 100
+
+        with self.assertRaises(TypeError):
+            i2c.tenbit = "True"
+
+        i2c.tenbit = False
+        self.assertEqual(i2c.tenbit, False)
+
+        i2c.tenbit = True
+        self.assertEqual(i2c.tenbit, True)
+
+    def test_page_bytes(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+        self.assertEqual(i2c.page_bytes, 8)
+
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56, page_bytes=16)
+        self.assertEqual(i2c.page_bytes, 16)
+
+        with self.assertRaises(TypeError):
+            i2c.page_bytes = "1"
+
+        with self.assertRaises(ValueError):
+            i2c.page_bytes = -1
+
+        with self.assertRaises(ValueError):
+            i2c.page_bytes = 0
+
+        with self.assertRaises(ValueError):
+            i2c.page_bytes = 4
+
+        with self.assertRaises(ValueError):
+            i2c.page_bytes = 10
+
+        with self.assertRaises(ValueError):
+            i2c.page_bytes = 2048
+
+        i2c.page_bytes = 32
+        self.assertEqual(i2c.page_bytes, 32)
+
+        i2c.page_bytes = 64
+        self.assertEqual(i2c.page_bytes, 64)
+
+    def test_iaddr_bytes(self):
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56)
+        self.assertEqual(i2c.iaddr_bytes, 1)
+
+        i2c = pylibi2c.I2CDevice("/dev/i2c-1", 0x56, iaddr_bytes=2)
+        self.assertEqual(i2c.iaddr_bytes, 2)
+
+        with self.assertRaises(TypeError):
+            i2c.iaddr_bytes = "1"
+
+        with self.assertRaises(ValueError):
+            i2c.iaddr_bytes = -1
+
+        with self.assertRaises(ValueError):
+            i2c.iaddr_bytes = 5
+
+        i2c.iaddr_bytes = 0
+        self.assertEqual(i2c.iaddr_bytes, 0)
+
+        i2c.iaddr_bytes = 1
+        self.assertEqual(i2c.iaddr_bytes, 1)
+
+        i2c.iaddr_bytes = 3
+        self.assertEqual(i2c.iaddr_bytes, 3)
+
+    def test_read(self):
+        self.assertEqual(len(self.i2c.read(0, self.i2c_size)), self.i2c_size)
+        self.assertEqual(len(self.i2c.read(0, 100)), 100)
+        self.assertEqual(len(self.i2c.read(13, 13)), 13)
+        self.assertEqual(len(self.i2c.read(13, 1)), 1)
+
+    def test_write(self):
+
+        # 0 - 0xff
+        w_buf = bytearray(range(self.i2c_size))
+        self.assertEqual(self.i2c.write(0, bytes(w_buf)), self.i2c_size)
+        r_buf = self.i2c.read(0, self.i2c_size)
+        self.assertEqual(len(r_buf), self.i2c_size)
+        self.assertSequenceEqual(w_buf, r_buf)
+
+        # Random data
+        w_buf = bytearray(self.i2c_size)
+        for i in range(self.i2c_size):
+            w_buf[i] = random.randint(0, 255)
+        self.assertEqual(self.i2c.write(0, bytes(w_buf)), self.i2c_size)
+        r_buf = self.i2c.read(0, self.i2c_size)
+        self.assertEqual(len(r_buf), self.i2c_size)
+        self.assertSequenceEqual(w_buf, r_buf)
+
+        # Not aligned write
+        data = "a212131edada123qdadaeqeqdsadskfljfjfj"
+        for addr in range(1, 200, 2):
+            self.assertEqual(self.i2c.write(addr, data), len(data))
+            self.assertEqual(self.i2c.read(addr, len(data)).decode("ascii"), data)
+
+    def test_ioctl_read(self):
+        self.assertEqual(len(self.i2c.ioctl_read(0, self.i2c_size)), self.i2c_size)
+        self.assertEqual(len(self.i2c.ioctl_read(0, 100)), 100)
+        self.assertEqual(len(self.i2c.ioctl_read(13, 13)), 13)
+        self.assertEqual(len(self.i2c.ioctl_read(13, 1)), 1)
+
+    def test_ioctl_ioctl_write(self):
+        # 0 - 0xff
+        w_buf = bytearray(range(self.i2c_size))
+        self.assertEqual(self.i2c.ioctl_write(0, bytes(w_buf)), self.i2c_size)
+        r_buf = self.i2c.ioctl_read(0, self.i2c_size)
+        self.assertEqual(len(r_buf), self.i2c_size)
+        self.assertSequenceEqual(w_buf, r_buf)
+
+        # Random data
+        w_buf = bytearray(self.i2c_size)
+        for i in range(self.i2c_size):
+            w_buf[i] = random.randint(0, 255)
+        self.assertEqual(self.i2c.ioctl_write(0, bytes(w_buf)), self.i2c_size)
+        r_buf = self.i2c.ioctl_read(0, self.i2c_size)
+        self.assertEqual(len(r_buf), self.i2c_size)
+        self.assertSequenceEqual(w_buf, r_buf)
+
+        # Not aligned write
+        data = "a212131edada123qdadaeqeqdsadskfljfjfj"
+        for addr in range(1, 200, 2):
+            self.assertEqual(self.i2c.ioctl_write(addr, data), len(data))
+            self.assertEqual(self.i2c.ioctl_read(addr, len(data)).decode("ascii"), data)
+
+
+if __name__ == '__main__':
+    unittest.main()