/* -*- c++ -*- */
/*
* Copyright 2008 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include "usrp2_impl.h"
#include "usrp2_thread.h"
#include "eth_buffer.h"
#include "pktfilter.h"
#include "control.h"
#include "ring.h"
#include
#include
#include
#include
#include
#include
#define USRP2_IMPL_DEBUG 0
#if USRP2_IMPL_DEBUG
#define DEBUG_LOG(x) ::write(2, x, 1)
#else
#define DEBUG_LOG(x)
#endif
static const int DEFAULT_RX_SCALE = 1024;
static const int DEFAULT_TX_SCALE = 3000;
namespace usrp2 {
static const double DEF_CMD_TIMEOUT = 0.1;
std::string
opcode_to_string(int opcode)
{
switch(opcode){
case OP_EOP: return "OP_EOP";
case OP_ID: return "OP_ID";
case OP_ID_REPLY: return "OP_ID_REPLY";
case OP_BURN_MAC_ADDR: return "OP_BURN_MAC_ADDR";
case OP_READ_TIME: return "OP_READ_TIME";
case OP_READ_TIME_REPLY: return "OP_READ_TIME_REPLY";
case OP_CONFIG_RX_V2: return "OP_CONFIG_RX_V2";
case OP_CONFIG_RX_REPLY_V2: return "OP_CONFIG_RX_REPLY_V2";
case OP_CONFIG_TX_V2: return "OP_CONFIG_TX_V2";
case OP_CONFIG_TX_REPLY_V2: return "OP_CONFIG_TX_REPLY_V2";
case OP_START_RX_STREAMING: return "OP_START_RX_STREAMING";
case OP_STOP_RX: return "OP_STOP_RX";
#if 0
case OP_WRITE_REG: return "OP_WRITE_REG";
case OP_WRITE_REG_MASKED: return "OP_WRITE_REG_MASKED";
case OP_READ_REG: return "OP_READ_REG";
case OP_READ_REG_REPLY: return "OP_READ_REG_REPLY";
#endif
default:
char buf[64];
snprintf(buf, sizeof(buf), "", opcode);
return buf;
}
}
/*!
* \param p points to fixed header
* \param payload_len_in_bytes is length of the fixed hdr and the payload
* \param[out] items is set to point to the first uint32 item in the payload
* \param[out] nitems is set to the number of uint32 items in the payload
* \param[out] md is filled in with the parsed metadata from the frame.
*/
static bool
parse_rx_metadata(void *p, size_t payload_len_in_bytes,
uint32_t **items, size_t *nitems_in_uint32s, rx_metadata *md)
{
if (payload_len_in_bytes < sizeof(u2_fixed_hdr_t)) // invalid format
return false;
// FIXME deal with the fact that (p % 4) == 2
//assert((((uintptr_t) p) % 4) == 0); // must be 4-byte aligned
u2_fixed_hdr_t *fh = static_cast(p);
// FIXME unaligned loads!
md->word0 = u2p_word0(fh);
md->timestamp = u2p_timestamp(fh);
// md->start_of_burst = (md->word0 & XXX) != 0;
// md->end_of_burst = (md->word0 & XXX) != 0;
// md->rx_overrun = (md->word0 & XXX) != 0;
*items = (uint32_t *)(&fh[1]);
size_t nbytes = payload_len_in_bytes - sizeof(u2_fixed_hdr_t);
assert((nbytes % sizeof(uint32_t)) == 0);
*nitems_in_uint32s = nbytes / sizeof(uint32_t);
return true;
}
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_decim(0), 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)
{
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());
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;
memset(d_pending_replies, 0, sizeof(d_pending_replies));
d_bg_thread = new usrp2_thread(this);
d_bg_thread->start();
// 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()
{
stop_bg();
d_bg_thread = 0; // thread class deletes itself
delete d_pf;
d_eth_buf->close();
delete d_eth_buf;
if (USRP2_IMPL_DEBUG) {
std::cerr << std::endl
<< "usrp2 destructor: received " << d_num_rx_frames
<< " frames, with " << d_num_rx_missing << " lost ("
<< (d_num_rx_frames == 0 ? 0 : (int)(100.0*d_num_rx_missing/d_num_rx_frames))
<< "%), totaling " << d_num_rx_bytes
<< " bytes" << std::endl;
}
}
bool
usrp2::impl::parse_mac_addr(const std::string &s, u2_mac_addr_t *p)
{
p->addr[0] = 0x00; // Matt's IAB
p->addr[1] = 0x50;
p->addr[2] = 0xC2;
p->addr[3] = 0x85;
p->addr[4] = 0x30;
p->addr[5] = 0x00;
int len = s.size();
switch (len){
case 5:
return sscanf(s.c_str(), "%hhx:%hhx", &p->addr[4], &p->addr[5]) == 2;
case 17:
return sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&p->addr[0], &p->addr[1], &p->addr[2],
&p->addr[3], &p->addr[4], &p->addr[5]) == 6;
default:
return false;
}
}
void
usrp2::impl::init_et_hdrs(u2_eth_packet_t *p, const std::string &dst)
{
p->ehdr.ethertype = htons(U2_ETHERTYPE);
parse_mac_addr(dst, &p->ehdr.dst);
memcpy(&p->ehdr.src, d_eth_buf->mac(), 6);
p->thdr.flags = 0; // FIXME transport header values?
p->thdr.seqno = d_tx_seqno++;
p->thdr.ack = 0;
}
void
usrp2::impl::init_etf_hdrs(u2_eth_packet_t *p, const std::string &dst,
int word0_flags, int chan, uint32_t timestamp)
{
init_et_hdrs(p, dst);
u2p_set_word0(&p->fixed, word0_flags, chan);
u2p_set_timestamp(&p->fixed, timestamp);
if (chan == CONTROL_CHAN) { // no sequence numbers, back it out
p->thdr.seqno = 0;
d_tx_seqno--;
}
}
void
usrp2::impl::init_config_rx_v2_cmd(op_config_rx_v2_cmd *cmd)
{
memset(cmd, 0, sizeof(*cmd));
init_etf_hdrs(&cmd->h, d_addr, 0, CONTROL_CHAN, -1);
cmd->op.opcode = OP_CONFIG_RX_V2;
cmd->op.len = sizeof(cmd->op);
cmd->op.rid = d_next_rid++;
cmd->eop.opcode = OP_EOP;
cmd->eop.len = sizeof(cmd->eop);
}
void
usrp2::impl::init_config_tx_v2_cmd(op_config_tx_v2_cmd *cmd)
{
memset(cmd, 0, sizeof(*cmd));
init_etf_hdrs(&cmd->h, d_addr, 0, CONTROL_CHAN, -1);
cmd->op.opcode = OP_CONFIG_TX_V2;
cmd->op.len = sizeof(cmd->op);
cmd->op.rid = d_next_rid++;
cmd->eop.opcode = OP_EOP;
cmd->eop.len = sizeof(cmd->eop);
}
bool
usrp2::impl::transmit_cmd(void *cmd, size_t len, pending_reply *p, double secs)
{
if (p)
d_pending_replies[p->rid()] = p;
// Transmit command
if (d_eth_buf->tx_frame(cmd, len) != eth_buffer::EB_OK) {
d_pending_replies[p->rid()] = 0;
return false;
}
int res = 1;
if (p)
res = p->wait(secs);
d_pending_replies[p->rid()] = 0;
return res == 1;
}
// ----------------------------------------------------------------
// Background loop: received packet demuxing
// ----------------------------------------------------------------
void
usrp2::impl::stop_bg()
{
d_bg_running = false;
d_bg_pending_cond.signal();
void *dummy_status;
d_bg_thread->join(&dummy_status);
}
void
usrp2::impl::bg_loop()
{
d_bg_running = true;
while(d_bg_running) {
DEBUG_LOG(":");
// Receive available frames from ethernet buffer. Handler will
// process control frames, enqueue data packets in channel
// rings, and signal blocked API threads
int res = d_eth_buf->rx_frames(this, 100); // FIXME magic timeout
if (res == eth_buffer::EB_ERROR)
break;
// Wait for user API thread(s) to process all enqueued packets.
// The channel ring thread that decrements d_num_enqueued to zero
// will signal this thread to continue.
{
omni_mutex_lock l(d_enqueued_mutex);
while(d_num_enqueued > 0 && d_bg_running)
d_bg_pending_cond.wait();
}
}
d_bg_running = false;
}
//
// passed to eth_buffer::rx_frames
//
data_handler::result
usrp2::impl::operator()(const void *base, size_t len)
{
u2_eth_samples_t *pkt = (u2_eth_samples_t *)base;
// FIXME unaligned load!
int chan = u2p_chan(&pkt->hdrs.fixed);
if (chan == CONTROL_CHAN) { // control packets
DEBUG_LOG("c");
return handle_control_packet(base, len);
}
else { // data packets
return handle_data_packet(base, len);
}
// not reached
}
data_handler::result
usrp2::impl::handle_control_packet(const void *base, size_t len)
{
// point to beginning of payload (subpackets)
unsigned char *p = (unsigned char *)base + sizeof(u2_eth_packet_t);
// FIXME (p % 4) == 2. Not good. Must watch for unaligned loads.
// FIXME iterate over payload, handling more than a single subpacket.
int opcode = p[0];
unsigned int oplen = p[1];
unsigned int rid = p[2];
pending_reply *rp = d_pending_replies[rid];
if (rp) {
unsigned int buflen = rp->len();
if (oplen != buflen) {
std::cerr << "usrp2: mismatched command reply length (expected: "
<< buflen << " got: " << oplen << "). "
<< "op = " << opcode_to_string(opcode) << std::endl;
}
// Copy reply into caller's buffer
memcpy(rp->buffer(), p, std::min(oplen, buflen));
rp->signal();
d_pending_replies[rid] = 0;
return data_handler::RELEASE;
}
// TODO: handle unsolicited, USRP2 initiated, or late replies
DEBUG_LOG("l");
return data_handler::RELEASE;
}
data_handler::result
usrp2::impl::handle_data_packet(const void *base, size_t len)
{
u2_eth_samples_t *pkt = (u2_eth_samples_t *)base;
d_num_rx_frames++;
d_num_rx_bytes += len;
/* --- FIXME start of fake transport layer handler --- */
if (d_rx_seqno != -1) {
int expected_seqno = (d_rx_seqno + 1) & 0xFF;
int seqno = pkt->hdrs.thdr.seqno;
if (seqno != expected_seqno) {
::write(2, "S", 1); // missing sequence number
int missing = seqno - expected_seqno;
if (missing < 0)
missing += 256;
d_num_rx_overruns++;
d_num_rx_missing += missing;
}
}
d_rx_seqno = pkt->hdrs.thdr.seqno;
/* --- end of fake transport layer handler --- */
// 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
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;
}
// ----------------------------------------------------------------
// misc commands
// ----------------------------------------------------------------
bool
usrp2::impl::burn_mac_addr(const std::string &new_addr)
{
op_burn_mac_addr_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_BURN_MAC_ADDR;
cmd.op.len = sizeof(cmd.op);
cmd.op.rid = d_next_rid++;
if (!parse_mac_addr(new_addr, &cmd.op.addr))
return false;
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
if (!transmit_cmd(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
// ----------------------------------------------------------------
// Receive
// ----------------------------------------------------------------
bool
usrp2::impl::set_rx_gain(double gain)
{
op_config_rx_v2_cmd cmd;
op_config_rx_reply_v2_t reply;
init_config_rx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_GAIN);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::set_rx_center_freq(double frequency, tune_result *result)
{
op_config_rx_v2_cmd cmd;
op_config_rx_reply_v2_t reply;
init_config_rx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_FREQ);
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));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
if (result && success) {
result->baseband_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.baseband_freq_hi),
ntohl(reply.baseband_freq_lo)));
result->dxc_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.ddc_freq_hi),
ntohl(reply.ddc_freq_lo)));
result->residual_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.residual_freq_hi),
ntohl(reply.residual_freq_lo)));
result->spectrum_inverted = (bool)(ntohx(reply.inverted) == 1);
}
return success;
}
bool
usrp2::impl::set_rx_decim(int decimation_factor)
{
op_config_rx_v2_cmd cmd;
op_config_rx_reply_v2_t reply;
init_config_rx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_INTERP_DECIM);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::set_rx_scale_iq(int scale_i, int scale_q)
{
op_config_rx_v2_cmd cmd;
op_config_rx_reply_v2_t reply;
init_config_rx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_SCALE_IQ);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::start_rx_streaming(unsigned int channel, unsigned int items_per_frame)
{
if (channel > MAX_CHAN) {
std::cerr << "usrp2: invalid channel number (" << channel
<< ")" << std::endl;
return false;
}
if (channel > 0) { // until firmware supports multiple streams
std::cerr << "usrp2: channel " << channel
<< " not implemented" << std::endl;
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);
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::stop_rx_streaming(unsigned int channel)
{
if (channel > MAX_CHAN) {
std::cerr << "usrp2: invalid channel number (" << channel
<< ")" << std::endl;
return false;
}
if (channel > 0) { // until firmware supports multiple streams
std::cerr << "usrp2: channel " << channel
<< " not implemented" << std::endl;
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
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;
bool success = (ntohx(reply.ok) == 1);
if (success)
d_channel_rings[channel].reset();
return success;
}
bool
usrp2::impl::rx_samples(unsigned int channel, rx_sample_handler *handler)
{
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){
std::cerr << "usrp2: channel " << channel
<< " not receiving" << std::endl;
return false;
}
// Wait for frames available in channel ring
DEBUG_LOG("W");
rp->wait_for_not_empty();
DEBUG_LOG("s");
// Iterate through frames and present to user
void *p;
size_t frame_len_in_bytes;
while (rp->dequeue(&p, &frame_len_in_bytes)) {
uint32_t *items; // points to beginning of data items
size_t nitems_in_uint32s;
rx_metadata md;
if (!parse_rx_metadata(p, frame_len_in_bytes, &items, &nitems_in_uint32s, &md))
return false;
bool want_more = (*handler)(items, nitems_in_uint32s, &md);
d_eth_buf->release_frame(p);
DEBUG_LOG("-");
dec_enqueued();
if (!want_more)
break;
}
return true;
}
// ----------------------------------------------------------------
// Transmit
// ----------------------------------------------------------------
bool
usrp2::impl::set_tx_gain(double gain)
{
op_config_tx_v2_cmd cmd;
op_config_tx_reply_v2_t reply;
init_config_tx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_GAIN);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::set_tx_center_freq(double frequency, tune_result *result)
{
op_config_tx_v2_cmd cmd;
op_config_tx_reply_v2_t reply;
init_config_tx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_FREQ);
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));
pending_reply p(cmd.op.rid, &reply, sizeof(reply));
if (!transmit_cmd(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT))
return false;
bool success = (ntohx(reply.ok) == 1);
if (result && success) {
result->baseband_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.baseband_freq_hi),
ntohl(reply.baseband_freq_lo)));
result->dxc_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.duc_freq_hi),
ntohl(reply.duc_freq_lo)));
result->residual_freq =
u2_fxpt_freq_to_double(
u2_fxpt_freq_from_hilo(ntohl(reply.residual_freq_hi),
ntohl(reply.residual_freq_lo)));
result->spectrum_inverted = (bool)(ntohx(reply.inverted) == 1);
}
return success;
}
bool
usrp2::impl::set_tx_interp(int interpolation_factor)
{
op_config_tx_v2_cmd cmd;
op_config_tx_reply_v2_t reply;
init_config_tx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_INTERP_DECIM);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::set_tx_scale_iq(int scale_i, int scale_q)
{
op_config_tx_v2_cmd cmd;
op_config_tx_reply_v2_t reply;
init_config_tx_v2_cmd(&cmd);
cmd.op.valid = htons(CFGV_SCALE_IQ);
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))
return false;
bool success = (ntohx(reply.ok) == 1);
return success;
}
bool
usrp2::impl::tx_complex_float(unsigned int channel,
const std::complex *samples,
size_t nsamples,
const tx_metadata *metadata)
{
uint32_t items[nsamples];
copy_host_complex_float_to_u2_complex_16(nsamples, samples, items);
return tx_raw(channel, items, nsamples, metadata);
}
bool
usrp2::impl::tx_complex_int16(unsigned int channel,
const std::complex *samples,
size_t nsamples,
const tx_metadata *metadata)
{
#ifdef WORDS_BIGENDIAN
// Already binary equivalent to 16-bit I/Q on the wire.
// No conversion required.
assert(sizeof(samples[0]) == sizeof(uint32_t));
return tx_raw(channel, (const uint32_t *) samples, nsamples, metadata);
#else
uint32_t items[nsamples];
copy_host_complex_16_to_u2_complex_16(nsamples, samples, items);
return tx_raw(channel, items, nsamples, metadata);
#endif
}
bool
usrp2::impl::tx_raw(unsigned int channel,
const uint32_t *items,
size_t nitems,
const tx_metadata *metadata)
{
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?).
// fragment as necessary then fire away
size_t nframes = (nitems + U2_MAX_SAMPLES - 1) / U2_MAX_SAMPLES;
size_t last_frame = nframes - 1;
u2_eth_packet_t hdrs;
size_t n = 0;
for (size_t fn = 0; fn < nframes; fn++){
uint32_t timestamp = 0;
uint32_t flags = 0;
if (fn == 0){
timestamp = metadata->timestamp;
if (metadata->send_now)
flags |= U2P_TX_IMMEDIATE;
if (metadata->start_of_burst)
flags |= U2P_TX_START_OF_BURST;
}
if (fn > 0){
flags |= U2P_TX_IMMEDIATE;
}
if (fn == last_frame){
if (metadata->end_of_burst)
flags |= U2P_TX_END_OF_BURST;
}
init_etf_hdrs(&hdrs, d_addr, flags, channel, timestamp);
size_t i = std::min((size_t) U2_MAX_SAMPLES, nitems - n);
eth_iovec iov[2];
iov[0].iov_base = &hdrs;
iov[0].iov_len = sizeof(hdrs);
iov[1].iov_base = const_cast(&items[n]);
iov[1].iov_len = i * sizeof(uint32_t);
if (d_eth_buf->tx_framev(iov, 2) != eth_buffer::EB_OK){
return false;
}
n += i;
}
return true;
}
} // namespace usrp2