X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=usrp2%2Fhost%2Flib%2Fusrp2_impl.cc;h=d1e85805c981ae94f33fc9b64b6fea39eb2d2568;hb=96b6c7b97e3fb9188bc6906153254dc36cedc2cb;hp=68cf676c6bc0c311e6ae0b7799c5e7761e63fcdc;hpb=e0fcbaee124d3e8c4c11bdda662f88e082352058;p=debian%2Fgnuradio diff --git a/usrp2/host/lib/usrp2_impl.cc b/usrp2/host/lib/usrp2_impl.cc index 68cf676c..d1e85805 100644 --- a/usrp2/host/lib/usrp2_impl.cc +++ b/usrp2/host/lib/usrp2_impl.cc @@ -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 { @@ -68,12 +67,19 @@ namespace usrp2 { 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 + case OP_CONFIG_MIMO: return "OP_CONFIG_MIMO"; + case OP_DBOARD_INFO: return "OP_DBOARD_INFO"; + case OP_DBOARD_INFO_REPLY: return "OP_DBOARD_INFO_REPLY"; + 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]; snprintf(buf, sizeof(buf), "", opcode); @@ -105,9 +111,13 @@ namespace usrp2 { md->word0 = u2p_word0(fh); md->timestamp = u2p_timestamp(fh); + // FIXME when we've got more info // md->start_of_burst = (md->word0 & XXX) != 0; // md->end_of_burst = (md->word0 & XXX) != 0; // md->rx_overrun = (md->word0 & XXX) != 0; + md->start_of_burst = 0; + md->end_of_burst = 0; + md->rx_overrun = 0; *items = (uint32_t *)(&fh[1]); size_t nbytes = payload_len_in_bytes - sizeof(u2_fixed_hdr_t); @@ -118,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_decim(0), 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_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; @@ -142,12 +155,54 @@ 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"); + + if (0){ + int dbid; + + tx_daughterboard_id(&dbid); + fprintf(stderr, "Tx dboard 0x%x\n", dbid); + fprintf(stderr, " freq_min = %g\n", tx_freq_min()); + fprintf(stderr, " freq_max = %g\n", tx_freq_max()); + fprintf(stderr, " gain_min = %g\n", tx_gain_min()); + fprintf(stderr, " gain_max = %g\n", tx_gain_max()); + fprintf(stderr, " gain_db_per_step = %g\n", tx_gain_db_per_step()); + + rx_daughterboard_id(&dbid); + fprintf(stderr, "Rx dboard 0x%x\n", dbid); + fprintf(stderr, " freq_min = %g\n", rx_freq_min()); + fprintf(stderr, " freq_max = %g\n", rx_freq_max()); + fprintf(stderr, " gain_min = %g\n", rx_gain_min()); + fprintf(stderr, " gain_max = %g\n", rx_gain_max()); + 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"; + + if (!set_rx_gain((rx_gain_min() + rx_gain_max()) / 2)) + std::cerr << "usrp2::ctor set_rx_gain failed\n"; + + // default interp and decim + if (!set_tx_interp(12)) + std::cerr << "usrp2::ctor set_tx_interp failed\n"; + + if (!set_rx_decim(12)) + std::cerr << "usrp2::ctor set_rx_decim failed\n"; + // 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() @@ -243,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; } @@ -318,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); } @@ -349,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; } @@ -390,51 +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 - } + { + omni_mutex_lock l(d_channel_rings_mutex); - // 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 + 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; - } - - - // ---------------------------------------------------------------- - // 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; } @@ -453,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) { @@ -473,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); @@ -510,10 +575,12 @@ 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); + if (success) + d_rx_decim = decimation_factor; return success; } @@ -528,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); @@ -550,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 @@ -596,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) @@ -675,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 // ---------------------------------------------------------------- @@ -690,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) { @@ -710,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); @@ -747,13 +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) { + 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) { @@ -765,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); @@ -773,21 +934,21 @@ namespace usrp2 { } bool - usrp2::impl::tx_complex_float(unsigned int channel, - const std::complex *samples, - size_t nsamples, - const tx_metadata *metadata) + usrp2::impl::tx_32fc(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); + copy_host_32fc_to_u2_16sc(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) + usrp2::impl::tx_16sc(unsigned int channel, + const std::complex *samples, + size_t nsamples, + const tx_metadata *metadata) { #ifdef WORDS_BIGENDIAN @@ -800,7 +961,7 @@ namespace usrp2 { #else uint32_t items[nsamples]; - copy_host_complex_16_to_u2_complex_16(nsamples, samples, items); + copy_host_16sc_to_u2_16sc(nsamples, samples, items); return tx_raw(channel, items, nsamples, metadata); #endif @@ -815,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 @@ -848,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; @@ -856,6 +1020,10 @@ namespace usrp2 { iov[1].iov_base = const_cast(&items[n]); iov[1].iov_len = i * sizeof(uint32_t); + size_t total = iov[0].iov_len + iov[1].iov_len; + if (total < 64) + fprintf(stderr, "usrp2::tx_raw: FIXME: short packet: %zd items (%zd bytes)\n", i, total); + if (d_eth_buf->tx_framev(iov, 2) != eth_buffer::EB_OK){ return false; } @@ -866,5 +1034,464 @@ namespace usrp2 { return true; } + // ---------------------------------------------------------------- + // misc commands + // ---------------------------------------------------------------- + + bool + usrp2::impl::config_mimo(int flags) + { + op_config_mimo_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_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_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) + return false; + + return ntohx(reply.ok) == 1; + } + + bool + usrp2::impl::fpga_master_clock_freq(long *freq) + { + *freq = 100000000L; // 100 MHz + return true; + } + + bool + usrp2::impl::adc_rate(long *rate) + { + return fpga_master_clock_freq(rate); + } + + bool + usrp2::impl::dac_rate(long *rate) + { + return fpga_master_clock_freq(rate); + } + + bool + usrp2::impl::tx_daughterboard_id(int *dbid) + { + *dbid = d_tx_db_info.dbid; + return true; + } + + bool + usrp2::impl::rx_daughterboard_id(int *dbid) + { + *dbid = d_rx_db_info.dbid; + return true; + } + + + // ---------------------------------------------------------------- + // low-level 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_and_wait(&cmd, sizeof(cmd), &p, 4*DEF_CMD_TIMEOUT)) + return false; + + bool success = (ntohx(reply.ok) == 1); + return success; + } + + static void + fill_dboard_info(db_info *dst, const u2_db_info_t *src) + { + dst->dbid = ntohl(src->dbid); + + dst->freq_min = + u2_fxpt_freq_to_double(u2_fxpt_freq_from_hilo(ntohl(src->freq_min_hi), + ntohl(src->freq_min_lo))); + dst->freq_max = + u2_fxpt_freq_to_double(u2_fxpt_freq_from_hilo(ntohl(src->freq_max_hi), + ntohl(src->freq_max_lo))); + + dst->gain_min = u2_fxpt_gain_to_double(ntohs(src->gain_min)); + dst->gain_max = u2_fxpt_gain_to_double(ntohs(src->gain_max)); + dst->gain_step_size = u2_fxpt_gain_to_double(ntohs(src->gain_step_size)); + } + + bool + usrp2::impl::dboard_info() + { + op_dboard_info_cmd cmd; + op_dboard_info_reply_t reply; + + memset(&cmd, 0, sizeof(cmd)); + init_etf_hdrs(&cmd.h, d_addr, 0, CONTROL_CHAN, -1); + cmd.op.opcode = OP_DBOARD_INFO; + 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); + if (success){ + fill_dboard_info(&d_tx_db_info, &reply.tx_db_info); + fill_dboard_info(&d_rx_db_info, &reply.rx_db_info); + } + return success; + } + + + bool + usrp2::impl::sync_to_pps() + { + 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_TO_PPS; + 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; + + 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 + usrp2::impl::peek32(uint32_t addr, uint32_t words) + { + std::vector 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 (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; + cmd.op.len = sizeof(cmd.op); + cmd.op.rid = d_next_rid++; + cmd.eop.opcode = OP_EOP; + cmd.eop.len = sizeof(cmd.eop); + + cmd.op.addr = htonl(addr); + 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 &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(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(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(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(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(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