Fix problem with commands timing out (specifically stop_rx_streaming)
[debian/gnuradio] / usrp2 / host / lib / usrp2_impl.cc
index d7769a842a0f3816571078690174a4d3e347254d..d1e85805c981ae94f33fc9b64b6fea39eb2d2568 100644 (file)
@@ -1,6 +1,6 @@
 /* -*- c++ -*- */
 /*
- * Copyright 2008 Free Software Foundation, Inc.
+ * Copyright 2008,2009 Free Software Foundation, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -46,7 +46,6 @@
 #endif
 
 static const int DEFAULT_RX_SCALE = 1024;
-static const int DEFAULT_TX_SCALE = 3000;
 
 namespace usrp2 {
 
@@ -74,6 +73,12 @@ namespace usrp2 {
     case OP_SYNC_TO_PPS: return "OP_SYNC_TO_PPS";
     case OP_PEEK: return "OP_PEEK";
     case OP_PEEK_REPLY: return "OP_PEEK_REPLY";
+    case OP_SET_TX_LO_OFFSET: return "OP_SET_TX_LO_OFFSET";
+    case OP_SET_TX_LO_OFFSET_REPLY: return "OP_SET_TX_LO_OFFSET_REPLY";
+    case OP_SET_RX_LO_OFFSET: return "OP_SET_RX_LO_OFFSET";
+    case OP_SET_RX_LO_OFFSET_REPLY: return "OP_SET_RX_LO_OFFSET_REPLY";
+    case OP_SYNC_EVERY_PPS: return "OP_SYNC_EVERY_PPS";
+    case OP_SYNC_EVERY_PPS_REPLY: return "OP_SYNC_EVERY_PPS_REPLY";
 
     default:
       char buf[64];
@@ -123,22 +128,25 @@ namespace usrp2 {
   }
 
 
-  usrp2::impl::impl(const std::string &ifc, props *p)
-    : d_eth_buf(new eth_buffer()), d_pf(0), d_bg_thread(0), d_bg_running(false),
-      d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
+  usrp2::impl::impl(const std::string &ifc, props *p, size_t rx_bufsize)
+    : d_eth_buf(new eth_buffer(rx_bufsize)), d_interface_name(ifc), d_pf(0), d_bg_thread(0),
+      d_bg_running(false), d_rx_seqno(-1), d_tx_seqno(0), d_next_rid(0),
       d_num_rx_frames(0), d_num_rx_missing(0), d_num_rx_overruns(0), d_num_rx_bytes(0), 
       d_num_enqueued(0), d_enqueued_mutex(), d_bg_pending_cond(&d_enqueued_mutex),
-      d_channel_rings(NCHANS), d_tx_interp(0), d_rx_decim(0)
+      d_channel_rings(NCHANS), d_tx_interp(0), d_rx_decim(0), d_dont_enqueue(true)
   {
     if (!d_eth_buf->open(ifc, htons(U2_ETHERTYPE)))
       throw std::runtime_error("Unable to register USRP2 protocol");
     
-    d_pf = pktfilter::make_ethertype_inbound(U2_ETHERTYPE, d_eth_buf->mac());
+    d_addr = p->addr;
+
+    // Create a packet filter for U2_ETHERTYPE packets sourced from target USRP2
+    u2_mac_addr_t usrp_mac;
+    parse_mac_addr(d_addr, &usrp_mac);
+    d_pf = pktfilter::make_ethertype_inbound_target(U2_ETHERTYPE, (const unsigned char*)&(usrp_mac.addr));
     if (!d_pf || !d_eth_buf->attach_pktfilter(d_pf))
       throw std::runtime_error("Unable to attach packet filter.");
     
-    d_addr = p->addr;
-    
     if (USRP2_IMPL_DEBUG)
       std::cerr << "usrp2 constructor: using USRP2 at " << d_addr << std::endl;
 
@@ -147,6 +155,10 @@ namespace usrp2 {
     d_bg_thread = new usrp2_thread(this);
     d_bg_thread->start();
 
+    // In case the USRP2 was left streaming RX
+    // FIXME: only one channel right now
+    stop_rx_streaming(0);
+
     if (!dboard_info())                // we're hosed
       throw std::runtime_error("Unable to retrieve daughterboard info");
 
@@ -170,6 +182,10 @@ namespace usrp2 {
       fprintf(stderr, "  gain_db_per_step = %g\n", rx_gain_db_per_step());
     }
 
+    // Ensure any custom values in hardware are cleared
+    if (!reset_db())
+      std::cerr << "usrp2::ctor reset_db failed\n";
+
     // default gains to mid point
     if (!set_tx_gain((tx_gain_min() + tx_gain_max()) / 2))
       std::cerr << "usrp2::ctor set_tx_gain failed\n";
@@ -187,9 +203,6 @@ namespace usrp2 {
     // set workable defaults for scaling
     if (!set_rx_scale_iq(DEFAULT_RX_SCALE, DEFAULT_RX_SCALE))
       std::cerr << "usrp2::ctor set_rx_scale_iq failed\n";
-
-    if (!set_tx_scale_iq(DEFAULT_TX_SCALE, DEFAULT_TX_SCALE))
-      std::cerr << "usrp2::ctor set_tx_scale_iq failed\n";
   }
   
   usrp2::impl::~impl()
@@ -285,22 +298,24 @@ namespace usrp2 {
     cmd->eop.len = sizeof(cmd->eop);
   }
 
+
+  bool
+  usrp2::impl::transmit_cmd(void *cmd, size_t len)
+  {
+    return d_eth_buf->tx_frame(cmd, len) == eth_buffer::EB_OK;
+  }
+
   bool
-  usrp2::impl::transmit_cmd(void *cmd, size_t len, pending_reply *p, double secs)
+  usrp2::impl::transmit_cmd_and_wait(void *cmd, size_t len, pending_reply *p, double secs)
   {
-    if (p)    
-      d_pending_replies[p->rid()] = p;
+    d_pending_replies[p->rid()] = p;
     
-    // Transmit command
-    if (d_eth_buf->tx_frame(cmd, len) != eth_buffer::EB_OK) {
+    if (!transmit_cmd(cmd, len)){
       d_pending_replies[p->rid()] = 0;
       return false;
     }
 
-    int res = 1;
-    if (p)
-      res = p->wait(secs);
-      
+    int res = p->wait_for_completion(secs);
     d_pending_replies[p->rid()] = 0;
     return res == 1;
   }
@@ -360,6 +375,10 @@ namespace usrp2 {
       return handle_control_packet(base, len);
     }
     else {                             // data packets
+
+      if (d_dont_enqueue)              // toss packet
+       return data_handler::RELEASE;
+
       return handle_data_packet(base, len);
     }
 
@@ -391,7 +410,7 @@ namespace usrp2 {
     
       // Copy reply into caller's buffer
       memcpy(rp->buffer(), p, std::min(oplen, buflen));
-      rp->signal();
+      rp->notify_completion();
       d_pending_replies[rid] = 0;
       return data_handler::RELEASE;
     }
@@ -432,24 +451,28 @@ namespace usrp2 {
     // FIXME unaligned load!
     unsigned int chan = u2p_chan(&pkt->hdrs.fixed);
 
-    if (!d_channel_rings[chan]) {
-      DEBUG_LOG("!");
-      return data_handler::RELEASE;    // discard packet, no channel handler
-    }
-
-    // Strip off ethernet header and transport header and enqueue the rest
+    {
+      omni_mutex_lock l(d_channel_rings_mutex);
 
-    size_t offset = offsetof(u2_eth_samples_t, hdrs.fixed);
-    if (d_channel_rings[chan]->enqueue(&pkt->hdrs.fixed, len-offset)) {
-      inc_enqueued();
-      DEBUG_LOG("+");
-      return data_handler::KEEP;       // channel ring runner will mark frame done
+      if (!d_channel_rings[chan]) {
+       DEBUG_LOG("!");
+       return data_handler::RELEASE;   // discard packet, no channel handler
+      }
+      
+      // Strip off ethernet header and transport header and enqueue the rest
+      
+      size_t offset = offsetof(u2_eth_samples_t, hdrs.fixed);
+      if (d_channel_rings[chan]->enqueue(&pkt->hdrs.fixed, len-offset)) {
+       inc_enqueued();
+       DEBUG_LOG("+");
+       return data_handler::KEEP;      // channel ring runner will mark frame done
+      }
+      else {
+       DEBUG_LOG("!");
+       return data_handler::RELEASE;   // discard, no room in channel ring
+      }        
+      return data_handler::RELEASE;
     }
-    else {
-      DEBUG_LOG("!");
-      return data_handler::RELEASE;    // discard, no room in channel ring
-    }          
-    return data_handler::RELEASE;
   }
 
 
@@ -468,13 +491,40 @@ namespace usrp2 {
     cmd.op.gain = htons(u2_double_to_fxpt_gain(gain));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
     return success;
   }
   
+  bool
+  usrp2::impl::set_rx_lo_offset(double frequency)
+  {
+    op_freq_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SET_RX_LO_OFFSET;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+
+    u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+    cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+    cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
   bool
   usrp2::impl::set_rx_center_freq(double frequency, tune_result *result)
   {
@@ -488,7 +538,7 @@ namespace usrp2 {
     cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -525,7 +575,7 @@ namespace usrp2 {
     cmd.op.decim = htonl(decimation_factor);
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -545,7 +595,7 @@ namespace usrp2 {
     cmd.op.scale_iq = htonl(((scale_i & 0xffff) << 16) | (scale_q & 0xffff));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -567,35 +617,43 @@ namespace usrp2 {
       return false;
     }
 
-    if (d_channel_rings[channel]) {
-      std::cerr << "usrp2: channel " << channel
-               << " already streaming" << std::endl;
-      return false;
-    }
-
-    d_channel_rings[channel] = ring_sptr(new ring(d_eth_buf->max_frames()));
-
-    if (items_per_frame == 0)
-      items_per_frame = U2_MAX_SAMPLES;                // minimize overhead
-    
-    op_start_rx_streaming_cmd cmd;
-    op_generic_t reply;
-
-    memset(&cmd, 0, sizeof(cmd));
-    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
-    cmd.op.opcode = OP_START_RX_STREAMING;
-    cmd.op.len = sizeof(cmd.op);
-    cmd.op.rid = d_next_rid++;
-    cmd.op.items_per_frame = htonl(items_per_frame);
-    cmd.eop.opcode = OP_EOP;
-    cmd.eop.len = sizeof(cmd.eop);
+    {
+      omni_mutex_lock l(d_channel_rings_mutex);
+      if (d_channel_rings[channel]) {
+       std::cerr << "usrp2: channel " << channel
+                 << " already streaming" << std::endl;
+       return false;
+      }
+      
+      if (items_per_frame == 0)
+       items_per_frame = U2_MAX_SAMPLES;               // minimize overhead
+      
+      op_start_rx_streaming_cmd cmd;
+      op_generic_t reply;
+
+      memset(&cmd, 0, sizeof(cmd));
+      init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+      cmd.op.opcode = OP_START_RX_STREAMING;
+      cmd.op.len = sizeof(cmd.op);
+      cmd.op.rid = d_next_rid++;
+      cmd.op.items_per_frame = htonl(items_per_frame);
+      cmd.eop.opcode = OP_EOP;
+      cmd.eop.len = sizeof(cmd.eop);
     
-    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
-      return false;
+      d_dont_enqueue = false;
+      bool success = false;
+      pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+      success = transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT);
+      success = success && (ntohx(reply.ok) == 1);
+      
+      if (success)
+       d_channel_rings[channel] = ring_sptr(new ring(d_eth_buf->max_frames()));
+      else
+       d_dont_enqueue = true;
 
-    bool success = (ntohx(reply.ok) == 1);
-    return success;
+      //fprintf(stderr, "usrp2::start_rx_streaming: success = %d\n", success);
+      return success;
+    }
   }
   
   bool
@@ -613,36 +671,32 @@ namespace usrp2 {
       return false;
     }
 
-#if 0 // don't be overzealous.    
-    if (!d_channel_rings[channel]) {
-      std::cerr << "usrp2: channel " << channel
-               << " not streaming" << std::endl;
-      return false;
-    }
-#endif
+    d_dont_enqueue = true;     // no new samples
+    flush_rx_samples(channel); // dump any we may already have
 
     op_stop_rx_cmd cmd;
     op_generic_t reply;
 
-    memset(&cmd, 0, sizeof(cmd));
-    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
-    cmd.op.opcode = OP_STOP_RX;
-    cmd.op.len = sizeof(cmd.op);
-    cmd.op.rid = d_next_rid++;
-    cmd.eop.opcode = OP_EOP;
-    cmd.eop.len = sizeof(cmd.eop);
-    
-    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
-      return false;
+    {
+      omni_mutex_lock l(d_channel_rings_mutex);
 
-    bool success = (ntohx(reply.ok) == 1);
-    if (success)
+      memset(&cmd, 0, sizeof(cmd));
+      init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+      cmd.op.opcode = OP_STOP_RX;
+      cmd.op.len = sizeof(cmd.op);
+      cmd.op.rid = d_next_rid++;
+      cmd.eop.opcode = OP_EOP;
+      cmd.eop.len = sizeof(cmd.eop);
+    
+      bool success = false;
+      pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+      success = transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT);
+      success = success && (ntohx(reply.ok) == 1);
       d_channel_rings[channel].reset();
-
-    return success;
+      //fprintf(stderr, "usrp2::stop_rx_streaming:  success = %d\n", success);
+      return success;
+    }
   }
-  
 
   bool
   usrp2::impl::rx_samples(unsigned int channel, rx_sample_handler *handler)
@@ -692,6 +746,36 @@ namespace usrp2 {
     return true;
   }
 
+  bool
+  usrp2::impl::flush_rx_samples(unsigned int channel)
+  {
+    if (channel > MAX_CHAN) {
+      std::cerr << "usrp2: invalid channel (" << channel
+                << " )" << std::endl;
+      return false;
+    }
+
+    if (channel > 0) {
+      std::cerr << "usrp2: channel " << channel
+                << " not implemented" << std::endl;
+      return false;
+    }
+
+    ring_sptr rp = d_channel_rings[channel];
+    if (!rp){
+      return false;
+    }
+
+    // Iterate through frames and drop them
+    void *p;
+    size_t frame_len_in_bytes;
+    while (rp->dequeue(&p, &frame_len_in_bytes)) {
+      d_eth_buf->release_frame(p);
+      dec_enqueued();
+    }
+    return true;
+  }
+
   // ----------------------------------------------------------------
   //                           Transmit
   // ----------------------------------------------------------------
@@ -707,13 +791,40 @@ namespace usrp2 {
     cmd.op.gain = htons(u2_double_to_fxpt_gain(gain));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
     return success;
   }
   
+  bool
+  usrp2::impl::set_tx_lo_offset(double frequency)
+  {
+    op_freq_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SET_TX_LO_OFFSET;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+
+    u2_fxpt_freq_t fxpt = u2_double_to_fxpt_freq(frequency);
+    cmd.op.freq_hi = htonl(u2_fxpt_freq_hi(fxpt));
+    cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
+
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
   bool
   usrp2::impl::set_tx_center_freq(double frequency, tune_result *result)
   {
@@ -727,7 +838,7 @@ namespace usrp2 {
     cmd.op.freq_lo = htonl(u2_fxpt_freq_lo(fxpt));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -764,15 +875,46 @@ namespace usrp2 {
     cmd.op.interp = htonl(interpolation_factor);
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
-    if (success)
+    if (success) {
       d_tx_interp = interpolation_factor;
+
+      // Auto-set TX scaling based on interpolation rate
+      int scale_i, scale_q;
+      default_tx_scale_iq(d_tx_interp, &scale_i, &scale_q);
+      return set_tx_scale_iq(scale_i, scale_q);
+    }
+
     return success;
   }
   
+  void
+  usrp2::impl::default_tx_scale_iq(int interpolation_factor, int *scale_i, int *scale_q)
+  {
+    // Calculate CIC interpolation (i.e., without halfband interpolators)
+    int i = interpolation_factor;
+    if (i > 128)
+      i = i >> 1;
+    if (i > 128)
+      i = i >> 1;
+
+    // Calculate dsp_core_tx gain absent scale multipliers
+    float gain = (1.65*i*i*i)/(4096*pow(2, ceil(log2(i*i*i))));
+    
+    // Calculate closest multiplier constant to reverse gain
+    int scale = (int)rint(1.0/gain);
+    // fprintf(stderr, "if=%i i=%i gain=%f scale=%i\n", interpolation_factor, i, gain, scale);
+
+    // Both I and Q are identical in this case
+    if (scale_i)
+      *scale_i = scale;
+    if (scale_q)
+      *scale_q = scale;
+  }
+
   bool
   usrp2::impl::set_tx_scale_iq(int scale_i, int scale_q)
   {
@@ -784,7 +926,7 @@ namespace usrp2 {
     cmd.op.scale_iq = htonl(((scale_i & 0xffff) << 16) | (scale_q & 0xffff));
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -834,10 +976,8 @@ namespace usrp2 {
     if (nitems == 0)
       return true;
 
-    // FIXME there's the possibility that we send fewer than 9 items in a frame.
-    // That would end up glitching the transmitter, since the ethernet will pad to
-    // 64-bytes total (9 items).  We really need some part of the stack to
-    // carry the real length (thdr?).
+    // FIXME can't deal with nitems < U2_MIN_SAMPLES (will be fixed in VRT)
+    // FIXME need to check the MTU instead of assuming 1500 bytes
 
     // fragment as necessary then fire away
 
@@ -867,7 +1007,12 @@ namespace usrp2 {
 
       init_etf_hdrs(&hdrs, d_addr, flags, channel, timestamp);
 
-      size_t i = std::min((size_t) U2_MAX_SAMPLES, nitems - n);
+      // Avoid short packet by splitting last two packets if reqd
+      size_t i;
+      if ((nitems - n) > U2_MAX_SAMPLES && (nitems - n) < (U2_MAX_SAMPLES + U2_MIN_SAMPLES))
+       i = (nitems - n) / 2;
+      else
+       i = std::min((size_t) U2_MAX_SAMPLES, nitems - n);
 
       eth_iovec iov[2];
       iov[0].iov_base = &hdrs;
@@ -904,11 +1049,12 @@ namespace usrp2 {
     cmd.op.opcode = OP_CONFIG_MIMO;
     cmd.op.len = sizeof(cmd.op);
     cmd.op.rid = d_next_rid++;
+    cmd.op.flags = flags;
     cmd.eop.opcode = OP_EOP;
     cmd.eop.len = sizeof(cmd.eop);
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     return ntohx(reply.ok) == 1;
@@ -967,7 +1113,7 @@ namespace usrp2 {
       return false;
 
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -1006,7 +1152,7 @@ namespace usrp2 {
     cmd.eop.len = sizeof(cmd.eop);
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     bool success = (ntohx(reply.ok) == 1);
@@ -1033,31 +1179,55 @@ namespace usrp2 {
     cmd.eop.len = sizeof(cmd.eop);
     
     pending_reply p(cmd.op.rid, &reply, sizeof(reply));
-    if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    return ntohx(reply.ok) == 1;
+  }
+
+  bool
+  usrp2::impl::sync_every_pps(bool enable)
+  {
+    op_generic_cmd cmd;
+    op_generic_t   reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_SYNC_EVERY_PPS;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.ok = enable ? 1 : 0;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
       return false;
 
     return ntohx(reply.ok) == 1;
   }
 
-  std::vector<uint8_t>
-  usrp2::impl::peek(uint32_t addr, uint32_t len)
+  std::vector<uint32_t>
+  usrp2::impl::peek32(uint32_t addr, uint32_t words)
   {
-    std::vector<uint8_t> result; // zero sized on error return
-    // fprintf(stderr, "usrp2::peek: addr=%08X len=%u\n", addr, len);
+    std::vector<uint32_t> result; // zero sized on error return
+    // fprintf(stderr, "usrp2::peek: addr=%08X words=%u\n", addr, words);
 
     if (addr % 4 != 0) {
       fprintf(stderr, "usrp2::peek: addr (=%08X) must be 32-bit word aligned\n", addr); 
       return result;
     }
 
-    if (len < 4 || len % 4 != 0) {
-      fprintf(stderr, "usrp2::peek: len (=%u) must be an integral multiple of 4\n", len);
+    if (words == 0)
       return result;
-    }
 
     op_peek_cmd   cmd;
     op_generic_t *reply;
 
+    int wlen = sizeof(uint32_t);
+    int rlen = sizeof(op_generic_t);
+    size_t bytes = words*wlen;
+
     memset(&cmd, 0, sizeof(cmd));
     init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
     cmd.op.opcode = OP_PEEK;
@@ -1067,19 +1237,261 @@ namespace usrp2 {
     cmd.eop.len = sizeof(cmd.eop);
 
     cmd.op.addr = htonl(addr);
-    cmd.op.bytes = htonl(len);
-
-    reply = (op_generic_t *)malloc(sizeof(*reply)+len);
-    pending_reply p(cmd.op.rid, reply, sizeof(*reply)+len);
-    if (transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) {
-      uint32_t bytes = reply->len-sizeof(*reply);
-      uint8_t *data = (uint8_t *)(reply)+sizeof(*reply);
-      for (unsigned int i = 0; i < bytes; i++)
-       result.push_back(data[i]);
+    cmd.op.bytes = htonl(bytes);
+
+    reply = (op_generic_t *)malloc(rlen+bytes);
+    pending_reply p(cmd.op.rid, reply, rlen+bytes);
+    if (transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) {
+      uint32_t nwords = (reply->len-rlen)/sizeof(uint32_t);
+      uint32_t *data = (uint32_t *)(reply+rlen/wlen);
+      for (unsigned int i = 0; i < nwords; i++)
+       result.push_back(ntohl(data[i]));
     }
 
     free(reply);
     return result;
   }
 
+  bool
+  usrp2::impl::poke32(uint32_t addr, const std::vector<uint32_t> &data)
+  {
+    if (addr % 4 != 0) {
+      fprintf(stderr, "usrp2::poke32: addr (=%08X) must be 32-bit word aligned\n", addr); 
+      return false;
+    }
+
+    int plen = sizeof(op_poke_cmd);
+    int wlen = sizeof(uint32_t);
+    int max_words = (MAX_SUBPKT_LEN-plen)/wlen;
+    int words = data.size();
+
+    if (words > max_words) {
+      fprintf(stderr, "usrp2::poke32: write size (=%u) exceeds maximum of %u words\n",
+             words, max_words);
+      return false;
+    }
+
+    //fprintf(stderr, "usrp2::poke32: addr=%08X words=%u\n", addr, words);
+
+    if (words == 0)
+      return true; // NOP
+
+    op_poke_cmd  *cmd;
+    op_generic_t *eop;
+
+    // Allocate, clear, and initialize command packet
+    int bytes = words*wlen;
+    int l = plen+bytes+sizeof(*eop); // op_poke_cmd+data+eop
+    cmd = (op_poke_cmd *)malloc(l);
+    //fprintf(stderr, "cmd=%p l=%i\n", cmd, l);
+    memset(cmd, 0, l);
+    init_etf_hdrs(&cmd->h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd->op.opcode = OP_POKE;
+    cmd->op.len = sizeof(cmd->op)+bytes;
+    cmd->op.rid = d_next_rid++;
+    cmd->op.addr = htonl(addr);
+
+    // Copy data from vector into packet space
+    uint32_t *dest = (uint32_t *)((uint8_t *)cmd+plen);
+    for (int i = 0; i < words; i++) {
+      //fprintf(stderr, "%03i@%p\n", i, dest);
+      *dest++ = htonl(data[i]);
+    }
+
+    // Write end-of-packet subpacket
+    eop = (op_generic_t *)dest;
+    eop->opcode = OP_EOP;
+    eop->len = sizeof(*eop);
+    //fprintf(stderr, "eop=%p len=%i\n", eop, eop->len);
+
+    // Send command to device and retrieve reply
+    bool ok = false;
+    op_generic_t reply;
+    pending_reply p(cmd->op.rid, &reply, sizeof(reply));
+    if (transmit_cmd_and_wait(cmd, l, &p, DEF_CMD_TIMEOUT))
+      ok = (ntohx(reply.ok) == 1);
+
+    free(cmd);
+    return ok;
+  }
+
+  bool
+  usrp2::impl::reset_db()
+  {
+    op_generic_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_RESET_DB;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
+  bool usrp2::impl::set_gpio_ddr(int bank, uint16_t value, uint16_t mask)
+  {
+    if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+      fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+      return false;
+    }
+
+    op_gpio_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_GPIO_SET_DDR;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.bank = static_cast<uint8_t>(bank);
+    cmd.op.value = htons(value);
+    cmd.op.mask = htons(mask);
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
+  bool usrp2::impl::set_gpio_sels(int bank, std::string sels)
+  {
+    if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+      fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+      return false;
+    }
+
+    if (sels.size() != 16) {
+      fprintf(stderr, "set_gpio_sels: sels must be exactly 16 bytes\n");
+      return false;
+    }
+
+    op_gpio_set_sels_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_GPIO_SET_SELS;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.bank = static_cast<uint8_t>(bank);
+    memcpy(&cmd.op.sels, sels.c_str(), 16);
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
+  bool usrp2::impl::write_gpio(int bank, uint16_t value, uint16_t mask)
+  {
+    if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+      fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+      return false;
+    }
+
+    op_gpio_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_GPIO_WRITE;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.bank = static_cast<uint8_t>(bank);
+    cmd.op.value = htons(value);
+    cmd.op.mask = htons(mask);
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
+  bool usrp2::impl::read_gpio(int bank, uint16_t *value)
+  {
+    if (bank != GPIO_TX_BANK && bank != GPIO_RX_BANK) {
+      fprintf(stderr, "set_gpio_ddr: bank must be one of GPIO_RX_BANK or GPIO_TX_BANK\n");
+      return false;
+    }
+
+    op_gpio_cmd cmd;
+    op_gpio_read_reply_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_GPIO_READ;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.bank = static_cast<uint8_t>(bank);
+    cmd.op.value = 0; // not used
+    cmd.op.mask = 0;  // not used
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    if (success && (value != NULL))
+      *value = ntohs(reply.value);
+
+    return success;
+  }
+
+  bool usrp2::impl::enable_gpio_streaming(int bank, int enable)
+  {
+    if (bank != GPIO_RX_BANK) {
+      fprintf(stderr, "enable_gpio_streaming: only RX streaming is currently implemented\n");
+      return false;
+    }
+
+    if ((enable & ~0x03) != 0) {
+      fprintf(stderr, "enable_gpio_streaming: invalid enable format\n");
+      return false;
+    }
+
+    op_gpio_cmd cmd;
+    op_generic_t reply;
+
+    memset(&cmd, 0, sizeof(cmd));
+    init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1);
+    cmd.op.opcode = OP_GPIO_STREAM;
+    cmd.op.len = sizeof(cmd.op);
+    cmd.op.rid = d_next_rid++;
+    cmd.op.bank = static_cast<uint8_t>(bank);
+    cmd.op.value = htons((uint16_t)enable);
+    cmd.op.mask = 0;  // not used
+    cmd.eop.opcode = OP_EOP;
+    cmd.eop.len = sizeof(cmd.eop);
+    
+    pending_reply p(cmd.op.rid, &reply, sizeof(reply));
+    if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
+      return false;
+
+    bool success = (ntohx(reply.ok) == 1);
+    return success;
+  }
+
 } // namespace usrp2