aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williams <alex.williams@ni.com>2018-10-12 17:17:13 -0700
committerBrent Stapleton <bstapleton@g.hmc.edu>2018-10-19 10:17:08 -0700
commita830fab2a00acd158f14d716e3493ef50afd8aeb (patch)
tree96425e10026d94dfcef46ebbfa8cc5bccb5cf90f
parent0e30a5ca0872762a36be15f030a763c7f67dd003 (diff)
downloaduhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.tar.gz
uhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.tar.bz2
uhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.zip
mpm: Add i2c APIs for simple transfers
-rw-r--r--mpm/include/mpm/i2c/CMakeLists.txt11
-rw-r--r--mpm/include/mpm/i2c/i2c_iface.hpp45
-rw-r--r--mpm/include/mpm/i2c/i2c_python.hpp18
-rw-r--r--mpm/include/mpm/i2c/i2c_regs_iface.hpp34
-rw-r--r--mpm/lib/CMakeLists.txt1
-rw-r--r--mpm/lib/i2c/CMakeLists.txt12
-rw-r--r--mpm/lib/i2c/i2c_regs_iface.cpp136
-rw-r--r--mpm/lib/i2c/i2cdev.c84
-rw-r--r--mpm/lib/i2c/i2cdev.h58
-rw-r--r--mpm/lib/i2c/i2cdev_iface.cpp95
-rw-r--r--mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp2
11 files changed, 496 insertions, 0 deletions
diff --git a/mpm/include/mpm/i2c/CMakeLists.txt b/mpm/include/mpm/i2c/CMakeLists.txt
new file mode 100644
index 000000000..13851bca0
--- /dev/null
+++ b/mpm/include/mpm/i2c/CMakeLists.txt
@@ -0,0 +1,11 @@
+#
+# Copyright 2018 Ettus Research, National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+INSTALL(FILES
+ i2c_iface.hpp
+ i2c_regs_iface.hpp
+ DESTINATION ${INCLUDE_DIR}/mpm/i2c
+)
diff --git a/mpm/include/mpm/i2c/i2c_iface.hpp b/mpm/include/mpm/i2c/i2c_iface.hpp
new file mode 100644
index 000000000..f19711242
--- /dev/null
+++ b/mpm/include/mpm/i2c/i2c_iface.hpp
@@ -0,0 +1,45 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <memory>
+#include <string>
+
+namespace mpm { namespace i2c {
+
+ /*! Implementation of a uhd::i2c_iface that uses Linux's i2c-dev underneath.
+ */
+ class i2c_iface : public boost::noncopyable
+ {
+ public:
+ using sptr = std::shared_ptr<i2c_iface>;
+
+ /*!
+ * \param bus The path to the i2c bus segment used (e.g. "/dev/i2c3")
+ * \param addr Address of the slave device on the bus segment
+ * \param ten_bit_addr Whether the slave device's address is 10 bits
+ * \param timeout_ms Time to wait for ACK from slave device
+ */
+ static sptr make_i2cdev(
+ const std::string &bus,
+ const uint16_t addr,
+ const bool ten_bit_addr,
+ const int timeout_ms
+ );
+
+ /*!
+ * \param tx Buffer of data to send
+ * \param tx_len Size (in bytes) of TX buffer
+ * \param rx Buffer to hold read data
+ * \param rx_len Number of bytes to read
+ */
+ virtual int transfer(uint8_t *tx, size_t tx_len, uint8_t *rx, size_t rx_len) = 0;
+ };
+
+}}; /* namespace mpm::i2c */
+
diff --git a/mpm/include/mpm/i2c/i2c_python.hpp b/mpm/include/mpm/i2c/i2c_python.hpp
new file mode 100644
index 000000000..c4211e238
--- /dev/null
+++ b/mpm/include/mpm/i2c/i2c_python.hpp
@@ -0,0 +1,18 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include "i2c_regs_iface.hpp"
+#include "i2c_iface.hpp"
+
+void export_i2c() {
+ LIBMPM_BOOST_PREAMBLE("i2c")
+
+ bp::def("make_i2cdev_regs_iface", &mpm::i2c::make_i2cdev_regs_iface);
+
+}
+
diff --git a/mpm/include/mpm/i2c/i2c_regs_iface.hpp b/mpm/include/mpm/i2c/i2c_regs_iface.hpp
new file mode 100644
index 000000000..b63f00994
--- /dev/null
+++ b/mpm/include/mpm/i2c/i2c_regs_iface.hpp
@@ -0,0 +1,34 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <mpm/types/regs_iface.hpp>
+#include <mpm/i2c/i2c_iface.hpp>
+
+namespace mpm { namespace i2c {
+
+ /*! The regs_iface class can only be used for certain i2c devices
+ * For more control over the length of write and read data, use the lower-level
+ * i2c_iface
+ */
+ mpm::types::regs_iface::sptr make_i2c_regs_iface(
+ mpm::i2c::i2c_iface::sptr i2c_iface,
+ const size_t reg_addr_size
+ );
+
+ /*! Convenience factory for regs_iface based on i2c based on i2cdev
+ */
+ mpm::types::regs_iface::sptr make_i2cdev_regs_iface(
+ const std::string &bus,
+ const uint16_t addr,
+ const bool ten_bit_addr,
+ const int timeout_ms,
+ const size_t reg_addr_size
+ );
+
+}}; /* namespace mpm::i2c */
+
diff --git a/mpm/lib/CMakeLists.txt b/mpm/lib/CMakeLists.txt
index 26ab591d8..615b60b67 100644
--- a/mpm/lib/CMakeLists.txt
+++ b/mpm/lib/CMakeLists.txt
@@ -9,6 +9,7 @@ SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host)
ADD_SUBDIRECTORY(dboards)
ADD_SUBDIRECTORY(chips)
ADD_SUBDIRECTORY(spi)
+ADD_SUBDIRECTORY(i2c)
ADD_SUBDIRECTORY(types)
if(ENABLE_MYKONOS)
diff --git a/mpm/lib/i2c/CMakeLists.txt b/mpm/lib/i2c/CMakeLists.txt
new file mode 100644
index 000000000..a6af95ff8
--- /dev/null
+++ b/mpm/lib/i2c/CMakeLists.txt
@@ -0,0 +1,12 @@
+#
+# Copyright 2018 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+SET(I2C_SOURCES
+ ${CMAKE_CURRENT_SOURCE_DIR}/i2cdev_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/i2c_regs_iface.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/i2cdev.c
+)
+
+USRP_PERIPHS_ADD_OBJECT(i2c ${I2C_SOURCES})
diff --git a/mpm/lib/i2c/i2c_regs_iface.cpp b/mpm/lib/i2c/i2c_regs_iface.cpp
new file mode 100644
index 000000000..c476cf636
--- /dev/null
+++ b/mpm/lib/i2c/i2c_regs_iface.cpp
@@ -0,0 +1,136 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <mpm/types/regs_iface.hpp>
+#include <mpm/i2c/i2c_iface.hpp>
+#include <mpm/i2c/i2c_regs_iface.hpp>
+#include <mpm/exception.hpp>
+
+using mpm::types::regs_iface;
+
+/*! I2C implementation of the regs iface
+ *
+ * Uses i2cdev
+ */
+class i2c_regs_iface_impl : public regs_iface
+{
+public:
+
+ i2c_regs_iface_impl(
+ mpm::i2c::i2c_iface::sptr i2c_iface,
+ const size_t reg_addr_size
+ ) : _i2c_iface(i2c_iface),
+ _reg_addr_size(reg_addr_size)
+ {
+ if (reg_addr_size > 4) {
+ throw mpm::runtime_error("reg_addr_size too largs for i2c_regs_iface");
+ }
+ }
+
+ uint8_t peek8(
+ const uint32_t addr
+ ) {
+ uint8_t rx[1];
+ uint8_t tx[5];
+ int i = 0;
+ for (; i < _reg_addr_size; i++) {
+ tx[i] = 0xff & (addr >> 8*(_reg_addr_size-i-1));
+ }
+
+ int err = _i2c_iface->transfer(tx, _reg_addr_size, rx, 1);
+ if (err) {
+ throw mpm::runtime_error("I2C read failed");
+ }
+
+ return rx[0];
+ }
+
+ void poke8(
+ const uint32_t addr,
+ const uint8_t data
+ ) {
+ uint8_t tx[5];
+ int i = 0;
+ for (; i < _reg_addr_size; i++) {
+ tx[i] = 0xff & (addr >> 8*(_reg_addr_size-i-1));
+ }
+ tx[i] = data;
+
+ int err = _i2c_iface->transfer(tx, _reg_addr_size + 1, NULL, 0);
+ if (err) {
+ throw mpm::runtime_error("I2C write failed");
+ }
+ }
+
+ uint16_t peek16(
+ const uint32_t addr
+ ) {
+ uint8_t rx[2];
+ uint8_t tx[5];
+ int i = 0;
+ for (; i < _reg_addr_size; i++) {
+ tx[i] = 0xff & (addr >> 8*(_reg_addr_size-i-1));
+ }
+
+ int err = _i2c_iface->transfer(tx, _reg_addr_size, rx, 2);
+ if (err) {
+ throw mpm::runtime_error("I2C read failed");
+ }
+
+ uint16_t data = rx[0];
+ data = (data << 8) | rx[1];
+ return data;
+ }
+
+ void poke16(
+ const uint32_t addr,
+ const uint16_t data
+ ) {
+ uint8_t tx[6];
+ int i = 0;
+ for (; i < _reg_addr_size; i++) {
+ tx[i] = 0xff & (addr >> 8*(_reg_addr_size-i-1));
+ }
+ tx[i] = (data >> 8) & 0xff;
+ tx[i+1] = data & 0xff;
+
+ int err = _i2c_iface->transfer(tx, _reg_addr_size + 2, NULL, 0);
+ if (err) {
+ throw mpm::runtime_error("I2C write failed");
+ }
+ }
+
+private:
+ mpm::i2c::i2c_iface::sptr _i2c_iface;
+
+ const size_t _reg_addr_size;
+};
+
+regs_iface::sptr mpm::i2c::make_i2c_regs_iface(
+ mpm::i2c::i2c_iface::sptr i2c_iface,
+ const size_t reg_addr_size
+) {
+ return std::make_shared<i2c_regs_iface_impl>(
+ i2c_iface,
+ reg_addr_size
+ );
+}
+
+mpm::types::regs_iface::sptr mpm::i2c::make_i2cdev_regs_iface(
+ const std::string &bus,
+ const uint16_t addr,
+ const bool ten_bit_addr,
+ const int timeout_ms,
+ const size_t reg_addr_size
+) {
+ auto i2c_iface_sptr = mpm::i2c::i2c_iface::make_i2cdev(
+ bus, addr, ten_bit_addr, timeout_ms
+ );
+ return std::make_shared<i2c_regs_iface_impl>(
+ i2c_iface_sptr,
+ reg_addr_size
+ );
+}
diff --git a/mpm/lib/i2c/i2cdev.c b/mpm/lib/i2c/i2cdev.c
new file mode 100644
index 000000000..2ced96870
--- /dev/null
+++ b/mpm/lib/i2c/i2cdev.c
@@ -0,0 +1,84 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "i2cdev.h"
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <stdio.h>
+
+int i2cdev_open(int *fd, const char *device, const unsigned int timeout_ms)
+{
+ if (!fd)
+ {
+ fprintf(stderr, "%s: was passed a null pointer\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ *fd = open(device, O_RDWR);
+ if (*fd < 0) {
+ fprintf(stderr, "%s: Failed to open device. %s\n",
+ __func__, strerror(*fd));
+ return *fd;
+ }
+
+ if (ioctl(*fd, I2C_TIMEOUT, timeout_ms) < 0)
+ {
+ int err = errno;
+ fprintf(stderr, "%s: Failed to set timeout. %s\n",
+ __func__, strerror(err));
+ return err;
+ }
+
+ return 0;
+}
+
+int i2cdev_transfer(int fd, uint16_t addr, int ten_bit_addr,
+ uint8_t *tx, size_t tx_len,
+ uint8_t *rx, size_t rx_len)
+{
+ int err;
+ struct i2c_msg msgs[2];
+ int num_msgs = 0;
+ struct i2c_rdwr_ioctl_data i2c_data = {
+ .msgs = msgs,
+ };
+
+ if (tx && tx_len > 0) {
+ msgs[num_msgs].addr = addr;
+ msgs[num_msgs].buf = tx;
+ msgs[num_msgs].len = tx_len;
+ msgs[num_msgs].flags = ten_bit_addr ? I2C_M_TEN : 0;
+ num_msgs++;
+ }
+
+ if (rx && rx_len > 0) {
+ msgs[num_msgs].addr = addr;
+ msgs[num_msgs].buf = rx;
+ msgs[num_msgs].len = rx_len;
+ msgs[num_msgs].flags = ten_bit_addr ? I2C_M_TEN : 0;
+ msgs[num_msgs].flags |= I2C_M_RD;
+ num_msgs++;
+ }
+
+ i2c_data.nmsgs = num_msgs;
+ if (num_msgs <= 0)
+ return -EINVAL;
+
+ err = ioctl(fd, I2C_RDWR, &i2c_data);
+ if (err < 0) {
+ fprintf(stderr, "%s: Failed I2C_RDWR: %d\n", __func__, err);
+ perror("ioctl: \n");
+ return err;
+ }
+
+ return 0;
+}
+
diff --git a/mpm/lib/i2c/i2cdev.h b/mpm/lib/i2c/i2cdev.h
new file mode 100644
index 000000000..4c7f84972
--- /dev/null
+++ b/mpm/lib/i2c/i2cdev.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2018 Ettus Research, a National Instruments Company
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef _I2CDEV_H_FOUND_
+#define _I2CDEV_H_FOUND_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * This API provides access to i2c devices. It uses the character device API
+ * from the Linux kernel and i2c-tools
+ *
+ * The kernel documentation can be found at
+ * https://www.kernel.org/doc/Documentation/i2c/dev-interface
+ */
+
+/*! Initialize a i2cdev interface
+ *
+ * \param fd Return value of the file descriptor
+ * \param device Path of i2cdev device, e.g. "/dev/i2c0"
+ * \param timeout_ms Timeout to wait for ACK in ms
+ *
+ * \returns 0 if all is good, or an error code otherwise
+ */
+int i2cdev_open(int *fd, const char *device, const unsigned int timeout_ms);
+
+/*! Do an i2c transaction over i2cdev
+ * If both tx and rx are to be done in one transaction, first tx data is
+ * transmitted, followed by a repeated start condition, then the rx data is
+ * read.
+ *
+ * \param fd File descriptor for the i2cdev bus segment
+ * \param addr i2c device address
+ * \param ten_bit_addr Nonzero if true (typically 0)
+ * \param tx Buffer of data to be written to device
+ * \param tx_len Total number of non-addr bytes to be written
+ * \param rx Buffer where read data can be stored
+ * \param rx_len Total number of bytes to be read
+ *
+ * Assumption: spidev was configured properly beforehand.
+ *
+ * \returns 0 if all is golden
+ */
+int i2cdev_transfer(int fd, uint16_t addr, int ten_bit_addr,
+ uint8_t *tx, size_t tx_len,
+ uint8_t *rx, size_t rx_len);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _I2CDEV_H_FOUND_ */
diff --git a/mpm/lib/i2c/i2cdev_iface.cpp b/mpm/lib/i2c/i2cdev_iface.cpp
new file mode 100644
index 000000000..43aeea5e2
--- /dev/null
+++ b/mpm/lib/i2c/i2cdev_iface.cpp
@@ -0,0 +1,95 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+
+#include <mpm/i2c/i2c_iface.hpp>
+#include <mpm/exception.hpp>
+
+#include "i2cdev.h"
+
+#include <fcntl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include <boost/format.hpp>
+#include <iostream>
+
+using namespace mpm::i2c;
+
+/******************************************************************************
+ * Implementation
+ *****************************************************************************/
+class i2cdev_iface_impl : public i2c_iface
+{
+public:
+
+ i2cdev_iface_impl(
+ const std::string &device,
+ const uint16_t addr,
+ const bool ten_bit_addr,
+ const unsigned int timeout_ms
+ ) : _addr(addr),
+ _ten_bit_addr(ten_bit_addr),
+ _timeout_ms(timeout_ms)
+ {
+ if (i2cdev_open(
+ &_fd,
+ device.c_str(),
+ timeout_ms) < 0)
+ {
+ throw mpm::runtime_error(str(
+ boost::format("Could not initialize i2cdev device %s")
+ % device));
+ }
+
+ if (_fd < 0)
+ {
+ throw mpm::runtime_error(str(
+ boost::format("Could not open i2cdev device %s")
+ % device));
+ }
+ }
+
+ ~i2cdev_iface_impl()
+ {
+ close(_fd);
+ }
+
+ int transfer(uint8_t *tx, size_t tx_len, uint8_t *rx, size_t rx_len)
+ {
+ int ret = i2cdev_transfer(_fd, _addr, _ten_bit_addr,
+ tx, tx_len, rx, rx_len);
+
+ if (ret) {
+ throw mpm::runtime_error(str(
+ boost::format("I2C Transaction failed!")
+ ));
+ }
+
+ return ret;
+ }
+
+private:
+ int _fd;
+ const uint16_t _addr;
+ const bool _ten_bit_addr;
+ const unsigned int _timeout_ms;
+};
+
+/******************************************************************************
+ * Factory
+ *****************************************************************************/
+i2c_iface::sptr i2c_iface::make_i2cdev(
+ const std::string &bus,
+ const uint16_t addr,
+ const bool ten_bit_addr,
+ const int timeout_ms
+) {
+ return std::make_shared<i2cdev_iface_impl>(
+ bus, addr, ten_bit_addr, timeout_ms
+ );
+}
+
diff --git a/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp
index 048ca6a44..d6e66ad1e 100644
--- a/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp
+++ b/mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp
@@ -44,6 +44,7 @@ private:
#include "../converters.hpp"
#include <mpm/xbar_iface.hpp>
#include <mpm/types/types_python.hpp>
+#include <mpm/i2c/i2c_python.hpp>
#include <mpm/spi/spi_python.hpp>
#include <mpm/ad937x/ad937x_ctrl.hpp>
#include <mpm/dboards/magnesium_manager.hpp>
@@ -58,6 +59,7 @@ BOOST_PYTHON_MODULE(libpyusrp_periphs)
export_converter();
export_types();
export_spi();
+ export_i2c();
export_mykonos();
export_xbar();
export_magnesium();