diff options
author | jcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5> | 2008-09-08 01:00:12 +0000 |
---|---|---|
committer | jcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5> | 2008-09-08 01:00:12 +0000 |
commit | e0fcbaee124d3e8c4c11bdda662f88e082352058 (patch) | |
tree | a51ef1c8b949681f45e5664478e8515065cfff5b /usrp2/host | |
parent | c86f6c23c6883f73d953d64c28ab42cedb77e4d7 (diff) |
Merged r9433:9527 from features/gr-usrp2 into trunk. Adds usrp2 and gr-usrp2 top-level components. Trunk passes distcheck with mb-gcc installed, but currently not without them. The key issue is that when mb-gcc is not installed, the build system skips over the usrp2/firmware directory, and the firmware include files don't get put into the dist tarball. But we can't do the usual DIST_SUBDIRS method as the firmware is a subpackage.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@9528 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'usrp2/host')
54 files changed, 6391 insertions, 0 deletions
diff --git a/usrp2/host/Makefile.am b/usrp2/host/Makefile.am new file mode 100644 index 0000000000..b6e30b5ff6 --- /dev/null +++ b/usrp2/host/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +include $(top_srcdir)/Makefile.common + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = usrp2.pc + +EXTRA_DIST = \ + usrp2.pc.in + +SUBDIRS = include lib apps diff --git a/usrp2/host/apps/Makefile.am b/usrp2/host/apps/Makefile.am new file mode 100644 index 0000000000..744dd57024 --- /dev/null +++ b/usrp2/host/apps/Makefile.am @@ -0,0 +1,44 @@ +# +# Copyright 2007, 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 <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = \ + $(USRP2_INCLUDES) \ + $(STD_DEFINES_AND_INCLUDES) \ + $(CPPUNIT_INCLUDES) \ + $(GRUEL_INCLUDES) + +LDADD = \ + $(USRP2_LA) \ + $(GRUEL_LA) \ + $(OMNITHREAD_LA) + +bin_PROGRAMS = \ + find_usrps \ + usrp2_burn_mac_addr + +noinst_PROGRAMS = \ + gen_const \ + rx_streaming_samples \ + tx_samples + +find_usrps_SOURCES = find_usrps.cc +usrp2_burn_mac_addr_SOURCES = usrp2_burn_mac_addr.cc +rx_streaming_samples_SOURCES = rx_streaming_samples.cc +gen_const_SOURCES = gen_const.cc +tx_samples_SOURCES = tx_samples.cc diff --git a/usrp2/host/apps/find_usrps.cc b/usrp2/host/apps/find_usrps.cc new file mode 100644 index 0000000000..1010c4aff5 --- /dev/null +++ b/usrp2/host/apps/find_usrps.cc @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include <iostream> +#include <getopt.h> + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-e ethN]\n", + progname); +} + +int +main(int argc, char **argv) +{ + int ch; + const char *interface = "eth0"; + + while ((ch = getopt(argc, argv, "he:")) != EOF){ + switch (ch){ + case 'e': + interface = optarg; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 0){ + usage(argv[0]); + exit(1); + } + + usrp2::props_vector_t r = usrp2::find(interface); + + for (size_t i = 0; i < r.size(); i++){ + std::cout << r[i] << std::endl; + } + + if (r.size() == 0){ + std::cerr << "No USRP2 found.\n"; + return 1; + } + + return 0; +} diff --git a/usrp2/host/apps/gen_2tone.py b/usrp2/host/apps/gen_2tone.py new file mode 100755 index 0000000000..ec681d3e08 --- /dev/null +++ b/usrp2/host/apps/gen_2tone.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# Copyright 2007 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 <http://www.gnu.org/licenses/>. +# + +from gnuradio import gr, eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +master_clock = 100e6 + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-f", "--freq1", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option("-g", "--freq2", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option ("-a", "--amplitude1", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + parser.add_option ("-b", "--amplitude2", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + + parser.add_option("-i", "--interp", type="int", default=32, + help="assume fgpa interpolation rate is INTERP [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + + src0 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq1, + options.amplitude1) + src1 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq2, + options.amplitude2) + + adder = gr.add_cc() + + + c2s = gr.complex_to_interleaved_short() + + stdout_sink = gr.file_descriptor_sink(gr.sizeof_short, 1) + + self.connect(src0, (adder,0)) + self.connect(src1, (adder,1)) + self.connect(adder, c2s, stdout_sink) + + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/usrp2/host/apps/gen_const.cc b/usrp2/host/apps/gen_const.cc new file mode 100644 index 0000000000..d2c36ebba6 --- /dev/null +++ b/usrp2/host/apps/gen_const.cc @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +int +main(int argc, char **argv) +{ + if (argc != 3){ + fprintf(stderr, "usage: %s i-val q-val\n", argv[0]); + return 1; + } + + int i_val = strtol(argv[1], 0, 0); + int q_val = strtol(argv[2], 0, 0); + + static const int NSAMPLES = 16384; + + uint32_t sample[NSAMPLES]; + sample[0] = ((i_val & 0xffff) << 16) | (q_val & 0xffff); + for (int i = 1; i < NSAMPLES; i++) + sample[i] = sample[0]; + + while(1){ + write(1, sample, sizeof(sample)); + } +} diff --git a/usrp2/host/apps/gen_sine.py b/usrp2/host/apps/gen_sine.py new file mode 100755 index 0000000000..6a44dd110b --- /dev/null +++ b/usrp2/host/apps/gen_sine.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Copyright 2007 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 <http://www.gnu.org/licenses/>. +# + +from gnuradio import gr, eng_notation +from gnuradio.eng_option import eng_option +from optparse import OptionParser +import sys + +master_clock = 100e6 + +class my_top_block(gr.top_block): + + def __init__(self): + gr.top_block.__init__(self) + + parser = OptionParser(option_class=eng_option) + parser.add_option("-f", "--freq", type="eng_float", default=1e6, + help="set waveform frequency to FREQ [default=%default]") + parser.add_option ("-a", "--amplitude", type="eng_float", default=16e3, + help="set waveform amplitude to AMPLITUDE [default=%default]", metavar="AMPL") + + parser.add_option("-i", "--interp", type="int", default=32, + help="assume fgpa interpolation rate is INTERP [default=%default]") + + (options, args) = parser.parse_args () + if len(args) != 0: + parser.print_help() + raise SystemExit, 1 + + + src0 = gr.sig_source_c(master_clock/options.interp, + gr.GR_SIN_WAVE, + options.freq, + options.amplitude) + + + c2s = gr.complex_to_interleaved_short() + + stdout_sink = gr.file_descriptor_sink(gr.sizeof_short, 1) + + self.connect(src0, c2s, stdout_sink) + + +if __name__ == '__main__': + try: + my_top_block().run() + except KeyboardInterrupt: + pass diff --git a/usrp2/host/apps/rx_samples.cc b/usrp2/host/apps/rx_samples.cc new file mode 100644 index 0000000000..c1c3b5917a --- /dev/null +++ b/usrp2/host/apps/rx_samples.cc @@ -0,0 +1,382 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "usrp2_basic.h" +#include <iostream> +#include <complex> +#include <getopt.h> +#include <string.h> +#include "strtod_si.h" +#include <signal.h> +#include <stdexcept> +#include "gri_if_stats.h" +#include <gr_realtime.h> + + +typedef std::complex<float> fcomplex; + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +/* + * Vectorize me! + */ +void +convert_samples_to_complex(size_t nsamples, + uint32_t *i_samples, + fcomplex *c_samples) +{ + uint32_t *p = i_samples; + for (size_t i = 0; i < nsamples; i++){ + int16_t si = ((int16_t) (p[i] >> 16)); + int16_t sq = ((int16_t) (p[i] & 0xffff)); + c_samples[i] = fcomplex((float) si, (float) sq); + } +} + + +static void +usage(const char *progname) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + fprintf(stderr, "Usage: %s [options]\n\n", p); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -o OUTPUT_FILE set output filename [default=NONE]\n"); + fprintf(stderr, " -f FREQ set frequency to FREQ [default=0]\n"); + fprintf(stderr, " -d DECIM set decimation rate to DECIM [default=32]\n"); + fprintf(stderr, " -N NSAMPLES total number of samples to receive [default=2.5e6]\n"); + fprintf(stderr, " -F SAMPLES_PER_FRAME number of samples in each frame [default=371]\n"); + fprintf(stderr, " -S SCALE fpga scaling factor for I & Q [default=1024]\n"); + fprintf(stderr, " -M DONT_LOCK|LOCK_TO_SMA|LOCK_TO_MIMO specify MIMO clock source\n"); + fprintf(stderr, " -P provide clock to MIMO connector\n"); +} + +struct pkt_info { + int d_nsamples; + int d_timestamp; + unsigned int d_seqno; + + pkt_info(int nsamples, int timestamp, int seqno) + : d_nsamples(nsamples), + d_timestamp(timestamp), + d_seqno(seqno) {} +}; + +int +main(int argc, char **argv) +{ + + // options and their defaults + const char *interface = "eth0"; + const char *mac_addr_str = 0; + const char *output_filename = 0; + double freq = 0; + int32_t decim = 32; + int32_t nsamples = static_cast<int32_t>(2.5e6); + int32_t samples_per_frame = 371; + int32_t scale = 1024; + int mimo_config = MC_WE_DONT_LOCK; + bool provide_clock = false; + + int ch; + double tmp; + u2_mac_addr_t mac_addr; + + setvbuf(stdout, 0, _IOFBF, 64 * 1024); // make stdout fully buffered + + while ((ch = getopt(argc, argv, "he:m:o:f:d:N:F:S:M:P")) != EOF){ + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; + if (!usrp2_basic::parse_mac_addr(optarg, &mac_addr)){ + std::cerr << "invalid mac addr: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'o': + output_filename = optarg; + break; + + case 'f': + if (!strtod_si(optarg, &freq)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'N': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + nsamples = static_cast<int32_t>(tmp); + break; + + case 'F': + samples_per_frame = strtol(optarg, 0, 0); + break; + + case 'd': + decim = strtol(optarg, 0, 0); + break; + + case 'S': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + scale = static_cast<int32_t>(tmp); + break; + + case 'M': + if (strcmp(optarg, "DONT_LOCK") == 0) + mimo_config = MC_WE_DONT_LOCK; + else if (strcmp(optarg, "LOCK_TO_SMA") == 0) + mimo_config = MC_WE_LOCK_TO_SMA; + else if (strcmp(optarg, "LOCK_TO_MIMO") == 0) + mimo_config = MC_WE_LOCK_TO_MIMO; + else { + usage(argv[0]); + exit(1); + } + break; + + case 'P': + provide_clock = true; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 0){ + usage(argv[0]); + exit(1); + } + + FILE *of = 0; + if (output_filename) + of = fopen(output_filename, "wb"); + + usrp2_basic *u2 = new usrp2_basic(); + + if (!u2->open(interface)){ + std::cerr << "couldn't open " << interface << std::endl; + return 0; + } + + + install_sig_handler(SIGINT, sig_handler); + if (1){ + install_sig_handler(SIGALRM, sig_handler); + alarm(5); + } + + + std::vector<op_id_reply_t> r = u2->find_usrps(); + + for (size_t i = 0; i < r.size(); i++){ + std::cout << r[i] << std::endl; + } + + if (r.size() == 0){ + std::cerr << "No USRP2 found.\n"; + return 1; + } + + u2_mac_addr_t which = r[0].addr; // pick the first one + + + gr_rt_status_t rt = gr_enable_realtime_scheduling(); + if (rt != RT_OK) + std::cerr << "failed to enable realtime scheduling\n"; + + if (provide_clock) + mimo_config |= MC_PROVIDE_CLK_TO_MIMO; + + u2->config_mimo(which, mimo_confg); + + + gri_if_stats start, stop; + gri_get_if_stats(interface, &start); + + if (!u2->start_rx(which, freq, decim, nsamples, samples_per_frame, scale, scale)){ + std::cerr << "start_rx failed\n"; + return 1; + } + + + std::vector<pkt_info> history; + history.reserve(64*1024); // preallocate 64K entries + + + long total_samples_recvd = 0; + + while (!signaled && total_samples_recvd < nsamples){ + u2_eth_samples_t pkt; + // fcomplex c_samples[U2_MAX_SAMPLES]; + + // read samples + int n = u2->read_samples(which, &pkt); + if (n <= 0) + break; + + total_samples_recvd += n; + + history.push_back(pkt_info(n, u2p_timestamp(&pkt.hdrs.fixed), pkt.hdrs.thdr.seqno)); + + // convert_samples_to_complex(n, pkt.samples, c_samples); + // size_t r = fwrite(c_samples, sizeof(fcomplex), n, of); + + if (of){ + fwrite(pkt.samples, sizeof(uint32_t), n, of); + fflush(of); + } + } + + + gri_get_if_stats(interface, &stop); + + if (!u2->stop_rx(which)){ + std::cerr << "stop_rx failed\n"; + return 1; + } + + + long expected_rx_packets = + (nsamples + samples_per_frame - 1)/samples_per_frame; + + long expected_rx_bytes = + expected_rx_packets * sizeof(u2_eth_packet_t) + nsamples * 4; + + + long total_pkts_recvd = 0; + total_samples_recvd = 0; + + int nbad_seqno = 0; + + for (unsigned i = 0; i < history.size(); i++){ + total_pkts_recvd++; + total_samples_recvd += history[i].d_nsamples; + + bool bad_seqno = history[i].d_seqno != (i & 0xff); + if (bad_seqno) + nbad_seqno++; + + printf("%3d %8d %8ld %8ld %3d %s\n", + history[i].d_nsamples, + history[i].d_timestamp, + total_pkts_recvd, total_samples_recvd, + history[i].d_seqno, + bad_seqno ? "BAD SEQNO" : "" + ); + } + + if (nbad_seqno == 0) + printf("\nAll sequence numbers are correct\n"); + else + printf("\n%d sequence numbers were INCORRECT\n", nbad_seqno); + + + printf("\nUser space statistics:\n"); + printf(" rx_samples: %8ld", total_samples_recvd); + printf(" expected %8d %s\n", + nsamples, + nsamples - total_samples_recvd == 0 ? "OK" : "NOT OK"); + + printf(" rx_packets: %8ld", total_pkts_recvd); + printf(" expected %8ld %s\n", + expected_rx_packets, + expected_rx_packets - total_pkts_recvd == 0 ? "OK" : "NOT OK"); + + + fflush(stdout); + + printf("\nKernel interface statistics:\n"); + + long long delta; + delta = stop.rx_bytes - start.rx_bytes; + printf(" rx_bytes: %8Ld", delta); + printf(" expected %8ld %s\n", + expected_rx_bytes, + expected_rx_bytes - delta == 0 ? "OK" : "NOT OK"); + + delta = stop.rx_packets - start.rx_packets; + printf(" rx_packets: %8Ld", delta); + printf(" expected %8ld %s\n", + expected_rx_packets, + expected_rx_packets - delta == 0 ? "OK" : "NOT OK"); + + printf(" rx_errs: %8Ld\n", stop.rx_errs - start.rx_errs); + printf(" rx_drop: %8Ld\n", stop.rx_drop - start.rx_drop); + printf(" tx_bytes: %8Ld\n", stop.tx_bytes - start.tx_bytes); + printf(" tx_packets: %8Ld\n", stop.tx_packets - start.tx_packets); + printf(" tx_errs: %8Ld\n", stop.tx_errs - start.tx_errs); + printf(" tx_drop: %8Ld\n", stop.tx_drop - start.tx_drop); + + + return 0; +} diff --git a/usrp2/host/apps/rx_streaming_samples.cc b/usrp2/host/apps/rx_streaming_samples.cc new file mode 100644 index 0000000000..70f2c5d73d --- /dev/null +++ b/usrp2/host/apps/rx_streaming_samples.cc @@ -0,0 +1,372 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include <usrp2/strtod_si.h> +#include <usrp2/copiers.h> +#include <usrp2/rx_nop_handler.h> +#include <gruel/realtime.h> +#include <sys/time.h> +#include <iostream> +#include <string.h> +#include <boost/scoped_ptr.hpp> +#include <boost/shared_ptr.hpp> +#include <stdexcept> +#include <signal.h> + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + +// ------------------------------------------------------------------------ + +// FIXME make this a template + +class complex_16_file_writer : public usrp2::rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + complex_16_file_writer(const std::string &filename, uint64_t max_samples) + : usrp2::rx_nop_handler(max_samples), d_filename(filename) + { + d_fp = fopen(filename.c_str(), "wb"); + if (d_fp == 0){ + perror(filename.c_str()); + throw std::invalid_argument(filename); + } + } + + ~complex_16_file_writer(); + + bool + operator()(const uint32_t *items, size_t nitems, const usrp2::rx_metadata *metadata) + { + bool ok = rx_nop_handler::operator()(items, nitems, metadata); + + size_t host_nitems = nitems; + std::complex<int16_t> host_items[host_nitems]; + + usrp2::copy_u2_complex_16_to_host_complex_16(nitems, items, host_items); + + size_t n = 0; + while (n < host_nitems){ + size_t r = fwrite(&host_items[n], sizeof(host_items[0]), host_nitems - n, d_fp); + n += r; + if (r == 0){ // out of space? + d_err = true; + perror(d_filename.c_str()); + ok = false; + break; + } + } + + return ok; + } +}; + +complex_16_file_writer::~complex_16_file_writer() +{ + fclose(d_fp); +} + +// ------------------------------------------------------------------------ + +class complex_float_file_writer : public usrp2::rx_nop_handler +{ + FILE *d_fp; + std::string d_filename; + +public: + + complex_float_file_writer(const std::string &filename, uint64_t max_samples) + : usrp2::rx_nop_handler(max_samples), d_filename(filename) + { + d_fp = fopen(filename.c_str(), "wb"); + if (d_fp == 0){ + perror(filename.c_str()); + throw std::invalid_argument(filename); + } + } + + ~complex_float_file_writer(); + + bool + operator()(const uint32_t *items, size_t nitems, const usrp2::rx_metadata *metadata) + { + bool ok = rx_nop_handler::operator()(items, nitems, metadata); + + size_t host_nitems = nitems; + std::complex<float> host_items[host_nitems]; + + usrp2::copy_u2_complex_16_to_host_complex_float(nitems, items, host_items); + + size_t n = 0; + while (n < host_nitems){ + size_t r = fwrite(&host_items[n], sizeof(host_items[0]), host_nitems - n, d_fp); + n += r; + if (r == 0){ // out of space? + d_err = true; + perror(d_filename.c_str()); + ok = false; + break; + } + } + + return ok; + } +}; + +complex_float_file_writer::~complex_float_file_writer() +{ + fclose(d_fp); +} + +// ------------------------------------------------------------------------ + +static void +usage(const char *progname) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + fprintf(stderr, "Usage: %s [options]\n\n", p); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -f FREQUENCY specify receive center frequency in Hz [default=0.0]\n"); + fprintf(stderr, " -d DECIM specify receive decimation rate [default=5]\n"); + fprintf(stderr, " -g GAIN specify receive daughterboard gain [default=0]\n"); + fprintf(stderr, " -N NSAMPLES specify number of samples to receive [default=infinite]\n"); + fprintf(stderr, " -o OUTPUT_FILENAME specify file to receive samples [default=none]\n"); + fprintf(stderr, " -s write complex<short> [default=complex<float>]\n"); + fprintf(stderr, " -v verbose output\n"); +} + +int +main(int argc, char **argv) +{ + // options and their defaults + const char *interface = "eth0"; + const char *mac_addr_str = ""; + double rx_freq = 0.0; + int rx_decim = 5; + double rx_gain = 0.0; + uint64_t nsamples = 0; + bool output_shorts = false; + char *output_filename = 0; + bool verbose = false; + + int ch; + + while ((ch = getopt(argc, argv, "he:m:f:d:g:N:o:sv")) != EOF){ + double tmp; + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; + break; + + case 'f': + if (!strtod_si(optarg, &rx_freq)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'g': + if (!strtod_si(optarg, &rx_gain)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'd': + rx_decim = strtol(optarg, 0, 0); + if (rx_decim < 4 or rx_decim > 512) { + std::cerr << "invalid decimation rate: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + break; + + case 'N': + if (!strtod_si(optarg, &tmp)) { + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + exit(1); + } + nsamples = static_cast<uint64_t>(tmp); + break; + + case 's': + output_shorts = true; + break; + + case 'o': + output_filename = optarg; + break; + + case 'v': + verbose = true; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + + install_sig_handler(SIGINT, sig_handler); + + usrp2::rx_nop_handler::sptr handler; + + if (output_filename){ + if (output_shorts) + handler = usrp2::rx_nop_handler::sptr(new complex_16_file_writer(output_filename, nsamples)); + else + handler = usrp2::rx_nop_handler::sptr(new complex_float_file_writer(output_filename, nsamples)); + } + else + handler = usrp2::rx_nop_handler::sptr(new usrp2::rx_nop_handler(nsamples)); + + gruel::rt_status_t rt = gruel::enable_realtime_scheduling(); + if (rt != gruel::RT_OK) + std::cerr << "Failed to enable realtime scheduling" << std::endl; + + usrp2::usrp2::sptr u2 = usrp2::usrp2::make(interface, mac_addr_str); + + // FIXME in case it was left running... + if (!u2->stop_rx_streaming()){ + fprintf(stderr, "stop_rx_streaming failed\n"); + } + + if (!u2->set_rx_gain(rx_gain)){ + fprintf(stderr, "set_rx_gain(%f) failed\n", rx_gain); + exit(1); + } + + usrp2::tune_result tr; + if (!u2->set_rx_center_freq(rx_freq, &tr)){ + fprintf(stderr, "set_rx_center_freq(%g) failed\n", rx_freq); + exit(1); + } + + if (verbose){ + printf("USRP2 MAC address: %s\n\n", u2->mac_addr().c_str()); + printf("Daughterboard configuration:\n"); + printf(" baseband_freq=%f\n", tr.baseband_freq); + printf(" ddc_freq=%f\n", tr.dxc_freq); + printf(" residual_freq=%f\n", tr.residual_freq); + printf(" inverted=%s\n\n", tr.spectrum_inverted ? "yes" : "no"); + } + + if (!u2->set_rx_decim(rx_decim)) { + fprintf(stderr, "set_rx_decim(%d) failed\n", rx_decim); + exit(1); + } + + if (verbose) + printf("USRP2 using decimation rate of %d\n", rx_decim); + + if (!u2->start_rx_streaming(0)){ + fprintf(stderr, "start_rx_streaming failed\n"); + exit(1); + } + + if (verbose) { + if (nsamples > 0) + printf("Receiving %zd samples\n\n", nsamples); + else + printf("Receiving infinite samples\n\n"); + } + + struct timeval start, end; + gettimeofday(&start, 0); + + while (!signaled && + !handler->has_errored_p() && + !handler->has_finished_p()) { + bool ok = u2->rx_samples(0, handler.get()); + if (!ok){ + fprintf(stderr, "u2->rx_samples failed\n"); + return 1; + } + } + + gettimeofday(&end, 0); + long n_usecs = end.tv_usec-start.tv_usec; + long n_secs = end.tv_sec-start.tv_sec; + double elapsed = (double)n_secs + (double)n_usecs*1e-6; + double mbs = handler->nsamples()*sizeof(uint32_t)/elapsed/1e6; + double pps = handler->nframes()/elapsed; + + u2->stop_rx_streaming(); + + if (verbose){ + printf("\nCopy handler called %li times.\n", handler->nframes()); + printf("Copy handler called with %li bytes.\n\n", handler->nsamples()*sizeof(uint32_t)); + printf("Elapsed time was %5.3f seconds.\n", elapsed); + printf("Packet rate was %1.0f pkts/sec.\n", pps); + printf("Approximate throughput was %5.2f MB/sec.\n", mbs); + printf("Total instances of overruns was %d.\n", u2->rx_overruns()); + printf("Total missing frames was %d.\n", u2->rx_missing()); + } + + return 0; +} diff --git a/usrp2/host/apps/stdin_int32_fft.py b/usrp2/host/apps/stdin_int32_fft.py new file mode 100755 index 0000000000..5391863a55 --- /dev/null +++ b/usrp2/host/apps/stdin_int32_fft.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# +# Copyright 2004,2005,2007,2008 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 3, 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., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gru +from gnuradio import eng_notation +from gnuradio.eng_option import eng_option +from gnuradio.wxgui import stdgui2, fftsink2, waterfallsink2, scopesink2, form, slider +from optparse import OptionParser +import wx +import sys +import numpy + + +class app_top_block(stdgui2.std_top_block): + def __init__(self, frame, panel, vbox, argv): + stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) + + self.frame = frame + self.panel = panel + + parser = OptionParser(option_class=eng_option) + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=None, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + self.options = options + self.options.gain = 1.0 + self.show_debug_info = True + + + input_rate = 100e6 / options.decim + + self.src = gr.file_descriptor_source(gr.sizeof_short, 0, False); + self.s2c = gr.interleaved_short_to_complex() + + if options.waterfall: + self.scope = \ + waterfallsink2.waterfall_sink_c (panel, fft_size=1024, sample_rate=input_rate) + elif options.oscilloscope: + self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate) + else: + self.scope = fftsink2.fft_sink_c (panel, fft_size=1024, y_divs=12, sample_rate=input_rate,ref_level=110,fft_rate=20) + + self.connect(self.src, self.s2c, self.scope) + + self._build_gui(vbox) + self._setup_events() + + # set initial values + + if options.freq is None: + # if no freq was specified, use the mid-point + options.freq = 0.0 + + if self.show_debug_info: + self.myform['decim'].set_value(self.options.decim) + + + def _set_status_msg(self, msg): + self.frame.GetStatusBar().SetStatusText(msg, 0) + + def _build_gui(self, vbox): + + def _form_set_freq(kv): + return self.set_freq(kv['freq']) + + vbox.Add(self.scope.win, 10, wx.EXPAND) + + # add control area at the bottom + self.myform = myform = form.form() + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0, 0) + myform['freq'] = form.float_field( + parent=self.panel, sizer=hbox, label="Center freq", weight=1, + callback=myform.check_input_and_call(_form_set_freq, self._set_status_msg)) + + hbox.Add((5,0), 0, 0) + vbox.Add(hbox, 0, wx.EXPAND) + + self._build_subpanel(vbox) + + def _build_subpanel(self, vbox_arg): + # build a secondary information panel (sometimes hidden) + + # FIXME figure out how to have this be a subpanel that is always + # created, but has its visibility controlled by foo.Show(True/False) + + def _form_set_decim(kv): + return self.set_decim(kv['decim']) + + if not(self.show_debug_info): + return + + panel = self.panel + vbox = vbox_arg + myform = self.myform + + #panel = wx.Panel(self.panel, -1) + #vbox = wx.BoxSizer(wx.VERTICAL) + + hbox = wx.BoxSizer(wx.HORIZONTAL) + hbox.Add((5,0), 0) + + myform['decim'] = form.int_field( + parent=panel, sizer=hbox, label="Decim", + callback=myform.check_input_and_call(_form_set_decim, self._set_status_msg)) + + hbox.Add((5,0), 0) + vbox.Add(hbox, 0, wx.EXPAND) + + + def set_freq(self, target_freq): + """ + Set the center frequency we're interested in. + + @param target_freq: frequency in Hz + @rypte: bool + + Tuning is a two step process. First we ask the front-end to + tune as close to the desired frequency as it can. Then we use + the result of that operation and our target_frequency to + determine the value for the digital down converter. + """ + + if True: + self.myform['freq'].set_value(target_freq) # update displayed value + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.set_baseband_freq(target_freq) + return True + + return False + + def set_gain(self, gain): + self.myform['gain'].set_value(gain) # update displayed value + + def set_decim(self, decim): + input_rate = 100e6 / self.options.decim + self.scope.set_sample_rate(input_rate) + if self.show_debug_info: # update displayed values + self.myform['decim'].set_value(self.u.decim_rate()) + return ok + + def _setup_events(self): + if not self.options.waterfall and not self.options.oscilloscope: + self.scope.win.Bind(wx.EVT_LEFT_DCLICK, self.evt_left_dclick) + + def evt_left_dclick(self, event): + (ux, uy) = self.scope.win.GetXY(event) + if event.CmdDown(): + # Re-center on maximum power + points = self.scope.win._points + if self.scope.win.peak_hold: + if self.scope.win.peak_vals is not None: + ind = numpy.argmax(self.scope.win.peak_vals) + else: + ind = int(points.shape()[0]/2) + else: + ind = numpy.argmax(points[:,1]) + (freq, pwr) = points[ind] + target_freq = freq/self.scope.win._scale_factor + print ind, freq, pwr + self.set_freq(target_freq) + else: + # Re-center on clicked frequency + target_freq = ux/self.scope.win._scale_factor + self.set_freq(target_freq) + + +def main (): + app = stdgui2.stdapp(app_top_block, "USRP FFT", nstatus=1) + app.MainLoop() + +if __name__ == '__main__': + main () diff --git a/usrp2/host/apps/streaming_fft.py b/usrp2/host/apps/streaming_fft.py new file mode 100755 index 0000000000..cd3606c982 --- /dev/null +++ b/usrp2/host/apps/streaming_fft.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import os +import os.path +import sys +from gnuradio.eng_option import eng_option +from optparse import OptionParser + +def main(): + parser = OptionParser(option_class=eng_option) + parser.add_option("-d", "--decim", type="int", default=16, + help="set fgpa decimation rate to DECIM [default=%default]") + parser.add_option("-f", "--freq", type="eng_float", default=0.0, + help="set frequency to FREQ", metavar="FREQ") + parser.add_option("-g", "--gain", type="string", default=None, + help="set gain to GAIN [default=%default]") + parser.add_option("-W", "--waterfall", action="store_true", default=False, + help="Enable waterfall display") + parser.add_option("-S", "--oscilloscope", action="store_true", default=False, + help="Enable oscilloscope display") + parser.add_option("-F", "--samples-per-frame", type="int", default=250, + help="[default=%default]") + parser.add_option("-e", "--eth", default="eth0", + help="specify ethernet interface [default=%default]") + + (options, args) = parser.parse_args() + if len(args) != 0: + parser.print_help() + sys.exit(1) + + + path = os.path.dirname(sys.argv[0]) + if path == '': + path = '.' + + + display_type = '' + if options.waterfall: + display_type = '-W' + if options.oscilloscope: + display_type = '-S' + + gain_clause = '' + if options.gain: + gain_clause = '-g ' + options.gain + + # FIXME: restore -F + cmd = "sudo %s/rx_streaming_samples -s -e %s -f %g -d %d %s -o /proc/self/fd/1 | %s/stdin_int32_fft.py %s -f %g -d %d" % ( + path, options.eth, options.freq, options.decim, gain_clause, + path, display_type, options.freq, options.decim) + + print cmd + os.system(cmd) + + +if __name__ == '__main__': + main() diff --git a/usrp2/host/apps/test.sh b/usrp2/host/apps/test.sh new file mode 100755 index 0000000000..c533c33396 --- /dev/null +++ b/usrp2/host/apps/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +sudo ./rx_streaming_samples -d 4 -v -N 100M + diff --git a/usrp2/host/apps/tx_samples.cc b/usrp2/host/apps/tx_samples.cc new file mode 100644 index 0000000000..b4a3c8ed6d --- /dev/null +++ b/usrp2/host/apps/tx_samples.cc @@ -0,0 +1,263 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <usrp2/usrp2.h> +#include <usrp2/strtod_si.h> +#include <iostream> +#include <complex> +#include <getopt.h> +#include <gruel/realtime.h> +#include <signal.h> +#include <string.h> +#include <stdexcept> + + +typedef std::complex<float> fcomplex; + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +static const char * +prettify_progname(const char *progname) // that's probably almost a word ;) +{ + const char *p = strrchr(progname, '/'); // drop leading directory path + if (p) + p++; + + if (strncmp(p, "lt-", 3) == 0) // drop lt- libtool prefix + p += 3; + + return p; +} + +static void +usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [options]\n\n", prettify_progname(progname)); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h show this message and exit\n"); + fprintf(stderr, " -e ETH_INTERFACE specify ethernet interface [default=eth0]\n"); + fprintf(stderr, " -m MAC_ADDR mac address of USRP2 HH:HH [default=first one found]\n"); + fprintf(stderr, " -I INPUT_FILE set input filename [default=stdin]\n"); + fprintf(stderr, " -r repeat. When EOF of input file is reached, seek to beginning\n"); + fprintf(stderr, " -f FREQ set frequency to FREQ [default=0]\n"); + fprintf(stderr, " -i INTERP set interpolation rate to INTERP [default=32]\n"); + fprintf(stderr, " -g gain set tx gain\n"); + fprintf(stderr, " -S SCALE fpga scaling factor for I & Q [default=256]\n"); +} + +#define GAIN_NOT_SET (-1000) +#define MAX_SAMPLES (371) + +int +main(int argc, char **argv) +{ + const char *interface = "eth0"; + const char *input_filename = 0; + bool repeat = false; + const char *mac_addr_str = ""; + double freq = 0; + int32_t interp = 32; + int32_t samples_per_frame = MAX_SAMPLES; + int32_t scale = 3000; + double gain = GAIN_NOT_SET; + + int ch; + double tmp; + + + while ((ch = getopt(argc, argv, "he:m:I:rf:i:S:F:g:")) != EOF){ + switch (ch){ + + case 'e': + interface = optarg; + break; + + case 'm': + mac_addr_str = optarg; +#if 0 + if (!usrp2_basic::parse_mac_addr(optarg, &mac_addr)){ + std::cerr << "invalid mac addr: " << optarg << std::endl; + usage(argv[0]); + return 1; + } +#endif + break; + + case 'I': + input_filename = optarg; + break; + + case 'r': + repeat = true; + break; + + case 'f': + if (!strtod_si(optarg, &freq)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + return 1; + } + break; + + case 'F': + samples_per_frame = strtol(optarg, 0, 0); + break; + + case 'i': + interp = strtol(optarg, 0, 0); + break; + + case 'S': + if (!strtod_si(optarg, &tmp)){ + std::cerr << "invalid number: " << optarg << std::endl; + usage(argv[0]); + return 1; + } + scale = static_cast<int32_t>(tmp); + break; + + case 'h': + default: + usage(argv[0]); + return 1; + } + } + + + if (argc - optind != 0){ + usage(argv[0]); + return 1; + } + + if (samples_per_frame < 9 || samples_per_frame > MAX_SAMPLES){ + std::cerr << prettify_progname(argv[0]) + << ": samples_per_frame is out of range. " + << "Must be in [9, " << MAX_SAMPLES << "].\n"; + usage(argv[0]); + return 1; + } + + + FILE *fp = 0; + if (input_filename == 0) + fp = stdin; + else { + fp = fopen(input_filename, "rb"); + if (fp == 0){ + perror(input_filename); + return 1; + } + } + + install_sig_handler(SIGINT, sig_handler); + + + gruel::rt_status_t rt = gruel::enable_realtime_scheduling(); + if (rt != gruel::RT_OK) + std::cerr << "Failed to enable realtime scheduling" << std::endl; + + + usrp2::usrp2::sptr u2 = usrp2::usrp2::make(interface, mac_addr_str); + + if (gain != GAIN_NOT_SET){ + if (!u2->set_tx_gain(gain)){ + std::cerr << "set_tx_gain failed\n"; + return 1; + } + } + + usrp2::tune_result tr; + if (!u2->set_tx_center_freq(freq, &tr)){ + fprintf(stderr, "set_tx_center_freq(%g) failed\n", freq); + return 1; + } + + printf("Daughterboard configuration:\n"); + printf(" baseband_freq=%f\n", tr.baseband_freq); + printf(" duc_freq=%f\n", tr.dxc_freq); + printf(" residual_freq=%f\n", tr.residual_freq); + printf(" inverted=%s\n\n", tr.spectrum_inverted ? "yes" : "no"); + + if (!u2->set_tx_interp(interp)){ + fprintf(stderr, "set_tx_interp(%d) failed\n", interp); + return 1; + } + + if (!u2->set_tx_scale_iq(scale, scale)){ + std::cerr << "set_tx_scale_iq failed\n"; + return 1; + } + + + usrp2::tx_metadata md; + md.timestamp = -1; + md.start_of_burst = 1; + md.send_now = 1; + + while (!signaled){ + + std::complex<int16_t> samples[MAX_SAMPLES]; + + int r = fread(samples, sizeof(uint32_t), samples_per_frame, fp); + + // fprintf(stderr, "fread -> %d\n", r); + + if (r == 0){ + if (!repeat) + break; + if (fseek(fp, 0, SEEK_SET) == -1) + break; + } + + // FIXME if r < 9, pad to 9 for minimum packet size constraint + + if (!u2->tx_complex_int16(0, samples, r, &md)){ + fprintf(stderr, "tx_complex_int16 failed\n"); + break; + } + } + + return 0; +} diff --git a/usrp2/host/apps/usrp2_burn_mac_addr.cc b/usrp2/host/apps/usrp2_burn_mac_addr.cc new file mode 100644 index 0000000000..6ee76c1301 --- /dev/null +++ b/usrp2/host/apps/usrp2_burn_mac_addr.cc @@ -0,0 +1,176 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <usrp2/usrp2.h> +#include <iostream> +#include <getopt.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <signal.h> +#include <stdexcept> + + +static volatile bool signaled = false; + +static void +sig_handler(int sig) +{ + signaled = true; +} + +static void +install_sig_handler(int signum, + void (*new_handler)(int)) +{ + struct sigaction new_action; + memset (&new_action, 0, sizeof (new_action)); + + new_action.sa_handler = new_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + + if (sigaction (signum, &new_action, 0) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +} + + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-e ethN] [-m old_mac_addr] new_mac_addr\n", + progname); + fprintf(stderr, " old_mac_addr defaults to 00:50:c2:85:3f:ff\n"); + fprintf(stderr, " new_mac_address must be HH:HH or HH:HH:HH:HH:HH:HH\n"); +} + +static bool +check_mac_addr_syntax(const std::string &s) +{ + unsigned char addr[6]; + + addr[0] = 0x00; // Matt's IAB + addr[1] = 0x50; + addr[2] = 0xC2; + addr[3] = 0x85; + addr[4] = 0x30; + addr[5] = 0x00; + + int len = s.size(); + + switch (len){ + + case 5: + return sscanf(s.c_str(), "%hhx:%hhx", &addr[4], &addr[5]) == 2; + + case 17: + return sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &addr[0], &addr[1], &addr[2], + &addr[3], &addr[4], &addr[5]) == 6; + default: + return false; + } + + return true; +} + + +int +main(int argc, char **argv) +{ + int ch; + const char *interface = "eth0"; + const char *old_mac_addr = "00:50:c2:85:3f:ff"; + const char *new_mac_addr = 0; + + while ((ch = getopt(argc, argv, "he:m:")) != EOF){ + switch (ch){ + case 'e': + interface = optarg; + break; + + case 'm': + old_mac_addr = optarg; + break; + + case 'h': + default: + usage(argv[0]); + exit(1); + } + } + + if (argc - optind != 1){ + usage(argv[0]); + exit(1); + } + + new_mac_addr = argv[optind]; + + if (!check_mac_addr_syntax(old_mac_addr)){ + fprintf(stderr, "invalid mac address: %s\n", old_mac_addr); + exit(1); + } + if (!check_mac_addr_syntax(new_mac_addr)){ + fprintf(stderr, "invalid mac address: %s\n", new_mac_addr); + exit(1); + } + + install_sig_handler(SIGINT, sig_handler); + + usrp2::usrp2::sptr u2; + + try { + u2 = usrp2::usrp2::make(interface, old_mac_addr); + } + catch (std::exception const &e){ + std::cerr << e.what() << std::endl; + return 1; + } + + if (!u2->burn_mac_addr(new_mac_addr)){ + std::cerr << "Failed to burn mac address: " + << new_mac_addr << std::endl; + return 1; + } + + u2.reset(); // close + + // wait 250 ms + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 250000000; + nanosleep(&ts, 0); + + try { + u2 = usrp2::usrp2::make(interface, new_mac_addr); + } + catch (std::exception const &e){ + std::cerr << "Failed to connect to USRP2 using new addr: " + << new_mac_addr << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/usrp2/host/include/Makefile.am b/usrp2/host/include/Makefile.am new file mode 100644 index 0000000000..e2f7282b1a --- /dev/null +++ b/usrp2/host/include/Makefile.am @@ -0,0 +1,23 @@ +# +# Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +include $(top_srcdir)/Makefile.common + +SUBDIRS = usrp2 diff --git a/usrp2/host/include/usrp2/Makefile.am b/usrp2/host/include/usrp2/Makefile.am new file mode 100644 index 0000000000..4c6dac8999 --- /dev/null +++ b/usrp2/host/include/usrp2/Makefile.am @@ -0,0 +1,34 @@ +# +# 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 <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +INCLUDES = $(STD_DEFINES_AND_INCLUDES) $(CPPUNIT_INCLUDES) + +usrp2includedir = $(includedir)/usrp2 + +usrp2include_HEADERS = \ + copiers.h \ + copy_handler.h \ + data_handler.h \ + metadata.h \ + rx_nop_handler.h \ + rx_sample_handler.h \ + strtod_si.h \ + tune_result.h \ + usrp2.h \ + usrp2_cdefs.h diff --git a/usrp2/host/include/usrp2/copiers.h b/usrp2/host/include/usrp2/copiers.h new file mode 100644 index 0000000000..43674f7acc --- /dev/null +++ b/usrp2/host/include/usrp2/copiers.h @@ -0,0 +1,63 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_COPIERS_H +#define INCLUDED_USRP2_COPIERS_H + +#include <complex> +#include <stdint.h> + +namespace usrp2 { + + // FIXME we may want to rework this, but this will get us on the air + + /* + * ---------------------------------------------------------------- + * Copy and convert from USRP2 wire format to host format + * ---------------------------------------------------------------- + */ + + void + copy_u2_complex_16_to_host_complex_16(size_t nitems, + const uint32_t *items, + std::complex<int16_t> *host_items); + + void + copy_u2_complex_16_to_host_complex_float(size_t nitems, + const uint32_t *items, + std::complex<float> *host_items); + + /* + * ---------------------------------------------------------------- + * Copy and convert from host format to USRP2 wire format + * ---------------------------------------------------------------- + */ + + void + copy_host_complex_16_to_u2_complex_16(size_t nitems, + const std::complex<int16_t> *host_items, + uint32_t *items); + + void + copy_host_complex_float_to_u2_complex_16(size_t nitems, + const std::complex<float> *host_items, + uint32_t *items); +} + + +#endif /* INCLUDED_USRP2_COPIERS_H */ diff --git a/usrp2/host/include/usrp2/copy_handler.h b/usrp2/host/include/usrp2/copy_handler.h new file mode 100644 index 0000000000..aef14cac80 --- /dev/null +++ b/usrp2/host/include/usrp2/copy_handler.h @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_COPY_HANDLER_H +#define INCLUDED_COPY_HANDLER_H + +#include <usrp2/data_handler.h> +#include <boost/utility.hpp> + +namespace usrp2 { + + class copy_handler : public data_handler, boost::noncopyable + { + uint8_t *d_dest; // next write pointer + size_t d_space; // space left in destination buffer + size_t d_bytes; // total bytes copied + size_t d_times; // number of times invoked + + public: + copy_handler(void *dest, size_t len); + ~copy_handler(); + + virtual data_handler::result operator()(const void *base, size_t len); + + size_t bytes() const { return d_bytes; } + size_t times() const { return d_times; } + + static const size_t MIN_COPY_LEN = 1484; // FIXME: calculate eth packet - thdr + bool full() const { return d_space < MIN_COPY_LEN; } + }; + +} // namespace usrp2 + +#endif /* INCLUDED_COPY_HANDLER_H */ diff --git a/usrp2/host/include/usrp2/data_handler.h b/usrp2/host/include/usrp2/data_handler.h new file mode 100644 index 0000000000..7317b38af9 --- /dev/null +++ b/usrp2/host/include/usrp2/data_handler.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_DATA_HANDLER_H +#define INCLUDED_DATA_HANDLER_H + +#include <stdint.h> +#include <stddef.h> + +namespace usrp2 { + + /*! + * \brief Abstract function object called to handle received data blocks. + */ + class data_handler + { + public: + + enum result_bits { + RELEASE = 0x0000, //< OK to release data (opposite of KEEP) + KEEP = 0x0001, //< do not discard data + DONE = 0x0002, //< do not call this object again + }; + + typedef int result; //< bitmask of result_bits + + /*! + * \param base points to the beginning of the data + * \param len is the length in bytes of the data + * \returns bitmask composed of DONE, KEEP + */ + virtual result operator()(const void *base, size_t len) = 0; + virtual ~data_handler(); + }; + +} // namespace usrp2 + +#endif /* INCLUDED_DATA_HANDLER_H */ diff --git a/usrp2/host/include/usrp2/metadata.h b/usrp2/host/include/usrp2/metadata.h new file mode 100644 index 0000000000..52efd07efa --- /dev/null +++ b/usrp2/host/include/usrp2/metadata.h @@ -0,0 +1,60 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_USRP2_METADATA_H +#define INCLUDED_USRP2_METADATA_H + +#include <stdint.h> + +namespace usrp2 { + + //! type of the timestamp returned from the USRP2 FPGA + typedef uint32_t fpga_timestamp; + + /*! + * \brief metadata associated with received frames + */ + struct rx_metadata { + uint32_t word0; //< debugging, extensions + fpga_timestamp timestamp; //< time that first sample of frame was received + unsigned int start_of_burst : 1; //< this frame is the start of a burst + unsigned int end_of_burst : 1; //< this frame is the end of a burst + unsigned int rx_overrun : 1; //< An Rx overrun occurred in the FPGA + // rssi + // agc_mode + + rx_metadata() : + word0(0), timestamp(0), start_of_burst(0), end_of_burst(0), rx_overrun(0) {} + }; + + /*! + * \brief metadata associated with transmitted frames + */ + struct tx_metadata { + fpga_timestamp timestamp; //< time to transmit first sample of frame + unsigned int send_now : 1; //< ignore timestamp, send now + unsigned int start_of_burst : 1; //< this frame is the start of a burst + unsigned int end_of_burst : 1; //< this frame is the end of a burst + // ... + + tx_metadata() : + timestamp(0), send_now(0), start_of_burst(0), end_of_burst(0) {} + }; + +}; // usrp2 + +#endif /* INCLUDED_USRP2_METADATA_H */ diff --git a/usrp2/host/include/usrp2/rx_nop_handler.h b/usrp2/host/include/usrp2/rx_nop_handler.h new file mode 100644 index 0000000000..44ca51398e --- /dev/null +++ b/usrp2/host/include/usrp2/rx_nop_handler.h @@ -0,0 +1,128 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_RX_NOP_HANDLER_H +#define INCLUDED_RX_NOP_HANDLER_H + +#include <usrp2/rx_sample_handler.h> +#include <boost/shared_ptr.hpp> + +/*! + * Base class for receive handlers that must copy into potentially limited + * range destination buffers. + * + * Maintains counters for number of items copied, times invoked, and test + * for whether maximum has been reached. + * + * Derived classes should override the () operator, but call this + * parent class method at some point at the start of their own operations. + */ + +namespace usrp2 { + + class rx_nop_handler : public rx_sample_handler + { + uint64_t d_max_samples; + uint64_t d_max_quantum; + uint64_t d_nsamples; + uint64_t d_nframes; + + protected: + bool d_err; + + public: + + // Shared pointer to an instance of this class + typedef boost::shared_ptr<rx_nop_handler> sptr; + + /*! + * Constructor + * + * \param max_samples Maximum number of samples to copy. Use zero for no maximum. + * \param max_quantum Maximum number of samples required to accept in one call. + * Use 0 to indicate no maximum. + */ + rx_nop_handler(uint64_t max_samples, uint64_t max_quantum=0) + : d_max_samples(max_samples), d_max_quantum(max_quantum), + d_nsamples(0), d_nframes(0), d_err(false) {} + + /*! + * Destructor. Derived classes must implement their own, non-inline destructor. + */ + virtual ~rx_nop_handler(); + + /*! + * \brief Returns number of frames this copier was called with + */ + uint64_t nframes() const { return d_nframes; } + + /*! + * \brief Returns actual number of samples copied + */ + uint64_t nsamples() const { return d_nsamples; } + + /*! + * \brief Returns maximum number of samples that will be copied + */ + uint64_t max_samples() const { return d_max_samples; } + + /*! + * Returns true if an error has occurred. Derived classes must set d_err to true + * when an error occurs in the () operator + */ + bool has_errored_p() const { return d_err; } + + /*! + * \brief Returns true if this instance has reached the maximum number of samples + */ + bool has_finished_p() const + { return d_max_samples == 0 ? false : d_nsamples >= d_max_samples-d_max_quantum; } + + + /*! + * Function operator invoked by USRP2 RX API. Derived classes must override this method + * but then invoke it at the start of their processing. This operator will always be + * called at least once. + * + * \param items points to the first 32-bit word of uninterpreted sample data in the frame. + * \param nitems is the number of entries in the frame in units of uint32_t's. + * \param metadata is the additional per frame data provided by the USRP2 FPGA. + * + * \p items points to the raw sample data received off of the ethernet. The data is + * packed into big-endian 32-bit unsigned ints for transport, but the actual format + * of the data is dependent on the current configuration of the USRP2. The most common + * format is 16-bit I & Q, with I in the top of the 32-bit word. + * + * \returns true if the object wants to be called again with new data; + * false if no additional data is wanted. + */ + virtual bool operator()(const uint32_t *items, size_t nitems, const rx_metadata *metadata) + { + // printf("W0: %08x TS: %08x\n", metadata->word0, metadata->timestamp); + // printf("I0: %08x\n", items[0]); + + d_nsamples += nitems; + d_nframes++; + + return !has_finished_p(); + } + }; + +} /* namespace usrp2 */ + +#endif /* INCLUDED_RX_NOP_HANDLER */ diff --git a/usrp2/host/include/usrp2/rx_sample_handler.h b/usrp2/host/include/usrp2/rx_sample_handler.h new file mode 100644 index 0000000000..480cbc4f8a --- /dev/null +++ b/usrp2/host/include/usrp2/rx_sample_handler.h @@ -0,0 +1,58 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_USRP2_RX_SAMPLE_HANDLER_H +#define INCLUDED_USRP2_RX_SAMPLE_HANDLER_H + +#include <usrp2/metadata.h> +#include <stddef.h> + + +namespace usrp2 { + + /*! + * \brief Abstract function object called to handle received data blocks. + * + * An object derived from this class is passed to usrp2::rx_samples + * to process the received frames of samples. + */ + class rx_sample_handler { + public: + virtual ~rx_sample_handler(); + + /*! + * \param items points to the first 32-bit word of uninterpreted sample data in the frame. + * \param nitems is the number of entries in the frame in units of uint32_t's. + * \param metadata is the additional per frame data provided by the USRP2 FPGA. + * + * \p items points to the raw sample data received off of the ethernet. The data is + * packed into big-endian 32-bit unsigned ints for transport, but the actual format + * of the data is dependent on the current configuration of the USRP2. The most common + * format is 16-bit I & Q, with I in the top of the 32-bit word. + * + * This is the general purpose, low level interface and relies on other functions + * to handle all required endian-swapping and format conversion. \sa FIXME. + * + * \returns true if the object wants to be called again with new data; + * false if no additional data is wanted. + */ + virtual bool operator()(const uint32_t *items, size_t nitems, const rx_metadata *metadata) = 0; + }; + +}; + +#endif /* INCLUDED_RX_SAMPLE_HANDLER_H */ diff --git a/usrp2/host/include/usrp2/strtod_si.h b/usrp2/host/include/usrp2/strtod_si.h new file mode 100644 index 0000000000..0a0f5a62cd --- /dev/null +++ b/usrp2/host/include/usrp2/strtod_si.h @@ -0,0 +1,39 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_STRTOD_SI_H +#define INCLUDED_STRTOD_SI_H + +#include "usrp2_cdefs.h" +__U2_BEGIN_DECLS + + +/*! + * \brief convert string at s to double honoring any trailing SI suffixes + * + * \param[in] s is the string to convert + * \param[out] result is the converted value + * \returns non-zero iff conversion was successful. + */ +int strtod_si(const char *s, double *result); + +__U2_END_DECLS + + +#endif /* INCLUDED_STRTOD_SI_H */ + diff --git a/usrp2/host/include/usrp2/tune_result.h b/usrp2/host/include/usrp2/tune_result.h new file mode 100644 index 0000000000..6fb2a68241 --- /dev/null +++ b/usrp2/host/include/usrp2/tune_result.h @@ -0,0 +1,45 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_TUNE_RESULT_H +#define INCLUDED_TUNE_RESULT_H + +namespace usrp2 { + + class tune_result + { + public: + // RF frequency that corresponds to DC in the IF + double baseband_freq; + + // frequency programmed into the DDC/DUC + double dxc_freq; + + // residual frequency (typically < 0.01 Hz) + double residual_freq; + + // is the spectrum inverted? + bool spectrum_inverted; + + tune_result() + : baseband_freq(0), dxc_freq(0), residual_freq(0), spectrum_inverted(false) {} + }; + +} // namespace usrp2 + +#endif /* INCLUDED_TUNE_RESULT_H */ diff --git a/usrp2/host/include/usrp2/usrp2.h b/usrp2/host/include/usrp2/usrp2.h new file mode 100644 index 0000000000..874337911b --- /dev/null +++ b/usrp2/host/include/usrp2/usrp2.h @@ -0,0 +1,287 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_H +#define INCLUDED_USRP2_H + +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> +#include <complex> +//#include <iosfwd> +#include <usrp2/rx_sample_handler.h> +#include <usrp2/tune_result.h> +#include <usrp2/rx_sample_handler.h> + + +namespace usrp2 { + + /*! + * Structure to hold properties of USRP2 hardware devices. + * + */ + struct props + { + std::string addr; + uint16_t hw_rev; + uint8_t fpga_md5sum[16]; + uint8_t sw_md5sum[16]; + }; + + typedef std::vector<props> props_vector_t; + + /*! + * \brief Search the ethernet for all USRP2s or for a specific USRP2. + * + * \param ifc is the name of the OS ethernet interface (e.g., "eth0") + * \param mac_addr is the MAC address of the desired USRP2, or "" to search for all. + * mac_addr must be either a zero length string, "", or must be of the form + * "01:02:03:04:05:06" or "05:06". + * + * \returns a vector of properties, 1 entry for each matching USRP2 found. + */ + props_vector_t find(const std::string &ifc, const std::string &mac_addr=""); + + class tune_result; + + class usrp2 : boost::noncopyable + { + public: + static const unsigned int MAX_CHAN = 30; + + /*! + * Shared pointer to this class + */ + typedef boost::shared_ptr<usrp2> sptr; + + /*! + * Static function to return an instance of usrp2 as a shared pointer + * + * \param ifc Network interface name, e.g., "eth0" + * \param addr Network mac address, e.g., "01:23:45:67:89:ab", "89:ab" or "". + * If \p addr is HH:HH, it's treated as if it were 00:50:c2:85:HH:HH + * "" will autoselect a USRP2 if there is only a single one on the local ethernet. + */ + static sptr make(const std::string &ifc, const std::string &addr=""); + + /*! + * Class destructor + */ + ~usrp2(); + + /*! + * Returns the MAC address associated with this USRP + */ + std::string mac_addr(); + + /*! + * Burn new mac address into EEPROM on USRP2 + * + * \param new_addr Network mac address, e.g., "01:23:45:67:89:ab" or "89:ab". + * If \p addr is HH:HH, it's treated as if it were 00:50:c2:85:HH:HH + */ + bool burn_mac_addr(const std::string &new_addr); + + /* + * ---------------------------------------------------------------- + * Rx configuration and control + * ---------------------------------------------------------------- + */ + + /*! + * Set receiver gain + */ + bool set_rx_gain(double gain); + + /*! + * Set receiver center frequency + */ + bool set_rx_center_freq(double frequency, tune_result *result); + + /*! + * Set receiver sample rate decimation + */ + bool set_rx_decim(int decimation_factor); + + /*! + * Set receiver IQ magnitude scaling + */ + bool set_rx_scale_iq(int scale_i, int scale_q); + + /*! + * Set received sample format + * + * domain: complex or real + * type: floating, fixed point, or raw + * depth: bits per sample + * + * Sets format over the wire for samples from USRP2. + */ + // bool set_rx_format(...); + + /*! + * Start streaming receive mode. USRP2 will send a continuous stream of + * DSP pipeline samples to host. Call rx_samples(...) to access. + * + * \param channel Stream channel number (0-30) + * \param items_per_frame Number of 32-bit items per frame. + */ + bool start_rx_streaming(unsigned int channel=0, unsigned int items_per_frame=0); + + /*! + * Stop streaming receive mode. + */ + bool stop_rx_streaming(unsigned int channel=0); + + /*! + * \brief Receive data from the specified channel + * This method is used to receive all data: streaming or discrete. + */ + bool rx_samples(unsigned int channel, rx_sample_handler *handler); + + /*! + * Returns number of times receive overruns have occurred + */ + unsigned int rx_overruns(); + + /*! + * Returns total number of missing frames from overruns. + */ + unsigned int rx_missing(); + + /* + * ---------------------------------------------------------------- + * Tx configuration and control + * ---------------------------------------------------------------- + */ + + /*! + * Set receiver gain + */ + bool set_tx_gain(double gain); + + /*! + * Set transmitter center frequency + */ + bool set_tx_center_freq(double frequency, tune_result *result); + + /*! + * Set transmitter sample rate interpolation + */ + bool set_tx_interp(int interpolation_factor); + + /*! + * Set transmit IQ magnitude scaling + */ + bool set_tx_scale_iq(int scale_i, int scale_q); + + /*! + * Set transmit sample format + * + * domain: complex or real + * type: floating, fixed point, or raw + * depth: bits per sample + * + * Sets format over the wire for samples to USRP2. + */ + // bool set_tx_format(...); + + /*! + * \brief transmit complex<float> samples to USRP2 + * + * \param channel specifies the channel to send them to + * \param samples are the samples to transmit + * \param nsamples is the number of samples to transmit + * \param metadata provides the timestamp and flags + */ + bool tx_complex_float(unsigned int channel, + const std::complex<float> *samples, + size_t nsamples, + const tx_metadata *metadata); + + /*! + * \brief transmit complex<int16_t> samples to USRP2 + * + * \param channel specifies the channel to send them to + * \param samples are the samples to transmit + * \param nsamples is the number of samples to transmit + * \param metadata provides the timestamp and flags + */ + bool tx_complex_int16(unsigned int channel, + const std::complex<int16_t> *samples, + size_t nsamples, + const tx_metadata *metadata); + + /*! + * \brief transmit raw uint32_t data items to USRP2 + * + * The caller is responsible for ensuring that the items are + * formatted appropriately for the USRP2 and its configuration. + * This method is used primarily by the system itself. Users + * should call tx_complex_float or tx_complex_16 instead. + * + * \param channel specifies the channel to send them to + * \param items are the data items to transmit + * \param nitems is the number of items to transmit + * \param metadata provides the timestamp and flags + */ + bool tx_raw(unsigned int channel, + const uint32_t *items, + size_t nitems, + const tx_metadata *metadata); + + // ---------------------------------------------------------------- + + /*! + * \brief MIMO configuration + * + * \param flags from usrp2_mimo_config.h + * + * <pre> + * one of these: + * + * MC_WE_DONT_LOCK + * MC_WE_LOCK_TO_SMA + * MC_WE_LOCK_TO_MIMO + * + * and optionally this: + * + * MC_PROVIDE_CLK_TO_MIMO + * </pre> + */ + bool config_mimo(int flags); + + class impl; // implementation details + + private: + // Static function to retrieve or create usrp2 instance + static sptr find_existing_or_make_new(const std::string &ifc, props *p); + + // Only class members can instantiate this class + usrp2(const std::string &ifc, props *p); + + // All private state is held in opaque pointer + std::auto_ptr<impl> d_impl; + }; + +}; + +std::ostream& operator<<(std::ostream &os, const usrp2::props &x); + + +#endif /* INCLUDED_USRP2_H */ diff --git a/usrp2/host/include/usrp2/usrp2_cdefs.h b/usrp2/host/include/usrp2/usrp2_cdefs.h new file mode 100644 index 0000000000..71395cda80 --- /dev/null +++ b/usrp2/host/include/usrp2/usrp2_cdefs.h @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_USRP2_CDEFS_H +#define INCLUDED_USRP2_CDEFS_H + +/* C++ needs to know that types and declarations are C, not C++. */ +#ifdef __cplusplus +# define __U2_BEGIN_DECLS extern "C" { +# define __U2_END_DECLS } +#else +# define __U2_BEGIN_DECLS +# define __U2_END_DECLS +#endif + +#endif /* INCLUDED_USRP2_CDEFS_H */ diff --git a/usrp2/host/lib/Makefile.am b/usrp2/host/lib/Makefile.am new file mode 100644 index 0000000000..5c98600bf2 --- /dev/null +++ b/usrp2/host/lib/Makefile.am @@ -0,0 +1,67 @@ +# +# Copyright 2007,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 <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = \ + $(USRP2_INCLUDES) \ + $(BOOST_CPPFLAGS) \ + $(STD_DEFINES_AND_INCLUDES) \ + $(CPPUNIT_INCLUDES) \ + $(GRUEL_INCLUDES) + +bin_PROGRAMS = usrp2_socket_opener +usrp2_socket_opener_SOURCES = usrp2_socket_opener.cc + +lib_LTLIBRARIES = \ + libusrp2.la + +libusrp2_la_SOURCES = \ + control.cc \ + copiers.cc \ + copy_handler.cc \ + data_handler.cc \ + eth_buffer.cc \ + ethernet.cc \ + find.cc \ + open_usrp2_socket.cc \ + pktfilter.cc \ + ring.cc \ + rx_nop_handler.cc \ + rx_sample_handler.cc \ + strtod_si.c \ + usrp2.cc \ + usrp2_impl.cc \ + usrp2_thread.cc + +libusrp2_la_LIBADD = \ + $(OMNITHREAD_LA) \ + $(GRUEL_LIBS) \ + $(BOOST_LDFLAGS) $(BOOST_THREAD_LIB) + +# Private headers not needed for above the API development +noinst_HEADERS = \ + control.h \ + eth_buffer.h \ + eth_common.h \ + ethernet.h \ + open_usrp2_socket.h \ + pktfilter.h \ + ring.h \ + usrp2_bytesex.h \ + usrp2_impl.h \ + usrp2_thread.h diff --git a/usrp2/host/lib/control.cc b/usrp2/host/lib/control.cc new file mode 100644 index 0000000000..97d969fa6a --- /dev/null +++ b/usrp2/host/lib/control.cc @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <omni_time.h> +#include "control.h" +#include <iostream> + +namespace usrp2 { + + pending_reply::pending_reply(unsigned int rid, void *buffer, size_t len) + : d_rid(rid), d_mutex(), d_cond(&d_mutex), d_buffer(buffer), d_len(len) + { + } + + pending_reply::~pending_reply() + { + signal(); // Needed? + } + + int + pending_reply::wait(double secs) + { + omni_mutex_lock l(d_mutex); + omni_time abs_timeout = omni_time::time(omni_time(secs)); + return d_cond.timedwait(abs_timeout.d_secs, abs_timeout.d_nsecs); + } + + void + pending_reply::signal() + { + d_cond.signal(); + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/control.h b/usrp2/host/lib/control.h new file mode 100644 index 0000000000..678f12e4b5 --- /dev/null +++ b/usrp2/host/lib/control.h @@ -0,0 +1,117 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_CONTROL_H +#define INCLUDED_CONTROL_H + +#include <omnithread.h> +#include <usrp2_eth_packet.h> + +namespace usrp2 { + /*! + * OP_CONFIG_RX_V2 command packet + */ + struct op_config_rx_v2_cmd + { + u2_eth_packet_t h; + op_config_rx_v2_t op; + op_generic_t eop; + }; + + struct op_start_rx_streaming_cmd + { + u2_eth_packet_t h; + op_start_rx_streaming_t op; + op_generic_t eop; + }; + + struct op_stop_rx_cmd { + u2_eth_packet_t h; + op_generic_t op; + op_generic_t eop; + }; + + struct op_config_tx_v2_cmd + { + u2_eth_packet_t h; + op_config_tx_v2_t op; + op_generic_t eop; + }; + + struct op_burn_mac_addr_cmd + { + u2_eth_packet_t h; + op_burn_mac_addr_t op; + op_generic_t eop; + }; + + /*! + * Control mechanism to allow API calls to block waiting for reply packets + */ + class pending_reply + { + private: + unsigned int d_rid; + omni_mutex d_mutex; + omni_condition d_cond; + void *d_buffer; + size_t d_len; + + public: + /*! + * Construct a pending reply from the reply ID, response packet + * buffer, and buffer length. + */ + pending_reply(unsigned int rid, void *buffer, size_t len); + + /*! + * Destructor. Signals creating thread. + */ + ~pending_reply(); + + /*! + * Block, waiting for reply packet. + * Returns: 1 = ok, reply packet in buffer + * 0 = timeout + */ + int wait(double secs); + + /*! + * Allows creating thread to resume after copying reply into buffer + */ + void signal(); + + /*! + * Retrieve pending reply ID + */ + unsigned int rid() const { return d_rid; } + + /*! + * Retrieve destination buffer address + */ + void *buffer() const { return d_buffer; } + + /*! + * Retrieve destination buffer length + */ + size_t len() const { return d_len; } + }; + +} // namespace usrp2 + +#endif /* INCLUDED_CONTROL_H */ diff --git a/usrp2/host/lib/copiers.cc b/usrp2/host/lib/copiers.cc new file mode 100644 index 0000000000..833bb790d0 --- /dev/null +++ b/usrp2/host/lib/copiers.cc @@ -0,0 +1,133 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <usrp2/copiers.h> +#include <gruel/inet.h> +#include <gr_math.h> +#include <math.h> +#include <stdexcept> +#include <assert.h> +#include <string.h> + +// FIXME need gruel::not_implemented + +namespace usrp2 { + + /* + * N.B., in all of these, uint32_t *items is NOT 32-bit aligned! + * FIXME Needs fix for non-x86 machines. + */ + + /* + * ---------------------------------------------------------------- + * Copy and convert from USRP2 wire format to host format + * ---------------------------------------------------------------- + */ + void + copy_u2_complex_16_to_host_complex_16(size_t nitems, + const uint32_t *items, + std::complex<int16_t> *host_items) + { +#ifdef WORDS_BIGENDIAN + + assert(sizeof(items[0]) == sizeof(host_items[0])); + memcpy(host_items, items, nitems * sizeof(items[0])); + +#else + + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohx(items[i]); + //printf("%9d\n", items[i]); + host_items[i] = std::complex<int16_t>((t >> 16), t & 0xffff); + } + +#endif + } + + + /* + * endian swap if required and map [-32768, 32767] -> [1.0, +1.0) + */ + void + copy_u2_complex_16_to_host_complex_float(size_t nitems, + const uint32_t *items, + std::complex<float> *host_items) + { + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohx(items[i]); + int16_t re = (t >> 16) & 0xffff; + int16_t im = (t & 0xffff); + host_items[i] = std::complex<float>(re * 1.0/32768, im * 1.0/32768); + } + } + + /* + * ---------------------------------------------------------------- + * Copy and convert from host format to USRP2 wire format + * ---------------------------------------------------------------- + */ + void + copy_host_complex_16_to_u2_complex_16(size_t nitems, + const std::complex<int16_t> *host_items, + uint32_t *items) + { +#ifdef WORDS_BIGENDIAN + + assert(sizeof(items[0]) == sizeof(host_items[0])); + memcpy(items, host_items, nitems * sizeof(items[0])); + +#else + + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + items[i] = htonl((host_items[i].real() << 16) | (host_items[i].imag() & 0xffff)); + } + +#endif + } + + + static inline int16_t + clip_and_scale(float x) + { + return static_cast<int16_t>(rintf(gr_branchless_clip(x, 1.0) * 32767.0)); + } + + void + copy_host_complex_float_to_u2_complex_16(size_t nitems, + const std::complex<float> *host_items, + uint32_t *items) + { + for (size_t i = 0; i < nitems; i++){ + int16_t re = clip_and_scale(host_items[i].real()); + int16_t im = clip_and_scale(host_items[i].imag()); + + items[i] = htonl((re << 16) | (im & 0xffff)); + } + } + +} diff --git a/usrp2/host/lib/copy_handler.cc b/usrp2/host/lib/copy_handler.cc new file mode 100644 index 0000000000..92759084bf --- /dev/null +++ b/usrp2/host/lib/copy_handler.cc @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/copy_handler.h> +#include <iostream> +#include <string.h> + +namespace usrp2 { + + copy_handler::copy_handler(void *dest, size_t len) + : d_dest((uint8_t *)dest), d_space(len), d_bytes(0), d_times(0) + { + } + + copy_handler::~copy_handler() + { + // NOP + } + + data_handler::result + copy_handler::operator()(const void *base, size_t len) + { + if (len > d_space) + return KEEP|DONE; // can't do anything, retry later + + memcpy(&d_dest[d_bytes], base, len); + d_space -= len; + d_bytes += len; + d_times++; + + if (d_space < MIN_COPY_LEN) + return DONE; // don't call me anymore + + return 0; + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/data_handler.cc b/usrp2/host/lib/data_handler.cc new file mode 100644 index 0000000000..ba91236816 --- /dev/null +++ b/usrp2/host/lib/data_handler.cc @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <usrp2/data_handler.h> + +namespace usrp2 { + + data_handler::~data_handler() + { + // default nop destructor + } + +} + diff --git a/usrp2/host/lib/eth_buffer.cc b/usrp2/host/lib/eth_buffer.cc new file mode 100644 index 0000000000..7970ab87a5 --- /dev/null +++ b/usrp2/host/lib/eth_buffer.cc @@ -0,0 +1,273 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "eth_buffer.h" +#include "ethernet.h" +#include <usrp2/data_handler.h> +#include <linux/if_packet.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <iostream> +#include <cmath> +#include <errno.h> +#include <stdexcept> +#include <string.h> + + +#define ETH_BUFFER_DEBUG 0 // define to 0 or 1 +#if ETH_BUFFER_DEBUG +#define DEBUG_LOG(x) ::write(2, (x), 1) +#else +#define DEBUG_LOG(X) +#endif + +#define MAX_MEM_SIZE 25e6 // ~0.25s @ 100 MB/s +#define MAX_SLAB_SIZE 131702 // 128 KB (FIXME fish out of /proc/slabinfo) +#define MAX_PKT_SIZE 1512 // we don't do jumbo frames + +namespace usrp2 { + + eth_buffer::eth_buffer(size_t rx_bufsize) + : d_fd(0), d_using_tpring(false), d_buflen(0), d_buf(0), d_frame_nr(0), + d_frame_size(0), d_head(0), d_ring(0), d_ethernet(new ethernet()) + { + if (rx_bufsize == 0) + d_buflen = (size_t)MAX_MEM_SIZE; + else + d_buflen = std::min((size_t)MAX_MEM_SIZE, rx_bufsize); + + memset(d_mac, 0, sizeof(d_mac)); + } + + eth_buffer::~eth_buffer() + { + close(); + } + + bool + eth_buffer::open(const std::string &ifname, int protocol) + { + if (!d_ethernet->open(ifname, protocol)) { + std::cerr << "eth_buffer: unable to open interface " + << ifname << std::endl; + return false; + } + + d_fd = d_ethernet->fd(); + memcpy(d_mac, d_ethernet->mac(), sizeof(d_mac)); + + struct tpacket_req req; + size_t page_size = getpagesize(); + + // Calculate minimum power-of-two aligned size for frames + req.tp_frame_size = + (unsigned int)rint(pow(2, ceil(log2(TPACKET_ALIGN(TPACKET_HDRLEN)+TPACKET_ALIGN(MAX_PKT_SIZE))))); + d_frame_size = req.tp_frame_size; + + // Calculate minimum contiguous pages needed to enclose a frame + int npages = (page_size > req.tp_frame_size) ? 1 : ((req.tp_frame_size+page_size-1)/page_size); + req.tp_block_size = page_size << (int)ceil(log2(npages)); + + // Calculate number of blocks + req.tp_block_nr = std::min((int)(MAX_SLAB_SIZE/sizeof(void*)), + (int)(d_buflen/req.tp_block_size)); + + // Recalculate buffer length + d_buflen = req.tp_block_nr*req.tp_block_size; + + // Finally, calculate total number of frames. Since frames, blocks, + // and pages are all power-of-two aligned, frames are contiguous + req.tp_frame_nr = d_buflen/req.tp_frame_size; + d_frame_nr = req.tp_frame_nr; + +#if 0 + if (ETH_BUFFER_DEBUG) + std::cerr << "eth_buffer:" + << " frame_size=" << req.tp_frame_size + << " block_size=" << req.tp_block_size + << " block_nr=" << req.tp_block_nr + << " frame_nr=" << req.tp_frame_nr + << " buflen=" << d_buflen + << std::endl; +#endif + + // Try to get kernel shared memory buffer + if (setsockopt(d_fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req))) { + perror("eth_buffer: setsockopt"); + d_using_tpring = false; + if (!(d_buf = (uint8_t *)malloc(d_buflen))) { + std::cerr << "eth_buffer: failed to allocate packet memory" << std::endl; + return false; + } + + std::cerr << "eth_buffer: using malloc'd memory for buffer" << std::endl; + } + else { + d_using_tpring = true; + void *p = mmap(0, d_buflen, PROT_READ|PROT_WRITE, MAP_SHARED, d_fd, 0); + if (p == MAP_FAILED){ + perror("eth_buffer: mmap"); + return false; + } + d_buf = (uint8_t *) p; + + if (ETH_BUFFER_DEBUG) + std::cerr << "eth_buffer: using kernel shared mem for buffer" << std::endl; + } + + // Initialize our pointers into the packet ring + d_ring = std::vector<uint8_t *>(req.tp_frame_nr); + for (unsigned int i=0; i < req.tp_frame_nr; i++) { + d_ring[i] = (uint8_t *)(d_buf+i*req.tp_frame_size); + } + + // If not using kernel ring, instantiate select/read thread here + + return true; + } + + bool + eth_buffer::close() + { + // if we have background thread, stop it here + + if (!d_using_tpring && d_buf) + free(d_buf); + + return d_ethernet->close(); + } + + bool + eth_buffer::attach_pktfilter(pktfilter *pf) + { + return d_ethernet->attach_pktfilter(pf); + } + + inline bool + eth_buffer::frame_available() + { + return (((tpacket_hdr *)d_ring[d_head])->tp_status != TP_STATUS_KERNEL); + } + + eth_buffer::result + eth_buffer::rx_frames(data_handler *f, int timeout_in_ms) + { + DEBUG_LOG("\n"); + + while (!frame_available()) { + if (timeout_in_ms == 0) { + DEBUG_LOG("w"); + return EB_WOULD_BLOCK; + } + + struct pollfd pfd; + pfd.fd = d_fd; + pfd.revents = 0; + pfd.events = POLLIN; + + DEBUG_LOG("P"); + + int pres = poll(&pfd, 1, timeout_in_ms); + if (pres == -1) { + perror("poll"); + return EB_ERROR; + } + + if (pres == 0) { + DEBUG_LOG("t"); + return EB_TIMED_OUT; + } + } + + // Iterate through available packets + while (frame_available()) { + // Get start of ethernet frame and length + tpacket_hdr *hdr = (tpacket_hdr *)d_ring[d_head]; + void *base = (uint8_t *)hdr+hdr->tp_mac; + size_t len = hdr->tp_len; + + // FYI, (base % 4 == 2) Not what we want given the current FPGA + // code. This means that our uint32_t samples are not 4-byte + // aligned. We'll have to deal with it downstream. + + if (0) + fprintf(stderr, "eth_buffer: base = %p tp_mac = %3d tp_net = %3d\n", + base, hdr->tp_mac, hdr->tp_net); + + // Invoke data handler + data_handler::result r = (*f)(base, len); + if (!(r & data_handler::KEEP)) + hdr->tp_status = TP_STATUS_KERNEL; // mark it free + + inc_head(); + + if (r & data_handler::DONE) + break; + } + + DEBUG_LOG("|"); + return EB_OK; + } + + eth_buffer::result + eth_buffer::tx_frame(const void *base, size_t len, int flags) + { + DEBUG_LOG("T"); + + if (flags & EF_DONTWAIT) // FIXME: implement flags + throw std::runtime_error("tx_frame: EF_DONTWAIT not implemented"); + + int res = d_ethernet->write_packet(base, len); + if (res < 0 || (unsigned int)res != len) + return EB_ERROR; + + return EB_OK; + } + + eth_buffer::result + eth_buffer::tx_framev(const eth_iovec *iov, int iovcnt, int flags) + { + DEBUG_LOG("T"); + + if (flags & EF_DONTWAIT) // FIXME: implement flags + throw std::runtime_error("tx_frame: EF_DONTWAIT not implemented"); + + int res = d_ethernet->write_packetv(iov, iovcnt); + if (res < 0) + return EB_ERROR; + + return EB_OK; + } + + void + eth_buffer::release_frame(void *base) + { + // Get d_frame_size aligned header + tpacket_hdr *hdr = (tpacket_hdr *)((intptr_t)base & ~(d_frame_size-1)); + hdr->tp_status = TP_STATUS_KERNEL; // mark it free + } + +} // namespace usrp2 diff --git a/usrp2/host/lib/eth_buffer.h b/usrp2/host/lib/eth_buffer.h new file mode 100644 index 0000000000..8dee9a4a25 --- /dev/null +++ b/usrp2/host/lib/eth_buffer.h @@ -0,0 +1,198 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP2_ETH_BUFFER_H +#define INCLUDED_USRP2_ETH_BUFFER_H + +#include "pktfilter.h" +#include <eth_common.h> +#include <boost/utility.hpp> +#include <vector> +#include <memory> +#include <stdint.h> + +namespace usrp2 { + + class ethernet; + class data_handler; + + /*! + * \brief high-performance interface to send and receive raw + * ethernet frames with out-of-order retirement of received frames. + * + * On many systems it should be possible to implement this on top of libpcap + * + * \internal + */ + class eth_buffer : boost::noncopyable + { + + int d_fd; // socket file descriptor + uint8_t d_mac[6]; // our mac address + bool d_using_tpring; // using kernel mapped packet ring + size_t d_buflen; // length of our buffer + uint8_t *d_buf; // packet ring + unsigned int d_frame_nr; // max frames on ring + size_t d_frame_size; // frame storage size + unsigned int d_head; // pointer to next frame + + std::vector<uint8_t *> d_ring; // pointers into buffer + std::auto_ptr<ethernet> d_ethernet; // our underlying interface + + bool frame_available(); + + void inc_head() + { + if (d_head + 1 >= d_frame_nr) + d_head = 0; + else + d_head = d_head + 1; + } + + + public: + + enum result { + EB_OK, //< everything's fine + EB_ERROR, //< A non-recoverable error occurred + EB_WOULD_BLOCK, //< A timeout of 0 was specified and nothing was ready + EB_TIMED_OUT, //< The timeout expired before anything was ready + }; + + static const unsigned int MAX_PKTLEN = 1512; + static const unsigned int MIN_PKTLEN = 64; + + /*! + * \param rx_bufsize is a hint as to the number of bytes of memory + * to allocate for received ethernet frames (0 -> reasonable default) + */ + eth_buffer(size_t rx_bufsize = 0); + ~eth_buffer(); + + /*! + * \brief open the specified interface + * + * \param ifname ethernet interface name, e.g., "eth0" + * \param protocol is the ethertype protocol number in network order. + * Use 0 to receive all protocols. + */ + bool open(const std::string &ifname, int protocol); + + /*! + * \brief close the interface + */ + bool close(); + + /*! + * \brief attach packet filter to socket to restrict which packets read sees. + * \param pf the packet filter + */ + bool attach_pktfilter(pktfilter *pf); + + /*! + * \brief return 6 byte string containing our MAC address + */ + const uint8_t *mac() const { return d_mac; } + + /*! + * \brief Call \p f for each frame in the receive buffer. + * \param f is the frame data handler + * \param timeout (in ms) controls behavior when there are no frames to read + * + * If \p timeout is 0, rx_frames will not wait for frames if none are + * available, and f will not be invoked. If \p timeout is -1 (the + * default), rx_frames will block indefinitely until frames are + * available. If \p timeout is positive, it indicates the number of + * milliseconds to wait for a frame to become available. Once the + * timeout has expired, rx_frames will return, f never having been + * invoked. + * + * \p f will be called on each ethernet frame that is available. + * \p f returns a bit mask with one of the following set or cleared: + * + * data_handler::KEEP - hold onto the frame and present it again during + * the next call to rx_frames, otherwise discard it + * + * data_handler::DONE - return from rx_frames now even though more frames + * might be available; otherwise continue if more + * frames are ready. + * + * The idea of holding onto a frame for the next iteration allows + * the caller to scan the received packet stream for particular + * classes of frames (such as command replies) leaving the rest + * intact. On the next call all kept frames, followed by any new + * frames received, will be presented in order to \p f. + * See usrp2.cc for an example of the pattern. + * + * \returns EB_OK if at least one frame was received + * \returns EB_WOULD_BLOCK if \p timeout is 0 and the call would have blocked + * \returns EB_TIMED_OUT if timeout occurred + * \returns EB_ERROR if there was an unrecoverable error. + */ + result rx_frames(data_handler *f, int timeout=-1); + + /* + * \brief Release frame from buffer + * + * Call to release a frame previously held by a data_handler::KEEP. + * The pointer may be offset from the base of the frame up to its length. + */ + void release_frame(void *p); + + /* + * \brief Write an ethernet frame to the interface. + * + * \param base points to the beginning of the frame (the 14-byte ethernet header). + * \param len is the length of the frame in bytes. + * \param flags is 0 or the bitwise-or of values from eth_flags + * + * The frame must begin with a 14-byte ethernet header. + * + * \returns EB_OK if the frame was successfully enqueued. + * \returns EB_WOULD_BLOCK if flags contains EF_DONT_WAIT and the call would have blocked. + * \returns EB_ERROR if there was an unrecoverable error. + */ + result tx_frame(const void *base, size_t len, int flags=0); + + /* + * \brief Write an ethernet frame to the interface using scatter/gather. + * + * \param iov points to an array of iovec structs + * \param iovcnt is the number of entries + * \param flags is 0 or the bitwise-or of values from eth_flags + * + * The frame must begin with a 14-byte ethernet header. + * + * \returns EB_OK if the frame was successfully enqueued. + * \returns EB_WOULD_BLOCK if flags contains EF_DONT_WAIT and the call would have blocked. + * \returns EB_ERROR if there was an unrecoverable error. + */ + result tx_framev(const eth_iovec *iov, int iovcnt, int flags=0); + + /* + * \brief Returns maximum possible number of frames in buffer + */ + unsigned int max_frames() const { return d_frame_nr; } + + }; + +}; // namespace usrp2 + +#endif /* INCLUDED_USRP2_ETH_BUFFER_H */ diff --git a/usrp2/host/lib/eth_common.h b/usrp2/host/lib/eth_common.h new file mode 100644 index 0000000000..7c9feaa25b --- /dev/null +++ b/usrp2/host/lib/eth_common.h @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_USRP2_ETH_COMMON_H +#define INCLUDED_USRP2_ETH_COMMON_H + +#include <sys/uio.h> // FIXME autoconf this + +namespace usrp2 { + + enum eth_flags { + EF_DONTWAIT = 0x0001, + }; + + typedef struct iovec eth_iovec; // FIXME autoconf this + +} // namespace usrp2 + + +#endif /* INCLUDED_USRP2_ETH_COMMON_H */ diff --git a/usrp2/host/lib/ethernet.cc b/usrp2/host/lib/ethernet.cc new file mode 100644 index 0000000000..65e989cec1 --- /dev/null +++ b/usrp2/host/lib/ethernet.cc @@ -0,0 +1,228 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ethernet.h" +#include "pktfilter.h" +#include <open_usrp2_socket.h> + +#include <iostream> +#include <unistd.h> +#include <fcntl.h> +//#include <features.h> +#include <errno.h> +#include <string.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/ethernet.h> +#include <netinet/in.h> + +#include <linux/types.h> +#include <netpacket/packet.h> +#include <linux/filter.h> // packet filter + +namespace usrp2 { + + static int + open_packet_socket (std::string ifname, int protocol) + { +#if 0 + if (protocol == 0) + protocol = htons(ETH_P_ALL); + + int fd = socket (PF_PACKET, SOCK_RAW, protocol); +#else + int fd = usrp2::open_usrp2_socket(); +#endif + + if (fd == -1){ + fprintf (stderr, "%s: socket: %s\n", ifname.c_str(), strerror (errno)); + return -1; + } + + // get interface index + struct ifreq ifr; + memset (&ifr, 0, sizeof(ifr)); + strncpy (ifr.ifr_name, ifname.c_str(), sizeof (ifr.ifr_name)); + int res = ioctl (fd, SIOCGIFINDEX, &ifr); + if (res != 0){ + ::close (fd); + fprintf (stderr, "%s: SIOCGIFINDEX: %s\n", ifname.c_str(), strerror(errno)); + return -1; + } + int ifindex = ifr.ifr_ifindex; + + // bind to the specified interface + sockaddr_ll sa; + memset (&sa, 0, sizeof (sa)); + sa.sll_family = AF_PACKET; + sa.sll_protocol = protocol; + sa.sll_ifindex = ifindex; + res = bind (fd, (struct sockaddr *)&sa, sizeof (sa)); + if (res != 0){ + ::close (fd); + fprintf (stderr, "%s: bind: %s\n", ifname.c_str(), strerror(errno)); + return -1; + } + return fd; + } + + static void + extract_mac_addr (unsigned char *mac, const unsigned char *hwaddr) + { + int i; + for (i = 0; i < 6; i++) + mac[i] = 0xff; + + i = 0; + for (int j = 0; j < 14; j++){ + if (hwaddr[j] != 0xff){ + mac[i++] = hwaddr[j]; + if (i == 6) + return; + } + } + } + + static bool + get_mac_addr (std::string ifname, int fd, unsigned char *mac) + { + struct ifreq ifr; + memset (&ifr, 0, sizeof(ifr)); + strncpy (ifr.ifr_name, ifname.c_str(), sizeof (ifr.ifr_name)); + int res = ioctl (fd, SIOCGIFHWADDR, &ifr); + if (res != 0){ + fprintf (stderr, "%s: SIOCGIFHWADDR: %s\n", ifname.c_str(), strerror(errno)); + return false; + } + else { + if (0){ + for (unsigned i = 0; i < sizeof (ifr.ifr_hwaddr.sa_data); i++) + fprintf (stderr, "%02x", ifr.ifr_hwaddr.sa_data[i]); + fprintf (stderr, "\n"); + } + } + extract_mac_addr (mac, (unsigned char *)ifr.ifr_hwaddr.sa_data); + return true; + } + + ethernet::ethernet () + { + d_fd = -1; + memset (d_mac, 0, sizeof (d_mac)); + } + + ethernet::~ethernet () + { + close (); + } + + bool + ethernet::open (std::string ifname, int protocol) + { + if (d_fd != -1){ + fprintf (stderr, "ethernet: already open\n"); + return false; + } + if ((d_fd = open_packet_socket (ifname, protocol)) == -1){ + return false; + } + get_mac_addr (ifname, d_fd, d_mac); + return true; + } + + bool + ethernet::close () + { + if (d_fd >= 0){ + ::close (d_fd); + d_fd = -1; + } + return true; + } + + int + ethernet::read_packet (void *buf, int buflen) + { + int len = recvfrom (d_fd, buf, buflen, 0, (sockaddr *) 0, 0); + return len; + } + + int + ethernet::read_packet_dont_block (void *buf, int buflen) + { + int len = recvfrom (d_fd, buf, buflen, MSG_DONTWAIT, 0, 0); + if (len == -1 && errno == EAGAIN) + return 0; + + return len; + } + + int + ethernet::write_packet (const void *buf, int buflen) + { + int retval = send (d_fd, buf, buflen, 0); + if (retval < 0){ + if (errno == EINTR) + return write_packet (buf, buflen); + + perror ("ethernet:write_packet: send"); + return -1; + } + return retval; + } + + int + ethernet::write_packetv(const eth_iovec *iov, size_t iovlen) + { + struct msghdr mh; + memset(&mh, 0, sizeof(mh)); + mh.msg_iov = const_cast<eth_iovec*>(iov); + mh.msg_iovlen = iovlen; + + int retval = sendmsg(d_fd, &mh, 0); + if (retval < 0){ + if (errno == EINTR) + return write_packetv(iov, iovlen); + + perror("ethernet:write_packetv: send"); + return -1; + } + return retval; + } + + bool + ethernet::attach_pktfilter (pktfilter *pf) + { + struct sock_fprog filter; + filter.len = pf->d_len; + filter.filter = pf->d_inst; + + int r = setsockopt (d_fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof (filter)); + if (r < 0){ + perror ("ethernet:attach: SO_ATTACH_FILTER"); + return false; + } + return true; + } + +} // namespace usrp2 diff --git a/usrp2/host/lib/ethernet.h b/usrp2/host/lib/ethernet.h new file mode 100644 index 0000000000..24624f441a --- /dev/null +++ b/usrp2/host/lib/ethernet.h @@ -0,0 +1,124 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_ETHERNET_H +#define INCLUDED_USRP2_ETHERNET_H + +#include <string> +#include <vector> +#include <eth_common.h> + +namespace usrp2 { + + class pktfilter; + + /*! + * \brief Read and write ethernet frames. + * + * This provides a low level interface to hardware that communicates + * via raw (non-IP) ethernet frames. + */ + class ethernet { + int d_fd; + uint8_t d_mac[6]; + + public: + ethernet (); + ~ethernet (); + + static const int MAX_PKTLEN = 1512; + static const int MIN_PKTLEN = 64; + + /*! + * \param ifname ethernet interface name, e.g., "eth0" + * \param protocol is the ethertype protocol number in network order. + * Use 0 to receive all protocols. + */ + bool open (std::string ifname, int protocol); + + bool close (); + + /*! + * \brief attach packet filter to socket to restrict which packets read sees. + * \param pf the packet filter + */ + bool attach_pktfilter (pktfilter *pf); + + /*! + * \brief return 6 byte string containing our MAC address + */ + const uint8_t *mac () const { return d_mac; } + + /*! + * \brief Return file descriptor associated with socket. + */ + int fd () const { return d_fd; } + + /*! + * \brief Read packet from interface. + * + * \param buf where to put the packet + * \param buflen maximum length of packet in bytes (should be >= 1528) + * + * \returns number of bytes read or -1 if trouble. + * + * Returned packet includes 14-byte ethhdr + */ + int read_packet (void *buf, int buflen); + + /*! + * \brief Read packet from interface, but don't block waiting + * + * \param buf where to put the packet + * \param buflen maximum length of packet in bytes (should be >= 1528) + * + * \returns number of bytes read, -1 if trouble or 0 if nothing available. + * + * Returned packet includes 14-byte ethhdr + */ + int read_packet_dont_block (void *buf, int buflen); + + /* + * \brief Write ethernet packet to interface. + * + * \param buf the packet to write + * \param buflen length of packet in bytes + * + * \returns number of bytes written or -1 if trouble. + * + * Packet must begin with 14-byte ethhdr, but does not include the FCS. + */ + int write_packet (const void *buf, int buflen); + + /* + * \brief Write ethernet packet to interface. + * + * \param iov scatter/gather array + * \param iovlen number of elements in iov + * + * \returns number of bytes written or -1 if trouble. + * + * Packet must begin with 14-byte ethhdr, but does not include the FCS. + */ + int write_packetv (const eth_iovec *iov, size_t iovlen); + + }; + +} // namespace usrp2 + +#endif /* INCLUDED_USRP2_ETHERNET_H */ diff --git a/usrp2/host/lib/find.cc b/usrp2/host/lib/find.cc new file mode 100644 index 0000000000..f6c04a58e4 --- /dev/null +++ b/usrp2/host/lib/find.cc @@ -0,0 +1,181 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2_eth_packet.h> +#include <usrp2/usrp2.h> +#include <boost/scoped_ptr.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> +#include "ethernet.h" +#include "pktfilter.h" +#include <string.h> +#include <iostream> +#include <stdexcept> + +#define FIND_DEBUG 0 + + +// FIXME move to gruel + +static struct timeval +time_duration_to_timeval(boost::posix_time::time_duration delta) +{ + long total_us = delta.total_microseconds(); + if (total_us < 0) + throw std::invalid_argument("duration_to_time: delta is negative"); + + struct timeval tv; + tv.tv_sec = total_us / 1000000; + tv.tv_usec = total_us % 1000000; + return tv; +} + + +namespace usrp2 { + + static props + reply_to_props(const op_id_reply_t *r) + { + const uint8_t *mac = (const uint8_t *)&r->addr; + char addr_buf[128]; + snprintf(addr_buf, sizeof(addr_buf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + props p; + p.addr = std::string(addr_buf); + p.hw_rev = ntohs(r->hw_rev); + memcpy(p.fpga_md5sum, r->fpga_md5sum, sizeof(p.fpga_md5sum)); + memcpy(p.sw_md5sum, r->sw_md5sum, sizeof(p.sw_md5sum)); + return p; + } + + static void + read_replies(ethernet *enet, struct timeval timeout, + const std::string &target_addr, props_vector_t &result) + { + struct reply { + u2_eth_packet_t h; + op_id_reply_t op_id_reply; + }; + + uint8_t pktbuf[ethernet::MAX_PKTLEN]; + memset(pktbuf, 0, sizeof(pktbuf)); + + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(enet->fd(), &read_fds); + + select(enet->fd()+1, &read_fds, 0, 0, &timeout); + while(1) { + memset(pktbuf, 0, sizeof(pktbuf)); + int len = enet->read_packet_dont_block(pktbuf, sizeof(pktbuf)); + if (len < 0){ + perror("usrp2_basic: read_packet_dont_block"); + return; + } + if (len == 0) + break; + + reply *rp = (reply *)pktbuf; + if (u2p_chan(&rp->h.fixed) != CONTROL_CHAN) // ignore + continue; + if (rp->op_id_reply.opcode != OP_ID_REPLY) // ignore + continue; + + props p = reply_to_props(&rp->op_id_reply); + if (FIND_DEBUG) + std::cerr << "usrp2::find: response from " << p.addr << std::endl; + + if ((target_addr == "") || (target_addr == p.addr)) + result.push_back(p); + } + } + + props_vector_t + find(const std::string &ifc, const std::string &addr) + { + if (FIND_DEBUG) { + std::cerr << "usrp2::find: Searching interface " << ifc << " for " + << (addr == "" ? "all USRP2s" : addr) + << std::endl; + } + + props_vector_t result; + struct command { + u2_eth_packet_t h; + op_generic_t op_id; + }; + + std::auto_ptr<ethernet> enet(new ethernet()); + + if (!enet->open(ifc, htons(U2_ETHERTYPE))) + return result; + + std::auto_ptr<pktfilter> pf(pktfilter::make_ethertype_inbound(U2_ETHERTYPE, enet->mac())); + if (!enet->attach_pktfilter(pf.get())) + return result; + + static u2_mac_addr_t broadcast_mac_addr = + {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; + + uint8_t pktbuf[ethernet::MAX_PKTLEN]; + memset(pktbuf, 0, sizeof(pktbuf)); + + command *c = (command *)pktbuf; + c->h.ehdr.ethertype = htons(U2_ETHERTYPE); + c->h.ehdr.dst = broadcast_mac_addr; + memcpy(&c->h.ehdr.src, enet->mac(), 6); + c->h.thdr.flags = 0; + c->h.thdr.seqno = 0; + c->h.thdr.ack = 0; + u2p_set_word0(&c->h.fixed, 0, CONTROL_CHAN); + u2p_set_timestamp(&c->h.fixed, -1); + c->op_id.opcode = OP_ID; + c->op_id.len = sizeof(c->op_id); + int len = std::max((size_t) ethernet::MIN_PKTLEN, sizeof(command)); + if (enet->write_packet(c, len) != len) + return result; + + if (FIND_DEBUG) + std::cerr << "usrp2::find: broadcast ID command" << std::endl; + + /* + * Gather all responses that occur within 50ms + */ + boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time()); + boost::posix_time::ptime limit(start + boost::posix_time::milliseconds(50)); + boost::posix_time::ptime now; + + while (1){ + now = boost::posix_time::microsec_clock::universal_time(); + if (now >= limit) + break; + + boost::posix_time::time_duration delta(limit - now); + struct timeval timeout = time_duration_to_timeval(delta); + + read_replies(enet.get(), timeout, addr, result); + } + return result; + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/open_usrp2_socket.cc b/usrp2/host/lib/open_usrp2_socket.cc new file mode 100644 index 0000000000..cd729205da --- /dev/null +++ b/usrp2/host/lib/open_usrp2_socket.cc @@ -0,0 +1,130 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <open_usrp2_socket.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <stdio.h> +#include <errno.h> +#include <string> + +static const char *helper = "usrp2_socket_opener"; + +static ssize_t +read_fd(int fd, void *ptr, size_t nbytes, int *recvfd) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t n; + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof (int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); +#else + int newfd; + + msg.msg_accrights = (char *) &newfd; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if ((n = recvmsg(fd, &msg, 0)) <= 0) + return n; + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL + && cmptr->cmsg_len == CMSG_LEN(sizeof(int))){ + if (cmptr->cmsg_level != SOL_SOCKET){ + fprintf(stderr, "read_fd: control level != SOL_SOCKET\n"); + return -1; + } + if (cmptr->cmsg_type != SCM_RIGHTS){ + fprintf(stderr, "read_fd: control type != SCM_RIGHTS\n"); + return -1; + } + *recvfd = *((int *) CMSG_DATA(cmptr)); + } else + *recvfd = -1; /* descriptor was not passed */ +#else + if (msg.msg_accrightslen == sizeof(int)) + *recvfd = newfd; + else + *recvfd = -1; /* descriptor was not passed */ +#endif + + return n; +} + +int +usrp2::open_usrp2_socket() +{ + int fd = -1, sockfd[2], status; + pid_t childpid; + char c, argsockfd[10]; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) != 0){ + perror("socketpair"); + return -1; + } + + if ((childpid = fork()) == 0) { /* child process */ + close(sockfd[0]); + snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]); + execlp(helper, helper, argsockfd, (char *) NULL); + std::string msg("execlp: couldn't exec " + std::string(helper)); + perror(msg.c_str()); + close(sockfd[0]); + close(sockfd[1]); + return -1; + } + + /* parent process - wait for the child to terminate */ + close(sockfd[1]); /* close the end we don't use */ + + waitpid(childpid, &status, 0); + if (!WIFEXITED(status)){ + fprintf(stderr, "child did not terminate\n"); + return -1; + } + if ((status = WEXITSTATUS(status)) == 0) + read_fd(sockfd[0], &c, 1, &fd); + else { + errno = status; /* bogus: set errno value from child's status */ + fd = -1; + } + + close(sockfd[0]); + return (fd); +} diff --git a/usrp2/host/lib/open_usrp2_socket.h b/usrp2/host/lib/open_usrp2_socket.h new file mode 100644 index 0000000000..d42440ea0a --- /dev/null +++ b/usrp2/host/lib/open_usrp2_socket.h @@ -0,0 +1,34 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_OPEN_USRP2_SOCKET_H +#define INCLUDED_OPEN_USRP2_SOCKET_H + +namespace usrp2 { + + /*! + * Return the result of executing: + * + * int fd = socket(PF_PACKET, SOCK_RAW, htons(0xBEEF)); + * + * Doing it in a way that we don't need to be running as root. + */ + int open_usrp2_socket(); +}; + + +#endif /* INCLUDED_OPEN_USRP2_SOCKET_H */ diff --git a/usrp2/host/lib/pktfilter.cc b/usrp2/host/lib/pktfilter.cc new file mode 100644 index 0000000000..8341d015b9 --- /dev/null +++ b/usrp2/host/lib/pktfilter.cc @@ -0,0 +1,152 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "pktfilter.h" +#include <iostream> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <features.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> +#include <netinet/in.h> +#include <assert.h> +#include <linux/types.h> +#include <linux/filter.h> // packet filter + +namespace usrp2 { + + /* + * This is all based on the Berkeley Packet Filter (BPF) as implemented on Linux. + * + * The BPF allows you to run an interpreted program (a filter) in the + * kernel that sorts through the packets looking for ones you are + * interested in. This eliminates the overhead of passing all of the + * networking packets up into user space for filtering there. + * + * For documentation on this see + * /usr/src/linux/Documentation/networking/filter.txt, The BSD + * Berkeley Packet Filter manual page, and "The BSD Packet Filter: A + * New Architecture for User-level Packet Capture", by Steven McCanne + * and Van Jacobson. + */ + + pktfilter::pktfilter () + : d_len (0), d_inst (0) + { + // NOP + } + + pktfilter::~pktfilter () + { + delete [] d_inst; + } + + inline static sock_filter + make_stmt (__u16 code, __u32 k) + { + sock_filter f; + f.code = code; + f.jt = 0; + f.jf = 0; + f.k = k; + return f; + } + + inline static sock_filter + make_jump (__u16 code, __u32 k, __u8 jt, __u8 jf) + { + sock_filter f; + f.code = code; + f.jt = jt; + f.jf = jf; + f.k = k; + return f; + } + + /* + * Return a filter that harvests packets with the specified ethertype. + */ + pktfilter * + pktfilter::make_ethertype (unsigned short ethertype) + { + static const int MAX_LEN = 20; + sock_filter *inst = new sock_filter [MAX_LEN]; + pktfilter *pf = new pktfilter (); + + // nothing quite like coding in assembly without the benefit of an assembler ;-) + + // ignore packets that don't have the right ethertype + + int i = 0; + inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 12); // load ethertype + inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, ethertype, 1, 0); + inst[i++] = make_stmt (BPF_RET|BPF_K, 0); // return 0 (ignore packet) + inst[i++] = make_stmt (BPF_RET|BPF_K, (unsigned) -1); // return whole packet + + assert (i <= MAX_LEN); + + pf->d_inst = inst; + pf->d_len = i; + + return pf; + } + + /* + * Return a filter that harvests inbound packets with the specified ethertype. + * \param ethertype the ethertype we're looking for + * \param our_mac our ethernet MAC address so we can avoid pkts we sent + */ + pktfilter * + pktfilter::make_ethertype_inbound (unsigned short ethertype, const unsigned char *our_mac) + { + static const int MAX_LEN = 20; + sock_filter *inst = new sock_filter [MAX_LEN]; + pktfilter *pf = new pktfilter (); + + __u16 smac_hi = (our_mac[0] << 8) | our_mac[1]; + __u32 smac_lo = (our_mac[2] << 24) | (our_mac[3] << 16) | (our_mac[4] << 8) | our_mac[5]; + + // nothing quite like coding in assembly without the benefit of an assembler ;-) + + // ignore packets that have a different ethertype + // and packets that have a source mac address == our_mac (packets we sent) + + int i = 0; + inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 12); // load ethertype + inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, ethertype, 0, 5); + inst[i++] = make_stmt (BPF_LD|BPF_W|BPF_ABS, 8); // load low 32-bit of src mac + inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, smac_lo, 0, 2); + inst[i++] = make_stmt (BPF_LD|BPF_H|BPF_ABS, 6); // load high 16-bits of src mac + inst[i++] = make_jump (BPF_JMP|BPF_JEQ|BPF_K, smac_hi, 1, 0); + inst[i++] = make_stmt (BPF_RET|BPF_K, (unsigned) -1); // return whole packet + inst[i++] = make_stmt (BPF_RET|BPF_K, 0); // return 0 (ignore packet) + + assert (i <= MAX_LEN); + + pf->d_inst = inst; + pf->d_len = i; + + return pf; + } + +} // namespace usrp2 diff --git a/usrp2/host/lib/pktfilter.h b/usrp2/host/lib/pktfilter.h new file mode 100644 index 0000000000..8b07fe1488 --- /dev/null +++ b/usrp2/host/lib/pktfilter.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005,2007,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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_PKTFILTER_H +#define INCLUDED_USRP2_PKTFILTER_H + +struct sock_filter; + +namespace usrp2 { + + /* + * \brief Compile programs for the Berkeley Packet Filter + */ + class pktfilter { + public: + + unsigned d_len; // # of instructions + struct sock_filter *d_inst; // the instructions + + pktfilter (); + ~pktfilter (); + + /*! + * \brief Return a filter that harvests packets with the specified ethertype. + * \param ethertype the ethertype we're looking for. + */ + static pktfilter *make_ethertype (unsigned short ethertype); + + /*! + * \brief Return a filter that harvests inbound packets with the specified ethertype. + * \param ethertype the ethertype we're looking for + * \param our_mac our MAC address so we can avoid pkts we sent + */ + static pktfilter *make_ethertype_inbound (unsigned short ethertype, + const unsigned char *our_mac); + }; + +} // namespace usrp2 + +#endif /* INCLUDED_USRP2_PKTFILTER_H */ diff --git a/usrp2/host/lib/ring.cc b/usrp2/host/lib/ring.cc new file mode 100644 index 0000000000..3c45821f84 --- /dev/null +++ b/usrp2/host/lib/ring.cc @@ -0,0 +1,78 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "ring.h" + +namespace usrp2 { + + ring::ring(unsigned int entries) + : d_max(entries), d_read_ind(0), d_write_ind(0), d_ring(entries), + d_mutex(), d_not_empty(&d_mutex) + { + for (unsigned int i = 0; i < entries; i++) { + d_ring[i].d_base = 0; + d_ring[i].d_len = 0; + } + } + + void + ring::wait_for_not_empty() + { + omni_mutex_lock l(d_mutex); + while (empty()) + d_not_empty.wait(); + } + + bool + ring::enqueue(void *p, size_t len) + { + omni_mutex_lock l(d_mutex); + if (full()) + return false; + + d_ring[d_write_ind].d_len = len; + d_ring[d_write_ind].d_base = p; + + inc_write_ind(); + d_not_empty.signal(); + return true; + } + + bool + ring::dequeue(void **p, size_t *len) + { + omni_mutex_lock l(d_mutex); + if (empty()) + return false; + + *p = d_ring[d_read_ind].d_base; + *len = d_ring[d_read_ind].d_len; + + inc_read_ind(); + return true; + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/ring.h b/usrp2/host/lib/ring.h new file mode 100644 index 0000000000..2f4346ca61 --- /dev/null +++ b/usrp2/host/lib/ring.h @@ -0,0 +1,83 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_RING_H +#define INCLUDED_RING_H + +#include <omnithread.h> +#include <stddef.h> +#include <vector> +#include <boost/shared_ptr.hpp> + +namespace usrp2 { + + class ring; + typedef boost::shared_ptr<ring> ring_sptr; + + class ring + { + private: + + size_t d_max; + size_t d_read_ind; + size_t d_write_ind; + + struct ring_desc + { + void *d_base; + size_t d_len; + }; + std::vector<ring_desc> d_ring; + + omni_mutex d_mutex; + omni_condition d_not_empty; + + void inc_read_ind() + { + if (d_read_ind + 1 >= d_max) + d_read_ind = 0; + else + d_read_ind = d_read_ind + 1; + } + + void inc_write_ind() + { + if (d_write_ind + 1 >= d_max) + d_write_ind = 0; + else + d_write_ind = d_write_ind + 1; + } + + bool empty() const { return d_read_ind == d_write_ind; } + bool full() const { return (d_write_ind+1)%d_max == d_read_ind; } + + public: + + ring(unsigned int entries); + + void wait_for_not_empty(); + + bool enqueue(void *p, size_t len); + bool dequeue(void **p, size_t *len); + }; + +} // namespace usrp2 + +#endif /* INCLUDED_RING_H */ diff --git a/usrp2/host/lib/rx_nop_handler.cc b/usrp2/host/lib/rx_nop_handler.cc new file mode 100644 index 0000000000..96c9164a52 --- /dev/null +++ b/usrp2/host/lib/rx_nop_handler.cc @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/rx_nop_handler.h> + +namespace usrp2 { + + rx_nop_handler::~rx_nop_handler() + { + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/rx_sample_handler.cc b/usrp2/host/lib/rx_sample_handler.cc new file mode 100644 index 0000000000..4521025cb6 --- /dev/null +++ b/usrp2/host/lib/rx_sample_handler.cc @@ -0,0 +1,27 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <usrp2/rx_sample_handler.h> + +usrp2::rx_sample_handler::~rx_sample_handler() +{ + // nop +} diff --git a/usrp2/host/lib/strtod_si.c b/usrp2/host/lib/strtod_si.c new file mode 100644 index 0000000000..5e5deb2b6d --- /dev/null +++ b/usrp2/host/lib/strtod_si.c @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 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 <http://www.gnu.org/licenses/>. + */ + +#include <usrp2/strtod_si.h> +#include <stdlib.h> + +#define true 1 +#define false 0 + +int +strtod_si(const char *s, double *result) +{ + *result = 0; + + char *endptr; + double r = strtod(s, &endptr); + if (s == endptr) + return false; + + switch (*endptr){ + case 'p': r *= 1e-12; break; + case 'n': r *= 1e-9; break; + case 'u': r *= 1e-6; break; + case 'm': r *= 1e-3; break; + case 'k': r *= 1e3; break; + case 'M': r *= 1e6; break; + case 'G': r *= 1e9; break; + case 'T': r *= 1e12; break; + default: + // ignore. FIXME could be more robust + break; + } + + *result = r; + return true; +} + + diff --git a/usrp2/host/lib/usrp2.cc b/usrp2/host/lib/usrp2.cc new file mode 100644 index 0000000000..c71fb728b5 --- /dev/null +++ b/usrp2/host/lib/usrp2.cc @@ -0,0 +1,291 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include "usrp2_impl.h" +#include <vector> +#include <boost/thread.hpp> +#include <boost/weak_ptr.hpp> +#include <string> +#include <stdexcept> + +namespace usrp2 { + + // --- Table of weak pointers to usrps we know about --- + + // (Could be cleaned up and turned into a template) + + struct usrp_table_entry { + // inteface + normalized mac addr ("eth0:01:23:45:67:89:ab") + std::string key; + boost::weak_ptr<usrp2::usrp2> value; + + usrp_table_entry(const std::string &_key, boost::weak_ptr<usrp2::usrp2> _value) + : key(_key), value(_value) {} + }; + + typedef std::vector<usrp_table_entry> usrp_table; + + static boost::mutex s_table_mutex; + static usrp_table s_table; + + usrp2::sptr + usrp2::find_existing_or_make_new(const std::string &ifc, props *pr) + { + std::string key = ifc + ":" + pr->addr; + + boost::mutex::scoped_lock guard(s_table_mutex); + + for (usrp_table::iterator p = s_table.begin(); p != s_table.end();){ + if (p->value.expired()) // weak pointer is now dead + p = s_table.erase(p); // erase it + else { + if (key == p->key) // found it + return usrp2::sptr(p->value); + else + ++p; // keep looking + } + } + + // We don't have the USRP2 we're looking for + + // create a new one and stick it in the table. + usrp2::sptr r(new usrp2::usrp2(ifc, pr)); + usrp_table_entry t(key, r); + s_table.push_back(t); + + return r; + } + + // --- end of table code --- + + static bool + parse_mac_addr(const std::string &s, std::string &ns) + { + 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: + if (sscanf(s.c_str(), "%hhx:%hhx", &p.addr[4], &p.addr[5]) != 2) + return false; + break; + + case 17: + if (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) + return false; + break; + + default: + return false; + } + + char buf[128]; + snprintf(buf, sizeof(buf), + "%02x:%02x:%02x:%02x:%02x:%02x", + p.addr[0],p.addr[1],p.addr[2], + p.addr[3],p.addr[4],p.addr[5]); + ns = std::string(buf); + return true; + } + + usrp2::sptr + usrp2::make(const std::string &ifc, const std::string &addr) + { + std::string naddr = ""; + if (addr != "" && !parse_mac_addr(addr, naddr)) + throw std::runtime_error("Invalid MAC address"); + + props_vector_t u2s = find(ifc, naddr); + int n = u2s.size(); + + if (n == 0) { + if (addr == "") + throw std::runtime_error("No USRPs found on interface " + ifc); + else + throw std::runtime_error("No USRP found with addr " + addr + " on interface " + ifc); + } + + if (n > 1) + throw std::runtime_error("Multiple USRPs found on interface; must select by MAC address."); + + return find_existing_or_make_new(ifc, &u2s[0]); + } + + // Private constructor. Sole function is to create an impl. + usrp2::usrp2(const std::string &ifc, props *p) + : d_impl(new usrp2::impl(ifc, p)) + { + // NOP + } + + // Public class destructor. d_impl will auto-delete. + usrp2::~usrp2() + { + // NOP + } + + std::string + usrp2::mac_addr() + { + return d_impl->mac_addr(); + } + + bool + usrp2::burn_mac_addr(const std::string &new_addr) + { + return d_impl->burn_mac_addr(new_addr); + } + + + // Receive + + bool + usrp2::set_rx_gain(double gain) + { + return d_impl->set_rx_gain(gain); + } + + bool + usrp2::set_rx_center_freq(double frequency, tune_result *result) + { + return d_impl->set_rx_center_freq(frequency, result); + } + + bool + usrp2::set_rx_decim(int decimation_factor) + { + return d_impl->set_rx_decim(decimation_factor); + } + + bool + usrp2::set_rx_scale_iq(int scale_i, int scale_q) + { + return d_impl->set_rx_scale_iq(scale_i, scale_q); + } + + bool + usrp2::start_rx_streaming(unsigned int channel, unsigned int items_per_frame) + { + return d_impl->start_rx_streaming(channel, items_per_frame); + } + + bool + usrp2::rx_samples(unsigned int channel, rx_sample_handler *handler) + { + return d_impl->rx_samples(channel, handler); + } + + bool + usrp2::stop_rx_streaming(unsigned int channel) + { + return d_impl->stop_rx_streaming(channel); + } + + unsigned int + usrp2::rx_overruns() + { + return d_impl->rx_overruns(); + } + + unsigned int + usrp2::rx_missing() + { + return d_impl->rx_missing(); + } + + // Transmit + + bool + usrp2::set_tx_gain(double gain) + { + return d_impl->set_tx_gain(gain); + } + + bool + usrp2::set_tx_center_freq(double frequency, tune_result *result) + { + return d_impl->set_tx_center_freq(frequency, result); + } + + bool + usrp2::set_tx_interp(int interpolation_factor) + { + return d_impl->set_tx_interp(interpolation_factor); + } + + bool + usrp2::set_tx_scale_iq(int scale_i, int scale_q) + { + return d_impl->set_tx_scale_iq(scale_i, scale_q); + } + + bool + usrp2::tx_complex_float(unsigned int channel, + const std::complex<float> *samples, + size_t nsamples, + const tx_metadata *metadata) + { + return d_impl->tx_complex_float(channel, samples, nsamples, metadata); + } + + bool + usrp2::tx_complex_int16(unsigned int channel, + const std::complex<int16_t> *samples, + size_t nsamples, + const tx_metadata *metadata) + { + return d_impl->tx_complex_int16(channel, samples, nsamples, metadata); + } + + bool + usrp2::tx_raw(unsigned int channel, + const uint32_t *items, + size_t nitems, + const tx_metadata *metadata) + { + return d_impl->tx_raw(channel, items, nitems, metadata); + } + +} // namespace usrp2 + + +std::ostream& operator<<(std::ostream &os, const usrp2::props &x) +{ + os << x.addr; + + char buf[128]; + snprintf(buf, sizeof(buf)," hw_rev = 0x%04x", x.hw_rev); + + os << buf; + return os; +} diff --git a/usrp2/host/lib/usrp2_bytesex.h b/usrp2/host/lib/usrp2_bytesex.h new file mode 100644 index 0000000000..4f63d07938 --- /dev/null +++ b/usrp2/host/lib/usrp2_bytesex.h @@ -0,0 +1,19 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#include <gruel/inet.h> diff --git a/usrp2/host/lib/usrp2_impl.cc b/usrp2/host/lib/usrp2_impl.cc new file mode 100644 index 0000000000..68cf676c6b --- /dev/null +++ b/usrp2/host/lib/usrp2_impl.cc @@ -0,0 +1,870 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <usrp2/usrp2.h> +#include <usrp2/tune_result.h> +#include <usrp2/copiers.h> +#include <gruel/inet.h> +#include <usrp2_types.h> +#include "usrp2_impl.h" +#include "usrp2_thread.h" +#include "eth_buffer.h" +#include "pktfilter.h" +#include "control.h" +#include "ring.h" +#include <stdexcept> +#include <iostream> +#include <stdio.h> +#include <stddef.h> +#include <assert.h> +#include <string.h> + +#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), "<unknown opcode: %d>", 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<u2_fixed_hdr_t *>(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<float> *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<int16_t> *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<uint32_t *>(&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 diff --git a/usrp2/host/lib/usrp2_impl.h b/usrp2/host/lib/usrp2_impl.h new file mode 100644 index 0000000000..37109c9e37 --- /dev/null +++ b/usrp2/host/lib/usrp2_impl.h @@ -0,0 +1,133 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_USRP2_IMPL_H +#define INCLUDED_USRP2_IMPL_H + +#include <usrp2/usrp2.h> +#include <usrp2/data_handler.h> +#include <usrp2_eth_packet.h> +#include <boost/scoped_ptr.hpp> +#include "control.h" +#include "ring.h" +#include <string> + +namespace usrp2 { + + class eth_buffer; + class pktfilter; + class usrp2_thread; + class usrp2_tune_result; + class pending_reply; + class ring; + + class usrp2::impl : private data_handler + { + static const size_t NRIDS = 256; + static const size_t NCHANS = 32; + + eth_buffer *d_eth_buf; + pktfilter *d_pf; + std::string d_addr; // FIXME: use u2_mac_addr_t instead + usrp2_thread *d_bg_thread; + volatile bool d_bg_running; // TODO: multistate if needed + + int d_rx_decim; + int d_rx_seqno; + int d_tx_seqno; + int d_next_rid; + unsigned int d_num_rx_frames; + unsigned int d_num_rx_missing; + unsigned int d_num_rx_overruns; + unsigned int d_num_rx_bytes; + + unsigned int d_num_enqueued; + omni_mutex d_enqueued_mutex; + omni_condition d_bg_pending_cond; + + // all pending_replies are stack allocated, thus no possibility of leaking these + pending_reply *d_pending_replies[NRIDS]; // indexed by 8-bit reply id + + std::vector<ring_sptr> d_channel_rings; // indexed by 5-bit channel number + + void inc_enqueued() { + omni_mutex_lock l(d_enqueued_mutex); + d_num_enqueued++; + } + + void dec_enqueued() { + omni_mutex_lock l(d_enqueued_mutex); + if (--d_num_enqueued == 0) + d_bg_pending_cond.signal(); + } + + static bool parse_mac_addr(const std::string &s, u2_mac_addr_t *p); + void init_et_hdrs(u2_eth_packet_t *p, const std::string &dst); + void init_etf_hdrs(u2_eth_packet_t *p, const std::string &dst, + int word0_flags, int chan, uint32_t timestamp); + void stop_bg(); + void init_config_rx_v2_cmd(op_config_rx_v2_cmd *cmd); + void init_config_tx_v2_cmd(op_config_tx_v2_cmd *cmd); + bool transmit_cmd(void *cmd, size_t len, pending_reply *p, double secs=0.0); + virtual data_handler::result operator()(const void *base, size_t len); + data_handler::result handle_control_packet(const void *base, size_t len); + data_handler::result handle_data_packet(const void *base, size_t len); + + public: + impl(const std::string &ifc, props *p); + ~impl(); + + void bg_loop(); + + std::string mac_addr() const { return d_addr; } // FIXME: convert from u2_mac_addr_t + bool burn_mac_addr(const std::string &new_addr); + + bool set_rx_gain(double gain); + bool set_rx_center_freq(double frequency, tune_result *result); + bool set_rx_decim(int decimation_factor); + bool set_rx_scale_iq(int scale_i, int scale_q); + bool start_rx_streaming(unsigned int channel, unsigned int items_per_frame); + bool rx_samples(unsigned int channel, rx_sample_handler *handler); + bool stop_rx_streaming(unsigned int channel); + unsigned int rx_overruns() const { return d_num_rx_overruns; } + unsigned int rx_missing() const { return d_num_rx_missing; } + + bool set_tx_gain(double gain); + bool set_tx_center_freq(double frequency, tune_result *result); + bool set_tx_interp(int interpolation_factor); + bool set_tx_scale_iq(int scale_i, int scale_q); + + bool tx_complex_float(unsigned int channel, + const std::complex<float> *samples, + size_t nsamples, + const tx_metadata *metadata); + + bool tx_complex_int16(unsigned int channel, + const std::complex<int16_t> *samples, + size_t nsamples, + const tx_metadata *metadata); + + bool tx_raw(unsigned int channel, + const uint32_t *items, + size_t nitems, + const tx_metadata *metadata); + }; + +} // namespace usrp2 + +#endif /* INCLUDED_USRP2_IMPL_H */ diff --git a/usrp2/host/lib/usrp2_socket_opener.cc b/usrp2/host/lib/usrp2_socket_opener.cc new file mode 100644 index 0000000000..27d3935e47 --- /dev/null +++ b/usrp2/host/lib/usrp2_socket_opener.cc @@ -0,0 +1,143 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +/*! + * setuid root program that opens a socket using (PF_PACKET, SOCK_RAW, + * htons(0xBEEF)), and sends the resulting file descriptor by way of + * of the file descriptor specified as the first command line argument. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <sys/types.h> +#include <sys/socket.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#elif defined(HAVE_NETINET_IN_H) +#include <netinet/in.h> +#endif + + +ssize_t +write_fd(int fd, const void *ptr, size_t nbytes, int sendfd) +{ + struct msghdr msg; + struct iovec iov[1]; + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *((int *) CMSG_DATA(cmptr)) = sendfd; +#else + msg.msg_accrights = (char *) &sendfd; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = const_cast<void *>(ptr); + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + return sendmsg(fd, &msg, 0); +} + +bool +reset_eids() +{ + if (setgid(getgid()) < 0){ + perror("setguid"); + return false; + } + + if (setuid(getuid()) < 0){ + perror("setuid"); + return false; + } + + return true; +} + + +static void +usage() +{ + fprintf(stderr, "usage: usrp2_socket_opener file-descriptor\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + if (argc != 2) + usage(); + + char *endptr; + int unix_domain_fd = strtol(argv[1], &endptr, 0); + if (*endptr != 0) + usage(); + + // FIXME get client credentials from unix_domain_fd using SCM_CREDENTIALS + + // open the raw socket + int socket_fd = socket(PF_PACKET, SOCK_RAW, htons(0xBEEF)); + if (socket_fd == -1){ + perror("socket(PF_PACKET, SOCK_RAW, htons(0xBEEF))"); + // printf("errno = %d\n", errno); + if (errno == EACCES || errno == ESPIPE){ + fprintf(stderr, "usrp2_socket_opener must be setuid root to open the socket using SOCK_RAW.\n"); + fprintf(stderr, "Running as root, please execute: \n"); + fprintf(stderr, " # chown root:usrp usrp2_socket_opener\n"); + fprintf(stderr, " # chmod 04750 usrp2_socket_opener\n"); + } + exit(2); + } + + // drop privs + if (!reset_eids()){ + fprintf(stderr, "Can't drop root permissions\n"); + exit(3); + } + + if (write_fd(unix_domain_fd, "", 1, socket_fd) != 1){ + perror("write_fd"); + exit(4); + } + + return 0; +} diff --git a/usrp2/host/lib/usrp2_thread.cc b/usrp2/host/lib/usrp2_thread.cc new file mode 100644 index 0000000000..9e4e6dbefd --- /dev/null +++ b/usrp2/host/lib/usrp2_thread.cc @@ -0,0 +1,63 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "usrp2_thread.h" +#include "usrp2_impl.h" +#include <gruel/realtime.h> +#include <iostream> + +#define USRP2_THREAD_DEBUG 1 + +namespace usrp2 { + + usrp2_thread::usrp2_thread(usrp2::impl *u2) : + omni_thread(NULL, PRIORITY_HIGH), + d_u2(u2) + { + } + + usrp2_thread::~usrp2_thread() + { + // we don't own this, just forget it + d_u2 = 0; + } + + void + usrp2_thread::start() + { + start_undetached(); + } + + void * + usrp2_thread::run_undetached(void *arg) + { + if (gruel::enable_realtime_scheduling() != gruel::RT_OK) + std::cerr << "usrp2: failed to enable realtime scheduling" << std::endl; + + // This is the first code to run in the new thread context. + d_u2->bg_loop(); + + return 0; + } + +} // namespace usrp2 + diff --git a/usrp2/host/lib/usrp2_thread.h b/usrp2/host/lib/usrp2_thread.h new file mode 100644 index 0000000000..c7f9e1aaff --- /dev/null +++ b/usrp2/host/lib/usrp2_thread.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 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 3, 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., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_USRP2_THREAD_H +#define INCLUDED_USRP2_THREAD_H + +#include <omnithread.h> +#include <usrp2_impl.h> + +namespace usrp2 { + + class usrp2_thread : public omni_thread + { + private: + usrp2::impl *d_u2; + + public: + usrp2_thread(usrp2::impl *u2); + ~usrp2_thread(); + + void start(); + + virtual void *run_undetached(void *arg); + }; + +} // namespace usrp2 + +#endif /* INCLUDED_USRP2_THREAD_H */ diff --git a/usrp2/host/usrp2.pc.in b/usrp2/host/usrp2.pc.in new file mode 100644 index 0000000000..eaef5f41d2 --- /dev/null +++ b/usrp2/host/usrp2.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: usrp2 +Description: Universal Software Radio Peripheral 2 +Requires: gnuradio-omnithread gruel +Version: @VERSION@ +Libs: -L${libdir} -lusrp2 +Cflags: -I${includedir} @DEFINES@ |