/* -*- c++ -*- */
/*
 * Copyright 2004 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio 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 2, or (at your option)
 * any later version.
 * 
 * GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "usrp_stuff.h"


#include <stdexcept>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>		// FIXME should check with autoconf (nanosleep)
#include <algorithm>

#include "fusb_stuff.h"
#include <usb.h>

static const int AUX_RXAGC = 1<<8;
static const int POWER_UP = 1<<7; // enables power supply
static const int RX_TXN = 1<<6; // Tx only:  T/R antenna switch
static const int RX2_RX1N = 1<<6; // Rx only: antenna switch between Rx2 and Tx/Rx port
static const int ENABLE = 1<<5; // enables mixer
static const int AUX_SEN = 1<<4;
static const int AUX_SCLK = 1<<3;
static const int PLL_LOCK_DETECT = 1<<2;
static const int AUX_SDO = 1<<1;
static const int CLOCK_OUT = 1<<0;

/////// from  usrp_bytesex.h ///////////////////

/*********** Big Endian Case *****

static inline unsigned short int
bswap_16 (unsigned short int x)
{
  return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8));
}

static inline short int
host_to_usrp_short (short int x)
{
  return bswap_16 (x);
}

static inline short int
usrp_to_host_short (short int x)
{
  return bswap_16 (x);
}

*************************************/

static inline short int
host_to_usrp_short (short int x)
{
  return x;
}

static inline short int
usrp_to_host_short (unsigned short int x)
{
  return x;
}

/////////////////////////////////////////////////////

USRPTransmitObject::USRPTransmitObject(double usrp_sample_rate, double Ts)
{
// Default parameter values
sample_rate = usrp_sample_rate;

int which_board = 0;
int interp;
double sample_period, sample_ratio;
if (fabs(sample_rate-0.25) < 1e-3)
   {
    interp = 512;
    sample_period = 4e-6;
   }
else if (fabs(sample_rate-0.5) < 1e-3)
   {
    interp = 256;
    sample_period = 2e-6;
   }
else if (fabs(sample_rate-1.0) < 1e-3)
   {
    interp = 128;
    sample_period = 1.0e-6;
   }
else if (fabs(sample_rate-2.0) < 1e-3)
   {
    interp = 64;
    sample_period = 0.5e-6;
   }
else if (fabs(sample_rate-4.0) < 1e-3)
   {
    interp = 32;
    sample_period = 0.25e-6;
   }
else
   {
   printf("Error in 'USRPTransmitObject':  sample_rate\n");
   printf("  must be 0.25, 0.5, 1.0, 2.0, or 4.0 Msample/s\n");
   printf("  in this case, sample_rate = %5.3f Msample/s\n",sample_rate);
   exit(1);
   }
if (Ts < sample_period)
    sample_ratio = -sample_period/Ts;
else
    sample_ratio = Ts/sample_period;
sampling_count = 0;
if (fabs(sample_ratio - floor(sample_ratio+0.5)) > 0.01)
   {
    printf("Error in 'USRPTransmitObject':  sample_period\n");
    printf("  and Ts (i.e., simulation time step) must be\n");
    printf("  integer multiples of each other\n");
    printf("  in this case, sample_period = %5.3e, Ts = %5.3e\n",
               sample_period, Ts);
    printf("  and ratio is %5.3e\n",fabs(sample_ratio));
    exit(1);
   }
sampling_ratio = (int) sample_ratio;
utx = usrp_standard_tx::make(which_board,interp,1,-1);
if (utx == 0)
  {
   printf("Error in 'USRPTransmitObject': make function failed\n");
   exit(1);
  }

if (!utx->set_nchannels(1))
   {
    printf("Error in 'USRPTransmitObject': set_nchannels function failed\n");
    exit(1);
   }

// initialize both slots
for (slot = 1; slot >= 0; slot--)
   {
   set_slot(slot);
   set_dac_gain(-20.0);
   set_dco_freq(0.0);
   }

utx->start();
size_of_buf = utx->block_size();
N = size_of_buf/sizeof(short);
if (N % 2 == 1)
  {
   printf("Error in 'USRPTransmitObject':  N is not even\n");
   printf("  in this case, N = %d\n",N);
   exit(1);
  }
if ((buf = (short *) calloc(N,sizeof(short))) == NULL)
  {
   printf("Error in 'USRPTransmitObject':  calloc call failed\n");
   printf("  for N = %d\n",N);
   exit(1);
  }
count = 0;
}

USRPTransmitObject::~USRPTransmitObject()
{
unsigned char reg_val;

dbid = utx->daughterboard_id(slot);

if (dbid == USRP_DBID_FLEX_400_TX || 
    dbid == USRP_DBID_FLEX_2400_TX ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
   {
   set_flex_io(utx, slot, "disable");
   }

utx->wait_for_completion();

// reset 9862
if (!usrp_9862_read(utx->d_udh, slot, 0, &reg_val))
     {
      printf("error in 'USRPTransmitObject':  usrp_9862_read failed\n");
      exit(1);
     }
if (!usrp_9862_write(utx->d_udh, slot, 0, (reg_val | (1<<5))))
     {
      printf("error in 'USRPTransmitObject':  usrp_9862_write failed\n");
      exit(1);
     }
if (!usrp_9862_write(utx->d_udh, slot, 0, reg_val))
     {
      printf("error in 'USRPTransmitObject':  usrp_9862_write failed\n");
      exit(1);
     }

delete utx;
}

// enable rf daughtercard
void USRPTransmitObject::enable_rf_card()
{
dbid = utx->daughterboard_id(slot);
if (dbid == USRP_DBID_FLEX_400_TX || 
    dbid == USRP_DBID_FLEX_2400_TX ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
    {
     set_flex_io(utx, slot, "power_up");
     set_flex_io(utx, slot, "enable_mixer");
    }
}


void USRPTransmitObject::set_dco_freq(double dco_freq_in)
{
unsigned char reg_val;

dco_freq = dco_freq_in;

if (!utx->set_tx_freq(slot,dco_freq))
  {
   printf("Error in 'USRPTransmitObject': set_tx_freq function failed\n");
   exit(1);
  }

if (dco_freq == 0.0)
   {
    if (!usrp_9862_read(utx->d_udh, slot, 20, &reg_val))
        {
        printf("error in 'USRPTransmitObject':  usrp_9862_read failed\n");
        exit(1);
        }
    if (!usrp_9862_write(utx->d_udh, slot, 20, (reg_val & (~(1<<4)))))
        {
        printf("error in 'USRPTransmitObject':  usrp_9862_write failed\n");
        exit(1);
       }
   }
else /// turn on fine modulation stage in 9862 chip for non-zero freq
   {
    if (!usrp_9862_read(utx->d_udh, slot, 20, &reg_val))
        {
        printf("error in 'USRPTransmitObject':  usrp_9862_read failed\n");
        exit(1);
        }
    if (!usrp_9862_write(utx->d_udh, slot, 20, (reg_val | (1<<4))))
        {
        printf("error in 'USRPTransmitObject':  usrp_9862_write failed\n");
        exit(1);
       }
   }
}

void USRPTransmitObject::set_vco_freq(double vco_freq_in)
{
vco_freq = vco_freq_in;

dbid = utx->daughterboard_id(slot);
if (dbid == USRP_DBID_FLEX_400_TX || 
    dbid == USRP_DBID_FLEX_2400_TX ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
   {
   if (dbid == USRP_DBID_FLEX_2400_TX ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
      {
      if (vco_freq < 2.3e9 || vco_freq > 2.9e9)
         {
          printf("Warning in 'USRPTransmitObject': vco_freq out of range\n");
          printf("   For 2.4 GHz transceiver, valid vco_freq range is 2.3 to 2.9 GHz\n");
          printf("   In this case, you set vco_freq = %5.3e\n",vco_freq);
          vco_freq = 2.452e9;
          printf("   **** changing vco_freq to '%5.3e' **** \n",vco_freq);
         }
      }
   if (dbid == USRP_DBID_FLEX_400_TX)
      {
      if (vco_freq < 400e6 || vco_freq > 500e6)
         {
          printf("Warning in 'USRPTransmitObject': vco_freq out of range\n");
          printf("   For 400 MHz transceiver, valid vco_freq range is 400 to 500 MHz\n");
          printf("   In this case, you set vco_freq = %5.3e\n",vco_freq);
          vco_freq = 452.0e6;
          printf("   **** changing vco_freq to '%5.3e' **** \n",vco_freq);
         }
      }
   actual_vco_freq = 
          set_adf4360_freq(utx->d_udh, dbid, vco_freq, slot);

   if (dbid == USRP_DBID_FLEX_2400_TX ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
      {
	if (fabs(actual_vco_freq - vco_freq) > 1e3)
	{
          printf("Error in 'USRPTransmitObject': vco_freq\n");
          printf("   must be altered in steps of 4 MHz for\n");
          printf("   the RFX2400 Transceiver board\n");
          printf("   In this case, vco_freq = %5.3e\n",vco_freq);
          printf("   Next closest frequency = %5.3e\n",actual_vco_freq);
          exit(1);          
	}
      }
   if (dbid == USRP_DBID_FLEX_400_TX)
      {
	if (fabs(actual_vco_freq/2.0 - vco_freq) > 1e3)
	{
          printf("Error in 'USRPTransmitObject': vco_freq\n");
          printf("   must be altered in steps of 2 MHz for\n");
          printf("   the RFX400 Transceiver board\n");
          printf("   In this case, vco_freq = %5.3e\n",vco_freq);
          printf("   Next closest frequency = %5.3e\n",actual_vco_freq/2.0);
          exit(1);          
	}
      }
   }
else
   {
    printf("Error in 'USRPTransmitObject': vco_freq can only be adjusted\n");
    printf("  with RF boards\n");
    exit(1);
   }
}



void USRPTransmitObject::set_dac_gain(double dac_gain_in)
{
dac_gain = dac_gain_in;

if (dac_gain < utx->pga_min() || dac_gain > utx->pga_max())
  {
   printf("Error in 'USRPTransmitObject':  dac_gain\n");
   printf("  must be <= %5.3f and >= %5.3f dB\n",
               utx->pga_max(), utx->pga_min());
   printf("  in this case, dac_gain = %5.3e\n",dac_gain);
   exit(1);
  }  

if (slot == 0)
   {
   if (!utx->set_pga(0,dac_gain))  
      {
       printf("Error in 'USRPTransmitObject': set_pga function failed\n");
       exit(1);
      }
   if (!utx->set_pga(1,dac_gain))  
      {
       printf("Error in 'USRPTransmitObject': set_pga function failed\n");
       exit(1);
      }
   }
else
   {
   if (!utx->set_pga(2,dac_gain)) 
      {
       printf("Error in 'USRPTransmitObject': set_pga function failed\n");
       exit(1);
      }
   if (!utx->set_pga(3,dac_gain)) 
      {
       printf("Error in 'USRPTransmitObject': set_pga function failed\n");
       exit(1);
      }
   }
}


void USRPTransmitObject::set_slot(int slot_in)
{
slot = slot_in;

dbid = utx->daughterboard_id(slot);

if (slot == 0)
   {
   if (!utx->set_mux(0x0098))
      {
       printf("Error in 'USRPTransmitObject': set_mux function failed\n");
       exit(1);
      }
   }
else
   {
   if (!utx->set_mux(0x9800))
      {
       printf("Error in 'USRPTransmitObject': set_mux function failed\n");
       exit(1);
      }
   }
}

double USRPTransmitObject::get_vco_freq()
{
return(actual_vco_freq);
}

void USRPTransmitObject::stream_dac_out(double tx_a, double tx_b)
{
bool underrun;
int ret,i;

if (sampling_ratio >= 1)
   {
    for (i = 0; i < sampling_ratio; i++)
      {
       if (tx_a < -1.0)
          buf[count] = host_to_usrp_short(-32767);
       else if (tx_a > 1.0)
          buf[count] = host_to_usrp_short(32767);
       else
          buf[count] = host_to_usrp_short((short) (tx_a*32767.0));
       count++;
       if (tx_b < -1.0)
          buf[count] = host_to_usrp_short(-32767);
       else if (tx_b > 1.0)
          buf[count] = host_to_usrp_short(32767);
       else
          buf[count] = host_to_usrp_short((short) (tx_b*32767.0));
       count++;
       if (count >= N)
         {
          ret = utx->write(buf, size_of_buf, &underrun);
          if ((unsigned) ret != size_of_buf)
            { 
            printf("Warning in 'USRPTransmitObject': return value mismatch\n");
            printf("   ret = %d, sizeof(buf) = %d\n",ret,size_of_buf);
            }
          if (underrun)
            {
            printf("Warning in 'USRPTransmitObject': underrun occured\n");
            }
          count = 0;
         }
      }
   }
else if ((sampling_count++) == -sampling_ratio)
   {
    sampling_count = 1;

    if (tx_a < -1.0)
       buf[count] = host_to_usrp_short(-32767);
    else if (tx_a > 1.0)
       buf[count] = host_to_usrp_short(32767);
    else
       buf[count] = host_to_usrp_short((short) (tx_a*32767.0));
    count++;
    if (tx_b < -1.0)
       buf[count] = host_to_usrp_short(-32767);
    else if (tx_b > 1.0)
       buf[count] = host_to_usrp_short(32767);
    else
       buf[count] = host_to_usrp_short((short) (tx_b*32767.0));
    count++;
    if (count >= N)
       {
        ret = utx->write(buf, size_of_buf, &underrun);
        if ((unsigned) ret != size_of_buf)
            { 
            printf("Warning in 'USRPTransmitObject': return value mismatch\n");
            printf("   ret = %d, sizeof(buf) = %d\n",ret,size_of_buf);
            }
        if (underrun)
            {
            printf("Warning in 'USRPTransmitObject': underrun occured\n");
            }
        count = 0;
       }
   }
}



/////////////////////////////////////////////////////

USRPReceiveObject::USRPReceiveObject(double usrp_sample_rate, double Ts)
{
sample_rate = usrp_sample_rate;
int which_board = 0;
int decim;
int mode = 0;
int i;
double sample_period, sample_ratio;


if (fabs(sample_rate-0.25) < 1e-3)
   {
    decim = 256;
    sample_period = 4.0e-6;
   }
else if (fabs(sample_rate-0.5) < 1e-3)
   {
    decim = 128;
    sample_period = 2.0e-6;
   }
else if (fabs(sample_rate-1.0) < 1e-3)
   {
    decim = 64;
    sample_period = 1.0e-6;
   }
else if (fabs(sample_rate-2.0) < 1e-3)
   {
    decim = 32;
    sample_period = 0.5e-6;
   }
else if (fabs(sample_rate-4.0) < 1e-3)
   {
    decim = 16;
    sample_period = 0.25e-6;
   }
else
   {
   printf("Error in 'USRPReceiveObject':  sample_rate\n");
   printf("  must be 0.25, 0.5, 1.0, 2.0, or 4.0 Msample/s\n");
   printf("  in this case, sample_rate = %5.3f Msample/s\n",sample_rate);
   exit(1);
   }

if (Ts < sample_period)
    sample_ratio = -sample_period/Ts;
else
    sample_ratio = Ts/sample_period;

sampling_count = 0;

if (fabs(sample_ratio - floor(sample_ratio+0.5)) > 0.01)
   {
    printf("Error in 'USRPReceiveObject':  sample_period\n");
    printf("  and Ts (i.e., simulation time step) must be\n");
    printf("  integer multiples of each other\n");
    printf("  in this case, sample_period = %5.3e, Ts = %5.3e\n",
               sample_period, Ts);
    printf("  and ratio is %5.3e\n",fabs(sample_ratio));
    exit(1);
   }
sampling_ratio = (int) sample_ratio;

//urx = usrp_standard_rx::make(which_board,decim,1,-1,mode,2048,64);
urx = usrp_standard_rx::make(which_board,decim,1,-1,mode);
if (urx == 0)
  {
   printf("Error in 'USRPReceiveObject': make function failed\n");
   exit(1);
  }


/// set the number of receive channels to USB to be one (with both I and Q)
if (!urx->set_nchannels(1))
   {
    printf("Error in 'USRPReceiveObject': set_nchannels function failed\n");
    exit(1);
   }
///// set format of USB received signals to be 16-bits
/////   and to deliver both I and Q channels
if (!urx->set_format(0x00000300))
  {
   printf("Error in 'USRPReceiveObject': set_format function failed\n");
   exit(1);
  }

// sets all channel slot frequencies to zero
set_dco_freq(0.0);

// initialize both slots
for (slot = 1; slot >= 0; slot--)
   {
   set_slot(slot);

   dbid = urx->daughterboard_id(slot);
   if (dbid == USRP_DBID_FLEX_400_TX || 
       dbid == USRP_DBID_FLEX_2400_TX ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
      {
      if (dbid == USRP_DBID_FLEX_2400_TX ||
          dbid == USRP_DBID_FLEX_2400_TX_MIMO_A ||
          dbid == USRP_DBID_FLEX_2400_TX_MIMO_B)
         set_vco_freq(2.4e9);
      else
         set_vco_freq(400e6);
      set_mixer_gain(0.0);  // 0 dB, NOT zero gain (range is -30 to 40 dB)
      }
   set_adc_gain(0.0);
   enable_offset_cancel();
   }


urx->start();


size_of_buf = urx->block_size();
N = size_of_buf/sizeof(short);


if (N % 2 == 1)
  {
   printf("Error in 'USRPReceiveObject':  N is not even\n");
   printf("  in this case, N = %d\n",N);
   exit(1);
  }

if ((buf = (short *) calloc(N,sizeof(short))) == NULL)
  {
   printf("Error in 'USRPReceiveObject':  calloc call failed\n");
   printf("  for N = %d\n",N);
   exit(1);
  }
for (i = 0; i < N; i++)
   buf[i] = 0;

count = 0;
}


USRPReceiveObject::~USRPReceiveObject()
{
dbid = urx->daughterboard_id(slot);

if (dbid == USRP_DBID_FLEX_400_RX || 
    dbid == USRP_DBID_FLEX_2400_RX ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
   {
   set_flex_io(urx, slot, "disable");
   }

delete urx;

}


// enable rf daughtercard
void USRPReceiveObject::enable_rf_card()
{
dbid = urx->daughterboard_id(slot);
if (dbid == USRP_DBID_FLEX_400_RX || 
    dbid == USRP_DBID_FLEX_2400_RX ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
    {
     set_flex_io(urx, slot, "power_up");
     set_flex_io(urx, slot, "enable_mixer");
    }
}


void USRPReceiveObject::set_dco_freq(double dco_freq_in)
{
dco_freq = dco_freq_in;

///// Since we only have one I/Q USB receive channel, we only
///// control the phase of downconverter 0
if (!urx->set_ddc_phase(0,0))
  {
   printf("Error in 'USRPReceiveObject': set_ddc_phase function failed\n");
   exit(1);
  }
///// Since we only have one I/Q USB receive channel, we only
///// control the frequency of downconverter 0, but change all
///// channels just to be uniform
if (!urx->set_rx_freq(0,dco_freq))
  {
   printf("Error in 'USRPReceiveObject': set_rx_freq function failed\n");
   exit(1);
  }
if (!urx->set_rx_freq(1,dco_freq))
  {
   printf("Error in 'USRPReceiveObject': set_rx_freq function failed\n");
   exit(1);
  }
if (!urx->set_rx_freq(2,dco_freq))
  {
   printf("Error in 'USRPReceiveObject': set_rx_freq function failed\n");
   exit(1);
  }
if (!urx->set_rx_freq(3,dco_freq))
  {
   printf("Error in 'USRPReceiveObject': set_rx_freq function failed\n");
   exit(1);
  }

// run the set_slot routine to get right muxing of signals
// as dco_freq is switched between zero and nonzero frequencies
set_slot(slot);
}

void USRPReceiveObject::set_vco_freq(double vco_freq_in)
{
vco_freq = vco_freq_in;

dbid = urx->daughterboard_id(slot);
if (dbid == USRP_DBID_FLEX_400_RX || 
    dbid == USRP_DBID_FLEX_2400_RX ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
   {
   if (dbid == USRP_DBID_FLEX_2400_RX ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
      {
      if (vco_freq < 2.3e9 || vco_freq > 2.9e9)
         {
          printf("Warning in 'USRPReceiveObject': vco_freq out of range\n");
          printf("   For 2.4 GHz transceiver, valid vco_freq range is 2.3 to 2.9 GHz\n");
          printf("   In this case, you set vco_freq = %5.3e\n",vco_freq);
          vco_freq = 2.452e9;
          printf("   **** changing vco_freq to '%5.3e' **** \n",vco_freq);
         }
      }
   if (dbid == USRP_DBID_FLEX_400_RX)
      {
      if (vco_freq < 400e6 || vco_freq > 500e6)
         {
          printf("Warning in 'USRPReceiveObject': vco_freq out of range\n");
          printf("   For 400 MHz transceiver, valid vco_freq range is 400 to 500 MHz\n");
          printf("   In this case, you set vco_freq = %5.3e\n",vco_freq);
          vco_freq = 452.0e6;
          printf("   **** changing vco_freq to '%5.3e' **** \n",vco_freq);
         }
      }
   actual_vco_freq = 
          set_adf4360_freq(urx->d_udh, dbid, vco_freq, slot);

   if (dbid == USRP_DBID_FLEX_2400_RX ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
      {
	if (fabs(actual_vco_freq - vco_freq) > 1e3)
	{
          printf("Error in 'USRPReceiveObject': vco_freq\n");
          printf("   must be altered in steps of 4 MHz for\n");
          printf("   the RFX2400 Transceiver board\n");
          printf("   In this case, vco_freq = %5.3e\n",vco_freq);
          printf("   Next closest frequency = %5.3e\n",actual_vco_freq);
          exit(1);          
	}
      }
   if (dbid == USRP_DBID_FLEX_400_RX)
      {
	if (fabs(actual_vco_freq/2.0 - vco_freq) > 1e3)
	{
          printf("Error in 'USRPReceiveObject': vco_freq\n");
          printf("   must be altered in steps of 2 MHz for\n");
          printf("   the RFX400 Transceiver board\n");
          printf("   In this case, vco_freq = %5.3e\n",vco_freq);
          printf("   Next closest frequency = %5.3e\n",actual_vco_freq/2.0);
          exit(1);          
	}
      }
   }
else
   {
    printf("Error in 'USRPReceiveObject': vco_freq can only be adjusted\n");
    printf("  with RF boards\n");
    exit(1);
   }
}

void USRPReceiveObject::set_mixer_gain(double mixer_gain_in)
{
mixer_gain = mixer_gain_in;

dbid = urx->daughterboard_id(slot);
if (dbid == USRP_DBID_FLEX_400_RX || 
    dbid == USRP_DBID_FLEX_2400_RX ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
   {
   /// set gain of mixer using AUX_DAC_A
   if (mixer_gain < -30.0 || mixer_gain > 40.0)
      {
       printf("Error in 'USRPReceiveObject': mixer_gain is out of bounds\n");
       printf("   -> mixer_gain must be between -30 and 40 dB\n");
       printf("   in this case, mixer_gain = %5.3e\n",mixer_gain);
       exit(1);
      }
   /// AUX_DAC 0 is AUX_DAC_A
   if (!urx->write_aux_dac(slot,0,(int) floor(((40.0-mixer_gain)/(70.0*3.27)+0.05)*4095)))  
       {
       printf("Error in 'USRPReceiveObject': set_pga function failed\n");
       exit(1);
       }
   }
else
   {
    printf("Error in 'USRPReceiveObject': mixer_gain can only be adjusted\n");
    printf("  with RF boards\n");
    exit(1);
   }
}


void USRPReceiveObject::enable_offset_cancel()
{
offset_cancel = 1;
   if (slot == 0) // enable offset cancel on ADC 0,1
      {
      if (!urx->set_dc_offset_cl_enable(0xf,0x3))
         {
          printf("Error in 'USRPReceiveObject': set_dc_offset_cl_enable function failed\n");
          exit(1);
         }
      }
   else // enable offset cancel on ADC 2,3
      {
      if (!urx->set_dc_offset_cl_enable(0xf,0xc))
         {
          printf("Error in 'USRPReceiveObject': set_dc_offset_cl_enable function failed\n");
          exit(1);
         }
      }
}


void USRPReceiveObject::disable_offset_cancel()
{
offset_cancel = 0;
   if (slot == 0) // disable offset cancel on ADC 0,1
      {
      if (!urx->set_dc_offset_cl_enable(0x0,0x3))
         {
          printf("Error in 'USRPReceiveObject': set_dc_offset_cl_enable function failed\n");
          exit(1);
         }
      }
   else // disable offset cancel on ADC 2,3
      {
      if (!urx->set_dc_offset_cl_enable(0x0,0xc))
         {
          printf("Error in 'USRPReceiveObject': set_dc_offset_cl_enable function failed\n");
          exit(1);
         }
      }

}


void USRPReceiveObject::set_adc_gain(double adc_gain_in)
{
adc_gain = adc_gain_in;

if (adc_gain < urx->pga_min() || adc_gain > urx->pga_max())
  {
   printf("Error in 'USRPReceiveObject':  adc_gain\n");
   printf("  must be <= %5.3f and >= %5.3f dB\n",
               urx->pga_max(), urx->pga_min());
   printf("  in this case, adc_gain = %5.3e\n",adc_gain);
   exit(1);
  }  

if (slot == 0)
   {
   // set gain for ADC 0,1
   if (!urx->set_pga(0,adc_gain))  
      {
       printf("Error in 'USRPReceiveObject': set_pga function failed\n");
       exit(1);
      }
   if (!urx->set_pga(1,adc_gain))  
      {
       printf("Error in 'USRPReceiveObject': set_pga function failed\n");
       exit(1);
      }
   }
else
   {
   // set gain for ADC 2,3
   if (!urx->set_pga(2,adc_gain)) 
      {
       printf("Error in 'USRPReceiveObject': set_pga function failed\n");
       exit(1);
      }
   if (!urx->set_pga(3,adc_gain)) 
      {
       printf("Error in 'USRPReceiveObject': set_pga function failed\n");
       exit(1);
      }
   }

}


void USRPReceiveObject::set_slot(int slot_in)
{
slot = slot_in;

///// Note:  Since we choose the number of USB receive channels to be
/////        one (with both I and Q), we need to mux that I/Q 
/////        dowconverter/decimator to the appropriate ADC 
/////        (which will be the 0,1 ADC pair
/////        for slot 0, and the 2,3 ADC pair for slot 1)

dbid = urx->daughterboard_id(slot);

if (slot == 0)
   {
   // simply pass through rx_a/rx_b as rx_i/rx_q
   if (dco_freq == 0.0 ||
       dbid == USRP_DBID_FLEX_400_RX || 
       dbid == USRP_DBID_FLEX_2400_RX ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
     {
      if (!urx->set_mux(0x10101010)) 
         {
         printf("Error in 'USRPReceiveObject': set_mux function failed\n");
         exit(1);
         }
     }
   else   // for baseband receiver, only look at rx_a to get rx_i/rx_q
     {
      if (!urx->set_mux(0x11001100)) 
         {
         printf("Error in 'USRPReceiveObject': set_mux function failed\n");
         exit(1);
         }
     }
   }
else
   {
   // simply pass through rx_a/rx_b as rx_i/rx_q
   if (dco_freq == 0.0 ||
       dbid == USRP_DBID_FLEX_400_RX || 
       dbid == USRP_DBID_FLEX_2400_RX ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_A ||
       dbid == USRP_DBID_FLEX_2400_RX_MIMO_B)
     {
      if (!urx->set_mux(0x32323232)) 
         {
          printf("Error in 'USRPReceiveObject': set_mux function failed\n");
          exit(1);
         }
     }
   else                  // only look at rx_a to get rx_i/rx_q
     {
      if (!urx->set_mux(0x33223322)) 
         {
          printf("Error in 'USRPReceiveObject': set_mux function failed\n");
          exit(1);
         }
     }
   }

}

double USRPReceiveObject::get_vco_freq()
{
return(actual_vco_freq);
}

void USRPReceiveObject::stream_adc_in(double *rx_a, double *rx_b)
{
bool overrun;
int ret;
int i;

if (sampling_ratio >= 1)
   {
    for (i = 0; i < sampling_ratio; i++)
      {
       (*rx_a) = ((double) usrp_to_host_short(buf[count]))/32768.0;
       count++;
       (*rx_b) = ((double) usrp_to_host_short(buf[count]))/32768.0;
       count++;

       if (count >= N)
         {
          ret = urx->read(buf, size_of_buf, &overrun);
          if ((unsigned) ret != size_of_buf)
            {
             printf("Warning in 'usrp_receive_interface': return value mismatch\n");
             printf("   ret = %d, sizeof(buf) = %d\n",ret,size_of_buf);
            }
          if (overrun)
            {
             printf("Warning in 'usrp_receive_interface': overrun occured\n");
            }
          count = 0;
         }
      }
   }
else if ((sampling_count++) == -sampling_ratio)
   {
    sampling_count = 1;
    (*rx_a) = ((double) usrp_to_host_short(buf[count]))/32768.0;
    count++;
    (*rx_b) = ((double) usrp_to_host_short(buf[count]))/32768.0;
    count++;

    if (count >= N)
       {
        ret = urx->read(buf, size_of_buf, &overrun);
        if ((unsigned) ret != size_of_buf)
            {
             printf("Warning in 'usrp_receive_interface': return value mismatch\n");
             printf("   ret = %d, sizeof(buf) = %d\n",ret,size_of_buf);
            }
        if (overrun)
            {
             printf("Warning in 'usrp_receive_interface': overrun occured\n");
            }
        count = 0;
       }
   }
}


////////////////// migrated function from Python //////////////////
//////////////////   added by Michael Perrott 7/2006 //////////////

void set_flex_io(usrp_standard_tx *utx, int which_slot, char *control_string)
{
int AUX_RXAGC, POWER_UP, RX_TXN, RX2_RX1N, ENABLE;
int AUX_SEN, AUX_SCLK, PLL_LOCK_DETECT, AUX_SDO, CLOCK_OUT;
int dbid_val;
// double min_gain, max_gain, gain;

AUX_RXAGC = 1<<8; 
POWER_UP = 1<<7; // enables power supply
RX_TXN = 1<<6; // Tx only:  T/R antenna switch for Tx/Rx port
RX2_RX1N = 1<<6; // Rx only:  antenna switch between Rx2 and Tx/Rx port
ENABLE = 1<<5; // enables mixer
AUX_SEN = 1<<4;
AUX_SCLK = 1<<3;
PLL_LOCK_DETECT = 1<<2;
AUX_SDO = 1<<1;
CLOCK_OUT = 1<<0;

// Note:  POWER_UP and RX_TXN are asserted low

dbid_val = utx->daughterboard_id(which_slot);

if (dbid_val == USRP_DBID_FLEX_2400_TX ||
    dbid_val == USRP_DBID_FLEX_2400_TX_MIMO_A ||
    dbid_val == USRP_DBID_FLEX_2400_TX_MIMO_B)
   {
   if (strcmp(control_string,"power_up") == 0)
     {
     ///// be sure that power never goes off (i.e., POWER_UP is
     ///// always low) - otherwise, you need to unplug USRP board
     ///// and reinitialize for 2400 flex board.
     if (!utx->write_io(which_slot,0,(POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }

     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!utx->write_io(which_slot,0,(POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else if (strcmp(control_string,"enable_mixer") == 0)
     {
     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!utx->write_io(which_slot,ENABLE,
                                   (POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }

     }
   else if (strcmp(control_string,"disable") == 0)
     {
     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }
     if (!utx->write_io(which_slot,RX_TXN,
                                   (POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else
     {
     printf("error in 'set_flex_io':  invalid control_string\n");
     printf("    input control_string = %s\n",control_string);
     exit(1);
     }
   }
else if (dbid_val == USRP_DBID_FLEX_400_TX)
   {
   if (strcmp(control_string,"power_up") == 0)
     {
     //// Now enable the synthesizer and mixer
     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!utx->write_io(which_slot,(POWER_UP),
                                   (POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else if (strcmp(control_string,"enable_mixer") == 0)
     {
     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!utx->write_io(which_slot,(POWER_UP | ENABLE),
                                   (POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else if (strcmp(control_string,"disable") == 0)
     {
     if (!utx->_write_oe(which_slot,(POWER_UP | RX_TXN | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }
     if (!utx->write_io(which_slot,RX_TXN,
                                   (POWER_UP | RX_TXN | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else
     {
     printf("error in 'set_flex_io':  invalid control_string\n");
     printf("    input control_string = %s\n",control_string);
     exit(1);
     }
   }
else
   {
     printf("error in 'set_flex_io':  invalid dbid value\n");
     printf("    input dbid = %d\n",dbid_val);
     exit(1);
   }

}


void set_flex_io(usrp_standard_rx *urx, int which_slot, char *control_string)
{
int AUX_RXAGC, POWER_UP, RX_TXN, RX2_RX1N, ENABLE;
int AUX_SEN, AUX_SCLK, PLL_LOCK_DETECT, AUX_SDO, CLOCK_OUT;
int dbid_val;
// double min_gain, max_gain, gain;

AUX_RXAGC = 1<<8; 
POWER_UP = 1<<7; // enables power supply
RX_TXN = 1<<6; // Tx only:  T/R antenna switch for Tx/Rx port
RX2_RX1N = 1<<6; // Rx only:  antenna switch between Rx2 and Tx/Rx port
ENABLE = 1<<5; // enables mixer
AUX_SEN = 1<<4;
AUX_SCLK = 1<<3;
PLL_LOCK_DETECT = 1<<2;
AUX_SDO = 1<<1;
CLOCK_OUT = 1<<0;

// Note:  POWER_UP and RX2_RX1N are asserted low

dbid_val = urx->daughterboard_id(which_slot);

if (dbid_val == USRP_DBID_FLEX_2400_RX ||
    dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_B)
   {
   if (strcmp(control_string,"power_up") == 0)
     {
     ///// be sure that power never goes off (i.e., POWER_UP is
     ///// always low) - otherwise, you need to unplug USRP board
     ///// and reinitialize for 2400 flex board.
     if (!urx->write_io(which_slot,0,(POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }

     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!urx->write_io(which_slot,0,(POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }

     }
   else if (strcmp(control_string,"enable_mixer") == 0)
     {
     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!urx->write_io(which_slot,ENABLE,(POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }

     }
   else if (strcmp(control_string,"disable") == 0)
     {
     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }
     if (!urx->write_io(which_slot,0,(POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else
     {
     printf("error in 'set_flex_io':  invalid control_string\n");
     printf("    input control_string = %s\n",control_string);
     exit(1);
     }
   }
else if (dbid_val == USRP_DBID_FLEX_400_RX)
   {
   if (strcmp(control_string,"power_up") == 0)
     {
     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!urx->write_io(which_slot,(POWER_UP | RX2_RX1N),
                 (POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else if (strcmp(control_string,"enable_mixer") == 0)
     {
     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }

     if (!urx->write_io(which_slot,(POWER_UP | RX2_RX1N | ENABLE),
                 (POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else if (strcmp(control_string,"disable") == 0)
     {
     if (!urx->_write_oe(which_slot,(POWER_UP | RX2_RX1N | ENABLE), 0xffff))
      {
       printf("Error in 'set_flex_io': write_oe function failed\n");
       exit(1);
      }
     if (!urx->write_io(which_slot,0,(POWER_UP | RX2_RX1N | ENABLE)))
      {
       printf("Error in 'set_flex_io': write_io function failed\n");
       exit(1);
      }
     }
   else
     {
     printf("error in 'set_flex_io':  invalid control_string\n");
     printf("    input control_string = %s\n",control_string);
     exit(1);
     }
   }
else
   {
     printf("error in 'set_flex_io':  invalid dbid value\n");
     printf("    input dbid = %d\n",dbid_val);
     exit(1);
   }

}

double set_adf4360_freq(struct usb_dev_handle *d_udh, int dbid_val, 
                        double freq, int which_slot)
{
// R-register
int R_RSV, BSC, TEST, LDP, ABP, R_DIV;
// N-register
int DIVSEL, DIV2, CPGAIN, N_RSV, freq_mult, A, B;
// Control register
int P, PD, CP2, CP1, PL, MTLD, CPG, CP3S, PDP, MUXOUT, CR, PC;
// Overall
int R_reg, N_reg, C_reg;

double ref_clk_freq, phdet_freq, desired_n, prescalar_val;
int enable, format;
unsigned char cur_reg[4];
double actual_freq;


///////////// common settings ////////////

// R-register
R_RSV = 0;
BSC = 3;
TEST = 0;
LDP = 1;
ABP = 0;
// N-register
N_RSV = 0;
// Control register
PD = 0;
PL = 0;
MTLD = 1;
CPG = 0;
CP3S = 0;
PDP = 1;
MUXOUT = 1;
CR = 0;
PC = 1;

if (dbid_val == USRP_DBID_FLEX_2400_RX ||
    dbid_val == USRP_DBID_FLEX_2400_TX ||
    dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_A ||
    dbid_val == USRP_DBID_FLEX_2400_TX_MIMO_A ||
    dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_B ||
    dbid_val == USRP_DBID_FLEX_2400_TX_MIMO_B)
   {
     // R-register
     R_DIV = 16;
     // N-register
     DIVSEL = 0;
     DIV2 = 0;
     CPGAIN = 0;
     freq_mult = 1;
     // Control register
     P = 1;
     CP2 = 0;
     CP1 = 0;

     ref_clk_freq = 64e6;
   }
else if (dbid_val == USRP_DBID_FLEX_400_RX ||
         dbid_val == USRP_DBID_FLEX_400_TX)
   {
     // R-register
     R_DIV = 16;
     // N-register
     DIVSEL = 0;
     if (dbid_val == USRP_DBID_FLEX_400_TX)
        DIV2 = 1;
     else
        DIV2 = 0;
     CPGAIN = 0;
     freq_mult = 2;
     // Control register
     P = 0;
     CP2 = 7;
     CP1 = 7;

     ref_clk_freq = 64e6;
   }
else
   {
     printf("error in 'set_adf4360_freq':  invalid dbid value\n");
     printf("    input dbid = %d\n",dbid_val);
     exit(1);
   }

phdet_freq = ref_clk_freq/((double) R_DIV);
desired_n = floor((freq*freq_mult/phdet_freq) + 0.5);

if (P == 0)
   prescalar_val = 8.0;
else if (P == 1)
   prescalar_val = 16.0;
else
   prescalar_val = 32.0;

B = (int) floor(desired_n/prescalar_val);
A = (int) (desired_n - prescalar_val*((double) B));


actual_freq = desired_n*phdet_freq;
/**************** used for troubleshooting
 printf("A = %d, B = %d, actual = %5.3e\n",A,B,actual_freq);
****************************************/

R_reg = R_RSV<<22 | BSC<<20 | TEST<<19 | LDP<<18 |
        ABP<<16 | R_DIV<<2 | 1;
C_reg = P<<22 | PD<<20 | CP2<<17 | CP1<<14 | PL<<12 |
        MTLD<<11 | CPG<<10 | CP3S<<9 | PDP<<8 |
        MUXOUT<<5 | CR<<4 | PC<<2 | 0;
N_reg = DIVSEL<<23 | DIV2<<22 | CPGAIN<<21 | B<<8 |
        N_RSV<<7 | A<<2 | 2;

if (which_slot == 0)
   {
    if (dbid_val == USRP_DBID_FLEX_400_RX ||
        dbid_val == USRP_DBID_FLEX_2400_RX ||
        dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_A ||
        dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_B)
       enable = SPI_ENABLE_RX_A;
    else
       enable = SPI_ENABLE_TX_A;
   }
else
   {
    if (dbid_val == USRP_DBID_FLEX_400_RX ||
        dbid_val == USRP_DBID_FLEX_2400_RX ||
        dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_A ||
        dbid_val == USRP_DBID_FLEX_2400_RX_MIMO_B)
       enable = SPI_ENABLE_RX_B;
    else
       enable = SPI_ENABLE_TX_B;
   }

format = SPI_FMT_MSB | SPI_FMT_HDR_0;

/********************* used for troubleshooting 
int i,mask_reg;
printf("R_reg: ");
for (i = 23; i >= 0; i--)
   {
   mask_reg = 1<<i;
   printf("%d",(R_reg&mask_reg) == 0 ? 0 : 1);
   }
printf("\n");
printf("C_reg: ");
for (i = 23; i >= 0; i--)
   {
   mask_reg = 1<<i;
   printf("%d",(C_reg&mask_reg) == 0 ? 0 : 1);
   }
printf("\n");

printf("N_reg: ");
for (i = 23; i >= 0; i--)
   {
   mask_reg = 1<<i;
   printf("%d",(N_reg&mask_reg) == 0 ? 0 : 1);
   }
printf("\n");
********************************************/

cur_reg[2] = (unsigned char) (R_reg & 0xff);
cur_reg[1] = (unsigned char) ((R_reg>>8) & 0xff);
cur_reg[0] = (unsigned char) ((R_reg>>16) & 0xff);
usrp_spi_write (d_udh,0,enable, format,cur_reg,3);

cur_reg[2] = (unsigned char) (C_reg & 0xff);
cur_reg[1] = (unsigned char) ((C_reg>>8) & 0xff);
cur_reg[0] = (unsigned char) ((C_reg>>16) & 0xff);
usrp_spi_write (d_udh,0,enable, format,cur_reg,3);

mysleep(20);  // wait 20 ms

cur_reg[2] = (unsigned char) (N_reg & 0xff);
cur_reg[1] = (unsigned char) ((N_reg>>8) & 0xff);
cur_reg[0] = (unsigned char) ((N_reg>>16) & 0xff);
usrp_spi_write (d_udh,0,enable, format,cur_reg,3);

return(actual_freq);
}




///////////////////////////////////////////////////////////////////

////////////////// usrp_dbid.cc ////////////////


static struct {
  unsigned short	dbid;
  const char	       *name;
} dbid_map[] = {
  { USRP_DBID_BASIC_TX,         "Basic Tx" },
  { USRP_DBID_BASIC_RX,         "Basic Rx" },
  { USRP_DBID_DBS_RX,           "DBS Rx" },
  { USRP_DBID_TV_RX,            "TV Rx" },
  { USRP_DBID_FLEX_400_RX,      "Flex 400 Rx" },
  { USRP_DBID_FLEX_900_RX,      "Flex 900 Rx" },
  { USRP_DBID_FLEX_1200_RX,     "Flex 1200 Rx" },
  { USRP_DBID_FLEX_2400_RX,     "Flex 2400 Rx" },
  { USRP_DBID_FLEX_2400_RX_MIMO_A, "Flex 2400 Rx (MIMO A)" },
  { USRP_DBID_FLEX_2400_RX_MIMO_B, "Flex 2400 Rx (MIMO B)" },
  { USRP_DBID_FLEX_400_TX,      "Flex 400 Tx" },
  { USRP_DBID_FLEX_900_TX,      "Flex 900 Tx" },
  { USRP_DBID_FLEX_1200_TX,     "Flex 1200 Tx" },
  { USRP_DBID_FLEX_2400_TX,     "Flex 2400 Tx" },
  { USRP_DBID_FLEX_2400_TX_MIMO_A, "Flex 2400 Tx (MIMO A)" },
  { USRP_DBID_FLEX_2400_TX_MIMO_B, "Flex 2400 Tx (MIMO B)" },
  { USRP_DBID_TV_RX_REV_2,      "TV Rx Rev 2" },
  { USRP_DBID_DBS_RX_REV_2_1,   "DBS Rx Rev 2_1" },
  { USRP_DBID_LF_TX,            "LF Tx" },
  { USRP_DBID_LF_RX,            "LF Rx" },
  { USRP_DBID_EXPERIMENTAL_TX,  "Experimental Tx" },
  { USRP_DBID_EXPERIMENTAL_RX,  "Experimental Rx" },
};

void usrp_dbid_to_string (int dbid, char *str_out)
{
  if (dbid == -1)
    sprintf(str_out,"<none>");

  if (dbid == -2)
    {
    sprintf(str_out,"<invalid EEPROM contents>");
    return;
    }

  for (unsigned i = 0; i < NELEM (dbid_map); i++)
    if (dbid == dbid_map[i].dbid)
      {
      sprintf(str_out,dbid_map[i].name);
      return;
      }

  sprintf(str_out,"Unknown (0x%04x)", dbid);
}


////////////////// usrp_standard.cc /////////////

// #define USE_FPGA_TX_CORDIC


using namespace ad9862;


// The FPGA NCOs use a signed 32-bit frequency tuning word,
// but we keep only the most significant 14 bits.

static unsigned int
compute_freq_control_word_fpga (double master_freq, double target_freq,
				double *actual_freq, bool verbose)
{
  static const int NBITS = 14;
  
  int	v = (int) floor(target_freq / master_freq * pow (2.0, 32.0)+0.5);
  // v += 1 << (32 - NBITS - 1);		// add 1/2
  v = (v >> (32 - NBITS)) << (32 - NBITS);	// keep only top NBITS

  *actual_freq = v * master_freq / pow (2.0, 32.0);

  if (verbose)
    printf(
	     "compute_freq_control_word_fpga: target = %g  actual = %g  delta = %g\n",
	     target_freq, *actual_freq, *actual_freq - target_freq);

  return (unsigned int) v;
}

// The 9862 uses an unsigned 24-bit frequency tuning word and 
// a separate register to control the sign.

static unsigned int
compute_freq_control_word_9862 (double master_freq, double target_freq,
				double *actual_freq, bool verbose)
{
  double sign = 1.0;

  if (target_freq < 0)
    sign = -1.0;

  int	v = (int) floor(fabs (target_freq)/master_freq * pow (2.0, 24.0)+0.5);
  *actual_freq = v * master_freq / pow (2.0, 24.0) * sign;

  if (verbose)
    printf(
     "compute_freq_control_word_9862: target = %g  actual = %g  delta = %g  v = %8d\n",
     target_freq, *actual_freq, *actual_freq - target_freq, v);

  return (unsigned int) v;
}


// ----------------------------------------------------------------

static int 
real_rx_mux_value (int mux, int nchan)
{
  if (mux != -1)
    return mux;

  return 0x32103210;
}

usrp_standard_rx::usrp_standard_rx (int which_board,
				    unsigned int decim_rate,
				    int nchan, int mux, int mode,
				    int fusb_block_size, int fusb_nblocks)
  : usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks),
    d_nchan (1), d_sw_mux (0x0), d_hw_mux (0x0)
{
  if (!set_format(make_format())){
    printf( "usrp_standard_rx: set_format failed\n");
    throw std::runtime_error ("usrp_standard_rx::ctor");
  }
  if (!set_nchannels (nchan)){
    printf( "usrp_standard_rx: set_nchannels failed\n");
    throw std::runtime_error ("usrp_standard_rx::ctor");
  }
  if (!set_decim_rate (decim_rate)){
    printf( "usrp_standard_rx: set_decim_rate failed\n");
    throw std::runtime_error ("usrp_standard_rx::ctor");
  }
  if (!set_mux (real_rx_mux_value (mux, nchan))){
    printf( "usrp_standard_rx: set_mux failed\n");
    throw std::runtime_error ("usrp_standard_rx::ctor");
  }
  if (!set_fpga_mode (mode)){
    printf( "usrp_standard_rx: set_fpga_mode failed\n");
    throw std::runtime_error ("usrp_standard_rx::ctor");
  }

  for (int i = 0; i < MAX_CHAN_RX; i++){
    set_rx_freq(i, 0);
    set_ddc_phase(i, 0);
  }
}

usrp_standard_rx::~usrp_standard_rx ()
{
  // printf( "\nusrp_standard_rx: dtor\n");
}

bool
usrp_standard_rx::start ()
{
  if (!usrp_basic_rx::start ())
    return false;

  // add our code here

  return true;
}

bool
usrp_standard_rx::stop ()
{
  bool ok = usrp_basic_rx::stop ();

  // add our code here

  return ok;
}

usrp_standard_rx *
usrp_standard_rx::make (int which_board,
			unsigned int decim_rate,
			int nchan, int mux, int mode,
			int fusb_block_size, int fusb_nblocks)
{
  usrp_standard_rx *u = 0;
  
  try {
    u = new usrp_standard_rx (which_board, decim_rate,
			      nchan, mux, mode,
			      fusb_block_size, fusb_nblocks);
    return u;
  }
  catch (...){
    delete u;
    return 0;
  }

  return u;
}

bool
usrp_standard_rx::set_decim_rate(unsigned int rate)
{
  if ((rate & 0x1) || rate < 4 || rate > 256){
    printf( "usrp_standard_rx::set_decim_rate: rate must be EVEN and in [4, 256]\n");
    return false;
  }

  d_decim_rate = rate;
  set_usb_data_rate ((adc_rate () / rate * nchannels ())
		     * (2 * sizeof (short)));

  bool s = disable_rx ();
  bool ok = _write_fpga_reg (FR_DECIM_RATE, d_decim_rate/2 - 1);
  restore_rx (s);
  return ok;
}

bool usrp_standard_rx::set_nchannels (int nchan)
{
  if (!(nchan == 1 || nchan == 2 || nchan == 4))
    return false;

  d_nchan = nchan;

  return write_hw_mux_reg ();
}


// map software mux value to hw mux value
//
// Software mux value:
//
//    3                   2                   1                       
//  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-------+-------+-------+-------+-------+-------+-------+-------+
// |   Q3  |   I3  |   Q2  |   I2  |   Q1  |   I1  |   Q0  |   I0  |
// +-------+-------+-------+-------+-------+-------+-------+-------+
//
// Each 4-bit I field is either 0,1,2,3
// Each 4-bit Q field is either 0,1,2,3 or 0xf (input is const zero)
// All Q's must be 0xf or none of them may be 0xf
//
//
// Hardware mux value:
//
//    3                   2                   1                       
//  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +-----------------------+-------+-------+-------+-------+-+-----+
// |      must be zero     | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH |
// +-----------------------+-------+-------+-------+-------+-+-----+


static bool
map_sw_mux_to_hw_mux (int sw_mux, int *hw_mux_ptr)
{
  int i, t, qx_is_0xf, hw_mux_value;
  // confirm that all I's are either 0,1,2,3 

  for (i = 0; i < 8; i += 2){
    t = (sw_mux >> (4 * i)) & 0xf;
    if (!(0 <= t && t <= 3))
      return false;
  }

  // confirm that all Q's are either 0,1,2,3 or 0xf

  for (i = 1; i < 8; i += 2){
    t = (sw_mux >> (4 * i)) & 0xf;
    if (!(t == 0xf || (0 <= t && t <= 3)))
      return false;
  }

  // confirm that all Q inputs are 0xf (const zero input),
  // or none of them are 0xf

  int q_and = 1;
  int q_or =  0;

  for (i = 0; i < 4; i++){
    qx_is_0xf = ((sw_mux >> (8 * i + 4)) & 0xf) == 0xf;
    q_and &= qx_is_0xf;
    q_or  |= qx_is_0xf;
  }

  if (q_and || !q_or){		// OK
    hw_mux_value = 0;

    for (i = 0; i < 8; i++){
      t = (sw_mux >> (4 * i)) & 0x3;
      hw_mux_value |= t << (2 * i + 4);
    }

    if (q_and)
      hw_mux_value |= 0x8;	// all Q's zero

    *hw_mux_ptr = hw_mux_value;
    return true;
  }
  else
    return false;
}

bool
usrp_standard_rx::set_mux (int mux)
{
  if (!map_sw_mux_to_hw_mux (mux, &d_hw_mux))
    {
    printf("sw_mux = 0x%08x  hw_mux = 0x%08x\n", mux, d_hw_mux);
    return false;
    }

  // printf( "sw_mux = 0x%08x  hw_mux = 0x%08x\n", mux, d_hw_mux);

  d_sw_mux = mux;
  return write_hw_mux_reg ();
}

bool
usrp_standard_rx::write_hw_mux_reg ()
{
  bool s = disable_rx ();
  bool ok = _write_fpga_reg (FR_RX_MUX, d_hw_mux | d_nchan);
  restore_rx (s);
  return ok;
}


bool
usrp_standard_rx::set_rx_freq (int channel, double freq)
{
  if (channel < 0 || channel > MAX_CHAN_RX)
    return false;

  unsigned int v =
    compute_freq_control_word_fpga (adc_freq(),
				    freq, &d_rx_freq[channel],
				    d_verbose);

  return _write_fpga_reg (FR_RX_FREQ_0 + channel, v);
}

unsigned int
usrp_standard_rx::decim_rate () const { return d_decim_rate; }

int
usrp_standard_rx::nchannels () const { return d_nchan; }

int
usrp_standard_rx::mux () const { return d_sw_mux; }

double 
usrp_standard_rx::rx_freq (int channel) const
{
  if (channel < 0 || channel >= MAX_CHAN_RX)
    return 0;

  return d_rx_freq[channel];
}

bool
usrp_standard_rx::set_fpga_mode (int mode)
{
  return _write_fpga_reg (FR_MODE, mode);
}

bool
usrp_standard_rx::set_ddc_phase(int channel, int phase)
{
  if (channel < 0 || channel >= MAX_CHAN_RX)
    return false;

  return _write_fpga_reg(FR_RX_PHASE_0 + channel, phase);
}


// To avoid quiet failures, check for things that our code cares about.

static bool
rx_format_is_valid(unsigned int format)
{
  int width =  usrp_standard_rx::format_width(format);
  int want_q = usrp_standard_rx::format_want_q(format);

  if (!(width == 8 || width == 16))	// FIXME add other widths when valid
    return false;

  if (!want_q)		// FIXME remove check when the rest of the code can handle I only
    return false;

  return true;
}

bool
usrp_standard_rx::set_format(unsigned int format)
{
  if (!rx_format_is_valid(format))
    return false;

  return _write_fpga_reg(FR_RX_FORMAT, format);
}

unsigned int
usrp_standard_rx::format() const
{
  return d_fpga_shadows[FR_RX_FORMAT];
}

// ----------------------------------------------------------------

unsigned int 
usrp_standard_rx::make_format(int width, int shift, bool want_q, bool bypass_halfband)
{
  unsigned int format = 
    (((width << bmFR_RX_FORMAT_WIDTH_SHIFT) & bmFR_RX_FORMAT_WIDTH_MASK)
     | (shift << bmFR_RX_FORMAT_SHIFT_SHIFT) & bmFR_RX_FORMAT_SHIFT_MASK);

  if (want_q)
    format |= bmFR_RX_FORMAT_WANT_Q;
  if (bypass_halfband)
    format |= bmFR_RX_FORMAT_BYPASS_HB;

  return format;
}

int
usrp_standard_rx::format_width(unsigned int format)
{
  return (format & bmFR_RX_FORMAT_WIDTH_MASK) >> bmFR_RX_FORMAT_WIDTH_SHIFT;
}

int
usrp_standard_rx::format_shift(unsigned int format)
{
  return (format & bmFR_RX_FORMAT_SHIFT_MASK) >> bmFR_RX_FORMAT_SHIFT_SHIFT;
}

bool
usrp_standard_rx::format_want_q(unsigned int format)
{
  return (format & bmFR_RX_FORMAT_WANT_Q) != 0;
}

bool
usrp_standard_rx::format_bypass_halfband(unsigned int format)
{
  return (format & bmFR_RX_FORMAT_BYPASS_HB) != 0;
}

//////////////////////////////////////////////////////////////////


// tx data is timed to CLKOUT1 (64 MHz)
// interpolate 4x
// fine modulator enabled


static unsigned char tx_regs_use_nco[] = {
  REG_TX_IF,		(TX_IF_USE_CLKOUT1
			 | TX_IF_I_FIRST
			 | TX_IF_2S_COMP
			 | TX_IF_INTERLEAVED),
  REG_TX_DIGITAL,	(TX_DIGITAL_2_DATA_PATHS
			 | TX_DIGITAL_INTERPOLATE_4X)
};


static int
real_tx_mux_value (int mux, int nchan)
{
  if (mux != -1)
    return mux;

  switch (nchan){
  case 1:
    return 0x0098;
  case 2:
    return 0xba98;
  default:
    assert (0);
  }
}

usrp_standard_tx::usrp_standard_tx (int which_board,
				    unsigned int interp_rate,
				    int nchan, int mux,
				    int fusb_block_size, int fusb_nblocks)
  : usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks),
    d_sw_mux (0x8), d_hw_mux (0x81)
{
  if (!usrp_9862_write_many_all (d_udh, tx_regs_use_nco, sizeof (tx_regs_use_nco))){
    printf( "usrp_standard_tx: failed to init AD9862 TX regs\n");
    throw std::runtime_error ("usrp_standard_tx::ctor");
  }
  if (!set_nchannels (nchan)){
    printf( "usrp_standard_tx: set_nchannels failed\n");
    throw std::runtime_error ("usrp_standard_tx::ctor");
  }
  if (!set_interp_rate (interp_rate)){
    printf( "usrp_standard_tx: set_interp_rate failed\n");
    throw std::runtime_error ("usrp_standard_tx::ctor");
  }
  if (!set_mux (real_tx_mux_value (mux, nchan))){
    printf( "usrp_standard_tx: set_mux failed\n");
    throw std::runtime_error ("usrp_standard_tx::ctor");
  }

  for (int i = 0; i < MAX_CHAN_TX; i++){
    d_tx_modulator_shadow[i] = (TX_MODULATOR_DISABLE_NCO
				| TX_MODULATOR_COARSE_MODULATION_NONE);
    d_coarse_mod[i] = CM_OFF;
    set_tx_freq (i, 0);
  }
}

usrp_standard_tx::~usrp_standard_tx ()
{
  // printf( "\nusrp_standard_tx: dtor\n");
}

bool
usrp_standard_tx::start ()
{
  if (!usrp_basic_tx::start ())
    return false;

  // add our code here

  return true;
}

bool
usrp_standard_tx::stop ()
{
  bool ok = usrp_basic_tx::stop ();

  // add our code here

  return ok;
}

usrp_standard_tx *
usrp_standard_tx::make (int which_board,
			unsigned int interp_rate,
			int nchan, int mux,
			int fusb_block_size, int fusb_nblocks)
{
  usrp_standard_tx *u = 0;
  
  try {
    u = new usrp_standard_tx (which_board, interp_rate, nchan, mux,
			      fusb_block_size, fusb_nblocks);
    return u;
  }
  catch (...){
    delete u;
    return 0;
  }

  return u;
}

bool
usrp_standard_tx::set_interp_rate (unsigned int rate)
{
  // printf( "usrp_standard_tx::set_interp_rate\n");

  if ((rate & 0x3) || rate < 4 || rate > 512){
    printf( "usrp_standard_tx::set_interp_rate: rate must be in [4, 512] and a multiple of 4.\n");
    return false;
  }

  d_interp_rate = rate;
  set_usb_data_rate ((dac_rate () / rate * nchannels ())
		     * (2 * sizeof (short)));

  // We're using the interp by 4 feature of the 9862 so that we can
  // use its fine modulator.  Thus, we reduce the FPGA's interpolation rate
  // by a factor of 4.

  bool s = disable_tx ();
  bool ok = _write_fpga_reg (FR_INTERP_RATE, d_interp_rate/4 - 1);
  restore_tx (s);
  return ok;
}

bool
usrp_standard_tx::set_nchannels (int nchan)
{
  if (!(nchan == 1 || nchan == 2))
    return false;

  d_nchan = nchan;
  return write_hw_mux_reg ();
}

bool
usrp_standard_tx::set_mux (int mux)
{
  d_sw_mux = mux;
  d_hw_mux = mux << 4;
  return write_hw_mux_reg ();
}

bool
usrp_standard_tx::write_hw_mux_reg ()
{
  bool s = disable_tx ();
  bool ok = _write_fpga_reg (FR_TX_MUX, d_hw_mux | d_nchan);
  restore_tx (s);
  return ok;
}

#ifdef USE_FPGA_TX_CORDIC

bool
usrp_standard_tx::set_tx_freq (int channel, double freq)
{
  if (channel < 0 || channel >= MAX_CHAN_TX)
    return false;

  // This assumes we're running the 4x on-chip interpolator.

  unsigned int v =
    compute_freq_control_word_fpga (dac_freq () / 4,
				    freq, &d_tx_freq[channel],
				    d_verbose);

  return _write_fpga_reg (FR_TX_FREQ_0 + channel, v);
}


#else

bool
usrp_standard_tx::set_tx_freq (int channel, double freq)
{
  if (channel < 0 || channel >= MAX_CHAN_TX)
    return false;

  // split freq into fine and coarse components

  coarse_mod_t	cm;
  double	coarse;

  assert (dac_freq () == 128000000);

  if (freq < -44e6)		// too low
    return false;
  else if (freq < -24e6){	// [-44, -24)
    cm = CM_NEG_FDAC_OVER_4;
    coarse = -dac_freq () / 4;
  }
  else if (freq < -8e6){	// [-24, -8)
    cm = CM_NEG_FDAC_OVER_8;
    coarse = -dac_freq () / 8;
  }
  else if (freq < 8e6){		// [-8, 8)
    cm = CM_OFF;
    coarse = 0;
  }
  else if (freq < 24e6){	// [8, 24)
    cm = CM_POS_FDAC_OVER_8;
    coarse = dac_freq () / 8;
  }
  else if (freq <= 44e6){	// [24, 44]
    cm = CM_POS_FDAC_OVER_4;
    coarse = dac_freq () / 4;
  }
  else				// too high
    return false;


  set_coarse_modulator (channel, cm);	// set bits in d_tx_modulator_shadow

  double fine = freq - coarse;


  // Compute fine tuning word...
  // This assumes we're running the 4x on-chip interpolator.
  // (This is required to use the fine modulator.)

  unsigned int v =
    compute_freq_control_word_9862 (dac_freq () / 4,
				    fine, &d_tx_freq[channel], d_verbose);

  d_tx_freq[channel] += coarse;		// adjust actual
  
  unsigned char high, mid, low;

  high = (v >> 16) & 0xff;
  mid =  (v >>  8) & 0xff;
  low =  (v >>  0) & 0xff;

  bool ok = true;

  // write the fine tuning word
  ok &= _write_9862 (channel, REG_TX_NCO_FTW_23_16, high);
  ok &= _write_9862 (channel, REG_TX_NCO_FTW_15_8,  mid);
  ok &= _write_9862 (channel, REG_TX_NCO_FTW_7_0,   low);


  d_tx_modulator_shadow[channel] |= TX_MODULATOR_ENABLE_NCO;

  if (fine < 0)
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_FINE_TUNE;
  else
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_NEG_FINE_TUNE;

  ok &=_write_9862 (channel, REG_TX_MODULATOR, d_tx_modulator_shadow[channel]);

  return ok;
}
#endif

bool
usrp_standard_tx::set_coarse_modulator (int channel, coarse_mod_t cm)
{
  if (channel < 0 || channel >= MAX_CHAN_TX)
    return false;

  switch (cm){
  case CM_NEG_FDAC_OVER_4:
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE;
    break;

  case CM_NEG_FDAC_OVER_8:
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE;
    break;

  case CM_OFF:
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK;
    break;

  case CM_POS_FDAC_OVER_8:
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8;
    break;

  case CM_POS_FDAC_OVER_4:
    d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK;
    d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4;
    break;

  default:
    return false;
  }

  d_coarse_mod[channel] = cm;
  return true;
}

unsigned int
usrp_standard_tx::interp_rate () const { return d_interp_rate; }

int
usrp_standard_tx::nchannels () const { return d_nchan; }

int
usrp_standard_tx::mux () const { return d_sw_mux; }

double
usrp_standard_tx::tx_freq (int channel) const
{
  if (channel < 0 || channel >= MAX_CHAN_TX)
    return 0;

  return d_tx_freq[channel];
}

usrp_standard_tx::coarse_mod_t
usrp_standard_tx::coarse_modulator (int channel) const
{
  if (channel < 0 || channel >= MAX_CHAN_TX)
    return CM_OFF;

  return d_coarse_mod[channel];
}


/////////////// usrp_basic.cc //////////////////



using namespace ad9862;


// These set the buffer size used for each end point using the fast
// usb interface.  The kernel ends up locking down this much memory.

static const int FUSB_BUFFER_SIZE = 2 * (1L << 20);	// 2 MB (was 8 MB)
//static const int FUSB_BUFFER_SIZE = 256 * (1L << 10);	// 256 kB
static const int FUSB_BLOCK_SIZE = fusb_sysconfig::max_block_size();
static const int FUSB_NBLOCKS    = FUSB_BUFFER_SIZE / FUSB_BLOCK_SIZE;


static const double POLLING_INTERVAL = 0.1;	// seconds

////////////////////////////////////////////////////////////////

static struct usb_dev_handle *
open_rx_interface (struct usb_device *dev)
{
  struct usb_dev_handle *udh = usrp_open_rx_interface (dev);
  if (udh == 0){
    printf( "usrp_basic_rx: can't open rx interface\n");
    usb_strerror ();
  }
  return udh;
}

static struct usb_dev_handle *
open_tx_interface (struct usb_device *dev)
{
  struct usb_dev_handle *udh = usrp_open_tx_interface (dev);
  if (udh == 0){
    printf( "usrp_basic_tx: can't open tx interface\n");
    usb_strerror ();
  }
  return udh;
}


//////////////////////////////////////////////////////////////////
//
//			usrp_basic
//
////////////////////////////////////////////////////////////////


// Given:
//   CLKIN = 64 MHz
//   CLKSEL pin = high 
//
// These settings give us:
//   CLKOUT1 = CLKIN = 64 MHz
//   CLKOUT2 = CLKIN = 64 MHz
//   ADC is clocked at  64 MHz
//   DAC is clocked at 128 MHz

static unsigned char common_regs[] = {
  REG_GENERAL,		0,
  REG_DLL,		(DLL_DISABLE_INTERNAL_XTAL_OSC
			 | DLL_MULT_2X
			 | DLL_FAST),
  REG_CLKOUT,		CLKOUT2_EQ_DLL_OVER_2,
  REG_AUX_ADC_CLK,	AUX_ADC_CLK_CLK_OVER_4
};


usrp_basic::usrp_basic (int which_board, 
			struct usb_dev_handle *
			open_interface (struct usb_device *dev))
  : d_udh (0),
    d_usb_data_rate (16000000),	// SWAG, see below
    d_bytes_per_poll ((int) (POLLING_INTERVAL * d_usb_data_rate)),
    d_verbose (false)
{
  /*
   * SWAG: Scientific Wild Ass Guess.
   *
   * d_usb_data_rate is used only to determine how often to poll for over- and under-runs.
   * We defualt it to 1/2  of our best case.  Classes derived from usrp_basic (e.g., 
   * usrp_standard_tx and usrp_standard_rx) call set_usb_data_rate() to tell us the
   * actual rate.  This doesn't change our throughput, that's determined by the signal
   * processing code in the FPGA (which we know nothing about), and the system limits
   * determined by libusb, fusb_*, and the underlying drivers.
   */
  memset (d_fpga_shadows, 0, sizeof (d_fpga_shadows));

  usrp_one_time_init ();

  // FIXME allow subclasses to load own code
  if (!usrp_load_standard_bits (which_board, false))
    throw std::runtime_error ("usrp_basic/usrp_load_standard_bits");

  struct usb_device *dev = usrp_find_device (which_board);
  if (dev == 0){
    printf( "usrp_basic: can't find usrp[%d]\n", which_board);
    throw std::runtime_error ("usrp_basic/usrp_find_device");
  }

  if (!(usrp_usrp1_p (dev) || usrp_usrp2_p (dev))){
    printf( "usrp_basic: sorry, this code only works with Rev 1 and 2 USRPs\n");
    printf("   apparent rev = %d\n", dev->descriptor.bcdDevice & 0x00FF);
    throw std::runtime_error ("usrp_basic/bad_rev");
  }

  if ((d_udh = open_interface (dev)) == 0)
    throw std::runtime_error ("usrp_basic/open_interface");

  // initialize registers that are common to rx and tx

  if (!usrp_9862_write_many_all (d_udh, common_regs, sizeof (common_regs))){
    printf( "usrp_basic: failed to init common AD9862 regs\n");
    throw std::runtime_error ("usrp_basic/init_9862");
  }

  _write_fpga_reg (FR_MODE, 0);		// ensure we're in normal mode
  _write_fpga_reg (FR_DEBUG_EN, 0);	// disable debug outputs
}

usrp_basic::~usrp_basic ()
{
  if (d_udh)
    {
    // not sure if this is for transmit or receive, so try releasing both 
    usb_release_interface(d_udh,USRP_TX_INTERFACE);
    usb_release_interface(d_udh,USRP_RX_INTERFACE);
    usb_close (d_udh);
    }
}

bool
usrp_basic::start ()
{
  return true;		// nop
}

bool
usrp_basic::stop ()
{
  return true;		// nop
}

void
usrp_basic::set_usb_data_rate (int usb_data_rate)
{
  d_usb_data_rate = usb_data_rate;
  d_bytes_per_poll = (int) (usb_data_rate * POLLING_INTERVAL);
}

bool
usrp_basic::write_aux_dac (int slot, int which_dac, int value)
{
  return usrp_write_aux_dac (d_udh, slot, which_dac, value);
}

bool
usrp_basic::read_aux_adc (int slot, int which_adc, int *value)
{
  return usrp_read_aux_adc (d_udh, slot, which_adc, value);
}

int
usrp_basic::read_aux_adc (int slot, int which_adc)
{
  int	value;
  if (!read_aux_adc (slot, which_adc, &value))
    return READ_FAILED;

  return value;
}

bool
usrp_basic::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf)
{
  return usrp_eeprom_write (d_udh, i2c_addr, eeprom_offset, buf.data (), buf.size ());
}

void usrp_basic::read_eeprom (int i2c_addr, int eeprom_offset, int len, 
                       char *out_str)
{
  if (len <= 0)
    {
    sprintf(out_str,"");
    return;
    }

  if (!usrp_eeprom_read (d_udh, i2c_addr, eeprom_offset, out_str, len))
    {
    sprintf(out_str,"");
    return;
    }
}

bool
usrp_basic::write_i2c (int i2c_addr, const std::string buf)
{
  return usrp_i2c_write (d_udh, i2c_addr, buf.data (), buf.size ());
}

void usrp_basic::read_i2c (int i2c_addr, int len, char *out_str)
{
  if (len <= 0)
    {
     sprintf(out_str,"");
     return;
    }

  if (!usrp_i2c_read (d_udh, i2c_addr, out_str, len))
    {
     sprintf(out_str,"");
     return;
    }

}

// ----------------------------------------------------------------

bool
usrp_basic::set_adc_offset (int which, int offset)
{
  if (which < 0 || which > 3)
    return false;

  return _write_fpga_reg (FR_ADC_OFFSET_0 + which, offset);
}

bool
usrp_basic::set_dac_offset (int which, int offset, int offset_pin)
{
  if (which < 0 || which > 3)
    return false;

  int which_codec = which >> 1;
  int tx_a = (which & 0x1) == 0;
  int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1);
  int hi = (offset >> 2);
  bool ok;

  if (tx_a){
    ok =  _write_9862 (which_codec, REG_TX_A_OFFSET_LO, lo);
    ok &= _write_9862 (which_codec, REG_TX_A_OFFSET_HI, hi);
  }
  else {
    ok =  _write_9862 (which_codec, REG_TX_B_OFFSET_LO, lo);
    ok &= _write_9862 (which_codec, REG_TX_B_OFFSET_HI, hi);
  }
  return ok;
}

bool
usrp_basic::set_adc_buffer_bypass (int which, bool bypass)
{
  if (which < 0 || which > 3)
    return false;

  int codec = which >> 1;
  int reg = (which & 1) == 0 ? 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 = _read_9862 (codec, reg, &cur_rx);
  ok &= _read_9862 (codec, REG_RX_PWR_DN, &cur_pwr_dn);
  if (!ok)
    return false;

  if (bypass){
    cur_rx |= RX_X_BYPASS_INPUT_BUFFER;
    cur_pwr_dn |= ((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B;
  }
  else {
    cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER;
    cur_pwr_dn &= ~(((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B);
  }

  ok &= _write_9862 (codec, reg, cur_rx);
  ok &= _write_9862 (codec, REG_RX_PWR_DN, cur_pwr_dn);
  return ok;
}

// ----------------------------------------------------------------

bool
usrp_basic::_write_fpga_reg (int regno, int value)
{
  if (d_verbose){
    printf("_write_fpga_reg(%3d, 0x%08x)\n", regno, value);
    fflush (stdout);
  }

  if (regno >= 0 && regno < MAX_REGS)
    d_fpga_shadows[regno] = value;

  return usrp_write_fpga_reg (d_udh, regno, value);
}

bool
usrp_basic::_read_fpga_reg (int regno, int *value)
{
  return usrp_read_fpga_reg (d_udh, regno, value);
}

int
usrp_basic::_read_fpga_reg (int regno)
{
  int value;
  if (!_read_fpga_reg (regno, &value))
    return READ_FAILED;
  return value;
}

bool
usrp_basic::_write_9862 (int which_codec, int regno, unsigned char value)
{
  if (0 && d_verbose){
    // FIXME really want to enable logging in usrp_prims:usrp_9862_write
    printf("_write_9862(codec = %d, regno = %2d, val = 0x%02x)\n", which_codec, regno, value);
    fflush(stdout);
  }

  return usrp_9862_write (d_udh, which_codec, regno, value);
}


bool
usrp_basic::_read_9862 (int which_codec, int regno, unsigned char *value) const
{
  return usrp_9862_read (d_udh, which_codec, regno, value);
}

int
usrp_basic::_read_9862 (int which_codec, int regno) const
{
  unsigned char value;
  if (!_read_9862 (which_codec, regno, &value))
    return READ_FAILED;
  return value;
}

bool
usrp_basic::_write_spi (int optional_header, int enables, int format, std::string buf)
{
  return usrp_spi_write (d_udh, optional_header, enables, format,
			 buf.data(), buf.size());
}

void usrp_basic::_read_spi (int optional_header, int enables, int format, 
                            int len, char *str_out)
{
  if (len <= 0)
    {
     sprintf(str_out,"");
     return;
    }

  if (!usrp_spi_read (d_udh, optional_header, enables, format, str_out, len))
    {
     sprintf(str_out,"");
     return;
    }

}


bool
usrp_basic::_set_led (int which, bool on)
{
  return usrp_set_led (d_udh, which, on);
}

////////////////////////////////////////////////////////////////
//
//			   usrp_basic_rx
//
////////////////////////////////////////////////////////////////

static unsigned char rx_init_regs[] = {
  REG_RX_PWR_DN,	0,
  REG_RX_A,		0,	// minimum gain = 0x00 (max gain = 0x14)
  REG_RX_B,		0,	// minimum gain = 0x00 (max gain = 0x14)
  REG_RX_MISC,		(RX_MISC_HS_DUTY_CYCLE | RX_MISC_CLK_DUTY),
  REG_RX_IF,		(RX_IF_USE_CLKOUT1
			 | RX_IF_2S_COMP),
  REG_RX_DIGITAL,	(RX_DIGITAL_2_CHAN)
};


usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nblocks)
  : usrp_basic (which_board, open_rx_interface),
    d_devhandle (0), d_ephandle (0),
    d_bytes_seen (0), d_first_read (true),
    d_rx_enable (false)
{
  // initialize rx specific registers

  if (!usrp_9862_write_many_all (d_udh, rx_init_regs, sizeof (rx_init_regs))){
    printf( "usrp_basic_rx: failed to init AD9862 RX regs\n");
    throw std::runtime_error ("usrp_basic_rx/init_9862");
  }

  if (0){
    // FIXME power down 2nd codec rx path
    usrp_9862_write (d_udh, 1, REG_RX_PWR_DN, 0x1);	// power down everything
  }

  // Reset the rx path and leave it disabled.
  set_rx_enable (false);
  usrp_set_fpga_rx_reset (d_udh, true);
  usrp_set_fpga_rx_reset (d_udh, false);

  set_fpga_rx_sample_rate_divisor (2);	// usually correct

  set_dc_offset_cl_enable(0xf, 0xf);	// enable DC offset removal control loops

  probe_rx_slots (false);

  // check fusb buffering parameters

  if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE)
    throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size");

  if (fusb_nblocks < 0)
    throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks");
  
  if (fusb_block_size == 0)
    fusb_block_size = FUSB_BLOCK_SIZE;

  if (fusb_nblocks == 0)
    {
     if (FUSB_BUFFER_SIZE / fusb_block_size < 1)
        fusb_nblocks = 1;
     else
        fusb_nblocks = FUSB_BUFFER_SIZE / fusb_block_size;
    }

  d_devhandle = fusb_sysconfig::make_devhandle (d_udh);
  d_ephandle = d_devhandle->make_ephandle (USRP_RX_ENDPOINT, true,
					   fusb_block_size, fusb_nblocks);

  _write_fpga_reg(FR_ATR_MASK_1, 0);	// zero Rx side Auto Transmit/Receive regs
  _write_fpga_reg(FR_ATR_TXVAL_1, 0);
  _write_fpga_reg(FR_ATR_RXVAL_1, 0);
  _write_fpga_reg(FR_ATR_MASK_3, 0);
  _write_fpga_reg(FR_ATR_TXVAL_3, 0);
  _write_fpga_reg(FR_ATR_RXVAL_3, 0);
}

static unsigned char rx_fini_regs[] = {
  REG_RX_PWR_DN,	0x1				// power down everything
};

usrp_basic_rx::~usrp_basic_rx ()
{
  if (!set_rx_enable (false)){
    printf( "usrp_basic_rx: set_fpga_rx_enable failed\n");
    usb_strerror ();
  }

  d_ephandle->stop ();
  delete d_ephandle;
  delete d_devhandle;

  if (!usrp_9862_write_many_all (d_udh, rx_fini_regs, sizeof (rx_fini_regs))){
    printf( "usrp_basic_rx: failed to fini AD9862 RX regs\n");
  }
}


bool
usrp_basic_rx::start ()
{
  if (!usrp_basic::start ())	// invoke parent's method
    return false;

  // fire off reads before asserting rx_enable

  if (!d_ephandle->start ()){
    printf( "usrp_basic_rx: failed to start end point streaming");
    usb_strerror ();
    return false;
  }

  if (!set_rx_enable (true)){
    printf( "usrp_basic_rx: set_rx_enable failed\n");
    usb_strerror ();
    return false;
  }
  
  return true;
}

bool
usrp_basic_rx::stop ()
{
  bool ok = usrp_basic::stop();

  if (!d_ephandle->stop()){
    printf( "usrp_basic_rx: failed to stop end point streaming");
    usb_strerror ();
    ok = false;
  }
  if (!set_rx_enable(false)){
    printf( "usrp_basic_rx: set_rx_enable(false) failed\n");
    usb_strerror ();
    ok = false;
  }
  return false;
}

usrp_basic_rx *
usrp_basic_rx::make (int which_board, int fusb_block_size, int fusb_nblocks)
{
  usrp_basic_rx *u = 0;
  
  try {
    u = new usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks);
    return u;
  }
  catch (...){
    delete u;
    return 0;
  }

  return u;
}

bool
usrp_basic_rx::set_fpga_rx_sample_rate_divisor (unsigned int div)
{
  return _write_fpga_reg (FR_RX_SAMPLE_RATE_DIV, div - 1);
}


/*
 * \brief read data from the D/A's via the FPGA.
 * \p len must be a multiple of 512 bytes.
 *
 * \returns the number of bytes read, or -1 on error.
 *
 * If overrun is non-NULL it will be set true iff an RX overrun is detected.
 */
int
usrp_basic_rx::read (void *buf, int len, bool *overrun)
{
  int	r;
  
  if (overrun)
    *overrun = false;
  
  if (len < 0 || (len % 512) != 0){
    printf( "usrp_basic_rx::read: invalid length = %d\n", len);
    return -1;
  }

  r = d_ephandle->read (buf, len);
  if (r > 0)
    d_bytes_seen += r;

  /*
   * In many cases, the FPGA reports an rx overrun right after we
   * enable the Rx path.  If this is our first read, check for the
   * overrun to clear the condition, then ignore the result.
   */
  if (0 && d_first_read){	// FIXME
    d_first_read = false;
    bool bogus_overrun;
    usrp_check_rx_overrun (d_udh, &bogus_overrun);
  }

  if (overrun != 0 && d_bytes_seen >= d_bytes_per_poll){
    d_bytes_seen = 0;
    if (!usrp_check_rx_overrun (d_udh, overrun)){
      printf( "usrp_basic_rx: usrp_check_rx_overrun failed\n");
      usb_strerror ();
    }
  }
    
  return r;
}

bool
usrp_basic_rx::set_rx_enable (bool on)
{
  d_rx_enable = on;
  return usrp_set_fpga_rx_enable (d_udh, on);
}

// conditional disable, return prev state
bool
usrp_basic_rx::disable_rx ()
{
  bool enabled = rx_enable ();
  if (enabled)
    set_rx_enable (false);
  return enabled;
}

// conditional set
void
usrp_basic_rx::restore_rx (bool on)
{
  if (on != rx_enable ())
    set_rx_enable (on);
}

bool
usrp_basic_rx::set_pga (int which, double gain)
{
  if (which < 0 || which > 3)
    return false;

  if (pga_min() > gain)
    gain = pga_min();
  if (pga_max() < gain)
    gain = pga_max();

  int codec = which >> 1;
  int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B;

  // read current value to get input buffer bypass flag.
  unsigned char cur_rx;
  if (!_read_9862 (codec, reg, &cur_rx))
    return false;

  int int_gain = (int) floor((gain - pga_min ()) / pga_db_per_step() + 0.5);

  cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f);
  return _write_9862 (codec, reg, cur_rx);
}

double
usrp_basic_rx::pga (int which) const
{
  if (which < 0 || which > 3)
    return READ_FAILED;

  int codec = which >> 1;
  int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B;
  unsigned char v;
  bool ok = _read_9862 (codec, reg, &v);
  if (!ok)
    return READ_FAILED;

  return (pga_db_per_step() * (v & 0x1f)) + pga_min();
}

static int
slot_id_to_oe_reg (int slot_id)
{
  static int reg[4]  = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 };
  assert (0 <= slot_id && slot_id < 4);
  return reg[slot_id];
}

static int
slot_id_to_io_reg (int slot_id)
{
  static int reg[4]  = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 };
  assert (0 <= slot_id && slot_id < 4);
  return reg[slot_id];
}

void
usrp_basic_rx::probe_rx_slots (bool verbose)
{
  struct usrp_dboard_eeprom	eeprom;
  static int slot_id_map[2] = { SLOT_RX_A, SLOT_RX_B };
  static const char *slot_name[2] = { "RX d'board A", "RX d'board B" };
  char msg[1000];

  for (int i = 0; i < 2; i++){
    int slot_id = slot_id_map [i];

    usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom);

    switch (s){
    case UDBE_OK:
      d_dbid[i] = eeprom.id;
      usrp_dbid_to_string(eeprom.id,msg);
      set_adc_offset (2*i+0, eeprom.offset[0]);
      set_adc_offset (2*i+1, eeprom.offset[1]);
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_NO_EEPROM:
      d_dbid[i] = -1;
      sprintf(msg,"<none>");
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_INVALID_EEPROM:
      d_dbid[i] = -2;
      sprintf(msg,"Invalid EEPROM contents");
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_BAD_SLOT:
    default:
      assert (0);
    }

    if (verbose){
      fflush (stdout);
      printf( "%s: %s\n", slot_name[i], msg);
    }
  }
}

bool
usrp_basic_rx::_write_oe (int which_dboard, int value, int mask)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)),
			  (mask << 16) | (value & 0xffff));
}

bool
usrp_basic_rx::write_io (int which_dboard, int value, int mask)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)),
			  (mask << 16) | (value & 0xffff));
}

bool
usrp_basic_rx::read_io (int which_dboard, int *value)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  int t;
  int reg = which_dboard + 1;	// FIXME, *very* magic number (fix in serial_io.v)
  bool ok = _read_fpga_reg (reg, &t);
  if (!ok)
    return false;

  *value = (t >> 16) & 0xffff;	// FIXME, more magic
  return true;
}

int
usrp_basic_rx::read_io (int which_dboard)
{
  int	value;
  if (!read_io (which_dboard, &value))
    return READ_FAILED;
  return value;
}

bool
usrp_basic_rx::write_aux_dac (int which_dboard, int which_dac, int value)
{
  return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard),
				    which_dac, value);
}

bool
usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc, int *value)
{
  return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard),
				   which_adc, value);
}

int
usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc)
{
  return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc);
}

int
usrp_basic_rx::block_size () const { return d_ephandle->block_size(); }

bool
usrp_basic_rx::set_dc_offset_cl_enable(int bits, int mask)
{
  return _write_fpga_reg(FR_DC_OFFSET_CL_EN, 
			 (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask));
}

////////////////////////////////////////////////////////////////
//
//			   usrp_basic_tx
//
////////////////////////////////////////////////////////////////


//
// DAC input rate 64 MHz interleaved for a total input rate of 128 MHz
// DAC input is latched on rising edge of CLKOUT2
// NCO is disabled
// interpolate 2x
// coarse modulator disabled
//

static unsigned char tx_init_regs[] = {
  REG_TX_PWR_DN,	0,
  REG_TX_A_OFFSET_LO,	0,
  REG_TX_A_OFFSET_HI,	0,
  REG_TX_B_OFFSET_LO,	0,
  REG_TX_B_OFFSET_HI,	0,
  REG_TX_A_GAIN,	(TX_X_GAIN_COARSE_FULL | 0),
  REG_TX_B_GAIN,	(TX_X_GAIN_COARSE_FULL | 0),
  REG_TX_PGA,		0xff,			// maximum gain (0 dB)
  REG_TX_MISC,		0,
  REG_TX_IF,		(TX_IF_USE_CLKOUT1
			 | TX_IF_I_FIRST
			 | TX_IF_INV_TX_SYNC
			 | TX_IF_2S_COMP
			 | TX_IF_INTERLEAVED),
  REG_TX_DIGITAL,	(TX_DIGITAL_2_DATA_PATHS
			 | TX_DIGITAL_INTERPOLATE_4X),
  REG_TX_MODULATOR,	(TX_MODULATOR_DISABLE_NCO
			 | TX_MODULATOR_COARSE_MODULATION_NONE),
  REG_TX_NCO_FTW_7_0,	0,
  REG_TX_NCO_FTW_15_8,	0,
  REG_TX_NCO_FTW_23_16,	0
};

usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nblocks)
  : usrp_basic (which_board, open_tx_interface),
    d_devhandle (0), d_ephandle (0),
    d_bytes_seen (0), d_first_write (true),
    d_tx_enable (false)
{
  if (!usrp_9862_write_many_all (d_udh, tx_init_regs, sizeof (tx_init_regs))){
    printf( "usrp_basic_tx: failed to init AD9862 TX regs\n");
    throw std::runtime_error ("usrp_basic_tx/init_9862");
  }

  if (0){
    // FIXME power down 2nd codec tx path
    usrp_9862_write (d_udh, 1, REG_TX_PWR_DN,
		     (TX_PWR_DN_TX_DIGITAL
		      | TX_PWR_DN_TX_ANALOG_BOTH));
  }

  // Reset the tx path and leave it disabled.
  set_tx_enable (false);
  usrp_set_fpga_tx_reset (d_udh, true);
  usrp_set_fpga_tx_reset (d_udh, false);

  set_fpga_tx_sample_rate_divisor (4);	// we're using interp x4

  probe_tx_slots (false);

  // check fusb buffering parameters

  if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE)
    throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size");

  if (fusb_nblocks < 0)
    throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks");
  
  if (fusb_block_size == 0)
    fusb_block_size = FUSB_BLOCK_SIZE;

  if (fusb_nblocks == 0)
    {
      if (FUSB_BUFFER_SIZE / fusb_block_size < 1)
        fusb_nblocks = 1;
      else
        fusb_nblocks = FUSB_BUFFER_SIZE / fusb_block_size;
    }

  d_devhandle = fusb_sysconfig::make_devhandle (d_udh);
  d_ephandle = d_devhandle->make_ephandle (USRP_TX_ENDPOINT, false,
					   fusb_block_size, fusb_nblocks);

  _write_fpga_reg(FR_ATR_CTL, 1);
  _write_fpga_reg(FR_ATR_MASK_0, RX_TXN | ENABLE);	
  _write_fpga_reg(FR_ATR_TXVAL_0, ENABLE);
  _write_fpga_reg(FR_ATR_RXVAL_0, RX_TXN);
  _write_fpga_reg(FR_ATR_MASK_2, RX_TXN | ENABLE);
  _write_fpga_reg(FR_ATR_TXVAL_2, ENABLE);
  _write_fpga_reg(FR_ATR_RXVAL_2, RX_TXN);
}


static unsigned char tx_fini_regs[] = {
  REG_TX_PWR_DN,	(TX_PWR_DN_TX_DIGITAL
			 | TX_PWR_DN_TX_ANALOG_BOTH),
  REG_TX_MODULATOR,	(TX_MODULATOR_DISABLE_NCO
			 | TX_MODULATOR_COARSE_MODULATION_NONE)
};

usrp_basic_tx::~usrp_basic_tx ()
{
  d_ephandle->stop ();
  delete d_ephandle;
  delete d_devhandle;

  if (!usrp_9862_write_many_all (d_udh, tx_fini_regs, sizeof (tx_fini_regs))){
    printf( "usrp_basic_tx: failed to fini AD9862 TX regs\n");
  }

  _write_fpga_reg(FR_ATR_CTL, 1);
}

bool
usrp_basic_tx::start ()
{
  if (!usrp_basic::start ())
    return false;

  if (!set_tx_enable (true)){
    printf( "usrp_basic_tx: set_tx_enable failed\n");
    usb_strerror ();
    return false;
  }
  
  if (!d_ephandle->start ()){
    printf( "usrp_basic_tx: failed to start end point streaming");
    usb_strerror ();
    return false;
  }

  return true;
}

bool
usrp_basic_tx::stop ()
{
  bool ok = usrp_basic::stop ();

  if (!set_tx_enable (false)){
    printf( "usrp_basic_tx: set_tx_enable(false) failed\n");
    usb_strerror ();
    ok = false;
  }
  if (!d_ephandle->stop ()){
    printf( "usrp_basic_tx: failed to stop end point streaming");
    usb_strerror ();
    ok = false;
  }
  return ok;
}

usrp_basic_tx *
usrp_basic_tx::make (int which_board, int fusb_block_size, int fusb_nblocks)
{
  usrp_basic_tx *u = 0;
  
  try {
    u = new usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks);
    return u;
  }
  catch (...){
    delete u;
    return 0;
  }

  return u;
}

bool
usrp_basic_tx::set_fpga_tx_sample_rate_divisor (unsigned int div)
{
  return _write_fpga_reg (FR_TX_SAMPLE_RATE_DIV, div - 1);
}

/*!
 * \brief Write data to the A/D's via the FPGA.
 *
 * \p len must be a multiple of 512 bytes.
 * \returns number of bytes written or -1 on error.
 *
 * if \p underrun is non-NULL, it will be set to true iff
 * a transmit underrun condition is detected.
 */
int
usrp_basic_tx::write (const void *buf, int len, bool *underrun)
{
  int	r;
  
  if (underrun)
    *underrun = false;
  
  if (len < 0 || (len % 512) != 0){
    printf( "usrp_basic_tx::write: invalid length = %d\n", len);
    return -1;
  }

  r = d_ephandle->write (buf, len);
  if (r > 0)
    d_bytes_seen += r;
    
  /*
   * In many cases, the FPGA reports an tx underrun right after we
   * enable the Tx path.  If this is our first write, check for the
   * underrun to clear the condition, then ignore the result.
   */
  if (d_first_write && d_bytes_seen >= 4 * FUSB_BLOCK_SIZE){
    d_first_write = false;
    bool bogus_underrun;
    usrp_check_tx_underrun (d_udh, &bogus_underrun);
  }

  if (underrun != 0 && d_bytes_seen >= d_bytes_per_poll){
    d_bytes_seen = 0;
    if (!usrp_check_tx_underrun (d_udh, underrun)){
      printf( "usrp_basic_tx: usrp_check_tx_underrun failed\n");
      usb_strerror ();
    }
  }

  return r;
}

void
usrp_basic_tx::wait_for_completion ()
{
  d_ephandle->wait_for_completion ();
}

bool
usrp_basic_tx::set_tx_enable (bool on)
{
  d_tx_enable = on;
  // printf( "set_tx_enable %d\n", on);
  return usrp_set_fpga_tx_enable (d_udh, on);
}

// conditional disable, return prev state
bool
usrp_basic_tx::disable_tx ()
{
  bool enabled = tx_enable ();
  if (enabled)
    set_tx_enable (false);
  return enabled;
}

// conditional set
void
usrp_basic_tx::restore_tx (bool on)
{
  if (on != tx_enable ())
    set_tx_enable (on);
}

bool
usrp_basic_tx::set_pga (int which, double gain)
{
  if (which < 0 || which > 3)
    return false;

  if (pga_min() > gain)
    gain = pga_min();
  if (pga_max() < gain)
    gain = pga_max();

  int codec = which >> 1;	// 0 and 1 are same, as are 2 and 3

  int int_gain = (int) floor((gain - pga_min ()) / pga_db_per_step() + 0.5);

  return _write_9862 (codec, REG_TX_PGA, int_gain);
}

double
usrp_basic_tx::pga (int which) const
{
  if (which < 0 || which > 3)
    return READ_FAILED;

  int codec = which >> 1;
  unsigned char v;
  bool ok = _read_9862 (codec, REG_TX_PGA, &v);
  if (!ok)
    return READ_FAILED;

  return (pga_db_per_step() * v) + pga_min();
}

void
usrp_basic_tx::probe_tx_slots (bool verbose)
{
  struct usrp_dboard_eeprom	eeprom;
  static int slot_id_map[2] = { SLOT_TX_A, SLOT_TX_B };
  static const char *slot_name[2] = { "TX d'board A", "TX d'board B" };
  char msg[1000];

  for (int i = 0; i < 2; i++){
    int slot_id = slot_id_map [i];
    usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom);

    switch (s){
    case UDBE_OK:
      d_dbid[i] = eeprom.id;
      usrp_dbid_to_string(eeprom.id,msg);
      // FIXME, figure out interpretation of dc offset for TX d'boards
      // offset = (eeprom.offset[1] << 16) | (eeprom.offset[0] & 0xffff);
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_NO_EEPROM:
      d_dbid[i] = -1;
      sprintf(msg,"<none>");
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_INVALID_EEPROM:
      d_dbid[i] = -2;
      sprintf(msg,"Invalid EEPROM contents");
      _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000);
      _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000);
      break;
      
    case UDBE_BAD_SLOT:
    default:
      assert (0);
    }

    if (verbose){
      fflush (stdout);
      printf( "%s: %s\n", slot_name[i], msg);
    }
  }
}

bool
usrp_basic_tx::_write_oe (int which_dboard, int value, int mask)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)),
			  (mask << 16) | (value & 0xffff));
}

bool
usrp_basic_tx::write_io (int which_dboard, int value, int mask)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)),
			  (mask << 16) | (value & 0xffff));
}

bool
usrp_basic_tx::read_io (int which_dboard, int *value)
{
  if (! (0 <= which_dboard && which_dboard <= 1))
    return false;

  int t;
  int reg = which_dboard + 1;	// FIXME, *very* magic number (fix in serial_io.v)
  bool ok = _read_fpga_reg (reg, &t);
  if (!ok)
    return false;

  *value = t & 0xffff;		// FIXME, more magic
  return true;
}

int
usrp_basic_tx::read_io (int which_dboard)
{
  int	value;
  if (!read_io (which_dboard, &value))
    return READ_FAILED;
  return value;
}

bool
usrp_basic_tx::write_aux_dac (int which_dboard, int which_dac, int value)
{
  return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard),
				    which_dac, value);
}

bool
usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc, int *value)
{
  return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard),
				   which_adc, value);
}

int
usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc)
{
  return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc);
}

int
usrp_basic_tx::block_size () const { return d_ephandle->block_size(); }


///////////////////// usrp_prims.cc /////////////////////



extern "C" {
#include "md5.h"
};

#define VERBOSE 0

using namespace ad9862;

static const int FIRMWARE_HASH_SLOT	= 0;
static const int FPGA_HASH_SLOT 	= 1;

static const int hash_slot_addr[2] = {
  USRP_HASH_SLOT_0_ADDR,
  USRP_HASH_SLOT_1_ADDR
};

static char *usrp_firmware_filename = "usrp_firmware.ihx";
static char *usrp_fpga_filename = "usrp_fpga.rbf";


void find_file (const char *filename, int hw_rev, char *full_path)
{
  char path[1000];
  char *root_dir;
  char commoncode_dir[1000];
  FILE *fp;

  root_dir = getenv("CPPSIMSHAREDHOME");
  if (root_dir == NULL)
    {
     root_dir = getenv("CppSimSharedHome");
    }
  if (root_dir == NULL)
    {
      printf("Error in setting up path for firmware:\n");
      printf("   Environment variable CPPSIMSHAREDHOME is not defined\n");
      printf("   -> You need to define this according to the location\n");
      printf("      of the CppSimShared directory, or the directory\n");
      printf("      containing the CommonCode directory\n");
      exit(1);
    }
    sprintf(commoncode_dir,"%s/CommonCode/usrp",root_dir);

    sprintf (path, "%s/rev%d/%s", commoncode_dir, 2, filename);
    printf("path = '%s'\n",path);
    if ((fp = fopen(path,"r")) != NULL)
      {
      fclose(fp);
      strcpy(full_path,path);
      }
    else
      sprintf(full_path,"NULL");  
}

static void power_down_9862s (struct usb_dev_handle *udh);

void
usrp_one_time_init ()
{
  static bool first = true;

  if (first){
    first = false;
    usb_init ();			// usb library init
    usb_find_busses ();
    usb_find_devices ();
  }
}

void
usrp_rescan ()
{
  usb_find_busses ();
  usb_find_devices ();
}


// ----------------------------------------------------------------
// Danger, big, fragile KLUDGE.  The problem is that we want to be
// able to get from a usb_dev_handle back to a usb_device, and the
// right way to do this is buried in a non-installed include file.

static struct usb_device *
dev_handle_to_dev (usb_dev_handle *udh)
{
  struct usb_dev_handle_kludge {
    int			 fd;
    struct usb_bus	*bus;
    struct usb_device	*device;
  };

  return ((struct usb_dev_handle_kludge *) udh)->device;
}

// ----------------------------------------------------------------

/*
 * q must be a real USRP, not an FX2.  Return its hardware rev number.
 */
static int
_usrp_hw_rev (struct usb_device *q)
{
  return q->descriptor.bcdDevice & 0x00FF;
}

/*
 * q must be a real USRP, not an FX2.  Return true if it's configured.
 */
static bool
_usrp_configured_p (struct usb_device *q)
{
  return (q->descriptor.bcdDevice & 0xFF00) != 0;
}

bool
usrp_usrp_p (struct usb_device *q)
{
  return (q->descriptor.idVendor == USB_VID_FSF
	  && q->descriptor.idProduct == USB_PID_FSF_USRP);
}

bool
usrp_fx2_p (struct usb_device *q)
{
  return (q->descriptor.idVendor == USB_VID_CYPRESS
	  && q->descriptor.idProduct == USB_PID_CYPRESS_FX2);
}

bool
usrp_usrp0_p (struct usb_device *q)
{
  return usrp_usrp_p (q) && _usrp_hw_rev (q) == 0;
}

bool
usrp_usrp1_p (struct usb_device *q)
{
  return usrp_usrp_p (q) && _usrp_hw_rev (q) == 1;
}

bool
usrp_usrp2_p (struct usb_device *q)
{
  return usrp_usrp_p (q) && _usrp_hw_rev (q) == 2;
}


bool
usrp_unconfigured_usrp_p (struct usb_device *q)
{
  return usrp_usrp_p (q) && !_usrp_configured_p (q);
}

bool
usrp_configured_usrp_p (struct usb_device *q)
{
  return usrp_usrp_p (q) && _usrp_configured_p (q);
}

// ----------------------------------------------------------------

struct usb_device *
usrp_find_device (int nth, bool fx2_ok_p)
{
  struct usb_bus *p;
  struct usb_device *q;
  int	 n_found = 0;

  usrp_one_time_init ();
  
  p = usb_get_busses();
  while (p != NULL){
    q = p->devices;
    while (q != NULL){
      if (usrp_usrp_p (q) || (fx2_ok_p && usrp_fx2_p (q))){
	if (n_found == nth)	// return this one
	  return q;
	n_found++;		// keep looking
      }
      q = q->next;
    }
    p = p->next;
  }
  return 0;	// not found
}

static struct usb_dev_handle *usrp_open_interface(struct usb_device *dev, int interface, int altinterface)
{
  struct usb_dev_handle *udh = usb_open (dev);
  if (udh == 0)
    return 0;

  if (dev != dev_handle_to_dev (udh)){
    printf( "%s:%d: internal error!\n", __FILE__, __LINE__);
    abort ();
  }

#if defined(WIN32)
  // There's no get get_configuration function, and with some of the newer kernels
  // setting the configuration, even if to the same value, hoses any other processes
  // that have it open.  Hence we opt to not set it at all (We've only
  // got a single configuration anyway).  This may hose the win32 stuff...

  if (usb_set_configuration (udh, 1) < 0){
    /*
     * Ignore this error.  
     *
     * Seems that something changed in drivers/usb/core/devio.c:proc_setconfig such that
     * it returns -EBUSY if _any_ of the interfaces of a device are open.
     * We've only got a single configuration, so setting it doesn't even seem
     * like it should be required.
     */
  }
#endif

  if (usb_claim_interface (udh, interface) < 0){
    printf( "usb_claim_interface: failed interface %d\n",interface);
    printf( "%s\n", usb_strerror());
    usb_close (udh);
    return 0;
  }

  /******** this seems to kill Matlab when using as a mex function
  if (usb_set_altinterface (udh, altinterface) < 0){
    printf( "usb_set_alt_interface: failed\n");
    printf( "%s\n", usb_strerror());
    usb_release_interface (udh, interface);
    usb_close (udh);
    return 0;
  }
  *********************************************************/

  return udh;
}

struct usb_dev_handle *
usrp_open_cmd_interface (struct usb_device *dev)
{
  return usrp_open_interface (dev, USRP_CMD_INTERFACE, USRP_CMD_ALTINTERFACE);
}

struct usb_dev_handle *
usrp_open_rx_interface (struct usb_device *dev)
{
  return usrp_open_interface (dev, USRP_RX_INTERFACE, USRP_RX_ALTINTERFACE);
}

struct usb_dev_handle *
usrp_open_tx_interface (struct usb_device *dev)
{
  return usrp_open_interface (dev, USRP_TX_INTERFACE, USRP_TX_ALTINTERFACE);
}

bool
usrp_close_cmd_interface (struct usb_dev_handle *udh)
{
  usb_release_interface(udh,USRP_CMD_INTERFACE);
  return usb_close (udh) == 0;
}

// ----------------------------------------------------------------
// write internal ram using Cypress vendor extension

static bool
write_internal_ram (struct usb_dev_handle *udh, unsigned char *buf,
		    int start_addr, size_t len)
{
  int addr;
  int n;
  int a;
  int quanta = MAX_EP0_PKTSIZE;

  for (addr = start_addr; addr < start_addr + (int) len; addr += quanta){
    n = len + start_addr - addr;
    if (n > quanta)
      n = quanta;

    a = usb_control_msg (udh, 0x40, 0xA0,
			 addr, 0, (char *)(buf + (addr - start_addr)), n, 1000);

    if (a < 0){
      printf("write_internal_ram failed: %s\n", usb_strerror());
      return false;
    }
  }
  return true;
}

// ----------------------------------------------------------------
// whack the CPUCS register using the upload RAM vendor extension

static bool
reset_cpu (struct usb_dev_handle *udh, bool reset_p)
{
  unsigned char v;

  if (reset_p)
    v = 1;		// hold processor in reset
  else
    v = 0;	        // release reset

  return write_internal_ram (udh, &v, 0xE600, 1);
}

// ----------------------------------------------------------------
// Load intel format file into cypress FX2 (8051)

static bool
_usrp_load_firmware (struct usb_dev_handle *udh, const char *filename,
		     unsigned char hash[USRP_HASH_SIZE])
{
  FILE	*f = fopen (filename, "ra");
  if (f == 0){
    perror (filename);
    return false;
  }

  if (!reset_cpu (udh, true))	// hold CPU in reset while loading firmware
    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)){
    fgets(s, sizeof (s), f); /* we should not use more than 263 bytes normally */
    if(s[0]!=':'){
      printf("%s: invalid line: \"%s\"\n", filename, 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<length;i++){
	sscanf (s+9+i*2,"%02x", &b);
	data[i]=b;
	a=a+data[i];
      }

      sscanf (s+9+length*2,"%02x", &b);
      checksum=b;
      if (((a+checksum)&0xff)!=0x00){
	printf( "  ** Checksum failed: got 0x%02x versus 0x%02x\n", (-a)&0xff, checksum);
	goto fail;
      }
      if (!write_internal_ram (udh, data, addr, length))
	goto fail;
    }
    else if (type == 0x01){      // EOF
      break;
    }
    else if (type == 0x02){
      printf( "Extended address: whatever I do with it?\n");
      printf( "%s: invalid line: \"%s\"\n", filename, s);
      goto fail;
    }
  }

  // we jam the hash value into the FX2 memory before letting
  // the cpu out of reset.  When it comes out of reset it
  // may renumerate which will invalidate udh.

  if (!usrp_set_hash (udh, FIRMWARE_HASH_SLOT, hash))
    printf( "usrp: failed to write firmware hash slot\n");

  if (!reset_cpu (udh, false))		// take CPU out of reset
    goto fail;

  fclose (f);
  return true;

 fail:
  fclose (f);
  return false;
}

// ----------------------------------------------------------------
// write vendor extension command to USRP

static int
write_cmd (struct usb_dev_handle *udh,
	   int request, int value, int index,
	   unsigned char *bytes, int len)
{
  int	requesttype = (request & 0x80) ? VRT_VENDOR_IN : VRT_VENDOR_OUT;

  int r = usb_control_msg (udh, requesttype, request, value, index,
			   (char *) bytes, len, 1000);
  if (r < 0){
    // we get EPIPE if the firmware stalls the endpoint.
    if (errno != EPIPE)
      printf( "usb_control_msg failed: %s\n", usb_strerror ());
  }

  return r;
}

// ----------------------------------------------------------------
// load fpga

static bool
_usrp_load_fpga (struct usb_dev_handle *udh, const char *filename,
		 unsigned char hash[USRP_HASH_SIZE])
{
  bool ok = true;

  FILE	*fp = fopen (filename, "rb");
  if (fp == 0){
    perror (filename);
    return false;
  }

  unsigned char buf[MAX_EP0_PKTSIZE];	// 64 is max size of EP0 packet on FX2
  int n;

  usrp_set_led (udh, 1, 1);		// led 1 on


  // reset FPGA (and on rev1 both AD9862's, thus killing clock)
  usrp_set_fpga_reset (udh, 1);		// hold fpga in reset

  if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_BEGIN, 0, 0) != 0)
    goto fail;
  
  while ((n = fread (buf, 1, sizeof (buf), fp)) > 0){
    if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_XFER, buf, n) != n)
      goto fail;
  }

  if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_END, 0, 0) != 0)
    goto fail;
  
  fclose (fp);

  if (!usrp_set_hash (udh, FPGA_HASH_SLOT, hash))
    printf( "usrp: failed to write fpga hash slot\n");

  // On the rev1 USRP, 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.

  usrp_set_fpga_reset (udh, 0);		// fpga out of master reset

  // now these commands will work
  
  ok &= usrp_set_fpga_tx_enable (udh, 0);
  ok &= usrp_set_fpga_rx_enable (udh, 0);

  if (_usrp_hw_rev (dev_handle_to_dev (udh)) != 0){
    ok &= usrp_set_fpga_tx_reset (udh, 1);	// reset tx and rx  paths
    ok &= usrp_set_fpga_rx_reset (udh, 1);
    ok &= usrp_set_fpga_tx_reset (udh, 0);	// reset tx and rx  paths
    ok &= usrp_set_fpga_rx_reset (udh, 0);
  }

  if (!ok)
    printf( "usrp: failed to reset tx and/or rx path\n");

  power_down_9862s (udh);		// on the rev1, power these down!
  usrp_set_sleep_bits (udh, 0, 0xf);	// power up DACs and ADCs
  usrp_set_led (udh, 1, 0);		// led 1 off

  return true;

 fail:
  power_down_9862s (udh);		// on the rev1, power these down!
  fclose (fp);
  return false;
}

// ----------------------------------------------------------------

bool 
usrp_set_led (struct usb_dev_handle *udh, int which, bool on)
{
  int r = write_cmd (udh, VRQ_SET_LED, on, which, 0, 0);

  return r == 0;
}

bool
usrp_set_hash (struct usb_dev_handle *udh, int which,
	       const unsigned char hash[USRP_HASH_SIZE])
{
  which &= 1;
  
  // we use the Cypress firmware down load command to jam it in.
  int r = usb_control_msg (udh, 0x40, 0xa0, hash_slot_addr[which], 0,
			   (char *) hash, USRP_HASH_SIZE, 1000);
  return r == USRP_HASH_SIZE;
}

bool
usrp_get_hash (struct usb_dev_handle *udh, int which, 
	       unsigned char hash[USRP_HASH_SIZE])
{
  which &= 1;
  
  // we use the Cypress firmware upload command to fetch it.
  int r = usb_control_msg (udh, 0xc0, 0xa0, hash_slot_addr[which], 0,
			   (char *) hash, USRP_HASH_SIZE, 1000);
  return r == USRP_HASH_SIZE;
}

static bool
usrp_set_switch (struct usb_dev_handle *udh, int cmd_byte, bool on)
{
  return write_cmd (udh, cmd_byte, on, 0, 0, 0) == 0;
}


static bool
usrp0_fpga_write (struct usb_dev_handle *udh, int reg, int value)
{
  // on the rev0 usrp, we use thd FPGA_WRITE_REG command
  
  unsigned char buf[4];

  buf[0] = (value >> 24) & 0xff;
  buf[1] = (value >> 16) & 0xff;
  buf[2] = (value >>  8) & 0xff;
  buf[3] = (value >>  0) & 0xff;

  return write_cmd (udh, VRQ_FPGA_WRITE_REG,
		    0, reg & 0xff, buf, sizeof (buf)) == sizeof (buf);
}

static bool
usrp1_fpga_write (struct usb_dev_handle *udh,
		  int regno, int value)
{
  // on the rev1 usrp, we use the generic spi_write interface

  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 usrp_spi_write (udh, 0x00 | (regno & 0x7f),
			 SPI_ENABLE_FPGA,
			 SPI_FMT_MSB | SPI_FMT_HDR_1,
			 buf, sizeof (buf));
}

static bool
usrp1_fpga_read (struct usb_dev_handle *udh,
		 int regno, int *value)
{
  *value = 0;
  unsigned char buf[4];

  bool ok = usrp_spi_read (udh, 0x80 | (regno & 0x7f),
			   SPI_ENABLE_FPGA,
			   SPI_FMT_MSB | SPI_FMT_HDR_1,
			   buf, sizeof (buf));

  if (ok)
    *value = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];

  return ok;
}


bool
usrp_write_fpga_reg (struct usb_dev_handle *udh, int reg, int value)
{
  switch (_usrp_hw_rev (dev_handle_to_dev (udh))){
  case 0:
    return usrp0_fpga_write (udh, reg, value);
  case 1:
  case 2:
    return usrp1_fpga_write (udh, reg, value);
  default:
    abort ();
  }
}

bool
usrp_read_fpga_reg (struct usb_dev_handle *udh, int reg, int *value)
{
  switch (_usrp_hw_rev (dev_handle_to_dev (udh))){
  case 0:
    return false;		// no read back
  case 1:
  case 2:
    return usrp1_fpga_read (udh, reg, value);
  default:
    abort ();
  }
}

bool 
usrp_set_fpga_reset (struct usb_dev_handle *udh, bool on)
{
  return usrp_set_switch (udh, VRQ_FPGA_SET_RESET, on);
}

bool 
usrp_set_fpga_tx_enable (struct usb_dev_handle *udh, bool on)
{
  return usrp_set_switch (udh, VRQ_FPGA_SET_TX_ENABLE, on);
}

bool 
usrp_set_fpga_rx_enable (struct usb_dev_handle *udh, bool on)
{
  return usrp_set_switch (udh, VRQ_FPGA_SET_RX_ENABLE, on);
}

bool 
usrp_set_fpga_tx_reset (struct usb_dev_handle *udh, bool on)
{
  return usrp_set_switch (udh, VRQ_FPGA_SET_TX_RESET, on);
}

bool 
usrp_set_fpga_rx_reset (struct usb_dev_handle *udh, bool on)
{
  return usrp_set_switch (udh, VRQ_FPGA_SET_RX_RESET, on);
}

bool
usrp_set_sleep_bits (struct usb_dev_handle *udh, int bits, int mask)
{
  switch (_usrp_hw_rev (dev_handle_to_dev (udh))){
  case 0:
    bits &= 0xf;
    mask &= 0xf;
    return write_cmd (udh, VRQ_SET_SLEEP_BITS, (mask << 8) | bits, 0, 0, 0) == 0;
  default:
    return true;
  }
}

// ----------------------------------------------------------------
// conditional load stuff

static bool
compute_hash (const char *filename, unsigned char hash[USRP_HASH_SIZE])
{
  assert (USRP_HASH_SIZE == 16);
  memset (hash, 0, USRP_HASH_SIZE);

  FILE *fp = fopen (filename, "rb");
  if (fp == 0){
    perror (filename);
    return false;
  }
  int r = md5_stream (fp, hash);
  fclose (fp);
  
  return r == 0;
}

static usrp_load_status_t
usrp_conditionally_load_something (struct usb_dev_handle *udh,
				   const char *filename,
				   bool force,
				   int slot,
				   bool loader (struct usb_dev_handle *,
						const char *,
						unsigned char [USRP_HASH_SIZE]))
{
  unsigned char file_hash[USRP_HASH_SIZE];
  unsigned char usrp_hash[USRP_HASH_SIZE];
  FILE *fp;

  //  if (access (filename, R_OK) != 0)  
  if ((fp = fopen(filename,"r")) == NULL)
    {
    perror (filename);
    return ULS_ERROR;
    }
  else
     fclose(fp);

  if (!compute_hash (filename, file_hash))
    return ULS_ERROR;

  if (!force
      && usrp_get_hash (udh, slot, usrp_hash)
      && memcmp (file_hash, usrp_hash, USRP_HASH_SIZE) == 0)
    return ULS_ALREADY_LOADED;

  bool r = loader (udh, filename, file_hash);

  if (!r)
    return ULS_ERROR;

  return ULS_OK;
}

usrp_load_status_t
usrp_load_firmware (struct usb_dev_handle *udh,
		    const char *filename,
		    bool force)
{
  return usrp_conditionally_load_something (udh, filename, force,
					    FIRMWARE_HASH_SLOT,
					    _usrp_load_firmware);
}

usrp_load_status_t
usrp_load_fpga (struct usb_dev_handle *udh,
		const char *filename,
		bool force)
{
  return usrp_conditionally_load_something (udh, filename, force,
					    FPGA_HASH_SLOT,
					    _usrp_load_fpga);
}

static usb_dev_handle *
open_nth_cmd_interface (int nth)
{
  struct usb_device *udev = usrp_find_device (nth);
  if (udev == 0){
    printf( "usrp: failed to find usrp[%d]\n", nth);
    return 0;
  }

  struct usb_dev_handle *udh;

  udh = usrp_open_cmd_interface (udev);
  if (udh == 0){
    // FIXME this could be because somebody else has it open.
    // We should delay and retry...
    printf( "open_nth_cmd_interface: open_cmd_interface failed\n");
    usb_strerror ();
    return 0;
  }

  return udh;
 }


static bool
mdelay (int millisecs)
{
  mysleep(millisecs);
  return true;
}

usrp_load_status_t
usrp_load_firmware_nth (int nth, const char *filename, bool force){
  struct usb_dev_handle *udh = open_nth_cmd_interface (nth);
  if (udh == 0)
    return ULS_ERROR;

  usrp_load_status_t s = usrp_load_firmware (udh, filename, force);
  usrp_close_cmd_interface (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
    
    mysleep(1000);	// delay for 1 second

    usb_find_busses ();		// rescan busses and devices
    usb_find_devices ();

    return ULS_OK;

  default:
  case ULS_ERROR:		// some kind of problem
    return ULS_ERROR;
  }
}

static void
load_status_msg (usrp_load_status_t s, const char *type, const char *filename)
{
  switch (s){
  case ULS_ERROR:
    printf( "usrp: failed to load %s %s.\n", type, filename);
    break;
    
#if (VERBOSE)
  case ULS_ALREADY_LOADED:
    printf( "usrp: %s %s already loaded.\n", type, filename);
    break;

  case ULS_OK:
    printf( "usrp: %s %s loaded successfully.\n", type, filename);
    break;
#else
  case ULS_ALREADY_LOADED:
  case ULS_OK:
    break;
#endif
  }
}

bool
usrp_load_standard_bits (int nth, bool force)
{
  usrp_load_status_t 	s;
  char			filename[1000];
  int hw_rev;

  // first, figure out what hardware rev we're dealing with
  {
    struct usb_device *udev = usrp_find_device (nth);
    if (udev == 0){
      printf( "usrp: failed to find usrp[%d]\n", nth);
      return false;
    }
    hw_rev = _usrp_hw_rev (udev);
  }

  // start by loading the firmware

  find_file(usrp_firmware_filename, hw_rev, filename);
  if (strcmp(filename,"NULL") == 0){
    printf( "Can't find firmware: %s\n", usrp_firmware_filename);
    return false;
  }

  s = usrp_load_firmware_nth (nth, filename, force);
  load_status_msg (s, "firmware", filename);

  if (s == ULS_ERROR)
    return false;

  // if we actually loaded firmware, we must reload fpga ...
  if (s == ULS_OK)
    force = true;

  // now move on to the fpga configuration bitstream

  find_file(usrp_fpga_filename, hw_rev, filename);
  if (strcmp(filename,"NULL") == 0){
    printf( "Can't find fpga bitstream: %s\n", usrp_fpga_filename);
    return false;
  }

  struct usb_dev_handle *udh = open_nth_cmd_interface (nth);
  if (udh == 0)
    return false;
  
  s = usrp_load_fpga (udh, filename, force);
  usrp_close_cmd_interface (udh);
  load_status_msg (s, "fpga bitstream", filename);

  if (s == ULS_ERROR)
    return false;

  return true;
}

bool
_usrp_get_status (struct usb_dev_handle *udh, int which, bool *trouble)
{
  unsigned char	status;
  *trouble = true;
  
  if (write_cmd (udh, VRQ_GET_STATUS, 0, which,
		 &status, sizeof (status)) != sizeof (status))
    return false;

  *trouble = status;
  return true;
}

bool
usrp_check_rx_overrun (struct usb_dev_handle *udh, bool *overrun_p)
{
  return _usrp_get_status (udh, GS_RX_OVERRUN, overrun_p);
}

bool
usrp_check_tx_underrun (struct usb_dev_handle *udh, bool *underrun_p)
{
  return _usrp_get_status (udh, GS_TX_UNDERRUN, underrun_p);
}


bool
usrp_i2c_write (struct usb_dev_handle *udh, int i2c_addr,
		const void *buf, int len)
{
  if (len < 1 || len > MAX_EP0_PKTSIZE)
    return false;

  return write_cmd (udh, VRQ_I2C_WRITE, i2c_addr, 0,
		    (unsigned char *) buf, len) == len;
}


bool
usrp_i2c_read (struct usb_dev_handle *udh, int i2c_addr,
	       void *buf, int len)
{
  if (len < 1 || len > MAX_EP0_PKTSIZE)
    return false;

  return write_cmd (udh, VRQ_I2C_READ, i2c_addr, 0,
		    (unsigned char *) buf, len) == len;
}

bool
usrp_spi_write (struct usb_dev_handle *udh,
		int optional_header, int enables, int format,
		const void *buf, int len)
{
  if (len < 0 || len > MAX_EP0_PKTSIZE)
    return false;

  return write_cmd (udh, VRQ_SPI_WRITE,
		    optional_header,
		    ((enables & 0xff) << 8) | (format & 0xff),
		    (unsigned char *) buf, len) == len;
}


bool
usrp_spi_read (struct usb_dev_handle *udh,
	       int optional_header, int enables, int format,
	       void *buf, int len)
{
  if (len < 0 || len > MAX_EP0_PKTSIZE)
    return false;

  return write_cmd (udh, VRQ_SPI_READ,
		    optional_header,
		    ((enables & 0xff) << 8) | (format & 0xff),
		    (unsigned char *) buf, len) == len;
}

bool
usrp_9862_write (struct usb_dev_handle *udh, int which_codec,
		 int regno, int value)
{
  if (0)
    printf( "usrp_9862_write which = %d, reg = %2d, val = %3d (0x%02x)\n",
	     which_codec, regno, value, value);

  unsigned char buf[1];

  buf[0] = value;
  
  return usrp_spi_write (udh, 0x00 | (regno & 0x3f),
			 which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B,
			 SPI_FMT_MSB | SPI_FMT_HDR_1,
			 buf, 1);
}

bool
usrp_9862_read (struct usb_dev_handle *udh, int which_codec,
		int regno, unsigned char *value)
{
  return usrp_spi_read (udh, 0x80 | (regno & 0x3f),
			which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B,
			SPI_FMT_MSB | SPI_FMT_HDR_1,
			value, 1);
}

bool
usrp_9862_write_many (struct usb_dev_handle *udh,
		      int which_codec,
		      const unsigned char *buf,
		      int len)
{
  if (len & 0x1)
    return false;		// must be even

  bool result = true;

  while (len > 0){
    result &= usrp_9862_write (udh, which_codec, buf[0], buf[1]);
    len -= 2;
    buf += 2;
  }

  return result;
}


bool
usrp_9862_write_many_all (struct usb_dev_handle *udh,
			   const unsigned char *buf, int len)
{
  // FIXME handle 2/2 and 4/4 versions

  bool result;
  result  = usrp_9862_write_many (udh, 0, buf, len);
  result &= usrp_9862_write_many (udh, 1, buf, len);
  return result;
}

static void
power_down_9862s (struct usb_dev_handle *udh)
{
  static const unsigned char regs[] = {
    REG_RX_PWR_DN,	0x01,			// everything
    REG_TX_PWR_DN,	0x0f,			// pwr dn digital and analog_both
    REG_TX_MODULATOR,	0x00			// coarse & fine modulators disabled
  };

  switch (_usrp_hw_rev (dev_handle_to_dev (udh))){
  case 1:
  case 2:
    usrp_9862_write_many_all (udh, regs, sizeof (regs));
    break;
  }
}



static const int EEPROM_PAGESIZE = 16;

bool
usrp_eeprom_write (struct usb_dev_handle *udh, int i2c_addr,
		   int eeprom_offset, const void *buf, int len)
{
  unsigned char cmd[2];
  const unsigned char *p = (unsigned char *) buf;
  
  // 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...

  while (len-- > 0){
    cmd[0] = eeprom_offset++;
    cmd[1] = *p++;
    bool r = usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd));
    mdelay (10);		// delay 10ms worst case write time
    if (!r)
      return false;
  }
  
  return true;
}

bool
usrp_eeprom_read (struct usb_dev_handle *udh, int i2c_addr,
		  int eeprom_offset, void *buf, int len)
{
  unsigned char *p = (unsigned char *) buf;
  int n;

  // 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 (!usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd)))
    return false;

  while (len > 0){
    if (len < MAX_EP0_PKTSIZE)
       n = len;
    else
       n = MAX_EP0_PKTSIZE;

    if (!usrp_i2c_read (udh, i2c_addr, p, n))
      return false;
    len -= n;
    p += n;
  }
  return true;
}
 
// ----------------------------------------------------------------

static bool
slot_to_codec (int slot, int *which_codec)
{
  *which_codec = 0;
  
  switch (slot){
  case SLOT_TX_A:
  case SLOT_RX_A:
    *which_codec = 0;
    break;

  case SLOT_TX_B:
  case SLOT_RX_B:
    *which_codec = 1;
    break;

  default:
    printf( "usrp_prims:slot_to_codec: invalid slot = %d\n", slot);
    return false;
  }
  return true;
}

static bool
tx_slot_p (int slot)
{
  switch (slot){
  case SLOT_TX_A:
  case SLOT_TX_B:
    return true;

  default:
    return false;
  }
}

bool
usrp_write_aux_dac (struct usb_dev_handle *udh, int slot,
		    int which_dac, int value)
{
  int which_codec;
  
  if (!slot_to_codec (slot, &which_codec))
    return false;

  if (!(0 <= which_dac && which_dac < 4)){
    printf( "usrp_write_aux_dac: invalid dac = %d\n", which_dac);
    return false;
  }

  value &= 0x0fff;	// mask to 12-bits
  
  if (which_dac == 3){
    // dac 3 is really 12-bits.  Use value as is.
    bool r = true;
    r &= usrp_9862_write (udh, which_codec, 43, (value >> 4));       // most sig
    r &= usrp_9862_write (udh, which_codec, 42, (value & 0xf) << 4); // least sig
    return r;
  }
  else {
    // dac 0, 1, and 2 are really 8 bits.  
    value = value >> 4;		// shift value appropriately
    return usrp_9862_write (udh, which_codec, 36 + which_dac, value);
  }
}


bool
usrp_read_aux_adc (struct usb_dev_handle *udh, int slot,
		   int which_adc, int *value)
{
  *value = 0;
  int	which_codec;

  if (!slot_to_codec (slot, &which_codec))
    return false;

  if (!(0 <= which_codec && which_codec < 2)){
    printf( "usrp_read_aux_adc: invalid adc = %d\n", which_adc);
    return false;
  }

  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 (tx_slot_p (slot))
    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.

  usrp_9862_write (udh, which_codec, 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
  usrp_9862_write (udh, which_codec, 34, aux_adc_control);

  // read the 10-bit result back
  unsigned char v_lo = 0;
  unsigned char v_hi = 0;
  bool r = usrp_9862_read (udh, which_codec, rd_reg, &v_lo);
  r &= usrp_9862_read (udh, which_codec, rd_reg + 1, &v_hi);

  if (r)
    *value = ((v_hi << 2) | ((v_lo >> 6) & 0x3)) << 2;	// format as 12-bit
  
  return r;
}

// ----------------------------------------------------------------

static int slot_to_i2c_addr (int slot)
{
  switch (slot){
  case SLOT_TX_A:	return I2C_ADDR_TX_A;
  case SLOT_RX_A:	return I2C_ADDR_RX_A;
  case SLOT_TX_B:	return I2C_ADDR_TX_B;
  case SLOT_RX_B:	return I2C_ADDR_RX_B;
  default:		return -1;
  }
}

static void
set_chksum (unsigned char *buf)
{
  int sum = 0;
  unsigned int i;
  for (i = 0; i < DB_EEPROM_CLEN - 1; i++)
    sum += buf[i];
  buf[i] = -sum;
}

static usrp_dbeeprom_status_t
read_dboard_eeprom (struct usb_dev_handle *udh,
		    int slot_id, unsigned char *buf)
{
  int i2c_addr = slot_to_i2c_addr (slot_id);
  if (i2c_addr == -1)
    return UDBE_BAD_SLOT;

  if (!usrp_eeprom_read (udh, i2c_addr, 0, buf, DB_EEPROM_CLEN))
    return UDBE_NO_EEPROM;

  if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE)
    return UDBE_INVALID_EEPROM;

  int sum = 0;
  for (unsigned int i = 0; i < DB_EEPROM_CLEN; i++)
    sum += buf[i];

  if ((sum & 0xff) != 0)
    return UDBE_INVALID_EEPROM;

  return UDBE_OK;
}

usrp_dbeeprom_status_t
usrp_read_dboard_eeprom (struct usb_dev_handle *udh,
			 int slot_id, usrp_dboard_eeprom *eeprom)
{
  unsigned char buf[DB_EEPROM_CLEN];

  memset (eeprom, 0, sizeof (*eeprom));

  usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf);
  if (s != UDBE_OK)
    return s;

  eeprom->id = (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB];
  eeprom->oe = (buf[DB_EEPROM_OE_MSB] << 8) | buf[DB_EEPROM_OE_LSB];
  eeprom->offset[0] = (buf[DB_EEPROM_OFFSET_0_MSB] << 8) | buf[DB_EEPROM_OFFSET_0_LSB];
  eeprom->offset[1] = (buf[DB_EEPROM_OFFSET_1_MSB] << 8) | buf[DB_EEPROM_OFFSET_1_LSB];

  return UDBE_OK;
}

bool
usrp_write_dboard_offsets (struct usb_dev_handle *udh, int slot_id,
			   short offset0, short offset1)
{
  unsigned char buf[DB_EEPROM_CLEN];

  usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf);
  if (s != UDBE_OK)
    return false;

  buf[DB_EEPROM_OFFSET_0_LSB] = (offset0 >> 0) & 0xff;
  buf[DB_EEPROM_OFFSET_0_MSB] = (offset0 >> 8) & 0xff;
  buf[DB_EEPROM_OFFSET_1_LSB] = (offset1 >> 0) & 0xff;
  buf[DB_EEPROM_OFFSET_1_MSB] = (offset1 >> 8) & 0xff;
  set_chksum (buf);

  return usrp_eeprom_write (udh, slot_to_i2c_addr (slot_id),
			    0, buf, sizeof (buf));
}

///////////////////// sleep function //////////////////

/***** Windows version
#include <windows.h>
#include <winbase.h>

void mysleep(int ms)
{
Sleep((DWORD) ms);
}
************/

/***** Linux ******/
void mysleep(int ms)
{
struct timespec timeOut,remains;
timeOut.tv_sec = 0;
timeOut.tv_nsec = ms*1000000;
nanosleep(&timeOut,&remains);
}
