diff options
| author | Alex Williams <alex.williams@ni.com> | 2018-10-12 17:17:13 -0700 | 
|---|---|---|
| committer | Brent Stapleton <bstapleton@g.hmc.edu> | 2018-10-19 10:17:08 -0700 | 
| commit | a830fab2a00acd158f14d716e3493ef50afd8aeb (patch) | |
| tree | 96425e10026d94dfcef46ebbfa8cc5bccb5cf90f /mpm | |
| parent | 0e30a5ca0872762a36be15f030a763c7f67dd003 (diff) | |
| download | uhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.tar.gz uhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.tar.bz2 uhd-a830fab2a00acd158f14d716e3493ef50afd8aeb.zip | |
mpm: Add i2c APIs for simple transfers
Diffstat (limited to 'mpm')
| -rw-r--r-- | mpm/include/mpm/i2c/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | mpm/include/mpm/i2c/i2c_iface.hpp | 45 | ||||
| -rw-r--r-- | mpm/include/mpm/i2c/i2c_python.hpp | 18 | ||||
| -rw-r--r-- | mpm/include/mpm/i2c/i2c_regs_iface.hpp | 34 | ||||
| -rw-r--r-- | mpm/lib/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | mpm/lib/i2c/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | mpm/lib/i2c/i2c_regs_iface.cpp | 136 | ||||
| -rw-r--r-- | mpm/lib/i2c/i2cdev.c | 84 | ||||
| -rw-r--r-- | mpm/lib/i2c/i2cdev.h | 58 | ||||
| -rw-r--r-- | mpm/lib/i2c/i2cdev_iface.cpp | 95 | ||||
| -rw-r--r-- | mpm/python/pyusrp_periphs/n3xx/pyusrp_periphs.cpp | 2 | 
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(); | 
