//
// Copyright 2010 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
#include "usrp1_ctrl.hpp"
#include "usrp_commands.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace uhd;
enum firmware_code {
USRP_FPGA_LOAD_SUCCESS,
USRP_FPGA_ALREADY_LOADED,
USRP_FIRMWARE_LOAD_SUCCESS,
USRP_FIRMWARE_ALREADY_LOADED
};
#define FX2_FIRMWARE_LOAD 0xa0
/***********************************************************************
* Helper Functions
**********************************************************************/
/*!
* Create a file hash
* The hash will be used to identify the loaded firmware and fpga image
* \param filename file used to generate hash value
* \return hash value in a size_t type
*/
static size_t generate_hash(const char *filename)
{
std::ifstream file(filename);
if (!file)
std::cerr << "error: cannot open input file " << filename << std::endl;
size_t hash = 0;
char ch;
while (file.get(ch)) {
boost::hash_combine(hash, ch);
}
if (!file.eof())
std::cerr << "error: file error " << filename << std::endl;
file.close();
return hash;
}
/*!
* Verify checksum of a Intel HEX record
* \param record a line from an Intel HEX file
* \return true if record is valid, false otherwise
*/
static bool checksum(std::string *record)
{
size_t len = record->length();
unsigned int i;
unsigned char sum = 0;
unsigned int val;
for (i = 1; i < len; i += 2) {
std::istringstream(record->substr(i, 2)) >> std::hex >> val;
sum += val;
}
if (sum == 0)
return true;
else
return false;
}
/*!
* Parse Intel HEX record
*
* \param record a line from an Intel HEX file
* \param len output length of record
* \param addr output address
* \param type output type
* \param data output data
* \return true if record is sucessfully read, false on error
*/
bool parse_record(std::string *record, unsigned int &len,
unsigned int &addr, unsigned int &type,
unsigned char* data)
{
unsigned int i;
std::string _data;
unsigned int val;
if (record->substr(0, 1) != ":")
return false;
std::istringstream(record->substr(1, 2)) >> std::hex >> len;
std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
std::istringstream(record->substr(7, 2)) >> std::hex >> type;
for (i = 0; i < len; i++) {
std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
data[i] = (unsigned char) val;
}
return true;
}
/*!
* USRP control implementation for device discovery and configuration
*/
class usrp_ctrl_impl : public usrp_ctrl {
public:
usrp_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport)
{
_ctrl_transport = ctrl_transport;
}
~usrp_ctrl_impl(void)
{
/* NOP */
}
int usrp_load_firmware(std::string filestring, bool force)
{
const char *filename = filestring.c_str();
size_t hash = generate_hash(filename);
size_t loaded_hash;
if (usrp_get_firmware_hash(loaded_hash) < 0) {
std::cerr << "firmware hash retrieval failed" << std::endl;
return -1;
}
if (!force && (hash == loaded_hash))
return USRP_FIRMWARE_ALREADY_LOADED;
//FIXME: verify types
unsigned int len;
unsigned int addr;
unsigned int type;
unsigned char data[512];
int ret;
std::ifstream file;
file.open(filename, std::ifstream::in);
if (!file.good()) {
std::cerr << "cannot open firmware input file" << std::endl;
return -1;
}
unsigned char reset_y = 1;
unsigned char reset_n = 0;
//hit the reset line
usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
&reset_y, 1);
while (!file.eof()) {
std::string record;
file >> record;
//check for valid record
if (!checksum(&record) ||
!parse_record(&record, len, addr, type, data)) {
std::cerr << "error: bad record" << std::endl;
file.close();
return -1;
}
//type 0x00 is data
if (type == 0x00) {
ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0,
data, len);
if (ret < 0) {
std::cerr << "error: usrp_control_write failed: ";
std::cerr << ret << std::endl;
file.close();
return -1;
}
}
//type 0x01 is end
else if (type == 0x01) {
usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
&reset_n, 1);
usrp_set_firmware_hash(hash);
file.close();
//wait for things to settle
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
return USRP_FIRMWARE_LOAD_SUCCESS;
}
//type anything else is unhandled
else {
std::cerr << "error: unsupported record" << std::endl;
file.close();
return -1;
}
}
//file did not end
std::cerr << "error: bad record" << std::endl;
file.close();
return -1;
}
int usrp_load_fpga(std::string filestring)
{
const char *filename = filestring.c_str();
size_t hash = generate_hash(filename);
size_t loaded_hash;
if (usrp_get_fpga_hash(loaded_hash) < 0) {
std::cerr << "fpga hash retrieval failed" << std::endl;
return -1;
}
if (hash == loaded_hash)
return USRP_FPGA_ALREADY_LOADED;
const int ep0_size = 64;
unsigned char buf[ep0_size];
int ret;
FILE *fp;
if ((fp = fopen(filename, "rb")) == NULL) {
std::cerr << "cannot open fpga input file" << std::endl;
fclose(fp);
return -1;
}
if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) {
std::cerr << "fpga load error" << std::endl;
fclose(fp);
return -1;
}
ssize_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER,
buf, n);
if (ret != n) {
std::cerr << "fpga load error " << ret << std::endl;
fclose(fp);
return -1;
}
}
if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) {
std::cerr << "fpga load error" << std::endl;
fclose(fp);
return -1;
}
usrp_set_fpga_hash(hash);
fclose(fp);
return 0;
}
int usrp_load_eeprom(std::string filestring)
{
const char *filename = filestring.c_str();
const uint16_t i2c_addr = 0x50;
//FIXME: verify types
int len;
unsigned int addr;
unsigned char data[256];
unsigned char sendbuf[17];
int ret;
std::ifstream file;
file.open(filename, std::ifstream::in);
if (!file.good()) {
std::cerr << "cannot open EEPROM input file" << std::endl;
return -1;
}
file.read((char *)data, 256);
len = file.gcount();
if(len == 256) {
std::cerr << "error: image size too large" << std::endl;
file.close();
return -1;
}
const int pagesize = 16;
addr = 0;
while(len > 0) {
sendbuf[0] = addr;
memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : len);
ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : len)+1);
if (ret < 0) {
std::cerr << "error: usrp_i2c_write failed: ";
std::cerr << ret << std::endl;
file.close();
return -1;
}
addr += pagesize;
len -= pagesize;
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
file.close();
return 0;
}
int usrp_set_led(int led_num, bool on)
{
return usrp_control_write_cmd(VRQ_SET_LED, on, led_num);
}
int usrp_get_firmware_hash(size_t &hash)
{
return usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
(unsigned char*) &hash, sizeof(size_t));
}
int usrp_set_firmware_hash(size_t hash)
{
return usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
(unsigned char*) &hash, sizeof(size_t));
}
int usrp_get_fpga_hash(size_t &hash)
{
return usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
(unsigned char*) &hash, sizeof(size_t));
}
int usrp_set_fpga_hash(size_t hash)
{
return usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
(unsigned char*) &hash, sizeof(size_t));
}
int usrp_tx_enable(bool on)
{
return usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0);
}
int usrp_rx_enable(bool on)
{
return usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0);
}
int usrp_tx_reset(bool on)
{
return usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0);
}
int usrp_control_write(boost::uint8_t request,
boost::uint16_t value,
boost::uint16_t index,
unsigned char *buff,
boost::uint16_t length)
{
return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType
request, // bRequest
value, // wValue
index, // wIndex
buff, // data
length); // wLength
}
int usrp_control_read(boost::uint8_t request,
boost::uint16_t value,
boost::uint16_t index,
unsigned char *buff,
boost::uint16_t length)
{
return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType
request, // bRequest
value, // wValue
index, // wIndex
buff, // data
length); // wLength
}
int usrp_control_write_cmd(uint8_t request, uint16_t value, uint16_t index)
{
return usrp_control_write(request, value, index, 0, 0);
}
int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
{
return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len);
}
int usrp_i2c_read(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len)
{
return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len);
}
private:
uhd::transport::usb_control::sptr _ctrl_transport;
};
/***********************************************************************
* Public make function for usrp_ctrl interface
**********************************************************************/
usrp_ctrl::sptr usrp_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){
return sptr(new usrp_ctrl_impl(ctrl_transport));
}