Add C shared library code to make I2C calls from user space
See README for detailed notes.
BUG=chrome-os-partner:5172
TEST=manual (tested functionality of this API from Python test code)
Change-Id: I7dbc8356de744f4cf72160299affeafae193c395
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..69b4855
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8d57e72
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,32 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+CC?=cc
+CFLAGS+=-g -Iinclude/ -Wimplicit -fPIC -Wall
+LDFLAGS+=-luuid
+EXEC_NAME=smogcheck
+SONAME=libsmogcheck.so
+
+all: binary
+
+libs:
+ $(CC) $(CFLAGS) -o lib/lib_smogcheck.o -c lib/lib_smogcheck.c
+
+shared_libs: libs
+ $(CC) -shared -fPIC -o ${SONAME} lib/lib_smogcheck.o -W1,-h${SONAME} \
+ -lpthread -lrt -lbase
+
+binary: shared_libs
+ $(CC) $(CFLAGS) -o smogcheck.o -c smogcheck.c
+ $(CC) $(CFLAGS) -o smogcheck smogcheck.o lib/lib_smogcheck.o -luuid
+
+install:
+ install -D ${EXEC_NAME} $(DESTDIR)/usr/local/sbin/${EXEC_NAME}
+ install -D -m 0644 $(SONAME) $(DESTDIR)/usr/lib/$(SONAME)
+ ln -s $(SONAME) $(DESTDIR)/usr/lib/$(SONAME).0
+
+.PHONY: clean cov all
+
+clean:
+ rm -f *.o lib/*.o ${EXEC_NAME} ${SONAME}
diff --git a/README b/README
index 3395a42..e12d7b6 100644
--- a/README
+++ b/README
@@ -1,3 +1,18 @@
-Experiment C code to communicate with I2C bus directly.
+C library to communicate with I2C bus directly.
-TODO(tgao): fill in details.
+Notes:
+[1] The goal of this module is to enable direct I/O operations on I2C bus
+ with minimal overhead. Although i2c-tools has been made available in
+ Chromium OS, it still carries much overhead (as revealed by strace).
+
+[2] The original source code was posted to Linux Kernel Documentation
+ (http://www.mjmwired.net/kernel/Documentation/i2c/dev-interface) and
+ has been modified and adopted here. According to this source, there are
+ two versions of i2c-dev.h, one distributed with the Linux kernel and the
+ other with i2c-tools. This module depends on the kernel version.
+
+[3] The included Makefile creates a C shared library object (.so file), which
+ can then be imported into Python via ctypes for developing new Autotest
+ test cases for I2C devices.
+
+Source for i2c-tools: http://www.lm-sensors.org/wiki/I2CTools
diff --git a/include/lib/lib_smogcheck.h b/include/lib/lib_smogcheck.h
new file mode 100644
index 0000000..6020591
--- /dev/null
+++ b/include/lib/lib_smogcheck.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef __LIB_SMOGCHECK_H__
+#define __LIB_SMOGCHECK_H__
+
+#include <linux/types.h>
+
+int GetDeviceFile(int adapter_nr);
+int SetSlaveAddress(int fd, int addr);
+int WriteByte(int fd, __u8 reg, __u8 byte_val);
+int ReadByte(int fd, __u8 reg);
+int WriteWord(int fd, __u8 reg, __u16 word_val);
+int ReadWord(int fd, __u8 reg);
+
+#endif // __LIB_SMOGCHECK_H__
diff --git a/lib/lib_smogcheck.c b/lib/lib_smogcheck.c
new file mode 100644
index 0000000..9928ee9
--- /dev/null
+++ b/lib/lib_smogcheck.c
@@ -0,0 +1,206 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// lib_smogcheck.c: C code to make direct I2C calls using ioctl().
+//
+// Adapted and modified from:
+// http://www.mjmwired.net/kernel/Documentation/i2c/dev-interface
+
+#include "lib/lib_smogcheck.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define arraysize(x) (sizeof(x)/sizeof(x[0]))
+
+// Opens a device file for I/O operations.
+//
+// Args:
+// adapter_nr: an integer, adapater's number address.
+//
+// Returns:
+// a file descriptor (non-negative integer), ready to use. Or -1 if error.
+int GetDeviceFile(int adapter_nr) {
+ int file;
+ char filename[20] = {0};
+
+ snprintf(filename, arraysize(filename) - 1, "/dev/i2c-%d", adapter_nr);
+ printf("Attempt to open device %s\n", filename);
+ file = open(filename, O_RDWR);
+ if (file < 0) {
+ printf("Error opening file %d: %s\n", errno, strerror(errno));
+ return -1;
+ }
+ printf("Successfully opened device %s\n", filename);
+ return file;
+}
+
+// Sets I2C device address to communicate with.
+//
+// Args:
+// fd: an integer, open device file descriptor.
+// addr: an integer, I2C slave address to set.
+//
+// Returns:
+// 0 if success, -1 if error.
+int SetSlaveAddress(int fd, int addr) {
+ if (fd < 0) {
+ printf("Error: invalid file descriptor %d. Expect integer >= 0\n", fd);
+ return -1;
+ } else if (addr < 0x08 || addr > 0x77) {
+ printf("Error: invalid 7-bit I2C slave address %d. Expect range is " \
+ "an integer in [8, 112]\n", addr);
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SLAVE, addr) < 0) {
+ printf("Error communicating to slave address: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+// Precondition checks.
+//
+// Args:
+// fd: a non-negative integer, open device file descriptor.
+// reg: an 8-bit unsigned integer, non-negative register number.
+//
+// Returns:
+// ret: a boolean, true if preconditions are met and false if error.
+bool PreconditionCheck(int fd, __u8 reg) {
+ bool ret = false;
+ if (fd < 0) {
+ printf("Error: invalid file descriptor %d. Expect integer >= 0\n", fd);
+ } else if (reg < 0 || reg > 255) {
+ printf("Error: invalid register number %d. Expect integer in 0~255\n", reg);
+ } else {
+ ret = true;
+ }
+ return ret;
+}
+
+// Writes a byte to specified register address.
+//
+// Args:
+// fd: an integer, open device file descriptor.
+// reg: an 8-bit unsigned integer, non-negative register number.
+// byte_val: an 8-bit unsigned integer, value to write.
+//
+// Returns:
+// 0 if success, -1 if error.
+int WriteByte(int fd, __u8 reg, __u8 byte_val) {
+ union i2c_smbus_data data;
+ data.byte = byte_val;
+ struct i2c_smbus_ioctl_data args;
+ args.read_write = I2C_SMBUS_WRITE;
+ args.command = reg;
+ args.size = I2C_SMBUS_BYTE_DATA;
+ args.data = &data;
+
+ if (PreconditionCheck(fd, reg) != true) {
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SMBUS, &args)) {
+ printf("Error writing to register: %s\n", strerror(errno));
+ return -1;
+ }
+ printf("Wrote to register %d: 0x%x\n", reg, byte_val);
+ return 0;
+}
+
+// Reads a byte value from a specified register address.
+//
+// Args:
+// fd: an integer, open device file descriptor.
+// reg: an 8-bit unsigned integer, non-negative register number.
+//
+// Returns:
+// byte value read if success, -1 if error.
+int ReadByte(int fd, __u8 reg) {
+ union i2c_smbus_data data;
+ struct i2c_smbus_ioctl_data args;
+ args.read_write = I2C_SMBUS_READ;
+ args.command = reg;
+ args.size = I2C_SMBUS_BYTE_DATA;
+ args.data = &data;
+
+ if (PreconditionCheck(fd, reg) != true) {
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SMBUS, &args)) {
+ printf("Error reading from bus: %s\n", strerror(errno));
+ return -1;
+ }
+ printf("Read back from bus: 0x%x\n", data.byte);
+ return data.byte;
+}
+
+// Writes a word to specified register address.
+//
+// Args:
+// fd: an integer, open device file descriptor.
+// reg: an 8-bit unsigned integer, register number.
+// byte_val: a 16-bit unsigned integer, value to write.
+//
+// Returns:
+// 0 if success, -1 if error.
+int WriteWord(int fd, __u8 reg, __u16 word_val) {
+ union i2c_smbus_data data;
+ data.byte = word_val;
+ struct i2c_smbus_ioctl_data args;
+ args.read_write = I2C_SMBUS_WRITE;
+ args.command = reg;
+ args.size = I2C_SMBUS_WORD_DATA;
+ args.data = &data;
+
+ if (PreconditionCheck(fd, reg) != true) {
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SMBUS, &args)) {
+ printf("Error writing to register: %s\n", strerror(errno));
+ return -1;
+ }
+ printf("Wrote to register %d: 0x%x\n", reg, word_val);
+ return 0;
+}
+
+// Reads a word from a specified register address.
+//
+// Args:
+// fd: an integer, open device file descriptor.
+// reg: an 8-bit unsigned integer, register number.
+//
+// Returns:
+// word value read if success, -1 if error.
+int ReadWord(int fd, __u8 reg) {
+ union i2c_smbus_data data;
+ struct i2c_smbus_ioctl_data args;
+ args.read_write = I2C_SMBUS_READ;
+ args.command = reg;
+ args.size = I2C_SMBUS_WORD_DATA;
+ args.data = &data;
+
+ if (PreconditionCheck(fd, reg) != true) {
+ return -1;
+ }
+
+ if (ioctl(fd, I2C_SMBUS, &args)) {
+ printf("Error reading from bus: %s\n", strerror(errno));
+ return -1;
+ }
+ printf("Read back from bus: 0x%x\n", data.word);
+ return data.word;
+}
diff --git a/smogcheck.c b/smogcheck.c
new file mode 100644
index 0000000..0aba2de
--- /dev/null
+++ b/smogcheck.c
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// smogcheck.c: demonstrate use of lib_smogcheck.c
+
+#include <stdlib.h>
+#include "lib/lib_smogcheck.h"
+
+void ExitOnError(int val) {
+ if (val < 0) {
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main() {
+ int fd = -1;
+ int ret = -1;
+ const int kPcaSlaveAddress = 0x27;
+ const int kI2cBusAddress = 0x2;
+ const int kInaSlaveAddress = 0x40;
+ const __u8 kLedRegister = 0x3;
+ const __u8 kVoltageRegister = 0x2;
+ const __u8 kTurnOnLedOne = 0xfe;
+
+ fd = GetDeviceFile(kI2cBusAddress);
+ ExitOnError(fd);
+
+ // Enable LED 1
+ ret = SetSlaveAddress(fd, kPcaSlaveAddress);
+ ExitOnError(ret);
+
+ ret = WriteByte(fd, kLedRegister, kTurnOnLedOne);
+ ExitOnError(ret);
+
+ ret = ReadByte(fd, kLedRegister);
+ ExitOnError(ret);
+
+ // Read Backup Power voltage
+ ret = SetSlaveAddress(fd, kInaSlaveAddress);
+ ExitOnError(ret);
+
+ ret = ReadWord(fd, kVoltageRegister);
+ ExitOnError(ret);
+
+ return EXIT_SUCCESS;
+}