1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
//
// Copyright 2013-2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
/*
* x300_mb_eeprom_iface
* This interface was created to prevent MB EEPROM corruption while reading
* data during discovery. For devices with firmware version newer than 5.0,
* the EEPROM data is read into firmware memory and available without
* claiming the device. For devices with firmware versions 5.0 and older,
* the code makes sure to claim the device before driving the I2C bus. This
* has the unfortunate side effect of preventing multiple processes from
* discovering the device simultaneously, but is far better than having EEPROM
* corruption.
*/
#include "x300_mb_eeprom_iface.hpp"
#include "x300_claim.hpp"
#include "x300_fw_common.h"
#include "x300_impl.hpp"
#include "x300_regs.hpp"
#include <uhd/exception.hpp>
#include <uhd/utils/byteswap.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/platform.hpp>
#include <boost/thread.hpp>
#include <memory>
using namespace uhd;
static const uint32_t X300_FW_SHMEM_IDENT_MIN_VERSION = 0x50001;
class x300_mb_eeprom_iface_impl : public x300_mb_eeprom_iface
{
public:
x300_mb_eeprom_iface_impl(wb_iface::sptr wb, i2c_iface::sptr i2c) : _wb(wb), _i2c(i2c)
{
_compat_num = _wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_COMPAT_NUM));
}
~x300_mb_eeprom_iface_impl() override
{
/* NOP */
}
/*!
* Write bytes over the i2c.
* \param addr the address
* \param buf the vector of bytes
*/
void write_i2c(uint16_t addr, const byte_vector_t& buf) override
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
if (uhd::usrp::x300::claim_status(_wb) != uhd::usrp::x300::CLAIMED_BY_US) {
throw uhd::io_error("Attempted to write MB EEPROM without claim to device.");
}
_i2c->write_i2c(addr, buf);
}
/*!
* Read bytes over the i2c.
* \param addr the address
* \param num_bytes number of bytes to read
* \return a vector of bytes
*/
byte_vector_t read_i2c(uint16_t addr, size_t num_bytes) override
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
byte_vector_t bytes;
if (_compat_num > X300_FW_SHMEM_IDENT_MIN_VERSION) {
bytes = read_eeprom(addr, 0, num_bytes);
} else {
auto status = uhd::usrp::x300::claim_status(_wb);
// Claim device before driving the I2C bus
if (status == uhd::usrp::x300::CLAIMED_BY_US
or uhd::usrp::x300::try_to_claim(_wb)) {
bytes = _i2c->read_i2c(addr, num_bytes);
if (status != uhd::usrp::x300::CLAIMED_BY_US) {
// We didn't originally have the claim, so give it up
uhd::usrp::x300::release(_wb);
}
}
}
return bytes;
}
/*!
* Write bytes to an eeprom.
* \param addr the address
* \param offset byte offset
* \param buf the vector of bytes
*/
void write_eeprom(uint16_t addr, uint16_t offset, const byte_vector_t& buf) override
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
if (uhd::usrp::x300::claim_status(_wb) != uhd::usrp::x300::CLAIMED_BY_US) {
throw uhd::io_error("Attempted to write MB EEPROM without claim to device.");
}
_i2c->write_eeprom(addr, offset, buf);
}
/*!
* Read bytes from an eeprom.
* \param addr the address
* \param offset byte offset
* \param num_bytes number of bytes to read
* \return a vector of bytes
*/
byte_vector_t read_eeprom(uint16_t addr, uint16_t offset, size_t num_bytes) override
{
UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR);
byte_vector_t bytes;
uhd::usrp::x300::claim_status_t status = uhd::usrp::x300::claim_status(_wb);
if (_compat_num >= X300_FW_SHMEM_IDENT_MIN_VERSION) {
// Get MB EEPROM data from firmware memory
if (num_bytes == 0)
return bytes;
size_t bytes_read = 0;
for (size_t word = offset / 4; bytes_read < num_bytes; word++) {
uint32_t value =
byteswap(_wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_IDENT + word)));
for (size_t byte = offset % 4; byte < 4 and bytes_read < num_bytes;
byte++) {
bytes.push_back(uint8_t((value >> (byte * 8)) & 0xff));
bytes_read++;
}
}
} else {
// Claim device before driving the I2C bus
if (status == uhd::usrp::x300::CLAIMED_BY_US
or uhd::usrp::x300::try_to_claim(_wb)) {
bytes = _i2c->read_eeprom(addr, offset, num_bytes);
if (status != uhd::usrp::x300::CLAIMED_BY_US) {
// We didn't originally have the claim, so give it up
uhd::usrp::x300::release(_wb);
}
}
}
return bytes;
}
private:
wb_iface::sptr _wb;
i2c_iface::sptr _i2c;
uint32_t _compat_num;
};
x300_mb_eeprom_iface::~x300_mb_eeprom_iface(void)
{
/* NOP */
}
x300_mb_eeprom_iface::sptr x300_mb_eeprom_iface::make(
wb_iface::sptr wb, i2c_iface::sptr i2c)
{
return std::make_shared<x300_mb_eeprom_iface_impl>(wb, i2c->eeprom16());
}
|