// // 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)); }