/* * Copyright 2014 Range Networks, Inc. * * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. 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. */ #include "rnrad1Core.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fpga_regs.h" #include #include #include #include "ad9862.h" #include using namespace ad9862; static const double POLLING_INTERVAL = 0.1; // seconds static const char *_get_usb_error_str (int usb_err) { switch (usb_err) { case LIBUSB_SUCCESS: return "Success (no error)"; case LIBUSB_ERROR_IO: return "Input/output error"; case LIBUSB_ERROR_INVALID_PARAM: return "Invalid parameter"; case LIBUSB_ERROR_ACCESS: return "Access denied (insufficient permissions)"; case LIBUSB_ERROR_NO_DEVICE: return "No such device (it may have been disconnected)"; case LIBUSB_ERROR_NOT_FOUND: return "Entity not found"; case LIBUSB_ERROR_BUSY: return "Resource busy"; case LIBUSB_ERROR_TIMEOUT: return "Operation timed out"; case LIBUSB_ERROR_OVERFLOW: return "Overflow"; case LIBUSB_ERROR_PIPE: return "Pipe error"; case LIBUSB_ERROR_INTERRUPTED: return "System call interrupted (perhaps due to signal)"; case LIBUSB_ERROR_NO_MEM: return "Insufficient memory"; case LIBUSB_ERROR_NOT_SUPPORTED: return "Operation not supported or unimplemented on this platform"; case LIBUSB_ERROR_OTHER: return "Unknown error"; } return "Unknown error"; } int usbMsg (struct libusb_device_handle *udh, int request, int value, int index, unsigned char *bytes, int len) { int requesttype = (request & 0x80) ? VRT_VENDOR_IN : VRT_VENDOR_OUT; int ret = libusb_control_transfer(udh, requesttype, request, value, index, bytes, len, 1000); if (ret < 0) { // we get EPIPE if the firmware stalls the endpoint. if (ret != LIBUSB_ERROR_PIPE) { LOG(ALERT) << "libusb_control_transfer failed: " << _get_usb_error_str(ret); } } return ret; } struct libusb_device_handle *rad1OpenInterface (libusb_device *dev, int interface, int altinterface) { libusb_device_handle *udh; int ret; if (libusb_open (dev, &udh) < 0) return NULL; if (dev != libusb_get_device (udh)){ LOG(ERR) << "Can't get USB device"; exit(0); } if ((ret = libusb_claim_interface (udh, interface)) < 0) { LOG(ERR) << "Can't claim USB interface: " << interface << " bc: " << _get_usb_error_str(ret); libusb_close (udh); return NULL; } if ((ret = libusb_set_interface_alt_setting (udh, interface, altinterface)) < 0) { LOG(ERR) << "Can't set USB alt interface: " << altinterface << " bc: " << _get_usb_error_str(ret); libusb_release_interface (udh, interface); libusb_close (udh); return NULL; } return udh; } static const int FIRMWARE_HASH_SLOT = 0; static const int FPGA_HASH_SLOT = 1; static const int hashSlotAddr[2] = { RAD1_HASH_SLOT_0_ADDR, RAD1_HASH_SLOT_1_ADDR }; bool rad1GetHash (libusb_device_handle *udh, int which, unsigned char hash[RAD1_HASH_SIZE]) { which &= 1; // we use the Cypress firmware upload command to fetch it. int r = libusb_control_transfer (udh, 0xc0, 0xa0, hashSlotAddr[which], 0, (unsigned char *) hash, RAD1_HASH_SIZE, 1000); if (r < 0) { LOG(ERR) << "Failed to get hash, USB err: " << _get_usb_error_str(r); } return r == RAD1_HASH_SIZE; } struct libusb_device *rad1FindDevice (int nth, bool allowFx2, libusb_context *ctx) { libusb_device **list; struct libusb_device *q; int nFound = 0; size_t cnt = libusb_get_device_list(ctx, &list); size_t i = 0; if (cnt < 0) LOG(ERR) << "libusb_get_device_list failed: " <<_get_usb_error_str(cnt); for (i = 0; i < cnt; i++) { q = list[i]; libusb_device_descriptor desc; int ret = libusb_get_device_descriptor(q, &desc); if (ret < 0) { LOG(ERR) << "usb_get_device_descriptor failed: " <<_get_usb_error_str(ret); } if ((desc.idVendor == USB_VID_FSF && desc.idProduct == USB_PID_FSF_RAD1) || (allowFx2 && desc.idVendor == USB_VID_CYPRESS && desc.idProduct == USB_PID_CYPRESS_FX2)) { if (nFound == nth) // return this one return q; nFound++; // keep looking } } // The list needs to be freed. Right now just release it if nothing is found. libusb_free_device_list(list, 1); return 0; // not found } static libusb_device_handle *openNthCmdInterface (int nth, libusb_context *ctx) { libusb_device *udev = rad1FindDevice (nth, false, ctx); if (udev == 0){ LOG(ERR) << "Failed to find rad1 num " << nth; return 0; } libusb_device_handle *udh; udh = rad1OpenInterface (udev, RAD1_CMD_INTERFACE, RAD1_CMD_ALTINTERFACE); if (udh == 0){ LOG(ERR) << "Can't open_cmd_interface"; return 0; } return udh; } enum rad1LoadStatus { ULS_ERROR = 0, ULS_OK, ULS_ALREADY_LOADED }; bool rad1SetHash (libusb_device_handle *udh, int which, const unsigned char hash[RAD1_HASH_SIZE]) { which &= 1; // we use the Cypress firmware down load command to jam it in. int r = libusb_control_transfer (udh, 0x40, 0xa0, hashSlotAddr[which], 0, (unsigned char *) hash, RAD1_HASH_SIZE, 1000); if (r < 0) { LOG(ERR) << "Failed to set hash bc: " << _get_usb_error_str(r); } return r == RAD1_HASH_SIZE; } static bool writeRam (libusb_device_handle *udh, unsigned char *buf, int startAddr, size_t len) { int addr; int n; int a; int quanta = MAX_EP0_PKTSIZE; for (addr = startAddr; addr < startAddr + (int) len; addr += quanta){ n = len + startAddr - addr; if (n > quanta) n = quanta; a = libusb_control_transfer(udh, 0x40, 0xA0, addr, 0, (unsigned char*)(buf + (addr - startAddr)), n, 1000); if (a < 0){ LOG(ERR) << "Write to RAM failed bc: " << _get_usb_error_str(a); return false; } } return true; } // Load intel format file into cypress FX2 (8051) bool rad1LoadFirmware (libusb_device_handle *udh, const char *filename, unsigned char hash[RAD1_HASH_SIZE]) { FILE *f = fopen (filename, "ra"); if (f == 0){ perror (filename); return false; } // hold CPU in reset while loading firmware unsigned char reset = 1; if (!writeRam (udh, &reset, 0xE600, 1)) goto fail; char s[1024]; int length; int addr; int type; unsigned char data[256]; unsigned char checksum, a; unsigned int b; int i; while (!feof(f)){ char *shut_up_gcc = fgets(s, sizeof (s), f); /* we should not use more than 263 bytes normally */ if(s[0]!=':'){ LOG(ERR) "File " << filename << " has invalid line: " << s; goto fail; } sscanf(s+1, "%02x", &length); sscanf(s+3, "%04x", &addr); sscanf(s+7, "%02x", &type); if(type==0){ a=length+(addr &0xff)+(addr>>8)+type; for(i=0;i 0){ if (usbMsg (udh, VRQ_FPGA_LOAD, 0, FL_XFER, buf, n) != n) goto fail; } if (usbMsg (udh, VRQ_FPGA_LOAD, 0, FL_END, 0, 0) != 0) goto fail; fclose (fp); if (!rad1SetHash (udh, FPGA_HASH_SLOT, hash)) LOG(ERR) << "Failed to write FPGA hash"; // On the rev1 RAD1, the {tx,rx}_{enable,reset} bits are // controlled over the serial bus, and hence aren't observed until // we've got a good fpga bitstream loaded. usbMsg(udh, VRQ_FPGA_SET_RESET, 0, 0, 0, 0); // fpga out of master reset // now these commands will work ok &= (usbMsg(udh, VRQ_FPGA_SET_TX_ENABLE, 0, 0, 0, 0)!=0); ok &= (usbMsg(udh, VRQ_FPGA_SET_RX_ENABLE, 0, 0, 0, 0)!=0); ok &= (usbMsg(udh, VRQ_FPGA_SET_TX_RESET , 1, 0, 0, 0)!=0); ok &= (usbMsg(udh, VRQ_FPGA_SET_RX_RESET , 1, 0, 0, 0)!=0); ok &= (usbMsg(udh, VRQ_FPGA_SET_TX_RESET , 0, 0, 0, 0)!=0); ok &= (usbMsg(udh, VRQ_FPGA_SET_RX_RESET , 0, 0, 0, 0)!=0); if (!ok) LOG(NOTICE) << "Failed to reset TX/RX path"; // Manually reset all regs except master control to zero. // FIXME may want to remove this when we rework FPGA reset strategy. // In the mean while, this gets us reproducible behavior. // FIXME!!! for (int i = 0; i < FR_USER_0; i++){ if (i == FR_MASTER_CTRL) continue; buf[0] = (0 >> 24) & 0xff; // MSB first buf[1] = (0 >> 16) & 0xff; buf[2] = (0 >> 8) & 0xff; buf[3] = (0 >> 0) & 0xff; usbMsg (udh, VRQ_SPI_WRITE, 0x00 | (i & 0x7f), ((SPI_ENABLE_FPGA & 0xff) << 8) | ((SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff), buf, 4); //rad1writeFpgaReg(udh, i, 0); } usbMsg (udh, VRQ_SET_LED, 1, 0, 0, 0); // led 1 off return true; fail: fclose (fp); return false; } bool computeHash (const char *filename, unsigned char hash[RAD1_HASH_SIZE]) { assert (RAD1_HASH_SIZE == 16); memset (hash, 0, RAD1_HASH_SIZE); FILE *fp = fopen (filename, "rb"); if (fp == 0){ perror (filename); return false; } int i = 0; while (!feof(fp)) {getc(fp);i++;} sprintf((char *)hash,"%d",i); //int r = md5_stream (fp, (void*) hash); fclose (fp); return true; } rad1LoadStatus rad1LoadFirmwareNth (int nth, const char *filename, bool force, libusb_context *ctx) { libusb_device_handle *udh = openNthCmdInterface (nth, ctx); if (udh == 0) return ULS_ERROR; rad1LoadStatus s; unsigned char file_hash[RAD1_HASH_SIZE]; unsigned char rad1_hash[RAD1_HASH_SIZE]; if (access (filename, R_OK) != 0){ perror (filename); s = ULS_ERROR; } else if (!computeHash (filename, file_hash)) s = ULS_ERROR; else if (!force && rad1GetHash (udh, FIRMWARE_HASH_SLOT, rad1_hash) && memcmp (file_hash, rad1_hash, RAD1_HASH_SIZE) == 0) { //printf("hash: %s %s\n",file_hash,rad1_hash); s = ULS_ALREADY_LOADED; } else if (!rad1LoadFirmware(udh, filename, file_hash)) s = ULS_ERROR; else s = ULS_OK; libusb_close(udh); switch (s){ case ULS_ALREADY_LOADED: // nothing changed... return ULS_ALREADY_LOADED; break; case ULS_OK: // we loaded firmware successfully. // It's highly likely that the board will renumerate (simulate a // disconnect/reconnect sequence), invalidating our current // handle. // FIXME. Turn this into a loop that rescans until we refind ourselves sleep(2); return ULS_OK; default: case ULS_ERROR: // some kind of problem return ULS_ERROR; } } static void load_status_msg (rad1LoadStatus s, const char *type, const char *filename) { switch (s){ case ULS_ERROR: LOG(ERR) << "Failed to load " << type << " " << filename; break; case ULS_ALREADY_LOADED: //LOG(ERR) << "Failed to already loaded " << type << " " << filename; break; case ULS_OK: break; } } bool rad1_load_standard_bits (int nth, bool force, const std::string fpga_filename, const std::string firmware_filename, libusb_context *ctx) { rad1LoadStatus s; const char *filename; const char *proto_filename; int hw_rev; // start by loading the firmware s = rad1LoadFirmwareNth (nth, firmware_filename.data(), force, ctx); load_status_msg (s, "firmware", firmware_filename.data()); if (s == ULS_ERROR) return false; // if we actually loaded firmware, we must reload fpga ... if (s == ULS_OK) force = true; libusb_device_handle *udh = openNthCmdInterface (nth, ctx); if (udh == 0) return false; unsigned char file_hash[RAD1_HASH_SIZE]; unsigned char rad1_hash[RAD1_HASH_SIZE]; filename = fpga_filename.data(); if (access (filename, R_OK) != 0){ perror (filename); s = ULS_ERROR; } else if (!computeHash (filename, file_hash)) s = ULS_ERROR; else if (!force && rad1GetHash (udh, FPGA_HASH_SLOT, rad1_hash) && memcmp (file_hash, rad1_hash, RAD1_HASH_SIZE) == 0) { s = ULS_ALREADY_LOADED; //printf("hash: %s %s\n",file_hash,rad1_hash); } else if (!rad1LoadFpga(udh, filename, file_hash)) s = ULS_ERROR; else s = ULS_OK; libusb_close(udh); load_status_msg (s, "fpga bitstream", fpga_filename.data()); if (s == ULS_ERROR) return false; return true; } rnrad1Core::rnrad1Core (int which_board, int interface, int altInterface, const std::string fpga_filename = "", const std::string firmware_filename = "", bool skipLoad = false) { mudh = 0; mctx = 0; mUsbDataRate = 16000000; mBytesPerPoll = ((int) POLLING_INTERVAL*mUsbDataRate); mFpgaMasterClockFreq = 52000000; memset (mFpgaShadows, 0, sizeof (mFpgaShadows)); LOG(INFO) << "rad1Core starting on board number "< MAX_EP0_PKTSIZE) return false; return usbMsg (mudh, VRQ_I2C_WRITE, i2c_addr, 0, buf, len) == len; } bool rnrad1Core::readI2c (int i2c_addr, unsigned char *buf, int len) { if (len <= 0) return false; if (usbMsg (mudh, VRQ_I2C_READ, i2c_addr, 0, (unsigned char *) buf, len) != len) return false; return true; } bool rnrad1Core::setAdcOffset (int which_adc, int offset) { if (which_adc < 0 || which_adc > 3) return false; return writeFpgaReg (FR_ADC_OFFSET_0 + which_adc, offset); } bool rnrad1Core::setDacOffset (int which_dac, int offset, int offset_pin) { int tx_a = (which_dac & 0x1) == 0; int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1); int hi = (offset >> 2); bool ok; if (tx_a){ ok = write9862 (REG_TX_A_OFFSET_LO, lo); ok &= write9862 (REG_TX_A_OFFSET_HI, hi); } else { ok = write9862 (REG_TX_B_OFFSET_LO, lo); ok &= write9862 (REG_TX_B_OFFSET_HI, hi); } return ok; } bool rnrad1Core::setAdcBufferBypass (int which_adc, bool bypass) { if (which_adc < 0 || which_adc > 1) return false; bool useA = (which_adc & 1) == 0; int reg = useA ? REG_RX_A : REG_RX_B; unsigned char cur_rx; unsigned char cur_pwr_dn; // If the input buffer is bypassed, we need to power it down too. bool ok = read9862 (reg, &cur_rx); ok &= read9862 (REG_RX_PWR_DN, &cur_pwr_dn); if (!ok) return false; if (bypass){ cur_rx |= RX_X_BYPASS_INPUT_BUFFER; cur_pwr_dn |= (useA ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); } else { cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER; cur_pwr_dn &= ~(useA ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); } ok &= write9862 (reg, cur_rx); ok &= write9862 (REG_RX_PWR_DN, cur_pwr_dn); return ok; } bool rnrad1Core::setDcOffsetClEnable(int bits, int mask) { return writeFpgaReg(FR_DC_OFFSET_CL_EN, (mFpgaShadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); } bool rnrad1Core::writeAuxDac (int which_dac, int value) { if (!(0 <= which_dac && which_dac < 4)){ LOG(ERR) << "Write to invalid dac" << which_dac; return false; } value &= 0x0fff; // mask to 12-bits // dac 0, 1, and 2 are really 8 bits. value = value >> 4; // shift value appropriately int regno = 36 + which_dac; return write9862(regno, value); } bool rnrad1Core::readAuxAdc (bool isTx, int which_adc, int *value) { *value = 0; unsigned char aux_adc_control = AUX_ADC_CTRL_REFSEL_A // on chip reference | AUX_ADC_CTRL_REFSEL_B; // on chip reference int rd_reg = 26; // base address of two regs to read for result // program the ADC mux bits if (isTx) aux_adc_control |= AUX_ADC_CTRL_SELECT_A2 | AUX_ADC_CTRL_SELECT_B2; else { rd_reg += 2; aux_adc_control |= AUX_ADC_CTRL_SELECT_A1 | AUX_ADC_CTRL_SELECT_B1; } // I'm not sure if we can set the mux and issue a start conversion // in the same cycle, so let's do them one at a time. write9862(34, aux_adc_control); if (which_adc == 0) aux_adc_control |= AUX_ADC_CTRL_START_A; else { rd_reg += 4; aux_adc_control |= AUX_ADC_CTRL_START_B; } // start the conversion write9862(34, aux_adc_control); // read the 10-bit result back unsigned char v_lo = 0; unsigned char v_hi = 0; bool r = read9862(rd_reg, &v_lo); r &= read9862(rd_reg, &v_hi); if (r) *value = ((v_hi << 2) | ((v_lo >> 6) & 0x3)) << 2; // format as 12-bit return r; } int rnrad1Core::readAuxAdc (bool isTx, int which_adc) { int value; if (!readAuxAdc (isTx, which_adc, &value)) return READ_FAILED; return value; } bool rnrad1Core::writeEeprom (int i2c_addr, int eeprom_offset, const std::string buf) { unsigned char cmd[2]; const unsigned char *p = (unsigned char *) buf.data(); // The simplest thing that could possibly work: // all writes are single byte writes. // // We could speed this up using the page write feature, // but we write so infrequently, why bother... int len = buf.size(); while (len-- > 0){ cmd[0] = eeprom_offset++; cmd[1] = *p++; bool r = writeI2c (i2c_addr, cmd, 2); usleep (10*1000); // delay 10ms worst case write time if (!r) return false; } return true; } std::string rnrad1Core::readEeprom (int i2c_addr, int eeprom_offset, int len) { if (len <= 0) return ""; char buf[len]; unsigned char *p = (unsigned char *) buf; // We setup a random read by first doing a "zero byte write". // Writes carry an address. Reads use an implicit address. unsigned char cmd[1]; cmd[0] = eeprom_offset; if (!writeI2c(i2c_addr, cmd, 1)) return ""; int orig_len = len; while (len > 0){ int n = std::min (len, MAX_EP0_PKTSIZE); if (!readI2c(i2c_addr, p, n)) {return "";} len -= n; p += n; } return std::string (buf, orig_len); } bool rnrad1Core::setLed (int which_led, bool on) { int r = usbMsg (mudh, VRQ_SET_LED, on, which_led, 0, 0); return r == 0; } bool rnrad1Core::writeFpgaReg (int regno, int value) //< 7-bit regno, 32-bit value { if (regno >= 0 && regno < MAX_REGS) mFpgaShadows[regno] = value; unsigned char buf[4]; buf[0] = (value >> 24) & 0xff; // MSB first buf[1] = (value >> 16) & 0xff; buf[2] = (value >> 8) & 0xff; buf[3] = (value >> 0) & 0xff; return writeSpi (0x00 | (regno & 0x7f), SPI_ENABLE_FPGA, SPI_FMT_MSB | SPI_FMT_HDR_1, buf, 4); } bool rnrad1Core::readFpgaReg (int regno, int *value) //< 7-bit regno, 32-bit value { *value = 0; unsigned char buf[4]; bool ok = readSpi (0x80 | (regno & 0x7f), SPI_ENABLE_FPGA, SPI_FMT_MSB | SPI_FMT_HDR_1, buf, 4); if (ok) *value = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; return ok; } int rnrad1Core::readFpgaReg (int regno) { int value; if (!readFpgaReg (regno, &value)) return READ_FAILED; return value; } bool rnrad1Core::write9862 (int regno, unsigned char value) { unsigned char buf[1]; buf[0] = value; return writeSpi(0x00 | (regno & 0x3f), SPI_ENABLE_CODEC_A, SPI_FMT_MSB | SPI_FMT_HDR_1, (unsigned char *) buf, 1); } bool rnrad1Core::read9862 (int regno, unsigned char *value) const { return readSpi(0x80 | (regno & 0x3f), SPI_ENABLE_CODEC_A, SPI_FMT_MSB | SPI_FMT_HDR_1, value, 1); } int rnrad1Core::read9862 (int regno) const { unsigned char value; if (!read9862 (regno, &value)) return READ_FAILED; return value; } bool rnrad1Core::writeSpi (int optional_header, int enables, int format, unsigned char *buf, int len) { return (usbMsg (mudh, VRQ_SPI_WRITE, optional_header, ((enables & 0xff) << 8) | (format & 0xff), buf, len) == len); } bool rnrad1Core::readSpi (int optional_header, int enables, int format, unsigned char *buf, int len) const { if (len <= 0 || len > MAX_EP0_PKTSIZE) return false; if (usbMsg (mudh, VRQ_SPI_READ, optional_header, ((enables & 0xff) << 8) | (format & 0xff), (unsigned char *) buf, len) != len) { return false;} return true; } bool rnrad1Core::start() { return true; }