From: eb Date: Wed, 2 May 2007 04:08:47 +0000 (+0000) Subject: Merged features/inband -r4812:5218 into trunk. This group of changes X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=0bf2128a621ae84099f43744e1b81800f2b9d2d7;p=debian%2Fgnuradio Merged features/inband -r4812:5218 into trunk. This group of changes includes: * working stand-alone mblock code * work-in-progress on usrp inband signaling usrp now depends on mblock, and guile is a dependency. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@5221 221aa14e-8319-0410-a670-987f0aec2ac5 --- diff --git a/Makefile.common b/Makefile.common index 35ff2039..3906542b 100644 --- a/Makefile.common +++ b/Makefile.common @@ -73,14 +73,19 @@ GNURADIO_CORE_LA = $(top_builddir)/gnuradio-core/src/lib/libgnuradio-core.la GNURADIO_I = $(top_srcdir)/gnuradio-core/src/lib/swig/gnuradio.i # How to link in the USRP library from inside the tree -USRP_INCLUDES = -I$(top_srcdir)/usrp/host/lib \ +USRP_INCLUDES = -I$(top_srcdir)/usrp/host/lib/legacy \ + -I$(top_srcdir)/usrp/host/lib/inband \ -I$(top_srcdir)/usrp/firmware/include -USRP_LA = $(top_builddir)/usrp/host/lib/libusrp.la +USRP_LA = $(top_builddir)/usrp/host/lib/legacy/libusrp.la # How to link the PMT library from inside the tree PMT_INCLUDES = -I$(top_srcdir)/pmt/src/lib PMT_LA = $(top_builddir)/pmt/src/lib/libpmt.la +# How to link the mblock library from inside the tree +MBLOCK_INCLUDES = -I$(top_srcdir)/mblock/src/lib +MBLOCK_LA = $(top_builddir)/mblock/src/lib/libmblock.la + # This used to be set in configure.ac but is now defined here for all # Makefiles when this fragment is included. STD_DEFINES_AND_INCLUDES=$(DEFINES) $(OMNITHREAD_INCLUDES) $(GNURADIO_INCLUDES) $(BOOST_CFLAGS) @@ -88,3 +93,6 @@ STD_DEFINES_AND_INCLUDES=$(DEFINES) $(OMNITHREAD_INCLUDES) $(GNURADIO_INCLUDES) # Fix for BSD make not defining $(RM). We define it now in configure.ac # using AM_PATH_PROG, but now here have to add a -f to be like GNU make RM=$(RM_PROG) -f + +RUN_GUILE = GUILE_LOAD_PATH="@abs_top_srcdir@/pmt/src/scheme:@abs_top_srcdir@/mblock/src/scheme" @GUILE@ -e main -s +COMPILE_MBH = $(RUN_GUILE) $(top_srcdir)/mblock/src/scheme/gnuradio/compile-mbh.scm diff --git a/config/grc_mblock.m4 b/config/grc_mblock.m4 index 6e4bc4af..d5020a65 100644 --- a/config/grc_mblock.m4 +++ b/config/grc_mblock.m4 @@ -24,7 +24,9 @@ AC_DEFUN([GRC_MBLOCK],[ mblock/Makefile \ mblock/doc/Makefile \ mblock/src/Makefile \ - mblock/src/lib/Makefile + mblock/src/lib/Makefile \ + mblock/src/scheme/Makefile \ + mblock/src/scheme/gnuradio/Makefile \ ]) passed=yes @@ -48,6 +50,12 @@ AC_DEFUN([GRC_MBLOCK],[ fi done + AC_PATH_PROG(GUILE,guile) + if test "$GUILE" = "" ; then + AC_MSG_RESULT([Component mblock requires guile, which was not found.]) + passed=no + fi + GRC_BUILD_CONDITIONAL([mblock],[ dnl run_tests is created from run_tests.in. Make it executable. dnl AC_CONFIG_COMMANDS([run_tests_mblock], [chmod +x mblock/src/python/run_tests]) diff --git a/config/grc_pmt.m4 b/config/grc_pmt.m4 index 57d912bf..5e248719 100644 --- a/config/grc_pmt.m4 +++ b/config/grc_pmt.m4 @@ -24,10 +24,18 @@ AC_DEFUN([GRC_PMT],[ pmt/Makefile \ pmt/doc/Makefile \ pmt/src/Makefile \ - pmt/src/lib/Makefile + pmt/src/lib/Makefile \ + pmt/src/scheme/Makefile \ + pmt/src/scheme/gnuradio/Makefile \ ]) passed=yes + AC_PATH_PROG(GUILE,guile) + if test "$GUILE" = "" ; then + AC_MSG_RESULT([Component pmt requires guile, which was not found.]) + passed=no + fi + GRC_BUILD_CONDITIONAL([pmt],[ dnl run_tests is created from run_tests.in. Make it executable. dnl AC_CONFIG_COMMANDS([run_tests_pmt], [chmod +x pmt/src/python/run_tests]) diff --git a/config/grc_usrp.m4 b/config/grc_usrp.m4 index a7c0cf13..e0fe60f4 100644 --- a/config/grc_usrp.m4 +++ b/config/grc_usrp.m4 @@ -30,7 +30,9 @@ AC_DEFUN([GRC_USRP],[ usrp/host/Makefile \ usrp/host/misc/Makefile \ usrp/host/lib/Makefile \ - usrp/host/lib/std_paths.h \ + usrp/host/lib/inband/Makefile \ + usrp/host/lib/legacy/Makefile \ + usrp/host/lib/legacy/std_paths.h \ usrp/host/swig/Makefile \ usrp/host/apps/Makefile \ usrp/firmware/Makefile \ @@ -54,6 +56,16 @@ AC_DEFUN([GRC_USRP],[ AC_CHECK_FUNCS([sigaction snprintf]) passed=yes + # Don't do usrp if mblock skipped + # There *has* to be a better way to check if a value is in a string + for dir in $skipped_dirs + do + if test "$dir" = "mblock"; then + AC_MSG_RESULT([Component usrp requires mblock, which is not being built.]) + passed=no + fi + done + USRP_LIBUSB([],[passed=no;AC_MSG_RESULT([Unable to configure USB dependency.])]) USRP_SET_FUSB_TECHNIQUE([],[passed=no;AC_MSG_RESULT([Unable to set fast USB technique.])]) USRP_SDCC([2.4.0],[],[passed=no;AC_MSG_RESULT([Unable to find firmware compiler.])]) diff --git a/configure.ac b/configure.ac index 418c78c0..22aaa2a0 100644 --- a/configure.ac +++ b/configure.ac @@ -114,7 +114,7 @@ AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(fcntl.h limits.h strings.h time.h sys/ioctl.h sys/time.h unistd.h) AC_CHECK_HEADERS(linux/ppdev.h sys/mman.h sys/select.h sys/types.h) -AC_CHECK_HEADERS(sys/resource.h stdint.h sched.h signal.h) +AC_CHECK_HEADERS(sys/resource.h stdint.h sched.h signal.h sys/syscall.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -131,6 +131,7 @@ AC_FUNC_VPRINTF AC_CHECK_FUNCS([mmap select socket strcspn strerror strspn getpagesize sysconf]) AC_CHECK_FUNCS([snprintf gettimeofday nanosleep sched_setscheduler]) AC_CHECK_FUNCS([modf sqrt sigaction sigprocmask]) +AC_CHECK_FUNCS([sched_setaffinity]) AC_CHECK_LIB(m, sincos, [AC_DEFINE([HAVE_SINCOS],[1],[Define to 1 if your system has `sincos'.])]) AC_CHECK_LIB(m, sincosf,[AC_DEFINE([HAVE_SINCOSF],[1],[Define to 1 if your system has `sincosf'.])]) @@ -198,6 +199,8 @@ AC_ARG_ENABLE( build_dirs="config" GRC_OMNITHREAD dnl must come before gnuradio-core and mblock GRC_GNURADIO_CORE +GRC_PMT +GRC_MBLOCK dnl this must come after GRC_PMT GRC_USRP GRC_GR_USRP dnl this must come after GRC_USRP GRC_GR_AUDIO_ALSA @@ -216,8 +219,6 @@ GRC_GR_TRELLIS GRC_GR_VIDEO_SDL GRC_GR_QTGUI GRC_GR_WXGUI -GRC_PMT -GRC_MBLOCK dnl this must come after GRC_PMT GRC_GR_SOUNDER dnl this must come after GRC_USRP GRC_GNURADIO_EXAMPLES dnl must come last diff --git a/gnuradio-core/src/python/bin/Makefile.am b/gnuradio-core/src/python/bin/Makefile.am index 296132ec..d4df5127 100644 --- a/gnuradio-core/src/python/bin/Makefile.am +++ b/gnuradio-core/src/python/bin/Makefile.am @@ -24,7 +24,7 @@ include $(top_srcdir)/Makefile.common EXTRA_DIST = microtune.py -bin_SCRIPTS = \ +noinst_SCRIPTS = \ microtune.py CLEANFILES = *.pyc diff --git a/mblock/README b/mblock/README index ca91edfa..130073af 100644 --- a/mblock/README +++ b/mblock/README @@ -1,5 +1,5 @@ # -# Copyright 2006 Free Software Foundation, Inc. +# Copyright 2006,2007 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -20,3 +20,5 @@ # The "Message block" implementation, a work in progress... + +http://gnuradio.org/trac/wiki/MessageBlocks diff --git a/mblock/src/Makefile.am b/mblock/src/Makefile.am index bcbab245..c6c2fcc7 100644 --- a/mblock/src/Makefile.am +++ b/mblock/src/Makefile.am @@ -19,4 +19,4 @@ # Boston, MA 02110-1301, USA. # -SUBDIRS = lib +SUBDIRS = lib scheme diff --git a/mblock/src/lib/Makefile.am b/mblock/src/lib/Makefile.am index 6273e16d..604f9724 100644 --- a/mblock/src/lib/Makefile.am +++ b/mblock/src/lib/Makefile.am @@ -28,28 +28,41 @@ TESTS = test_mblock lib_LTLIBRARIES = libmblock.la libmblock-qa.la EXTRA_DIST = \ - README.locking + README.locking \ + qa_bitset.mbh +BUILT_SOURCES = \ + qa_bitset_mbh.cc + +qa_bitset_mbh.cc : qa_bitset.mbh + $(COMPILE_MBH) qa_bitset.mbh qa_bitset_mbh.cc + # These are the source files that go into the mblock shared library libmblock_la_SOURCES = \ + mb_class_registry.cc \ mb_connection.cc \ mb_endpoint.cc \ mb_exception.cc \ + mb_gettid.cc \ mb_mblock.cc \ mb_mblock_impl.cc \ mb_message.cc \ mb_msg_accepter.cc \ + mb_msg_accepter_msgq.cc \ mb_msg_accepter_smp.cc \ mb_msg_queue.cc \ mb_port.cc \ mb_port_simple.cc \ mb_protocol_class.cc \ mb_runtime.cc \ + mb_runtime_base.cc \ mb_runtime_nop.cc \ - mb_runtime_placeholder.cc \ - mb_runtime_thread_per_mblock.cc \ - mb_util.cc + mb_runtime_thread_per_block.cc \ + mb_time.cc \ + mb_timer_queue.cc \ + mb_util.cc \ + mb_worker.cc # magic flags @@ -62,19 +75,20 @@ libmblock_la_LIBADD = \ -lstdc++ include_HEADERS = \ + mb_class_registry.h \ mb_common.h \ mb_exception.h \ + mb_gettid.h \ mb_mblock.h \ mb_message.h \ mb_msg_accepter.h \ + mb_msg_accepter_msgq.h \ mb_msg_queue.h \ mb_port.h \ mb_port_simple.h \ mb_protocol_class.h \ mb_runtime.h \ - mb_runtime_nop.h \ - mb_runtime_placeholder.h \ - mb_runtime_thread_per_mblock.h \ + mb_time.h \ mb_util.h @@ -83,18 +97,30 @@ noinst_HEADERS = \ mb_endpoint.h \ mb_mblock_impl.h \ mb_msg_accepter_smp.h \ + mb_runtime_base.h \ + mb_runtime_nop.h \ + mb_runtime_thread_per_block.h \ + mb_timer_queue.h \ + mb_worker.h \ mbi_runtime_lock.h \ qa_mblock.h \ qa_mblock_prims.h \ - qa_mblock_send.h + qa_mblock_send.h \ + qa_mblock_sys.h \ + qa_timeouts.h # Build the qa code into its own library libmblock_qa_la_SOURCES = \ + qa_bitset.cc \ + qa_bitset_mbh.cc \ + qa_disconnect.cc \ qa_mblock.cc \ qa_mblock_prims.cc \ - qa_mblock_send.cc + qa_mblock_send.cc \ + qa_mblock_sys.cc \ + qa_timeouts.cc # magic flags @@ -107,9 +133,14 @@ libmblock_qa_la_LIBADD = \ -lstdc++ -noinst_PROGRAMS = test_mblock +noinst_PROGRAMS = \ + test_mblock \ + benchmark_send test_mblock_SOURCES = test_mblock.cc test_mblock_LDADD = libmblock-qa.la +benchmark_send_SOURCES = benchmark_send.cc +benchmark_send_LDADD = libmblock-qa.la + CLEANFILES = $(BUILT_SOURCES) *.pyc diff --git a/mblock/src/lib/benchmark_send.cc b/mblock/src/lib/benchmark_send.cc new file mode 100644 index 00000000..c9ebe57c --- /dev/null +++ b/mblock/src/lib/benchmark_send.cc @@ -0,0 +1,45 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +int +main(int argc, char **argv) +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + long nmsgs = 1000000; + long batch_size = 100; + + pmt_t arg = pmt_list2(pmt_from_long(nmsgs), // # of messages to send through pipe + pmt_from_long(batch_size)); + + rt->run("top", "qa_bitset_top", arg, &result); + + if (!pmt_equal(PMT_T, result)){ + std::cerr << "benchmark_send: incorrect result"; + return 1; + } + + return 0; +} diff --git a/mblock/src/lib/getres.cc b/mblock/src/lib/getres.cc new file mode 100644 index 00000000..c05ba792 --- /dev/null +++ b/mblock/src/lib/getres.cc @@ -0,0 +1,32 @@ +#include +#include + +int +main(int argc, char **argv) +{ + bool ok = true; + struct timespec ts; + int r; + + r = clock_getres(CLOCK_REALTIME, &ts); + if (r != 0){ + perror("clock_getres(CLOCK_REALTIME, ...)"); + ok = false; + } + else + printf("clock_getres(CLOCK_REALTIME, ...) => %11.9f\n", + (double) ts.tv_sec + ts.tv_nsec * 1e-9); + + + r = clock_getres(CLOCK_MONOTONIC, &ts); + if (r != 0){ + perror("clock_getres(CLOCK_MONOTONIC, ..."); + ok = false; + } + else + printf("clock_getres(CLOCK_MONOTONIC, ...) => %11.9f\n", + (double) ts.tv_sec + ts.tv_nsec * 1e-9); + + + return ok == true ? 0 : 1; +} diff --git a/mblock/src/lib/mb_class_registry.cc b/mblock/src/lib/mb_class_registry.cc new file mode 100644 index 00000000..9eee9361 --- /dev/null +++ b/mblock/src/lib/mb_class_registry.cc @@ -0,0 +1,47 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include + +static std::map s_registry; + +bool +mb_class_registry::register_maker(const std::string &name, mb_mblock_maker_t maker) +{ + s_registry[name] = maker; + return true; +} + +bool +mb_class_registry::lookup_maker(const std::string &name, mb_mblock_maker_t *maker) +{ + if (s_registry.count(name) == 0){ // not registered + *maker = (mb_mblock_maker_t) 0; + return false; + } + + *maker = s_registry[name]; + return true; +} diff --git a/mblock/src/lib/mb_class_registry.h b/mblock/src/lib/mb_class_registry.h new file mode 100644 index 00000000..b6e63d38 --- /dev/null +++ b/mblock/src/lib/mb_class_registry.h @@ -0,0 +1,51 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_MB_CLASS_REGISTRY_H +#define INCLUDED_MB_CLASS_REGISTRY_H + +#include + +//! conceptually, pointer to constructor +typedef mb_mblock_sptr (*mb_mblock_maker_t)(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg); + +/* + * \brief Maintain mapping between mblock class_name and factory (maker) + */ +class mb_class_registry : public boost::noncopyable { +public: + static bool register_maker(const std::string &name, mb_mblock_maker_t maker); + static bool lookup_maker(const std::string &name, mb_mblock_maker_t *maker); +}; + +template +mb_mblock_sptr mb_mblock_maker(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) +{ + return mb_mblock_sptr(new mblock(runtime, instance_name, user_arg)); +} + +#define REGISTER_MBLOCK_CLASS(name) \ + bool __RBC__ ## name = mb_class_registry::register_maker(#name, &mb_mblock_maker) + +#endif /* INCLUDED_MB_CLASS_REGISTRY_H */ diff --git a/mblock/src/lib/mb_exception.cc b/mblock/src/lib/mb_exception.cc index 4d4ca70b..23dfbd9f 100644 --- a/mblock/src/lib/mb_exception.cc +++ b/mblock/src/lib/mb_exception.cc @@ -38,6 +38,11 @@ mbe_not_implemented::mbe_not_implemented(mb_mblock *mb, const std::string &msg) { } +mbe_no_such_class::mbe_no_such_class(mb_mblock *mb, const std::string &class_name) + : mbe_base(mb, "No such class: " + class_name) +{ +} + mbe_no_such_component::mbe_no_such_component(mb_mblock *mb, const std::string &component_name) : mbe_base(mb, "No such component: " + component_name) { @@ -85,3 +90,17 @@ mbe_invalid_port_type::mbe_invalid_port_type(mb_mblock *mb, : mbe_base(mb, "Invalid port type for connection: " + mb_util::join_names(comp_name, port_name)) { } + +mbe_mblock_failed::mbe_mblock_failed(mb_mblock *mb, + const std::string &msg) + : mbe_base(mb, "Message block failed: " + msg) +{ +} + +mbe_terminate::mbe_terminate() +{ +} + +mbe_exit::mbe_exit() +{ +} diff --git a/mblock/src/lib/mb_exception.h b/mblock/src/lib/mb_exception.h index 40abd1c9..183f7089 100644 --- a/mblock/src/lib/mb_exception.h +++ b/mblock/src/lib/mb_exception.h @@ -38,6 +38,11 @@ public: mbe_not_implemented(mb_mblock *mb, const std::string &msg); }; +class mbe_no_such_class : public mbe_base +{ +public: + mbe_no_such_class(mb_mblock *, const std::string &class_name); +}; class mbe_no_such_component : public mbe_base { @@ -57,6 +62,7 @@ public: mbe_no_such_port(mb_mblock *, const std::string &port_name); }; + class mbe_duplicate_port : public mbe_base { public: @@ -87,6 +93,26 @@ public: const std::string &port_name); }; +class mbe_mblock_failed : public mbe_base +{ +public: + mbe_mblock_failed(mb_mblock *, const std::string &msg); +}; + +// not derived from mbe_base to simplify try/catch +class mbe_terminate +{ +public: + mbe_terminate(); +}; + +// not derived from mbe_base to simplify try/catch +class mbe_exit +{ +public: + mbe_exit(); +}; + #endif /* INCLUDED_MB_EXCEPTION_H */ diff --git a/mblock/src/lib/mb_gettid.cc b/mblock/src/lib/mb_gettid.cc new file mode 100644 index 00000000..4f6b3a56 --- /dev/null +++ b/mblock/src/lib/mb_gettid.cc @@ -0,0 +1,53 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include + +#define NEED_STUB + +#if defined(HAVE_SYS_SYSCALL_H) && defined(HAVE_UNISTD_H) + +#include +#include + +#if defined(SYS_gettid) +#undef NEED_STUB + +int mb_gettid() +{ + return syscall(SYS_gettid); +} + +#endif +#endif + +#if defined(NEED_STUB) + +int +mb_gettid() +{ + return 0; +} + +#endif diff --git a/mblock/src/lib/mb_gettid.h b/mblock/src/lib/mb_gettid.h new file mode 100644 index 00000000..d06276f5 --- /dev/null +++ b/mblock/src/lib/mb_gettid.h @@ -0,0 +1,26 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * \brief Return Linux taskid, or 0 if not available + */ +int mb_gettid(); + diff --git a/mblock/src/lib/mb_mblock.cc b/mblock/src/lib/mb_mblock.cc index 9b8658f2..afff1a92 100644 --- a/mblock/src/lib/mb_mblock.cc +++ b/mblock/src/lib/mb_mblock.cc @@ -25,29 +25,36 @@ #include #include +#include +#include +#include +static pmt_t s_sys_port = pmt_intern("%sys-port"); +static pmt_t s_halt = pmt_intern("%halt"); + mb_visitor::~mb_visitor() { // nop base case for virtual destructor. } -mb_mblock::mb_mblock() - : d_impl(mb_mblock_impl_sptr(new mb_mblock_impl(this))) +mb_mblock::mb_mblock(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) + : d_impl(mb_mblock_impl_sptr( + new mb_mblock_impl(dynamic_cast(runtime), + this, instance_name))) { } mb_mblock::~mb_mblock() { - disconnect_all(); - - // FIXME more? } void -mb_mblock::init_fsm() +mb_mblock::initial_transition() { // default implementation does nothing } @@ -58,6 +65,36 @@ mb_mblock::handle_message(mb_message_sptr msg) // default implementation does nothing } + +void +mb_mblock::main_loop() +{ + while (1){ + mb_message_sptr msg; + try { + while (1){ + msg = impl()->msgq().get_highest_pri_msg(); + + // check for %halt from %sys-port + if (pmt_eq(msg->port_id(), s_sys_port) && pmt_eq(msg->signal(), s_halt)) + exit(); + + handle_message(msg); + } + } + catch (pmt_exception e){ + std::cerr << "\nmb_mblock::main_loop: ignored pmt_exception: " + << e.what() + << "\nin mblock instance \"" << instance_name() + << "\" while handling message:" + << "\n port_id = " << msg->port_id() + << "\n signal = " << msg->signal() + << "\n data = " << msg->data() + << "\n metatdata = " << msg->metadata() << std::endl; + } + } +} + //////////////////////////////////////////////////////////////////////// // Forward other methods to implementation class // //////////////////////////////////////////////////////////////////////// @@ -74,9 +111,11 @@ mb_mblock::define_port(const std::string &port_name_string, void mb_mblock::define_component(const std::string &component_name, - mb_mblock_sptr component) + const std::string &class_name, + pmt_t user_arg) + { - d_impl->define_component(component_name, component); + d_impl->define_component(component_name, class_name, user_arg); } void @@ -97,7 +136,7 @@ mb_mblock::disconnect(const std::string &comp_name1, const std::string &port_nam } void -mb_mblock::disconnect_component(const std::string component_name) +mb_mblock::disconnect_component(const std::string &component_name) { d_impl->disconnect_component(component_name); } @@ -115,9 +154,9 @@ mb_mblock::nconnections() const } bool -mb_mblock::walk_tree(mb_visitor *visitor, const std::string &path) +mb_mblock::walk_tree(mb_visitor *visitor) { - return d_impl->walk_tree(visitor, path); + return d_impl->walk_tree(visitor); } std::string @@ -127,7 +166,7 @@ mb_mblock::instance_name() const } void -mb_mblock::set_instance_name(const std::string name) +mb_mblock::set_instance_name(const std::string &name) { d_impl->set_instance_name(name); } @@ -139,7 +178,7 @@ mb_mblock::class_name() const } void -mb_mblock::set_class_name(const std::string name) +mb_mblock::set_class_name(const std::string &name) { d_impl->set_class_name(name); } @@ -149,3 +188,42 @@ mb_mblock::parent() const { return d_impl->mblock_parent(); } + +void +mb_mblock::exit() +{ + throw mbe_exit(); // adios... +} + +void +mb_mblock::shutdown_all(pmt_t result) +{ + d_impl->runtime()->request_shutdown(result); +} + +pmt_t +mb_mblock::schedule_one_shot_timeout(const mb_time &abs_time, pmt_t user_data) +{ + mb_msg_accepter_sptr accepter = impl()->make_accepter(s_sys_port); + return d_impl->runtime()->schedule_one_shot_timeout(abs_time, user_data, + accepter); +} + +pmt_t +mb_mblock::schedule_periodic_timeout(const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data) +{ + mb_msg_accepter_sptr accepter = impl()->make_accepter(s_sys_port); + return d_impl->runtime()->schedule_periodic_timeout(first_abs_time, + delta_time, + user_data, + accepter); +} + +void +mb_mblock::cancel_timeout(pmt_t handle) +{ + d_impl->runtime()->cancel_timeout(handle); +} + diff --git a/mblock/src/lib/mb_mblock.h b/mblock/src/lib/mb_mblock.h index 594920f9..6d471c3d 100644 --- a/mblock/src/lib/mb_mblock.h +++ b/mblock/src/lib/mb_mblock.h @@ -24,6 +24,7 @@ #include #include #include +#include /*! @@ -34,7 +35,7 @@ class mb_visitor { public: virtual ~mb_visitor(); - virtual bool operator()(mb_mblock *mblock, const std::string &path) = 0; + virtual bool operator()(mb_mblock *mblock) = 0; }; // ---------------------------------------------------------------------- @@ -52,6 +53,7 @@ private: friend class mb_runtime; friend class mb_mblock_impl; + friend class mb_worker; protected: /*! @@ -59,22 +61,29 @@ protected: * * Initializing all mblocks in the system is a 3 step procedure. * - * The top level mblock's constructor is run. That constructor (a) - * registers all of its ports using define_port, (b) constructs and - * registers any subcomponents it may have via the define_component - * method, and then (c) issues connect calls to wire its - * subcomponents together. + * The top level mblock's constructor is run. That constructor + * (a) registers all of its ports using define_port, (b) registers any + * subcomponents it may have via the define_component method, and + * then (c) issues connect calls to wire its subcomponents together. + * + * \param runtime the runtime associated with this mblock + * \param instance_name specify the name of this instance + * (for debugging, NUMA mapping, etc) + * \param user_arg argument passed by user to constructor + * (ignored by the mb_mblock base class) */ - mb_mblock(); + mb_mblock(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); public: /*! * \brief Called by the runtime system to execute the initial * transition of the finite state machine. * - * Override this to initialize your finite state machine. + * This method is called by the runtime after all blocks are + * constructed and before the first message is delivered. Override + * this to initialize your finite state machine. */ - virtual void init_fsm(); + virtual void initial_transition(); protected: /*! @@ -113,11 +122,13 @@ protected: * names and identities of our sub-component mblocks. * * \param component_name The name of the sub-component (must be unique with this mblock). - * \param component The sub-component instance. + * \param class_name The class of the instance that is to be created. + * \param user_arg The argument to pass to the constructor of the component. */ void define_component(const std::string &component_name, - mb_mblock_sptr component); + const std::string &class_name, + pmt_t user_arg = PMT_NIL); /*! * \brief connect endpoint_1 to endpoint_2 @@ -160,7 +171,7 @@ protected: * \param component_name component to disconnect */ void - disconnect_component(const std::string component_name); + disconnect_component(const std::string &component_name); /*! * \brief disconnect all connections to all components @@ -175,8 +186,47 @@ protected: nconnections() const; //! Set the class name - void set_class_name(const std::string name); + void set_class_name(const std::string &name); + + /*! + * \brief Tell runtime that we are done. + * + * This method does not return. + */ + void exit(); + /*! + * \brief Ask runtime to execute the shutdown procedure for all blocks. + * + * \param result sets value of \p result output argument of runtime->run(...) + * + * The runtime first sends a maximum priority %shutdown message to + * all blocks. All blocks should handle the %shutdown message, + * perform whatever clean up is required, and call this->exit(); + * + * After a period of time (~100ms), any blocks which haven't yet + * called this->exit() are sent a maximum priority %halt message. + * %halt is detected in main_loop, and this->exit() is called. + * + * After an additional period of time (~100ms), any blocks which + * still haven't yet called this->exit() are sent a SIG (TBD) + * signal, which will blow them out of any blocking system calls and + * raise an mbe_terminate exception. The default top-level + * runtime-provided exception handler will call this->exit() to + * finish the process. + * + * runtime->run(...) returns when all blocks have called exit. + */ + void shutdown_all(pmt_t result); + + /*! + * \brief main event dispatching loop + * + * Although it is possible to override this, the default implementation + * should work for virtually all cases. + */ + virtual void main_loop(); + public: virtual ~mb_mblock(); @@ -187,18 +237,74 @@ public: std::string class_name() const; //! Set the instance name of this block. - void set_instance_name(const std::string name); + void set_instance_name(const std::string &name); //! Return the parent of this mblock, or 0 if we're the top-level block. mb_mblock *parent() const; + /*! + * \brief Schedule a "one shot" timeout. + * + * \param abs_time the absolute time at which the timeout should fire + * \param user_data the data passed in the %timeout message. + * + * When the timeout fires, a message will be sent to the mblock. + * + * The message will have port_id = %sys-port, signal = %timeout, + * data = user_data, metadata = the handle returned from + * schedule_one_shot_timeout, pri = MB_PRI_BEST. + * + * \returns a handle that can be used in cancel_timeout, and is passed + * as the metadata field of the generated %timeout message. + * + * To cancel a pending timeout, call cancel_timeout. + */ + pmt_t + schedule_one_shot_timeout(const mb_time &abs_time, pmt_t user_data); + + /*! + * \brief Schedule a periodic timeout. + * + * \param first_abs_time The absolute time at which the first timeout should fire. + * \param delta_time The relative delay between the first and successive timeouts. + * \param user_data the data passed in the %timeout message. + * + * When the timeout fires, a message will be sent to the mblock, and a + * new timeout will be scheduled for previous absolute time + delta_time. + * + * The message will have port_id = %sys-port, signal = %timeout, + * data = user_data, metadata = the handle returned from + * schedule_one_shot_timeout, pri = MB_PRI_BEST. + * + * \returns a handle that can be used in cancel_timeout, and is passed + * as the metadata field of the generated %timeout message. + * + * To cancel a pending timeout, call cancel_timeout. + */ + pmt_t + schedule_periodic_timeout(const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data); + + /*! + * \brief Attempt to cancel a pending timeout. + * + * Note that this only stops a future timeout from firing. It is + * possible that a timeout may have already fired and enqueued a + * %timeout message, but that that message has not yet been seen by + * handle_message. + * + * \param handle returned from schedule_one_shot_timeout or schedule_periodic_timeout. + */ + void cancel_timeout(pmt_t handle); + /*! * \brief Perform a pre-order depth-first traversal of the hierarchy. * * The traversal stops and returns false if any call to visitor returns false. */ bool - walk_tree(mb_visitor *visitor, const std::string &path="top"); + walk_tree(mb_visitor *visitor); //! \implementation diff --git a/mblock/src/lib/mb_mblock_impl.cc b/mblock/src/lib/mb_mblock_impl.cc index 1a9e5014..02cbb548 100644 --- a/mblock/src/lib/mb_mblock_impl.cc +++ b/mblock/src/lib/mb_mblock_impl.cc @@ -30,8 +30,8 @@ #include #include #include -#include #include +#include static pmt_t s_self = pmt_intern("self"); @@ -52,9 +52,10 @@ mb_mblock_impl::comp_is_defined(const std::string &name) //////////////////////////////////////////////////////////////////////// -mb_mblock_impl::mb_mblock_impl(mb_mblock *mb) - : d_mb(mb), d_mb_parent(0), d_runtime(mb_runtime_placeholder::singleton()), - d_instance_name(""), d_class_name("mblock") +mb_mblock_impl::mb_mblock_impl(mb_runtime_base *runtime, mb_mblock *mb, + const std::string &instance_name) + : d_runtime(runtime), d_mb(mb), d_mb_parent(0), + d_instance_name(instance_name), d_class_name("mblock") { } @@ -85,15 +86,28 @@ mb_mblock_impl::define_port(const std::string &port_name, void mb_mblock_impl::define_component(const std::string &name, - mb_mblock_sptr component) + const std::string &class_name, + pmt_t user_arg) { - mbi_runtime_lock l(this); + { + mbi_runtime_lock l(this); + + if (comp_is_defined(name)) // check for duplicate name + throw mbe_duplicate_component(d_mb, name); + } - if (comp_is_defined(name)) // check for duplicate name - throw mbe_duplicate_component(d_mb, name); + // We ask the runtime to create the component so that it can worry about + // mblock placement on a NUMA machine or on a distributed multicomputer - component->d_impl->d_mb_parent = d_mb; // set component's parent link - d_comp_map[name] = component; + mb_mblock_sptr component = + d_runtime->create_component(instance_name() + "/" + name, + class_name, user_arg); + { + mbi_runtime_lock l(this); + + component->d_impl->d_mb_parent = d_mb; // set component's parent link + d_comp_map[name] = component; + } } void @@ -125,6 +139,7 @@ mb_mblock_impl::disconnect(const std::string &comp_name1, mbi_runtime_lock l(this); d_conn_table.disconnect(comp_name1, port_name1, comp_name2, port_name2); + invalidate_all_port_caches(); } void @@ -133,6 +148,7 @@ mb_mblock_impl::disconnect_component(const std::string component_name) mbi_runtime_lock l(this); d_conn_table.disconnect_component(component_name); + invalidate_all_port_caches(); } void @@ -141,6 +157,7 @@ mb_mblock_impl::disconnect_all() mbi_runtime_lock l(this); d_conn_table.disconnect_all(); + invalidate_all_port_caches(); } int @@ -220,26 +237,25 @@ mb_mblock_impl::endpoints_are_compatible(const mb_endpoint &ep0, } bool -mb_mblock_impl::walk_tree(mb_visitor *visitor, const std::string &path) +mb_mblock_impl::walk_tree(mb_visitor *visitor) { - if (!(*visitor)(d_mb, path)) + if (!(*visitor)(d_mb)) return false; mb_comp_map_t::iterator it; for (it = d_comp_map.begin(); it != d_comp_map.end(); ++it) - if (!(it->second->walk_tree(visitor, path + "/" + it->first))) + if (!(it->second->walk_tree(visitor))) return false; return true; } mb_msg_accepter_sptr -mb_mblock_impl::make_accepter(const std::string port_name) +mb_mblock_impl::make_accepter(pmt_t port_name) { // FIXME this should probably use some kind of configurable factory mb_msg_accepter *ma = - new mb_msg_accepter_smp(d_mb->shared_from_this(), - pmt_intern(port_name)); + new mb_msg_accepter_smp(d_mb->shared_from_this(), port_name); return mb_msg_accepter_sptr(ma); } @@ -281,3 +297,31 @@ mb_mblock_impl::set_class_name(const std::string &name) d_class_name = name; } +/* + * This is the "Big Hammer" port cache invalidator. + * It invalidates _all_ of the port caches in the entire mblock tree. + * It's overkill, but was simple to code. + */ +void +mb_mblock_impl::invalidate_all_port_caches() +{ + class invalidator : public mb_visitor + { + public: + bool operator()(mb_mblock *mblock) + { + mb_mblock_impl_sptr impl = mblock->impl(); + mb_port_map_t::iterator it = impl->d_port_map.begin(); + mb_port_map_t::iterator end = impl->d_port_map.end(); + for (; it != end; ++it) + it->second->invalidate_cache(); + return true; + } + }; + + invalidator visitor; + + // Always true, except in early QA code + if (runtime()->top()) + runtime()->top()->walk_tree(&visitor); +} diff --git a/mblock/src/lib/mb_mblock_impl.h b/mblock/src/lib/mb_mblock_impl.h index fc0fa694..03ad414e 100644 --- a/mblock/src/lib/mb_mblock_impl.h +++ b/mblock/src/lib/mb_mblock_impl.h @@ -22,6 +22,7 @@ #define INCLUDED_MB_MBLOCK_IMPL_H #include +#include #include #include #include @@ -37,9 +38,9 @@ typedef std::map mb_comp_map_t; */ class mb_mblock_impl : boost::noncopyable { + mb_runtime_base *d_runtime; // pointer to runtime mb_mblock *d_mb; // pointer to our associated mblock mb_mblock *d_mb_parent; // pointer to our parent - mb_runtime *d_runtime; // pointer to runtime std::string d_instance_name; // hierarchical name std::string d_class_name; // name of this (derived) class @@ -51,7 +52,8 @@ class mb_mblock_impl : boost::noncopyable mb_msg_queue d_msgq; // incoming messages for us public: - mb_mblock_impl(mb_mblock *mb); + mb_mblock_impl(mb_runtime_base *runtime, mb_mblock *mb, + const std::string &instance_name); ~mb_mblock_impl(); /*! @@ -79,11 +81,13 @@ public: * names and identities of our sub-component mblocks. * * \param component_name The name of the sub-component (must be unique with this mblock). - * \param component The sub-component instance. + * \param class_name The class of the instance that is to be created. + * \param user_arg The argument to pass to the constructor of the component. */ void define_component(const std::string &component_name, - mb_mblock_sptr component); + const std::string &class_name, + pmt_t user_arg); /*! * \brief connect endpoint_1 to endpoint_2 @@ -141,10 +145,10 @@ public: nconnections(); bool - walk_tree(mb_visitor *visitor, const std::string &path=""); + walk_tree(mb_visitor *visitor); mb_msg_accepter_sptr - make_accepter(const std::string port_name); + make_accepter(pmt_t port_name); mb_msg_queue & msgq() { return d_msgq; } @@ -183,10 +187,10 @@ public: mb_mblock_sptr component(const std::string &comp_name); //! Return the runtime instance - mb_runtime *runtime() { return d_runtime; } + mb_runtime_base *runtime() { return d_runtime; } //! Set the runtime instance - void set_runtime(mb_runtime *runtime) { d_runtime = runtime; } + void set_runtime(mb_runtime_base *runtime) { d_runtime = runtime; } /* * Our implementation methods @@ -210,6 +214,12 @@ private: endpoints_are_compatible(const mb_endpoint &ep0, const mb_endpoint &ep1); + /*! + * \brief walk mblock tree and invalidate all port resolution caches. + * \implementation + */ + void + invalidate_all_port_caches(); }; diff --git a/mblock/src/lib/mb_message.cc b/mblock/src/lib/mb_message.cc index 23803726..14e34992 100644 --- a/mblock/src/lib/mb_message.cc +++ b/mblock/src/lib/mb_message.cc @@ -23,6 +23,34 @@ #include #endif #include +#include +#include + +static const int CACHE_LINE_SIZE = 64; // good guess + + +#if MB_MESSAGE_LOCAL_ALLOCATOR + +static pmt_pool global_msg_pool(sizeof(mb_message), CACHE_LINE_SIZE); + +void * +mb_message::operator new(size_t size) +{ + void *p = global_msg_pool.malloc(); + + // fprintf(stderr, "mb_message::new p = %p\n", p); + assert((reinterpret_cast(p) & (CACHE_LINE_SIZE - 1)) == 0); + return p; +} + +void +mb_message::operator delete(void *p, size_t size) +{ + global_msg_pool.free(p); +} + +#endif + mb_message_sptr mb_make_message(pmt_t signal, pmt_t data, pmt_t metadata, mb_pri_t priority) @@ -40,3 +68,16 @@ mb_message::~mb_message() { // NOP } + +std::ostream& +operator<<(std::ostream& os, const mb_message &msg) +{ + os << ""; + + return os; +} diff --git a/mblock/src/lib/mb_message.h b/mblock/src/lib/mb_message.h index 95440f8b..6132866e 100644 --- a/mblock/src/lib/mb_message.h +++ b/mblock/src/lib/mb_message.h @@ -22,6 +22,9 @@ #define INCLUDED_MB_MESSAGE_H #include +#include + +#define MB_MESSAGE_LOCAL_ALLOCATOR 0 // define to 0 or 1 class mb_message; typedef boost::shared_ptr mb_message_sptr; @@ -66,6 +69,20 @@ public: pmt_t port_id() const { return d_port_id; } void set_port_id(pmt_t port_id){ d_port_id = port_id; } + +#if (MB_MESSAGE_LOCAL_ALLOCATOR) + void *operator new(size_t); + void operator delete(void *, size_t); +#endif }; +std::ostream& operator<<(std::ostream& os, const mb_message &msg); + +inline +std::ostream& operator<<(std::ostream& os, const mb_message_sptr msg) +{ + os << *(msg.get()); + return os; +} + #endif /* INCLUDED_MB_MESSAGE_H */ diff --git a/mblock/src/lib/mb_msg_accepter_msgq.cc b/mblock/src/lib/mb_msg_accepter_msgq.cc new file mode 100644 index 00000000..990491fc --- /dev/null +++ b/mblock/src/lib/mb_msg_accepter_msgq.cc @@ -0,0 +1,46 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include + +pmt_t s_sys_port = pmt_intern("%sys-port"); + +mb_msg_accepter_msgq::mb_msg_accepter_msgq(mb_msg_queue *msgq) + : d_msgq(msgq) +{ +} + +mb_msg_accepter_msgq::~mb_msg_accepter_msgq() +{ +} + +void +mb_msg_accepter_msgq::operator()(pmt_t signal, pmt_t data, + pmt_t metadata, mb_pri_t priority) +{ + mb_message_sptr msg = mb_make_message(signal, data, metadata, priority); + msg->set_port_id(s_sys_port); + d_msgq->insert(msg); +} diff --git a/mblock/src/lib/mb_msg_accepter_msgq.h b/mblock/src/lib/mb_msg_accepter_msgq.h new file mode 100644 index 00000000..f598c730 --- /dev/null +++ b/mblock/src/lib/mb_msg_accepter_msgq.h @@ -0,0 +1,39 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_MB_MSG_ACCEPTER_MSGQ_H +#define INCLUDED_MB_MSG_ACCEPTER_MSGQ_H + +#include +#include + +/*! + * \brief Concrete class that accepts messages and inserts them into a message queue. + */ +class mb_msg_accepter_msgq : public mb_msg_accepter { + mb_msg_queue *d_msgq; + +public: + mb_msg_accepter_msgq(mb_msg_queue *msgq); + ~mb_msg_accepter_msgq(); + void operator()(pmt_t signal, pmt_t data, pmt_t metadata, mb_pri_t priority); +}; + +#endif /* INCLUDED_MB_MSG_ACCEPTER_MSGQ_H */ diff --git a/mblock/src/lib/mb_msg_queue.cc b/mblock/src/lib/mb_msg_queue.cc index 79e245ad..c687e876 100644 --- a/mblock/src/lib/mb_msg_queue.cc +++ b/mblock/src/lib/mb_msg_queue.cc @@ -83,7 +83,7 @@ mb_msg_queue::get_highest_pri_msg_helper() } } - return mb_message_sptr(); // equivalent of a zero pointer + return mb_message_sptr(); // eqv to a zero pointer } @@ -109,3 +109,20 @@ mb_msg_queue::get_highest_pri_msg() } } +mb_message_sptr +mb_msg_queue::get_highest_pri_msg_timedwait(const mb_time &abs_time) +{ + unsigned long secs = abs_time.d_secs; + unsigned long nsecs = abs_time.d_nsecs; + + omni_mutex_lock l(d_mutex); + + while (1){ + mb_message_sptr msg = get_highest_pri_msg_helper(); + if (msg) // Got one; return it + return msg; + + if (!d_not_empty.timedwait(secs, nsecs)) // timed out + return mb_message_sptr(); // eqv to zero pointer + } +} diff --git a/mblock/src/lib/mb_msg_queue.h b/mblock/src/lib/mb_msg_queue.h index 7977b7d8..4f805ea8 100644 --- a/mblock/src/lib/mb_msg_queue.h +++ b/mblock/src/lib/mb_msg_queue.h @@ -23,6 +23,7 @@ #include #include +#include /*! * \brief priority queue for mblock messages @@ -63,6 +64,19 @@ public: * If the queue is empty, this call blocks until it can return a message. */ mb_message_sptr get_highest_pri_msg(); + + /* + * \brief Delete highest pri message from the queue and return it. + * If the queue is empty, this call blocks until it can return a message + * or real-time exceeds the absolute time, abs_time. + * + * \param abs_time specifies the latest absolute time to wait until. + * \sa mb_time::time + * + * \returns a valid mb_message_sptr, or the equivalent of a zero pointer + * if the call timed out while waiting. + */ + mb_message_sptr get_highest_pri_msg_timedwait(const mb_time &abs_time); }; #endif /* INCLUDED_MB_MSG_QUEUE_H */ diff --git a/mblock/src/lib/mb_port.cc b/mblock/src/lib/mb_port.cc index b265db2d..28bb402d 100644 --- a/mblock/src/lib/mb_port.cc +++ b/mblock/src/lib/mb_port.cc @@ -31,7 +31,8 @@ mb_port::mb_port(mb_mblock *mblock, const std::string &protocol_class_name, bool conjugated, mb_port::port_type_t port_type) - : d_port_name(port_name), d_conjugated(conjugated), d_port_type(port_type), + : d_port_name(port_name), d_port_symbol(pmt_intern(port_name)), + d_conjugated(conjugated), d_port_type(port_type), d_mblock(mblock) { pmt_t pc = mb_protocol_class_lookup(pmt_intern(protocol_class_name)); diff --git a/mblock/src/lib/mb_port.h b/mblock/src/lib/mb_port.h index 3c3e9636..3fdd25af 100644 --- a/mblock/src/lib/mb_port.h +++ b/mblock/src/lib/mb_port.h @@ -40,6 +40,7 @@ public: private: std::string d_port_name; + pmt_t d_port_symbol; // the port_name as a pmt symbol pmt_t d_protocol_class; bool d_conjugated; port_type_t d_port_type; @@ -58,6 +59,7 @@ protected: public: std::string port_name() const { return d_port_name; } + pmt_t port_symbol() const { return d_port_symbol; } pmt_t protocol_class() const { return d_protocol_class; } bool conjugated() const { return d_conjugated; } port_type_t port_type() const { return d_port_type; } @@ -77,9 +79,15 @@ public: */ virtual void send(pmt_t signal, - pmt_t data = PMT_NIL, - pmt_t metadata = PMT_NIL, + pmt_t data = PMT_F, + pmt_t metadata = PMT_F, mb_pri_t priority = MB_PRI_DEFAULT) = 0; + + /* + * \brief Invalidate any cached peer resolutions + * \implementation + */ + virtual void invalidate_cache() = 0; }; #endif /* INCLUDED_MB_PORT_H */ diff --git a/mblock/src/lib/mb_port_simple.cc b/mblock/src/lib/mb_port_simple.cc index 80cb65ef..cbfb0f27 100644 --- a/mblock/src/lib/mb_port_simple.cc +++ b/mblock/src/lib/mb_port_simple.cc @@ -37,7 +37,8 @@ mb_port_simple::mb_port_simple(mb_mblock *mblock, const std::string &protocol_class_name, bool conjugated, mb_port::port_type_t port_type) - : mb_port(mblock, port_name, protocol_class_name, conjugated, port_type) + : mb_port(mblock, port_name, protocol_class_name, conjugated, port_type), + d_cache_valid(false) { } @@ -67,6 +68,9 @@ mb_port_simple::find_accepter(mb_port_simple *start) mb_endpoint peer_ep; mb_msg_accepter_sptr r; + if (start->d_cache_valid) + return start->d_cached_accepter; + mbi_runtime_lock l(p->mblock()); // Set up initial context. @@ -78,6 +82,8 @@ mb_port_simple::find_accepter(mb_port_simple *start) case mb_port::EXTERNAL: // binding is in parent's name space context = p->mblock()->parent(); + if (!context) // can't be bound if there's no parent + return mb_msg_accepter_sptr(); // not bound break; default: @@ -97,7 +103,11 @@ mb_port_simple::find_accepter(mb_port_simple *start) case mb_port::INTERNAL: // Terminate here. case mb_port::EXTERNAL: r = pp->make_accepter(); - // FIXME cache the result + + // cache the result + + start->d_cached_accepter = r; + start->d_cache_valid = true; return r; case mb_port::RELAY: // Traverse to other side of relay port. @@ -130,5 +140,12 @@ mb_port_simple::find_accepter(mb_port_simple *start) mb_msg_accepter_sptr mb_port_simple::make_accepter() { - return d_mblock->impl()->make_accepter(port_name()); + return d_mblock->impl()->make_accepter(port_symbol()); +} + +void +mb_port_simple::invalidate_cache() +{ + d_cache_valid = false; + d_cached_accepter.reset(); } diff --git a/mblock/src/lib/mb_port_simple.h b/mblock/src/lib/mb_port_simple.h index 5cfbd3dc..9bfe0eaf 100644 --- a/mblock/src/lib/mb_port_simple.h +++ b/mblock/src/lib/mb_port_simple.h @@ -28,6 +28,9 @@ */ class mb_port_simple : public mb_port { + bool d_cache_valid; + mb_msg_accepter_sptr d_cached_accepter; + protected: static mb_msg_accepter_sptr find_accepter(mb_port_simple *start); @@ -57,6 +60,13 @@ public: pmt_t data = PMT_NIL, pmt_t metadata = PMT_NIL, mb_pri_t priority = MB_PRI_DEFAULT); + + /* + * \brief Invalidate any cached peer resolutions + * \implementation + */ + void invalidate_cache(); + }; #endif /* INCLUDED_MB_PORT_SIMPLE_H */ diff --git a/mblock/src/lib/mb_protocol_class.cc b/mblock/src/lib/mb_protocol_class.cc index f3eeb603..a11017fa 100644 --- a/mblock/src/lib/mb_protocol_class.cc +++ b/mblock/src/lib/mb_protocol_class.cc @@ -24,6 +24,7 @@ #endif #include +#include static pmt_t s_ALL_PROTOCOL_CLASSES = PMT_NIL; @@ -80,3 +81,25 @@ mb_protocol_class_lookup(pmt_t name) return PMT_NIL; } + +mb_protocol_class_init::mb_protocol_class_init(const char *data, size_t len) +{ + std::stringbuf sb; + sb.str(std::string(data, len)); + + while (1){ + pmt_t obj = pmt_deserialize(sb); + + if (0){ + pmt_write(obj, std::cout); + std::cout << std::endl; + } + + if (pmt_is_eof_object(obj)) + return; + + mb_make_protocol_class(pmt_nth(0, obj), // protocol-class name + pmt_nth(1, obj), // list of incoming msg names + pmt_nth(2, obj)); // list of outgoing msg names + } +} diff --git a/mblock/src/lib/mb_protocol_class.h b/mblock/src/lib/mb_protocol_class.h index f4382ada..60b87709 100644 --- a/mblock/src/lib/mb_protocol_class.h +++ b/mblock/src/lib/mb_protocol_class.h @@ -39,4 +39,14 @@ pmt_t mb_protocol_class_outgoing(pmt_t pc); //< return outgoing message set pmt_t mb_protocol_class_lookup(pmt_t name); //< lookup an existing protocol class by name + +/*! + * \brief Initialize one or more protocol class from a serialized description. + * Used by machine generated code. + */ +class mb_protocol_class_init { +public: + mb_protocol_class_init(const char *data, size_t len); +}; + #endif /* INCLUDED_MB_PROTOCOL_CLASS_H */ diff --git a/mblock/src/lib/mb_runtime.cc b/mblock/src/lib/mb_runtime.cc index 34a0af35..7f3b7867 100644 --- a/mblock/src/lib/mb_runtime.cc +++ b/mblock/src/lib/mb_runtime.cc @@ -24,15 +24,16 @@ #endif #include -#include +#include mb_runtime_sptr mb_make_runtime() { - return mb_runtime_sptr(new mb_runtime_thread_per_mblock()); + return mb_runtime_sptr(new mb_runtime_thread_per_block()); } mb_runtime::~mb_runtime() { // nop } + diff --git a/mblock/src/lib/mb_runtime.h b/mblock/src/lib/mb_runtime.h index 0929e30d..a804af06 100644 --- a/mblock/src/lib/mb_runtime.h +++ b/mblock/src/lib/mb_runtime.h @@ -37,40 +37,32 @@ mb_runtime_sptr mb_make_runtime(); class mb_runtime : boost::noncopyable, public boost::enable_shared_from_this { - omni_mutex d_brl; // big runtime lock (avoid using this if possible...) +protected: + mb_mblock_sptr d_top; public: mb_runtime(){} virtual ~mb_runtime(); /*! - * \brief Run the mblock hierarchy rooted at \p top + * \brief Construct and run the specified mblock hierarchy. * * This routine turns into the m-block scheduler, and * blocks until the system is shutdown. * - * \param top top-level mblock + * \param name name of the top-level mblock (conventionally "top") + * \param class_name The class of the top-level mblock to create. + * \param user_arg The argument to pass to the top-level mblock constructor + * * \returns true if the system ran successfully. */ - virtual bool run(mb_mblock_sptr top) = 0; - - - // ---------------------------------------------------------------- - // Stuff from here down is really private to the implementation... - // ---------------------------------------------------------------- - - /*! - * \brief lock the big runtime lock - * \implementation - */ - inline void lock() { d_brl.lock(); } - - /*! - * \brief unlock the big runtime lock - * \implementation - */ - inline void unlock() { d_brl.unlock(); } + virtual bool run(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg, + pmt_t *result = 0) = 0; + // QA only... + mb_mblock_sptr top() { return d_top; } }; #endif /* INCLUDED_MB_RUNTIME_H */ diff --git a/mblock/src/lib/mb_runtime_base.cc b/mblock/src/lib/mb_runtime_base.cc new file mode 100644 index 00000000..104b6afc --- /dev/null +++ b/mblock/src/lib/mb_runtime_base.cc @@ -0,0 +1,57 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include + +/* + * Default nop implementations... + */ + +void +mb_runtime_base::request_shutdown(pmt_t result) +{ +} + +pmt_t +mb_runtime_base::schedule_one_shot_timeout(const mb_time &abs_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter) +{ + return PMT_F; +} + +pmt_t +mb_runtime_base::schedule_periodic_timeout(const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter) +{ + return PMT_F; +} + +void +mb_runtime_base::cancel_timeout(pmt_t handle) +{ +} + diff --git a/mblock/src/lib/mb_runtime_base.h b/mblock/src/lib/mb_runtime_base.h new file mode 100644 index 00000000..cb76e450 --- /dev/null +++ b/mblock/src/lib/mb_runtime_base.h @@ -0,0 +1,78 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_MB_RUNTIME_BASE_H +#define INCLUDED_MB_RUNTIME_BASE_H + +#include +#include +#include + +/* + * \brief This is the runtime class used by the implementation. + */ +class mb_runtime_base : public mb_runtime +{ + omni_mutex d_brl; // big runtime lock (avoid using this if possible...) + +protected: + mb_msg_accepter_sptr d_accepter; + +public: + + /*! + * \brief lock the big runtime lock + * \implementation + */ + inline void lock() { d_brl.lock(); } + + /*! + * \brief unlock the big runtime lock + * \implementation + */ + inline void unlock() { d_brl.unlock(); } + + virtual void request_shutdown(pmt_t result); + + virtual mb_mblock_sptr + create_component(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg) = 0; + + virtual pmt_t + schedule_one_shot_timeout(const mb_time &abs_time, pmt_t user_data, + mb_msg_accepter_sptr accepter); + + virtual pmt_t + schedule_periodic_timeout(const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter); + virtual void + cancel_timeout(pmt_t handle); + + mb_msg_accepter_sptr + accepter() { return d_accepter; } + +}; + + +#endif /* INCLUDED_MB_RUNTIME_BASE_H */ diff --git a/mblock/src/lib/mb_runtime_nop.cc b/mblock/src/lib/mb_runtime_nop.cc index a19f0793..bcae1ebd 100644 --- a/mblock/src/lib/mb_runtime_nop.cc +++ b/mblock/src/lib/mb_runtime_nop.cc @@ -24,6 +24,8 @@ #endif #include #include +#include +#include mb_runtime_sptr mb_make_runtime_nop() @@ -42,23 +44,41 @@ mb_runtime_nop::~mb_runtime_nop() // nop for now } + bool -mb_runtime_nop::run(mb_mblock_sptr top) +mb_runtime_nop::run(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg, pmt_t *result) { class initial_visitor : public mb_visitor { public: - bool operator()(mb_mblock *mblock, const std::string &path) + bool operator()(mb_mblock *mblock) { - mblock->set_instance_name(path); - mblock->init_fsm(); + mblock->initial_transition(); return true; } }; - initial_visitor visitor; + initial_visitor visitor; + + if (result) + *result = PMT_T; - top->walk_tree(&visitor); + d_top = create_component(instance_name, class_name, user_arg); + d_top->walk_tree(&visitor); return true; } + +mb_mblock_sptr +mb_runtime_nop::create_component(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg) +{ + mb_mblock_maker_t maker; + if (!mb_class_registry::lookup_maker(class_name, &maker)) + throw mbe_no_such_class(0, class_name + " (in " + instance_name + ")"); + + return maker(this, instance_name, user_arg); +} diff --git a/mblock/src/lib/mb_runtime_nop.h b/mblock/src/lib/mb_runtime_nop.h index dc788799..d7fe105b 100644 --- a/mblock/src/lib/mb_runtime_nop.h +++ b/mblock/src/lib/mb_runtime_nop.h @@ -21,7 +21,7 @@ #ifndef INCLUDED_MB_RUNTIME_NOP_H #define INCLUDED_MB_RUNTIME_NOP_H -#include +#include /*! * \brief Public constructor (factory) for mb_runtime_nop objects. @@ -31,14 +31,22 @@ mb_runtime_sptr mb_make_runtime_nop(); /*! * \brief Concrete runtime that does nothing. Used only during early QA tests. */ -class mb_runtime_nop : public mb_runtime +class mb_runtime_nop : public mb_runtime_base { - public: mb_runtime_nop(); ~mb_runtime_nop(); - bool run(mb_mblock_sptr top); + bool run(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg, + pmt_t *result); + +protected: + mb_mblock_sptr + create_component(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg); }; #endif /* INCLUDED_MB_RUNTIME_NOP_H */ diff --git a/mblock/src/lib/mb_runtime_placeholder.cc b/mblock/src/lib/mb_runtime_placeholder.cc deleted file mode 100644 index 5ce7cc2a..00000000 --- a/mblock/src/lib/mb_runtime_placeholder.cc +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- 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 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with 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 -#endif -#include -#include -#include - - -static mb_runtime *s_singleton = 0; - - -mb_runtime_placeholder::mb_runtime_placeholder() -{ - // nop -} - -mb_runtime_placeholder::~mb_runtime_placeholder() -{ - // nop -} - -bool -mb_runtime_placeholder::run(mb_mblock_sptr top) -{ - throw mbe_not_implemented(top.get(), "mb_runtime_placeholder::run"); -} - -mb_runtime * -mb_runtime_placeholder::singleton() -{ - if (s_singleton) - return s_singleton; - - s_singleton = new mb_runtime_placeholder(); - return s_singleton; -} diff --git a/mblock/src/lib/mb_runtime_placeholder.h b/mblock/src/lib/mb_runtime_placeholder.h deleted file mode 100644 index b55d39f9..00000000 --- a/mblock/src/lib/mb_runtime_placeholder.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- 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 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef INCLUDED_MB_RUNTIME_PLACEHOLDER_H -#define INCLUDED_MB_RUNTIME_PLACEHOLDER_H - -#include - -/*! - * \brief Concrete runtime that serves as a placeholder until the real - * runtime is known. - * - * The singleton instance of this class is installed in the d_runtime - * instance variable of each mb_mblock_impl at construction time. - * Having a valid instance of runtime removes the "pre runtime::run" - * corner case, and allows us to lock and unlock the big runtime lock - * even though there's no "real runtime" yet. - */ -class mb_runtime_placeholder : public mb_runtime -{ - -public: - mb_runtime_placeholder(); - ~mb_runtime_placeholder(); - - //! throws mbe_not_implemented - bool run(mb_mblock_sptr top); - - //! Return the placeholder singleton - static mb_runtime *singleton(); -}; - -#endif /* INCLUDED_MB_RUNTIME_PLACEHOLDER_H */ diff --git a/mblock/src/lib/mb_runtime_thread_per_block.cc b/mblock/src/lib/mb_runtime_thread_per_block.cc new file mode 100644 index 00000000..7ad87aa2 --- /dev/null +++ b/mblock/src/lib/mb_runtime_thread_per_block.cc @@ -0,0 +1,349 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static pmt_t s_halt = pmt_intern("%halt"); +static pmt_t s_sys_port = pmt_intern("%sys-port"); +static pmt_t s_shutdown = pmt_intern("%shutdown"); +static pmt_t s_request_shutdown = pmt_intern("%request-shutdown"); +static pmt_t s_worker_state_changed = pmt_intern("%worker-state-changed"); +static pmt_t s_timeout = pmt_intern("%timeout"); +static pmt_t s_request_timeout = pmt_intern("%request-timeout"); +static pmt_t s_cancel_timeout = pmt_intern("%cancel-timeout"); +static pmt_t s_send_halt = pmt_intern("send-halt"); +static pmt_t s_exit_now = pmt_intern("exit-now"); + +static void +send_sys_msg(mb_msg_queue &msgq, pmt_t signal, + pmt_t data = PMT_F, pmt_t metadata = PMT_F, + mb_pri_t priority = MB_PRI_BEST) +{ + mb_message_sptr msg = mb_make_message(signal, data, metadata, priority); + msg->set_port_id(s_sys_port); + msgq.insert(msg); +} + + +mb_runtime_thread_per_block::mb_runtime_thread_per_block() + : d_shutdown_in_progress(false), + d_shutdown_result(PMT_T) +{ + d_accepter = mb_msg_accepter_sptr(new mb_msg_accepter_msgq(&d_msgq)); +} + +mb_runtime_thread_per_block::~mb_runtime_thread_per_block() +{ + // FIXME iterate over workers and ensure that they are dead. + + if (!d_workers.empty()) + std::cerr << "\nmb_runtime_thread_per_block: dtor (# workers = " + << d_workers.size() << ")\n"; +} + +void +mb_runtime_thread_per_block::request_shutdown(pmt_t result) +{ + (*accepter())(s_request_shutdown, result, PMT_F, MB_PRI_BEST); +} + +bool +mb_runtime_thread_per_block::run(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg, pmt_t *result) +{ + if (result) // set it to something now, in case we throw + *result = PMT_F; + + // reset the shutdown state + d_shutdown_in_progress = false; + d_shutdown_result = PMT_T; + + assert(d_workers.empty()); + + while (!d_timer_queue.empty()) // ensure timer queue is empty + d_timer_queue.pop(); + + /* + * Create the top-level component, and recursively all of its + * subcomponents. + */ + d_top = create_component(instance_name, class_name, user_arg); + + try { + run_loop(); + } + catch (...){ + d_top.reset(); + throw; + } + + if (result) + *result = d_shutdown_result; + + d_top.reset(); + return true; +} + +void +mb_runtime_thread_per_block::run_loop() +{ + while (1){ + mb_message_sptr msg; + + if (d_timer_queue.empty()) // Any timeouts pending? + msg = d_msgq.get_highest_pri_msg(); // Nope. Block forever. + + else { + mb_timeout_sptr to = d_timer_queue.top(); // Yep. Get earliest timeout. + + // wait for a msg or the timeout... + msg = d_msgq.get_highest_pri_msg_timedwait(to->d_when); + + if (!msg){ // We timed out. + d_timer_queue.pop(); // Remove timeout from timer queue. + + // send the %timeout msg + (*to->d_accepter)(s_timeout, to->d_user_data, to->handle(), MB_PRI_BEST); + + if (to->d_is_periodic){ + to->d_when = to->d_when + to->d_delta; // update time of next firing + d_timer_queue.push(to); // push it back into the queue + } + continue; + } + } + + pmt_t signal = msg->signal(); + + if (pmt_eq(signal, s_worker_state_changed)){ // %worker-state-changed + omni_mutex_lock l1(d_workers_mutex); + reap_dead_workers(); + if (d_workers.empty()) // no work left to do... + return; + } + else if (pmt_eq(signal, s_request_shutdown)){ // %request-shutdown + if (!d_shutdown_in_progress){ + d_shutdown_in_progress = true; + d_shutdown_result = msg->data(); + + // schedule a timeout for ourselves... + schedule_one_shot_timeout(mb_time::time(0.100), s_send_halt, d_accepter); + send_all_sys_msg(s_shutdown); + } + } + else if (pmt_eq(signal, s_request_timeout)){ // %request-timeout + mb_timeout_sptr to = + boost::any_cast(pmt_any_ref(msg->data())); + d_timer_queue.push(to); + } + else if (pmt_eq(signal, s_cancel_timeout)){ // %cancel-timeout + d_timer_queue.cancel(msg->data()); + } + else if (pmt_eq(signal, s_timeout) + && pmt_eq(msg->data(), s_send_halt)){ // %timeout, send-halt + + // schedule another timeout for ourselves... + schedule_one_shot_timeout(mb_time::time(0.100), s_exit_now, d_accepter); + send_all_sys_msg(s_halt); + } + else if (pmt_eq(signal, s_timeout) + && pmt_eq(msg->data(), s_exit_now)){ // %timeout, exit-now + + // We only get here if we've sent all workers %shutdown followed + // by %halt, and one or more of them is still alive. They must + // be blocked in the kernel. FIXME We could add one more step: + // pthread_kill(...) but for now, we'll just ignore them... + return; + } + else { + std::cerr << "mb_runtime_thread_per_block: unhandled msg: " << msg << std::endl; + } + } +} + +void +mb_runtime_thread_per_block::reap_dead_workers() +{ + // Already holding mutex + // omni_mutex_lock l1(d_workers_mutex); + + for (worker_iter_t wi = d_workers.begin(); wi != d_workers.end(); ){ + bool is_dead; + + // We can't join while holding the worker mutex, since that would + // attempt to destroy the mutex we're holding (omnithread's join + // deletes the omni_thread object after the pthread_join + // completes) Instead, we lock just long enough to figure out if + // the worker is dead. + { + omni_mutex_lock l2((*wi)->d_mutex); + is_dead = (*wi)->d_state == mb_worker::TS_DEAD; + } + + if (is_dead){ + if (0) + std::cerr << "\nruntime: " + << "(" << (*wi)->id() << ") " + << (*wi)->d_mblock->instance_name() << " is TS_DEAD\n"; + void *ignore; + (*wi)->join(&ignore); + wi = d_workers.erase(wi); + continue; + } + ++wi; + } +} + +// +// Create the thread, then create the component in the thread. +// Return a pointer to the created mblock. +// +// Can be invoked from any thread +// +mb_mblock_sptr +mb_runtime_thread_per_block::create_component(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg) +{ + mb_mblock_maker_t maker; + if (!mb_class_registry::lookup_maker(class_name, &maker)) + throw mbe_no_such_class(0, class_name + " (in " + instance_name + ")"); + + // FIXME here's where we'd lookup NUMA placement requests & mblock + // priorities and communicate them to the worker we're creating... + + // Create the worker thread + mb_worker *w = + new mb_worker(this, maker, instance_name, user_arg); + + w->start_undetached(); // start it + + // Wait for it to reach TS_RUNNING or TS_DEAD + + bool is_dead; + mb_worker::cause_of_death_t why_dead; + { + omni_mutex_lock l(w->d_mutex); + while (!(w->d_state == mb_worker::TS_RUNNING + || w->d_state == mb_worker::TS_DEAD)) + w->d_state_cond.wait(); + + is_dead = w->d_state == mb_worker::TS_DEAD; + why_dead = w->d_why_dead; + } + + // If the worker failed to init (constructor or initial_transition + // raised an exception), reap the worker now and raise an exception. + + if (is_dead && why_dead != mb_worker::RIP_EXIT){ + + void *ignore; + w->join(&ignore); + + // FIXME with some work we ought to be able to propagate the + // exception from the worker. + throw mbe_mblock_failed(0, instance_name); + } + + assert(w->d_mblock); + + // Add w to the vector of workers, and return the mblock. + { + omni_mutex_lock l(d_workers_mutex); + d_workers.push_back(w); + } + + if (0) + std::cerr << "\nruntime: created " + << "(" << w->id() << ") " + << w->d_mblock->instance_name() << "\n"; + + return w->d_mblock; +} + +void +mb_runtime_thread_per_block::send_all_sys_msg(pmt_t signal, + pmt_t data, + pmt_t metadata, + mb_pri_t priority) +{ + omni_mutex_lock l1(d_workers_mutex); + + for (worker_iter_t wi = d_workers.begin(); wi != d_workers.end(); ++wi){ + send_sys_msg((*wi)->d_mblock->impl()->msgq(), + signal, data, metadata, priority); + } +} + +// +// Can be invoked from any thread. +// Sends a message to the runtime. +// +pmt_t +mb_runtime_thread_per_block::schedule_one_shot_timeout + (const mb_time &abs_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter) +{ + mb_timeout_sptr to(new mb_timeout(abs_time, user_data, accepter)); + (*d_accepter)(s_request_timeout, pmt_make_any(to), PMT_F, MB_PRI_BEST); + return to->handle(); +} + +// +// Can be invoked from any thread. +// Sends a message to the runtime. +// +pmt_t +mb_runtime_thread_per_block::schedule_periodic_timeout + (const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter) +{ + mb_timeout_sptr to(new mb_timeout(first_abs_time, delta_time, + user_data, accepter)); + (*d_accepter)(s_request_timeout, pmt_make_any(to), PMT_F, MB_PRI_BEST); + return to->handle(); +} + +// +// Can be invoked from any thread. +// Sends a message to the runtime. +// +void +mb_runtime_thread_per_block::cancel_timeout(pmt_t handle) +{ + (*d_accepter)(s_cancel_timeout, handle, PMT_F, MB_PRI_BEST); +} diff --git a/mblock/src/lib/mb_runtime_thread_per_block.h b/mblock/src/lib/mb_runtime_thread_per_block.h new file mode 100644 index 00000000..773b8b4e --- /dev/null +++ b/mblock/src/lib/mb_runtime_thread_per_block.h @@ -0,0 +1,84 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_MB_RUNTIME_THREAD_PER_BLOCK_H +#define INCLUDED_MB_RUNTIME_THREAD_PER_BLOCK_H + +#include +#include +#include +#include + +/*! + * \brief Concrete runtime that uses a thread per mblock + * \implementation + * + * These are all implementation details. + */ +class mb_runtime_thread_per_block : public mb_runtime_base +{ +public: + omni_mutex d_workers_mutex; // hold while manipulating d_workers + std::vector d_workers; + bool d_shutdown_in_progress; + pmt_t d_shutdown_result; + mb_msg_queue d_msgq; + mb_timer_queue d_timer_queue; + + typedef std::vector::iterator worker_iter_t; + + mb_runtime_thread_per_block(); + ~mb_runtime_thread_per_block(); + + bool run(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg, + pmt_t *result); + + void request_shutdown(pmt_t result); + +protected: + mb_mblock_sptr + create_component(const std::string &instance_name, + const std::string &class_name, + pmt_t user_arg); + + pmt_t + schedule_one_shot_timeout(const mb_time &abs_time, pmt_t user_data, + mb_msg_accepter_sptr accepter); + + pmt_t + schedule_periodic_timeout(const mb_time &first_abs_time, + const mb_time &delta_time, + pmt_t user_data, + mb_msg_accepter_sptr accepter); + void + cancel_timeout(pmt_t handle); + +private: + void reap_dead_workers(); + void run_loop(); + + void send_all_sys_msg(pmt_t signal, pmt_t data = PMT_F, + pmt_t metadata = PMT_F, + mb_pri_t priority = MB_PRI_BEST); +}; + +#endif /* INCLUDED_MB_RUNTIME_THREAD_PER_BLOCK_H */ diff --git a/mblock/src/lib/mb_runtime_thread_per_mblock.cc b/mblock/src/lib/mb_runtime_thread_per_mblock.cc deleted file mode 100644 index 925dcdc4..00000000 --- a/mblock/src/lib/mb_runtime_thread_per_mblock.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- 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 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with 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 -#endif -#include -#include -#include - - -mb_runtime_thread_per_mblock::mb_runtime_thread_per_mblock() -{ - // nop for now -} - -mb_runtime_thread_per_mblock::~mb_runtime_thread_per_mblock() -{ - // nop for now -} - -bool -mb_runtime_thread_per_mblock::run(mb_mblock_sptr top) -{ - class initial_visitor : public mb_visitor - { - mb_runtime *d_rt; - - public: - initial_visitor(mb_runtime *rt) : d_rt(rt) {} - bool operator()(mb_mblock *mblock, const std::string &path) - { - mblock->impl()->set_runtime(d_rt); - mblock->set_instance_name(path); - mblock->init_fsm(); - return true; - } - }; - - initial_visitor visitor(this); - - d_top = top; // remember top of tree - - d_top->walk_tree(&visitor); - - return true; -} diff --git a/mblock/src/lib/mb_runtime_thread_per_mblock.h b/mblock/src/lib/mb_runtime_thread_per_mblock.h deleted file mode 100644 index cef756ec..00000000 --- a/mblock/src/lib/mb_runtime_thread_per_mblock.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- 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 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifndef INCLUDED_MB_RUNTIME_THREAD_PER_MBLOCK_H -#define INCLUDED_MB_RUNTIME_THREAD_PER_MBLOCK_H - -#include - -/*! - * \brief Concrete runtime that uses a single thread for all work. - */ -class mb_runtime_thread_per_mblock : public mb_runtime -{ - mb_mblock_sptr d_top; // top mblock - -public: - mb_runtime_thread_per_mblock(); - ~mb_runtime_thread_per_mblock(); - - bool run(mb_mblock_sptr top); -}; - -#endif /* INCLUDED_MB_RUNTIME_THREAD_PER_MBLOCK_H */ diff --git a/mblock/src/lib/mb_time.cc b/mblock/src/lib/mb_time.cc new file mode 100644 index 00000000..73c86e4f --- /dev/null +++ b/mblock/src/lib/mb_time.cc @@ -0,0 +1,84 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include + + +mb_time::mb_time(double real_secs) +{ + double floor_secs = floor(real_secs); + d_secs = (long) floor_secs; + d_nsecs = (long) ((real_secs - floor_secs) * 1e9); // always positive +} + +mb_time +mb_time::time(const mb_time &delta_t) +{ + unsigned long abs_sec, abs_nsec; + unsigned long rel_sec = delta_t.d_secs; + unsigned long rel_nsec = delta_t.d_nsecs; + + omni_thread::get_time(&abs_sec, &abs_nsec, rel_sec, rel_nsec); + return mb_time(abs_sec, abs_nsec); +} + + +mb_time +operator+(const mb_time &x, const mb_time &y) +{ + mb_time r(x.d_secs + y.d_secs, x.d_nsecs + y.d_nsecs); + while (r.d_nsecs >= 1000000000){ + r.d_nsecs -= 1000000000; + r.d_secs++; + } + return r; +} + +mb_time +operator-(const mb_time &x, const mb_time &y) +{ + // assert(!(x < y)); + + mb_time r(x.d_secs - y.d_secs, x.d_nsecs - y.d_nsecs); + while (r.d_nsecs < 0){ + r.d_nsecs += 1000000000; + r.d_secs--; + } + return r; +} + +mb_time +operator+(const mb_time &x, double y) +{ + return x + mb_time(y); +} + +mb_time +operator-(const mb_time &x, double y) +{ + return x - mb_time(y); +} diff --git a/mblock/src/lib/mb_time.h b/mblock/src/lib/mb_time.h new file mode 100644 index 00000000..872304ca --- /dev/null +++ b/mblock/src/lib/mb_time.h @@ -0,0 +1,89 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_MB_TIME_H +#define INCLUDED_MB_TIME_H + +struct mb_time { + long int d_secs; // seconds. + long int d_nsecs; // nanoseconds. Always in [0, 1e9-1] + + mb_time() : d_secs(0), d_nsecs(0) {} + mb_time(long secs, long nanosecs=0) : d_secs(secs), d_nsecs(nanosecs) {} + + // N.B., this only makes sense for differences between times. + // Double doesn't have enough bits to precisely represent an absolute time. + mb_time(double secs); + + // N.B. This only makes sense for differences between times. + // Double doesn't have enough bits to precisely represent an absolute time. + double double_time() const { return (double)d_secs + d_nsecs * 1e-9; } + + /*! + * \brief Return an absolute time suitable for use with + * schedule_one_shot_timeout & schedule_periodic_timeout + * + * The return value is the current time plus the given relative offset. + */ + static mb_time time(const mb_time &relative_offset = mb_time()); +}; + + +inline static bool +operator<(const mb_time &x, const mb_time &y) +{ + return ((x.d_secs < y.d_secs) + || (x.d_secs == y.d_secs && x.d_nsecs < y.d_nsecs)); +} + +inline static bool +operator>(const mb_time &x, const mb_time &y) +{ + return ((x.d_secs > y.d_secs) + || (x.d_secs == y.d_secs && x.d_nsecs > y.d_nsecs)); +} + +inline static bool +operator>=(const mb_time &x, const mb_time &y) +{ + return ((x.d_secs > y.d_secs) + || (x.d_secs == y.d_secs && x.d_nsecs >= y.d_nsecs)); +} + +inline static bool +operator<=(const mb_time &x, const mb_time &y) +{ + return ((x.d_secs < y.d_secs) + || (x.d_secs == y.d_secs && x.d_nsecs <= y.d_nsecs)); +} + +inline static bool +operator==(const mb_time &x, const mb_time &y) +{ + return (x.d_secs == y.d_secs && x.d_nsecs == y.d_nsecs); +} + + +mb_time operator+(const mb_time &x, const mb_time &y); +mb_time operator+(const mb_time &x, double y); +mb_time operator-(const mb_time &x, const mb_time &y); +mb_time operator-(const mb_time &x, double y); + +#endif /* INCLUDED_MB_TIME_H */ diff --git a/mblock/src/lib/mb_timer_queue.cc b/mblock/src/lib/mb_timer_queue.cc new file mode 100644 index 00000000..026035ec --- /dev/null +++ b/mblock/src/lib/mb_timer_queue.cc @@ -0,0 +1,63 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include + +static pmt_t +make_handle() +{ + static long counter = 0; + pmt_t n = pmt_from_long(counter++); + return pmt_list1(n); // guaranteed to be a unique object +} + +// one-shot constructor +mb_timeout::mb_timeout(const mb_time &abs_time, + pmt_t user_data, mb_msg_accepter_sptr accepter) + : d_when(abs_time), d_is_periodic(false), + d_user_data(user_data), d_handle(make_handle()), d_accepter(accepter) +{ +} + +// periodic constructor +mb_timeout::mb_timeout(const mb_time &first_abs_time, const mb_time &delta_time, + pmt_t user_data, mb_msg_accepter_sptr accepter) + : d_when(first_abs_time), d_delta(delta_time), d_is_periodic(true), + d_user_data(user_data), d_handle(make_handle()), d_accepter(accepter) +{ +} + +void +mb_timer_queue::cancel(pmt_t handle) +{ + container_type::iterator it; + + for (it = c.begin(); it != c.end();){ + if (pmt_equal((*it)->handle(), handle)) + it = c.erase(it); + else + ++it; + } + std::make_heap(c.begin(), c.end(), comp); +} diff --git a/mblock/src/lib/mb_timer_queue.h b/mblock/src/lib/mb_timer_queue.h new file mode 100644 index 00000000..a1768833 --- /dev/null +++ b/mblock/src/lib/mb_timer_queue.h @@ -0,0 +1,73 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_MB_TIMER_QUEUE_H +#define INCLUDED_MB_TIMER_QUEUE_H + +#include +#include +#include +#include +#include + +class mb_timeout { +public: + mb_time d_when; // absolute time to fire timeout + mb_time d_delta; // if periodic, delta_t to next timeout + bool d_is_periodic; // true iff this is a periodic timeout + pmt_t d_user_data; // data from %timeout msg + pmt_t d_handle; // handle for cancellation + mb_msg_accepter_sptr d_accepter; // where to send the message + + // one-shot constructor + mb_timeout(const mb_time &abs_time, + pmt_t user_data, mb_msg_accepter_sptr accepter); + + // periodic constructor + mb_timeout(const mb_time &first_abs_time, const mb_time &delta_time, + pmt_t user_data, mb_msg_accepter_sptr accepter); + + pmt_t handle() const { return d_handle; } +}; + +typedef boost::shared_ptr mb_timeout_sptr; + + +//! Sort criterion for priority_queue +class timeout_later +{ +public: + bool operator() (const mb_timeout_sptr t1, const mb_timeout_sptr t2) + { + return t1->d_when > t2->d_when; + } +}; + + +class mb_timer_queue : public std::priority_queue, + timeout_later> +{ +public: + void cancel(pmt_t handle); +}; + +#endif /* INCLUDED_MB_TIMER_QUEUE_H */ diff --git a/mblock/src/lib/mb_worker.cc b/mblock/src/lib/mb_worker.cc new file mode 100644 index 00000000..8a924844 --- /dev/null +++ b/mblock/src/lib/mb_worker.cc @@ -0,0 +1,178 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SCHED_H +#include +#endif + +#define VERBOSE 0 // define to 0 or 1 + + +static pmt_t s_worker_state_changed = pmt_intern("%worker-state-changed"); + + +mb_worker::mb_worker(mb_runtime_thread_per_block *runtime, + mb_mblock_maker_t maker, + const std::string &instance_name, + pmt_t user_arg) + : omni_thread((void *) 0, PRIORITY_NORMAL), + d_runtime(runtime), d_maker(maker), + d_instance_name(instance_name), d_user_arg(user_arg), + d_state_cond(&d_mutex), d_state(TS_UNINITIALIZED), + d_why_dead(RIP_NOT_DEAD_YET) +{ +} + +#if 0 +mb_worker::~mb_worker() +{ +} +#endif + +#ifdef HAVE_SCHED_SETAFFINITY +static void +set_affinity(const std::string &instance_name, const std::string &class_name) +{ + //static int counter = 0; + cpu_set_t mask; + CPU_ZERO(&mask); + + if (0){ + + //CPU_SET(counter & 0x1, &mask); + //counter++; + CPU_SET(0, &mask); + + int r = sched_setaffinity(mb_gettid(), sizeof(mask), &mask); + if (r == -1) + perror("sched_setaffinity"); + } +} +#else +static void +set_affinity(const std::string &instance_name, const std::string &class_name) +{ +} +#endif + +void +mb_worker::set_state(worker_state_t state) +{ + { + omni_mutex_lock l2(d_mutex); + + d_state = state; // update our state + d_state_cond.broadcast(); // Notify everybody who cares... + } + + // send msg to runtime, telling it something changed. + (*d_runtime->accepter())(s_worker_state_changed, PMT_F, PMT_F, MB_PRI_BEST); +} + +void * +mb_worker::run_undetached(void *ignored) +{ + // FIXME add pthread_sigmask stuff + + //set_affinity(d_instance_name, d_class_name); + set_affinity(d_instance_name, ""); + + try { + worker_thread_top_level(); + d_why_dead = RIP_EXIT; + } + catch (mbe_terminate){ + d_why_dead = RIP_TERMINATE; + } + catch (mbe_exit){ + d_why_dead = RIP_EXIT; + } + catch (std::logic_error e){ + if (d_why_dead == RIP_NOT_DEAD_YET) + d_why_dead = RIP_UNHANDLED_EXCEPTION; + + std::cerr << "\nmb_worker::run_undetached: unhandled exception:\n"; + std::cerr << " " << e.what() << std::endl; + } + catch (...){ + if (d_why_dead == RIP_NOT_DEAD_YET) + d_why_dead = RIP_UNHANDLED_EXCEPTION; + } + + if (VERBOSE) + std::cerr << "\nrun_undetached: about to return, d_why_dead = " + << d_why_dead << std::endl; + + set_state(TS_DEAD); + return 0; +} + +void +mb_worker::worker_thread_top_level() +{ + if (VERBOSE) + std::cerr << "worker_thread_top_level (enter):" << std::endl + << " instance_name: " << d_instance_name << std::endl + << " omnithread id: " << id() << std::endl + << " gettid: " << mb_gettid() << std::endl + << " getpid: " << getpid() << std::endl; + + cause_of_death_t pending_cause_of_death = RIP_NOT_DEAD_YET; + + try { + pending_cause_of_death = RIP_CTOR_EXCEPTION; + d_mblock = d_maker(d_runtime, d_instance_name, d_user_arg); + + if (VERBOSE) + std::cerr << "worker_thread_top_level (post-construction):" << std::endl + << " instance_name: " << d_instance_name << std::endl; + + pending_cause_of_death = RIP_INIT_EXCEPTION; + d_mblock->initial_transition(); + + if (VERBOSE) + std::cerr << "worker_thread_top_level (post-initial-transition):" << std::endl + << " instance_name: " << d_instance_name << std::endl; + + set_state(TS_RUNNING); + + pending_cause_of_death = RIP_UNHANDLED_EXCEPTION; + d_mblock->main_loop(); + } + catch (...){ + d_why_dead = pending_cause_of_death; + throw; + } + + if (VERBOSE) + std::cerr << "worker_thread_top_level (exit):" << std::endl + << " instance_name: " << d_instance_name << std::endl; +} diff --git a/mblock/src/lib/mb_worker.h b/mblock/src/lib/mb_worker.h new file mode 100644 index 00000000..b207f4db --- /dev/null +++ b/mblock/src/lib/mb_worker.h @@ -0,0 +1,106 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_MB_WORKER_H +#define INCLUDED_MB_WORKER_H + +#include +#include +#include + + +class mb_worker; +//typedef boost::shared_ptr mb_worker_sptr; + +class mb_runtime_thread_per_block; + +/*! + * \brief Worker thread for thread_per_block runtime + * \implementation + */ +class mb_worker : public omni_thread +{ +public: + //! worker thread states + enum worker_state_t { + TS_UNINITIALIZED, // new, uninitialized + TS_RUNNING, // normal steady-state condition. + TS_DEAD // thread is dead + }; + + //! why we're dead + enum cause_of_death_t { + RIP_NOT_DEAD_YET, // not dead + RIP_EXIT, // normal exit + RIP_TERMINATE, // caught terminate exception + RIP_CTOR_EXCEPTION, // constructor raised an exception + RIP_INIT_EXCEPTION, // initial_transition rasised an exception + RIP_UNHANDLED_EXCEPTION // somebody (most likely handle_message) raised an exception + }; + + /* + * Args used by new thread to create mb_mblock + */ + mb_runtime_thread_per_block *d_runtime; + mb_mblock_maker_t d_maker; + std::string d_instance_name; + pmt_t d_user_arg; + + mb_mblock_sptr d_mblock; //< holds pointer to created mblock + + /*! + * \brief General mutex for all these fields. + * + * They are accessed by both the main runtime thread and the newly + * created thread that runs the mblock's main loop. + */ + omni_mutex d_mutex; + omni_condition d_state_cond; //< state change notifications + worker_state_t d_state; + cause_of_death_t d_why_dead; + + mb_worker(mb_runtime_thread_per_block *runtime, + mb_mblock_maker_t maker, + const std::string &instance_name, + pmt_t user_arg); + + // ~mb_worker(); + + + /*! + * \brief This code runs as the top-level of the new thread + */ + void worker_thread_top_level(); + + /*! + * \brief Invokes the top-level of the new thread (name kind of sucks) + */ + void *run_undetached(void *arg); + +private: + // Neither d_mutex nor runtime->d_mutex may be held while calling this. + // It locks and unlocks them itself. + void set_state(worker_state_t state); +}; + + + +#endif /* INCLUDED_MB_WORKER_H */ diff --git a/mblock/src/lib/mbi_runtime_lock.h b/mblock/src/lib/mbi_runtime_lock.h index 3138a7e1..4174f6d5 100644 --- a/mblock/src/lib/mbi_runtime_lock.h +++ b/mblock/src/lib/mbi_runtime_lock.h @@ -48,9 +48,9 @@ */ class mbi_runtime_lock : boost::noncopyable { - mb_runtime *d_rt; + mb_runtime_base *d_rt; public: - mbi_runtime_lock(mb_runtime *rt) : d_rt(rt) { d_rt->lock(); } + mbi_runtime_lock(mb_runtime_base *rt) : d_rt(rt) { d_rt->lock(); } mbi_runtime_lock(mb_mblock_impl *mi) : d_rt(mi->runtime()) { d_rt->lock(); } mbi_runtime_lock(mb_mblock *mb) : d_rt(mb->impl()->runtime()) { d_rt->lock(); } ~mbi_runtime_lock(void) { d_rt->unlock(); } diff --git a/mblock/src/lib/qa_bitset.cc b/mblock/src/lib/qa_bitset.cc new file mode 100644 index 00000000..0acda823 --- /dev/null +++ b/mblock/src/lib/qa_bitset.cc @@ -0,0 +1,493 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include +#include +#include + +static pmt_t s_in = pmt_intern("in"); +static pmt_t s_out = pmt_intern("out"); +static pmt_t s_data = pmt_intern("data"); +static pmt_t s_start = pmt_intern("start"); +static pmt_t s_send_batch = pmt_intern("send-batch"); +static pmt_t s_long0 = pmt_from_long(0); + +static std::string +str(long x) +{ + std::ostringstream s; + s << x; + return s.str(); +} + +/*! + * \brief mblock used for QA. + * + * Messages arriving on "in" consist of a pair containing a (long) + * message number in the car, and a (long) bitmap in the cdr. For + * each message received on "in", a new message is sent on "out". The + * new message is the same format as the input, but the bitmap in + * the cdr has a "1" or'd into it that corresponds to the bit number + * specified in the constructor. + * + * The bitmap can be used by the ultimate receiver to confirm + * traversal of a set of blocks, if the blocks are assigned unique bit + * numbers. + */ +class qa_bitset : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + int d_bitno; + +public: + qa_bitset(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void handle_message(mb_message_sptr msg); +}; + +qa_bitset::qa_bitset(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::EXTERNAL); + d_out = define_port("out", "qa-bitset", true, mb_port::EXTERNAL); +} + +void +qa_bitset::handle_message(mb_message_sptr msg) +{ + if (pmt_eq(msg->port_id(), s_in) && pmt_eq(msg->signal(), s_data)){ + d_out->send(s_data, + pmt_cons(pmt_car(msg->data()), + pmt_from_long((1L << d_bitno) | pmt_to_long(pmt_cdr(msg->data()))))); + } +} + +REGISTER_MBLOCK_CLASS(qa_bitset); + +// ------------------------------------------------------------------------ + +/*! + * \brief mblock used for QA. Compose two qa_bitset mblocks. + */ +class qa_bitset2 : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + +public: + qa_bitset2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); +}; + +qa_bitset2::qa_bitset2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + long bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + + define_component("bs0", "qa_bitset", pmt_from_long(bitno)); + define_component("bs1", "qa_bitset", pmt_from_long(bitno + 1)); + connect("self", "in", "bs0", "in"); + connect("bs0", "out", "bs1", "in"); + connect("bs1", "out", "self", "out"); +} + +REGISTER_MBLOCK_CLASS(qa_bitset2); + +// ------------------------------------------------------------------------ + +/*! + * \brief mblock used for QA. Compose two qa_bitset2 mblocks. + */ +class qa_bitset4 : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + +public: + qa_bitset4(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); +}; + +qa_bitset4::qa_bitset4(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + long bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + + define_component("bs0", "qa_bitset2", pmt_from_long(bitno)); + define_component("bs1", "qa_bitset2", pmt_from_long(bitno + 2)); + connect("self", "in", "bs0", "in"); + connect("bs0", "out", "bs1", "in"); + connect("bs1", "out", "self", "out"); +} + +REGISTER_MBLOCK_CLASS(qa_bitset4); + +// ------------------------------------------------------------------------ + +/*! + * \brief mblock used for QA. Compose two qa_bitset4 mblocks. + */ +class qa_bitset8 : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + +public: + qa_bitset8(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); +}; + +qa_bitset8::qa_bitset8(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + long bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + + define_component("bs0", "qa_bitset4", pmt_from_long(bitno)); + define_component("bs1", "qa_bitset4", pmt_from_long(bitno + 4)); + connect("self", "in", "bs0", "in"); + connect("bs0", "out", "bs1", "in"); + connect("bs1", "out", "self", "out"); +} + +REGISTER_MBLOCK_CLASS(qa_bitset8); + +// ------------------------------------------------------------------------ + +/*! + * \brief mblock used for QA. Compose two qa_bitset8 mblocks. + */ +class qa_bitset16 : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + +public: + qa_bitset16(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); +}; + +qa_bitset16::qa_bitset16(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + long bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + + define_component("bs0", "qa_bitset8", pmt_from_long(bitno)); + define_component("bs1", "qa_bitset8", pmt_from_long(bitno + 8)); + connect("self", "in", "bs0", "in"); + connect("bs0", "out", "bs1", "in"); + connect("bs1", "out", "self", "out"); +} + +REGISTER_MBLOCK_CLASS(qa_bitset16); + +// ------------------------------------------------------------------------ + +/*! + * \brief mblock used for QA. Compose two qa_bitset16 mblocks. + */ +class qa_bitset32 : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + +public: + qa_bitset32(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); +}; + +qa_bitset32::qa_bitset32(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + long bitno = pmt_to_long(user_arg); // The bit we are to set + + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + + define_component("bs0", "qa_bitset16", pmt_from_long(bitno)); + define_component("bs1", "qa_bitset16", pmt_from_long(bitno + 16)); + connect("self", "in", "bs0", "in"); + connect("bs0", "out", "bs1", "in"); + connect("bs1", "out", "self", "out"); +} + +REGISTER_MBLOCK_CLASS(qa_bitset32); + +// ------------------------------------------------------------------------ + +class qa_bitset_src : public mb_mblock +{ + mb_port_sptr d_cs_top; + mb_port_sptr d_cs; + + mb_port_sptr d_out; + + long d_msg_number; // starting message number + long d_nmsgs_to_send; // # of messages to send + long d_batch_size; // # of messages to send per batch + +public: + qa_bitset_src(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void handle_message(mb_message_sptr msg); + +protected: + void send_one(); + void send_batch(); +}; + +qa_bitset_src::qa_bitset_src(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_msg_number = pmt_to_long(pmt_nth(0, user_arg)); + d_nmsgs_to_send = pmt_to_long(pmt_nth(1, user_arg)); + d_batch_size = pmt_to_long(pmt_nth(2, user_arg)); + + d_cs_top = define_port("cs_top", "qa-bitset-cs", true, mb_port::EXTERNAL); + d_cs = define_port("cs", "qa-bitset-cs", true, mb_port::EXTERNAL); + + d_out = define_port("out", "qa-bitset", true, mb_port::EXTERNAL); +} + +void +qa_bitset_src::handle_message(mb_message_sptr msg) +{ + if ((pmt_eq(msg->port_id(), d_cs_top->port_symbol()) + || pmt_eq(msg->port_id(), d_cs->port_symbol())) + && pmt_eq(msg->signal(), s_send_batch)){ + send_batch(); + } +} + +void +qa_bitset_src::send_batch() +{ + for (int i = 0; i < d_batch_size; i++) + send_one(); +} + +void +qa_bitset_src::send_one() +{ + if (d_nmsgs_to_send > 0){ + pmt_t msg_number = pmt_from_long(d_msg_number++); + d_out->send(s_data, pmt_cons(msg_number, s_long0)); + } + if (--d_nmsgs_to_send <= 0) + exit(); +} + +REGISTER_MBLOCK_CLASS(qa_bitset_src); + +// ------------------------------------------------------------------------ + +class qa_bitset_sink : public mb_mblock +{ + // Maximum number of messages we can track + static const size_t MAX_MSGS = 1 * 1024 * 1024; + + mb_port_sptr d_cs0; + mb_port_sptr d_cs1; + mb_port_sptr d_cs2; + mb_port_sptr d_cs3; + + mb_port_sptr d_in0; + mb_port_sptr d_in1; + mb_port_sptr d_in2; + mb_port_sptr d_in3; + + long d_nmsgs_to_recv; // # of messages to receive + long d_batch_size; // # of messages to receive per batch + uint32_t d_expected_mask; + + std::bitset d_bitset; + long d_nrecvd; + +public: + qa_bitset_sink(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void handle_message(mb_message_sptr msg); + +protected: + void receive_one(mb_message_sptr msg); +}; + +qa_bitset_sink::qa_bitset_sink(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_nrecvd(0) +{ + d_nmsgs_to_recv = pmt_to_long(pmt_nth(0, user_arg)); + d_batch_size = pmt_to_long(pmt_nth(1, user_arg)); + d_expected_mask = pmt_to_long(pmt_nth(2, user_arg)); + + if (d_nmsgs_to_recv > (long) MAX_MSGS) + throw std::out_of_range("qa_bitset_sink: nmsgs_to_recv is too big"); + + if (d_batch_size < 1) + throw std::out_of_range("qa_bitset_sink: batch_size must be >= 1"); + + d_cs0 = define_port("cs0", "qa-bitset-cs", true, mb_port::EXTERNAL); + d_cs1 = define_port("cs1", "qa-bitset-cs", true, mb_port::EXTERNAL); + d_cs2 = define_port("cs2", "qa-bitset-cs", true, mb_port::EXTERNAL); + d_cs3 = define_port("cs3", "qa-bitset-cs", true, mb_port::EXTERNAL); + + d_in0 = define_port("in0", "qa-bitset", false, mb_port::EXTERNAL); + d_in1 = define_port("in1", "qa-bitset", false, mb_port::EXTERNAL); + d_in2 = define_port("in2", "qa-bitset", false, mb_port::EXTERNAL); + d_in3 = define_port("in3", "qa-bitset", false, mb_port::EXTERNAL); +} + +void +qa_bitset_sink::handle_message(mb_message_sptr msg) +{ + if ((pmt_eq(msg->port_id(), d_in0->port_symbol()) + || pmt_eq(msg->port_id(), d_in1->port_symbol()) + || pmt_eq(msg->port_id(), d_in2->port_symbol()) + || pmt_eq(msg->port_id(), d_in3->port_symbol())) + && pmt_eq(msg->signal(), s_data)){ + + receive_one(msg); + } +} + +void +qa_bitset_sink::receive_one(mb_message_sptr msg) +{ + long msg_number = pmt_to_long(pmt_car(msg->data())); + uint32_t mask = pmt_to_long(pmt_cdr(msg->data())); + + // std::cout << msg->data() << std::endl; + + d_nrecvd++; + if (d_nrecvd % d_batch_size == d_batch_size - 1){ + d_cs0->send(s_send_batch); + d_cs1->send(s_send_batch); + d_cs2->send(s_send_batch); + d_cs3->send(s_send_batch); + } + + if (msg_number >= d_nmsgs_to_recv){ + std::cerr << "qa_bitset_sink::receive_one: msg_number too big (" + << msg_number << ")\n"; + shutdown_all(PMT_F); + return; + } + if (mask != d_expected_mask){ + fprintf(stderr, + "qa_bitset_sink::receive_one: Wrong mask. Expected 0x%08x, got 0x%08x\n", + d_expected_mask, mask); + shutdown_all(PMT_F); + return; + } + + if (d_bitset.test((size_t) msg_number)){ + std::cerr << "qa_bitset_sink::receive_one: duplicate msg_number (" + << msg_number << ")\n"; + shutdown_all(PMT_F); + return; + } + + d_bitset.set((size_t) msg_number); + if (d_nrecvd == d_nmsgs_to_recv) + shutdown_all(PMT_T); // we're done! +} + +REGISTER_MBLOCK_CLASS(qa_bitset_sink); + +// ------------------------------------------------------------------------ + +class qa_bitset_top : public mb_mblock +{ + static const int NPIPES = 4; + + std::vector d_cs; + + long d_nmsgs; // # of messages to send + long d_batch_size; // # of messages to receive per batch + +public: + qa_bitset_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void initial_transition(); +}; + +qa_bitset_top::qa_bitset_top(mb_runtime *runtime, + const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_nmsgs = pmt_to_long(pmt_nth(0, user_arg)); + d_nmsgs = (d_nmsgs / NPIPES) * NPIPES; + d_batch_size = pmt_to_long(pmt_nth(1, user_arg)); + + /* + * We build NPIPES sources which feed NPIPES pipelines, each of which + * consists of 8-mblocks. All pipelines feed into a single sink + * which keeps track the results. + */ + for (int i = 0; i < NPIPES; i++){ + d_cs.push_back(define_port("cs"+str(i), "qa-bitset-cs", false, mb_port::INTERNAL)); + + // sources of test messages + define_component("src"+str(i), "qa_bitset_src", + pmt_list3(pmt_from_long(i * d_nmsgs/NPIPES), + pmt_from_long(d_nmsgs/NPIPES), + pmt_from_long(d_batch_size))); + + // 8-mblock processing pipelines + define_component("pipeline"+str(i), "qa_bitset8", pmt_from_long(0)); + } + + // sink for output of pipelines + define_component("sink", "qa_bitset_sink", + pmt_list3(pmt_from_long(d_nmsgs), + pmt_from_long(d_batch_size * NPIPES), + pmt_from_long(0x000000ff))); + + for (int i = 0; i < NPIPES; i++){ + connect("self", "cs"+str(i), "src"+str(i), "cs_top"); + connect("src"+str(i), "out", "pipeline"+str(i), "in"); + connect("src"+str(i), "cs", "sink", "cs"+str(i)); + connect("pipeline"+str(i), "out", "sink", "in"+str(i)); + } +} + +void +qa_bitset_top::initial_transition() +{ + for (int i = 0; i < NPIPES; i++){ + d_cs[i]->send(s_send_batch); // prime the pump + d_cs[i]->send(s_send_batch); + } +} + +REGISTER_MBLOCK_CLASS(qa_bitset_top); diff --git a/mblock/src/lib/qa_bitset.mbh b/mblock/src/lib/qa_bitset.mbh new file mode 100644 index 00000000..0b2df003 --- /dev/null +++ b/mblock/src/lib/qa_bitset.mbh @@ -0,0 +1,61 @@ +;; -*- scheme -*- ; not really, but tells emacs how to format this +;; +;; 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 2, or (at your option) +;; any later version. +;; +;; GNU Radio is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License along +;; with this program; if not, write to the Free Software Foundation, Inc., +;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;; + +;; ---------------------------------------------------------------- +;; qa-bitset -- interface to mblock QA code +;; + +(define-protocol-class qa-bitset + + (:incoming + + (data n bitmask) + + ) + ) + +(define-protocol-class qa-bitset-cs + + (:outgoing + + (send-batch) + + ) + ) + +;; ---------------------------------------------------------------- +;; qa-disconnect -- interface to mblock QA code +;; + +(define-protocol-class qa-disconnect-cs + + (:outgoing + + (select-pipe n) + + ) + + (:incoming + + (ack n) + + ) + ) diff --git a/mblock/src/lib/qa_disconnect.cc b/mblock/src/lib/qa_disconnect.cc new file mode 100644 index 00000000..cde8d6e5 --- /dev/null +++ b/mblock/src/lib/qa_disconnect.cc @@ -0,0 +1,238 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include +#include +#include + +static pmt_t s_in = pmt_intern("in"); +static pmt_t s_out = pmt_intern("out"); +static pmt_t s_data = pmt_intern("data"); +static pmt_t s_ack = pmt_intern("ack"); +static pmt_t s_select_pipe = pmt_intern("select-pipe"); +static pmt_t s_long0 = pmt_from_long(0); +static pmt_t s_sys_port = pmt_intern("%sys-port"); +static pmt_t s_shutdown = pmt_intern("%shutdown"); + +class qa_disconnect_mux : public mb_mblock +{ + mb_port_sptr d_in; + mb_port_sptr d_out; + mb_port_sptr d_cs; + +public: + qa_disconnect_mux(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void initial_transition(); + void handle_message(mb_message_sptr msg); +}; + +qa_disconnect_mux::qa_disconnect_mux(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_in = define_port("in", "qa-bitset", false, mb_port::RELAY); + d_out = define_port("out", "qa-bitset", true, mb_port::RELAY); + d_cs = define_port("cs", "qa-disconnect-cs", true, mb_port::EXTERNAL); + + define_component("pipeline0", "qa_bitset8", pmt_from_long(0)); + define_component("pipeline1", "qa_bitset8", pmt_from_long(8)); +} + +void +qa_disconnect_mux::initial_transition(){} + +void +qa_disconnect_mux::handle_message(mb_message_sptr msg) +{ + if (pmt_eq(msg->port_id(), d_cs->port_symbol()) // select-pipe on cs + && pmt_eq(msg->signal(), s_select_pipe)){ + + long which_pipe = pmt_to_long(pmt_nth(0, msg->data())); + + disconnect_component("pipeline0"); + disconnect_component("pipeline1"); + + switch(which_pipe){ + + case 0: + connect("self", "in", "pipeline0", "in"); + connect("self", "out", "pipeline0", "out"); + break; + + case 1: + connect("self", "in", "pipeline1", "in"); + connect("self", "out", "pipeline1", "out"); + break; + } + + d_cs->send(s_ack, msg->data()); + return; + } +} + +REGISTER_MBLOCK_CLASS(qa_disconnect_mux); + +// ------------------------------------------------------------------------ + +class qa_disconnect_top : public mb_mblock +{ + enum state_t { + UNINITIALIZED, + WAIT_FOR_ACK, + WAIT_FOR_DATA + }; + + state_t d_state; + int d_msg_number; + int d_nmsgs_to_send; + + mb_port_sptr d_in; + mb_port_sptr d_out; + mb_port_sptr d_cs; + + void check_pipe_send_next_msg(); + void send_next_msg(); + void select_pipe(int n); + + // alternate pipes every 128 messages + static int which_pipe(int msg_number) { return (msg_number >> 7) & 0x1; } + bool time_to_switch() { return (d_msg_number & 0x7f) == 0; } + +public: + qa_disconnect_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void initial_transition(); + void handle_message(mb_message_sptr msg); +}; + +qa_disconnect_top::qa_disconnect_top(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_state(UNINITIALIZED), d_msg_number(0) +{ + d_nmsgs_to_send = pmt_to_long(pmt_nth(0, user_arg)); + + d_in = define_port("in", "qa-bitset", false, mb_port::INTERNAL); + d_out = define_port("out", "qa-bitset", true, mb_port::INTERNAL); + d_cs = define_port("cs", "qa-disconnect-cs", false, mb_port::INTERNAL); + + define_component("mux", "qa_disconnect_mux", PMT_F); + + connect("self", "cs", "mux", "cs"); + connect("self", "out", "mux", "in"); + connect("self", "in", "mux", "out"); +} + +void +qa_disconnect_top::initial_transition() +{ + check_pipe_send_next_msg(); +} + +void +qa_disconnect_top::handle_message(mb_message_sptr msg) +{ + if (0) + std::cerr << "qa_disconnect_top::handle_msg state = " + << d_state << "\n msg = " << msg << std::endl; + + if (pmt_eq(msg->port_id(), d_cs->port_symbol()) // ack on cs + && pmt_eq(msg->signal(), s_ack) + && d_state == WAIT_FOR_ACK){ + + send_next_msg(); + return; + } + + if (pmt_eq(msg->port_id(), d_in->port_symbol()) // data on in + && pmt_eq(msg->signal(), s_data) + && d_state == WAIT_FOR_DATA){ + + /* + * Confirm that msg passed through the pipe that we expect... + */ + static const long expected_mask[2] = { 0x000000ff, 0x0000ff00 }; + + long msg_number = pmt_to_long(pmt_car(msg->data())); + long mask = pmt_to_long(pmt_cdr(msg->data())); + + if (mask != expected_mask[which_pipe(msg_number)]){ + fprintf(stderr, "\nqa_disconnect_top: wrong mask in msg_number = 0x%08lx\n", + msg_number); + fprintf(stderr, " expected = 0x%08lx, actual = 0x%08lx\n", + expected_mask[which_pipe(msg_number)], mask); + shutdown_all(PMT_F); + return; + } + + if (msg_number == d_nmsgs_to_send - 1){ // we're done (and were successful) + shutdown_all(PMT_T); + return; + } + + check_pipe_send_next_msg(); + return; + } + + if (pmt_eq(msg->port_id(), s_sys_port) // ignore %shutdown on %sys-port + && pmt_eq(msg->signal(), s_shutdown)) + return; + + std::cerr << "qa_disconnect_top: unhandled msg: state = " + << d_state << "\n msg = " << msg << std::endl; +} + +void +qa_disconnect_top::select_pipe(int n) +{ + d_cs->send(s_select_pipe, pmt_list1(pmt_from_long(n))); + d_state = WAIT_FOR_ACK; +} + +void +qa_disconnect_top::send_next_msg() +{ + d_state = WAIT_FOR_DATA; + if (d_msg_number == d_nmsgs_to_send) // we've sent all we're supposed to + return; + + d_out->send(s_data, pmt_cons(pmt_from_long(d_msg_number), s_long0)); + d_msg_number++; +} + +void +qa_disconnect_top::check_pipe_send_next_msg() +{ + if (time_to_switch()) + select_pipe(which_pipe(d_msg_number)); + else + send_next_msg(); +} + +REGISTER_MBLOCK_CLASS(qa_disconnect_top); diff --git a/mblock/src/lib/qa_mblock.cc b/mblock/src/lib/qa_mblock.cc index 4be4a23c..cf422454 100644 --- a/mblock/src/lib/qa_mblock.cc +++ b/mblock/src/lib/qa_mblock.cc @@ -27,6 +27,8 @@ #include #include #include +#include +#include CppUnit::TestSuite * qa_mblock::suite() @@ -35,6 +37,8 @@ qa_mblock::suite() s->addTest (qa_mblock_prims::suite()); s->addTest (qa_mblock_send::suite()); + s->addTest (qa_mblock_sys::suite()); + s->addTest (qa_timeouts::suite()); return s; } diff --git a/mblock/src/lib/qa_mblock_prims.cc b/mblock/src/lib/qa_mblock_prims.cc index 79ed5a21..2995215f 100644 --- a/mblock/src/lib/qa_mblock_prims.cc +++ b/mblock/src/lib/qa_mblock_prims.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include static pmt_t s_cs = pmt_intern("cs"); @@ -47,42 +48,49 @@ static pmt_t s_out = pmt_intern("out"); class dp_1 : public mb_mblock { public: - dp_1(); + dp_1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dp_1(); }; -dp_1::dp_1() +dp_1::dp_1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { } dp_1::~dp_1(){} +REGISTER_MBLOCK_CLASS(dp_1); + // ---------------------------------------------------------------- class dp_2 : public mb_mblock { public: - dp_2(); + dp_2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dp_2(); }; -dp_2::dp_2() +dp_2::dp_2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { define_port("cs", "cs-protocol", false, mb_port::EXTERNAL); } dp_2::~dp_2(){} +REGISTER_MBLOCK_CLASS(dp_2); + // ---------------------------------------------------------------- class dp_3 : public mb_mblock { public: - dp_3(); + dp_3(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dp_3(); }; -dp_3::dp_3() +dp_3::dp_3(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { define_port("cs", "cs-protocol", false, mb_port::EXTERNAL); define_port("cs", "cs-protocol", false, mb_port::EXTERNAL); // duplicate def @@ -90,19 +98,23 @@ dp_3::dp_3() dp_3::~dp_3(){} +REGISTER_MBLOCK_CLASS(dp_3); + // ---------------------------------------------------------------- void qa_mblock_prims::test_define_ports() { - // std::vector intf; - - mb_mblock_sptr mb1 = mb_mblock_sptr(new dp_1()); - // intf = mb1->peer_interface(); - // CPPUNIT_ASSERT_EQUAL(size_t(0), intf.size()); + + mb_runtime_sptr rts = mb_make_runtime(); + mb_runtime *rt = rts.get(); + + // Should work + mb_mblock_sptr mb1 = mb_mblock_sptr(new dp_1(rt, "top", PMT_F)); // raises runtime_error because of unknown protocol "cs-protocol" - CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_2()), std::runtime_error); + CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_2(rt, "top", PMT_F)), + std::runtime_error); // define the protocol class pmt_t pc = mb_make_protocol_class(pmt_intern("cs-protocol"), @@ -112,10 +124,11 @@ qa_mblock_prims::test_define_ports() // std::cout << "pc = " << pc << '\n'; - mb_mblock_sptr mb2 = mb_mblock_sptr(new dp_2()); + mb_mblock_sptr mb2 = mb_mblock_sptr(new dp_2(rt, "top", PMT_F)); // raises pmt_exception because of duplicate port definition of "cs" - CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_3()), mbe_duplicate_port); + CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dp_3(rt, "top", PMT_F)), + mbe_duplicate_port); } // ================================================================ @@ -123,61 +136,74 @@ qa_mblock_prims::test_define_ports() class dc_0 : public mb_mblock { public: - dc_0(); + dc_0(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dc_0(); }; -dc_0::dc_0() +dc_0::dc_0(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { } dc_0::~dc_0() {} +REGISTER_MBLOCK_CLASS(dc_0); + // ---------------------------------------------------------------- class dc_ok : public mb_mblock { public: - dc_ok(); + dc_ok(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dc_ok(); }; -dc_ok::dc_ok() +dc_ok::dc_ok(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { - define_component("c0", mb_mblock_sptr(new dc_0())); - define_component("c1", mb_mblock_sptr(new dc_0())); - define_component("c2", mb_mblock_sptr(new dc_0())); + define_component("c0", "dc_0"); + define_component("c1", "dc_0"); + define_component("c2", "dc_0"); } dc_ok::~dc_ok(){} +REGISTER_MBLOCK_CLASS(dc_ok); + // ---------------------------------------------------------------- class dc_not_ok : public mb_mblock { public: - dc_not_ok(); + dc_not_ok(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~dc_not_ok(); }; -dc_not_ok::dc_not_ok() - : mb_mblock() +dc_not_ok::dc_not_ok(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { - define_component("c0", mb_mblock_sptr(new dc_0())); - define_component("c0", mb_mblock_sptr(new dc_0())); // duplicate name + define_component("c0", "dc_0"); + define_component("c0", "dc_0"); // duplicate name } dc_not_ok::~dc_not_ok(){} +REGISTER_MBLOCK_CLASS(dc_not_ok); + // ---------------------------------------------------------------- void qa_mblock_prims::test_define_components() { - mb_mblock_sptr mb1 = mb_mblock_sptr(new dc_ok()); // OK + mb_runtime_sptr rts = mb_make_runtime(); + mb_runtime *rt = rts.get(); + + // Should work + mb_mblock_sptr mb1 = mb_mblock_sptr(new dc_ok(rt, "top", PMT_F)); // raises pmt_exception because of duplicate component definition of "c0" - CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dc_not_ok()), mbe_duplicate_component); + CPPUNIT_ASSERT_THROW(mb_mblock_sptr(new dc_not_ok(rt, "top", PMT_F)), + mbe_duplicate_component); } // ================================================================ @@ -185,7 +211,9 @@ qa_mblock_prims::test_define_components() class tc_norm : public mb_mblock { public: - tc_norm(){ + tc_norm(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) + { define_port("data", "i/o", false, mb_port::EXTERNAL); define_port("norm", "i/o", false, mb_port::EXTERNAL); define_port("conj", "i/o", true, mb_port::EXTERNAL); @@ -197,22 +225,26 @@ public: tc_norm::~tc_norm(){} +REGISTER_MBLOCK_CLASS(tc_norm); + //////////////////////////////////////////////////////////////// class tc_0 : public mb_mblock { public: - tc_0(){ + tc_0(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) + { define_port("norm", "i/o", false, mb_port::EXTERNAL); define_port("conj", "i/o", true, mb_port::EXTERNAL); define_port("int", "i/o", false, mb_port::INTERNAL); - define_component("c0", mb_mblock_sptr(new tc_norm())); - define_component("c1", mb_mblock_sptr(new tc_norm())); - define_component("c2", mb_mblock_sptr(new tc_norm())); - define_component("c3", mb_mblock_sptr(new tc_norm())); - define_component("c4", mb_mblock_sptr(new tc_norm())); - define_component("c5", mb_mblock_sptr(new tc_norm())); + define_component("c0", "tc_norm"); + define_component("c1", "tc_norm"); + define_component("c2", "tc_norm"); + define_component("c3", "tc_norm"); + define_component("c4", "tc_norm"); + define_component("c5", "tc_norm"); // OK connect("c0", "norm", "c1", "conj"); @@ -284,14 +316,18 @@ public: tc_0::~tc_0(){} +REGISTER_MBLOCK_CLASS(tc_0); + //////////////////////////////////////////////////////////////// class tc_1 : public mb_mblock { public: - tc_1(){ - define_component("c0", mb_mblock_sptr(new tc_norm())); - define_component("c1", mb_mblock_sptr(new tc_norm())); + tc_1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) + { + define_component("c0", "tc_norm"); + define_component("c1", "tc_norm"); connect("c0", "norm", "c1", "conj"); } @@ -301,6 +337,8 @@ public: tc_1::~tc_1(){} +REGISTER_MBLOCK_CLASS(tc_1); + //////////////////////////////////////////////////////////////// void @@ -315,7 +353,10 @@ qa_mblock_prims::test_connect() pmt_list1(pmt_intern("in")), // in pmt_list1(pmt_intern("out"))); // out - mb_mblock_sptr mb0 = mb_mblock_sptr(new tc_0()); + mb_runtime_sptr rts = mb_make_runtime(); + mb_runtime *rt = rts.get(); + + mb_mblock_sptr mb0 = mb_mblock_sptr(new tc_0(rt, "top", PMT_F)); } //////////////////////////////////////////////////////////////// @@ -377,11 +418,14 @@ qa_mblock_prims::test_msg_queue() void qa_mblock_prims::test_make_accepter() { + mb_runtime_sptr rts = mb_make_runtime(); + mb_runtime *rt = rts.get(); + // create a block - mb_mblock_sptr mb = mb_mblock_sptr(new dp_2()); + mb_mblock_sptr mb = mb_mblock_sptr(new dp_2(rt, "top", PMT_F)); // use "internal use only" method... - mb_msg_accepter_sptr accepter = mb->impl()->make_accepter("cs"); + mb_msg_accepter_sptr accepter = mb->impl()->make_accepter(pmt_intern("cs")); // Now push a few messages into it... // signal data metadata pri diff --git a/mblock/src/lib/qa_mblock_send.cc b/mblock/src/lib/qa_mblock_send.cc index 46d6b644..cd81709e 100644 --- a/mblock/src/lib/qa_mblock_send.cc +++ b/mblock/src/lib/qa_mblock_send.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include static pmt_t s_data = pmt_intern("data"); @@ -57,6 +58,12 @@ define_protocol_classes() } +mb_mblock_sptr +get_top(mb_runtime_sptr rts) +{ + return dynamic_cast(rts.get())->top(); +} + // ================================================================ // test_simple_routing // ================================================================ @@ -70,12 +77,13 @@ class sr1 : public mb_mblock mb_port_sptr d_p3; public: - sr1(); + sr1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~sr1(); - void init_fsm(); + void initial_transition(); }; -sr1::sr1() +sr1::sr1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { d_p1 = define_port("p1", "qa-send-cs", true, mb_port::EXTERNAL); d_p2 = define_port("p2", "qa-send-cs", true, mb_port::EXTERNAL); @@ -85,9 +93,9 @@ sr1::sr1() sr1::~sr1(){} void -sr1::init_fsm() +sr1::initial_transition() { - // std::cout << instance_name() << "[sr1]: init_fsm\n"; + // std::cout << instance_name() << "[sr1]: initial_transition\n"; // send two messages to each port pmt_t our_name = pmt_intern(instance_name()); @@ -98,6 +106,8 @@ sr1::init_fsm() d_p2->send(s_status, pmt_list3(our_name, s_p2, pmt_from_long(1))); } +REGISTER_MBLOCK_CLASS(sr1); + // ---------------------------------------------------------------- // top-level container block for test_simple_routing @@ -106,17 +116,18 @@ class sr0 : public mb_mblock mb_port_sptr d_p0; public: - sr0(); + sr0(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~sr0(); - void init_fsm(); + void initial_transition(); }; -sr0::sr0() +sr0::sr0(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { d_p0 = define_port("p0", "qa-send-cs", false, mb_port::INTERNAL); - define_component("mb1", mb_mblock_sptr(new sr1())); - define_component("mb2", mb_mblock_sptr(new sr1())); + define_component("mb1", "sr1"); + define_component("mb2", "sr1"); connect("self", "p0", "mb1", "p1"); connect("mb1", "p2", "mb2", "p3"); @@ -126,9 +137,9 @@ sr0::sr0() sr0::~sr0(){} void -sr0::init_fsm() +sr0::initial_transition() { - // std::cout << instance_name() << "[sr0]: init_fsm\n"; + // std::cout << instance_name() << "[sr0]: initial_transition\n"; // send two messages to p0 pmt_t our_name = pmt_intern(instance_name()); @@ -136,6 +147,8 @@ sr0::init_fsm() d_p0->send(s_control, pmt_list3(our_name, s_p0, pmt_from_long(1))); } +REGISTER_MBLOCK_CLASS(sr0); + // ---------------------------------------------------------------- /* @@ -151,9 +164,10 @@ qa_mblock_send::test_simple_routing() mb_message_sptr msg; mb_runtime_sptr rt = mb_make_runtime_nop(); - mb_mblock_sptr mb0 = mb_mblock_sptr(new sr0()); - rt->run(mb0); + rt->run("top", "sr0", PMT_F); + mb_mblock_sptr mb0 = get_top(rt); + // Reach into the guts and see if the messages ended up where they should have // mb0 should have received two messages sent from mb1 via its p1 @@ -238,12 +252,13 @@ class rr2 : public mb_mblock mb_port_sptr d_p2; public: - rr2(); + rr2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~rr2(); - void init_fsm(); + void initial_transition(); }; -rr2::rr2() +rr2::rr2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { d_p1 = define_port("p1", "qa-send-cs", true, mb_port::EXTERNAL); d_p2 = define_port("p2", "qa-send-cs", false, mb_port::EXTERNAL); @@ -252,9 +267,9 @@ rr2::rr2() rr2::~rr2(){} void -rr2::init_fsm() +rr2::initial_transition() { - // std::cout << instance_name() << "[rr2]: init_fsm\n"; + // std::cout << instance_name() << "[rr2]: initial_transition\n"; // send two messages via p1 pmt_t our_name = pmt_intern(instance_name()); @@ -262,6 +277,8 @@ rr2::init_fsm() d_p1->send(s_status, pmt_list3(our_name, s_p1, pmt_from_long(1))); } +REGISTER_MBLOCK_CLASS(rr2); + // ---------------------------------------------------------------- // intermediate block for test_relay_routing @@ -272,16 +289,17 @@ class rr1 : public mb_mblock mb_port_sptr d_p2; public: - rr1(); + rr1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~rr1(); }; -rr1::rr1() +rr1::rr1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { d_p1 = define_port("p1", "qa-send-cs", true, mb_port::RELAY); d_p2 = define_port("p2", "qa-send-cs", false, mb_port::RELAY); - define_component("c0", mb_mblock_sptr(new rr2())); + define_component("c0", "rr2"); connect("self", "p1", "c0", "p1"); connect("self", "p2", "c0", "p2"); @@ -289,6 +307,8 @@ rr1::rr1() rr1::~rr1(){} +REGISTER_MBLOCK_CLASS(rr1); + // ---------------------------------------------------------------- // top-level container for test_relay_routing @@ -296,14 +316,15 @@ rr1::~rr1(){} class rr0_a : public mb_mblock { public: - rr0_a(); + rr0_a(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~rr0_a(); }; -rr0_a::rr0_a() +rr0_a::rr0_a(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { - define_component("c0", mb_mblock_sptr(new rr1())); - define_component("c1", mb_mblock_sptr(new rr2())); + define_component("c0", "rr1"); + define_component("c1", "rr2"); connect("c0", "p1", "c1", "p2"); connect("c0", "p2", "c1", "p1"); @@ -311,6 +332,7 @@ rr0_a::rr0_a() rr0_a::~rr0_a(){} +REGISTER_MBLOCK_CLASS(rr0_a); /* * This tests basic message routing using RELAY and EXTERNAL ports. @@ -323,8 +345,8 @@ qa_mblock_send::test_relay_routing_1() mb_message_sptr msg; mb_runtime_sptr rt = mb_make_runtime_nop(); - mb_mblock_sptr top = mb_mblock_sptr(new rr0_a()); - rt->run(top); + rt->run("top", "rr0_a", PMT_F); + mb_mblock_sptr top = get_top(rt); // Reach into the guts and see if the messages ended up where they should have @@ -377,14 +399,15 @@ qa_mblock_send::test_relay_routing_1() class rr0_b : public mb_mblock { public: - rr0_b(); + rr0_b(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); ~rr0_b(); }; -rr0_b::rr0_b() +rr0_b::rr0_b(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) { - define_component("c0", mb_mblock_sptr(new rr1())); - define_component("c1", mb_mblock_sptr(new rr1())); + define_component("c0", "rr1"); + define_component("c1", "rr1"); connect("c0", "p1", "c1", "p2"); connect("c0", "p2", "c1", "p1"); @@ -392,6 +415,7 @@ rr0_b::rr0_b() rr0_b::~rr0_b(){} +REGISTER_MBLOCK_CLASS(rr0_b); /* * This tests basic message routing using RELAY and EXTERNAL ports. @@ -404,8 +428,8 @@ qa_mblock_send::test_relay_routing_2() mb_message_sptr msg; mb_runtime_sptr rt = mb_make_runtime_nop(); - mb_mblock_sptr top = mb_mblock_sptr(new rr0_b()); - rt->run(top); + rt->run("top", "rr0_b", PMT_F); + mb_mblock_sptr top = get_top(rt); // Reach into the guts and see if the messages ended up where they should have diff --git a/mblock/src/lib/qa_mblock_sys.cc b/mblock/src/lib/qa_mblock_sys.cc new file mode 100644 index 00000000..a64f546c --- /dev/null +++ b/mblock/src/lib/qa_mblock_sys.cc @@ -0,0 +1,271 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include // QA only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static pmt_t s_data = pmt_intern("data"); +static pmt_t s_status = pmt_intern("status"); +static pmt_t s_control = pmt_intern("control"); +static pmt_t s_p0 = pmt_intern("p0"); +static pmt_t s_p1 = pmt_intern("p1"); +static pmt_t s_p2 = pmt_intern("p2"); +static pmt_t s_p3 = pmt_intern("p3"); +static pmt_t s_e1 = pmt_intern("e1"); +static pmt_t s_r1 = pmt_intern("r1"); + +static void +define_protocol_classes() +{ + mb_make_protocol_class(s_data, // name + pmt_list1(s_data), // incoming + pmt_list1(s_data)); // outgoing +} + + +// ================================================================ +// test_sys_1 +// ================================================================ + +class sys_1 : public mb_mblock +{ + pmt_t d_user_arg; + mb_port_sptr d_data; + +public: + sys_1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~sys_1(); + void initial_transition(); +}; + +sys_1::sys_1(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_user_arg(user_arg) +{ + d_data = define_port("data", "data", true, mb_port::EXTERNAL); +} + +sys_1::~sys_1(){} + +void +sys_1::initial_transition() +{ + shutdown_all(d_user_arg); +} + +REGISTER_MBLOCK_CLASS(sys_1); + +void +qa_mblock_sys::test_sys_1() +{ + define_protocol_classes(); + + pmt_t result; + pmt_t n1 = pmt_from_long(1); + pmt_t n2 = pmt_from_long(2); + + mb_runtime_sptr rt1 = mb_make_runtime(); + +#if 0 + try { + rt1->run("top-1", "sys_1", n1, &result); + } + catch (omni_thread_fatal e){ + std::cerr << "caught omni_thread_fatal: error = " << e.error + << ": " << strerror(e.error) << std::endl; + } + catch (omni_thread_invalid){ + std::cerr << "caught omni_thread_invalid\n"; + } +#else + rt1->run("top-1", "sys_1", n1, &result); +#endif + CPPUNIT_ASSERT(pmt_equal(n1, result)); + + // Execute run a second time, with the same rt, to ensure sanity. + rt1->run("top-2", "sys_1", n2, &result); + CPPUNIT_ASSERT(pmt_equal(n2, result)); +} + +// ================================================================ +// test_sys_2 +// ================================================================ + +class squarer : public mb_mblock +{ + mb_port_sptr d_data; + +public: + squarer(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + + void handle_message(mb_message_sptr msg); +}; + +squarer::squarer(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_data = define_port("data", "data", true, mb_port::EXTERNAL); +} + +void +squarer::handle_message(mb_message_sptr msg) +{ + if (!pmt_eq(msg->signal(), s_data)) // we only handle the "data" message + return; + + // long x -> (long x . long (x * x)) + + pmt_t x_pmt = msg->data(); + long x = pmt_to_long(x_pmt); + d_data->send(s_data, pmt_cons(x_pmt, pmt_from_long(x * x))); +} + +REGISTER_MBLOCK_CLASS(squarer); + +// ---------------------------------------------------------------- + +class sys_2 : public mb_mblock +{ + mb_port_sptr d_data; + +public: + sys_2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + void initial_transition(); + void handle_message(mb_message_sptr msg); +}; + +sys_2::sys_2(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_data = define_port("data", "data", true, mb_port::INTERNAL); + define_component("squarer", "squarer"); + connect("self", "data", "squarer", "data"); +} + +void +sys_2::initial_transition() +{ + // FIXME start timer to detect general failure + + d_data->send(s_data, pmt_from_long(0)); // send initial message +} + +void +sys_2::handle_message(mb_message_sptr msg) +{ + if (!pmt_eq(msg->signal(), s_data)) // we only handle the "data" message + return; + + // first check correctness of message + + long x = pmt_to_long(pmt_car(msg->data())); + long y = pmt_to_long(pmt_cdr(msg->data())); + + // std::cout << msg->data() << std::endl; + + if (y != x * x){ + std::cerr << "sys_2::handle_message: Expected y == x * x. Got y = " + << y << " for x = " << x << std::endl; + + shutdown_all(PMT_F); // failed + } + + if (x == 100) + shutdown_all(PMT_T); // done, OK + else + d_data->send(s_data, pmt_from_long(x + 1)); // send next request +} + +REGISTER_MBLOCK_CLASS(sys_2); + +// ---------------------------------------------------------------- + +void +qa_mblock_sys::test_sys_2() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + // std::cerr << "qa_mblock_sys::test_sys_2 (enter)\n"; + + rt->run("top-sys-2", "sys_2", PMT_F, &result); + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +// ================================================================ +// test_bitset_1 +// ================================================================ + +void +qa_mblock_sys::test_bitset_1() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + long nmsgs = 1000; + long batch_size = 8; + + pmt_t arg = pmt_list2(pmt_from_long(nmsgs), // # of messages to send through pipe + pmt_from_long(batch_size)); + + rt->run("top", "qa_bitset_top", arg, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +// ================================================================ +// test_disconnect +// ================================================================ + +void +qa_mblock_sys::test_disconnect() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + long nmsgs = 10240; + + pmt_t arg = pmt_list1(pmt_from_long(nmsgs)); // # of messages to send through pipe + + + rt->run("top", "qa_disconnect_top", arg, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} diff --git a/mblock/src/lib/qa_mblock_sys.h b/mblock/src/lib/qa_mblock_sys.h new file mode 100644 index 00000000..87333818 --- /dev/null +++ b/mblock/src/lib/qa_mblock_sys.h @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef INCLUDED_QA_MBLOCK_SYS_H +#define INCLUDED_QA_MBLOCK_SYS_H + +#include +#include + +class qa_mblock_sys : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_mblock_sys); + CPPUNIT_TEST(test_sys_1); + CPPUNIT_TEST(test_sys_2); + CPPUNIT_TEST(test_bitset_1); + CPPUNIT_TEST(test_disconnect); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_sys_1(); + void test_sys_2(); + void test_bitset_1(); + void test_disconnect(); +}; + +#endif /* INCLUDED_QA_MBLOCK_SYS_H */ + diff --git a/mblock/src/lib/qa_timeouts.cc b/mblock/src/lib/qa_timeouts.cc new file mode 100644 index 00000000..4439b6e8 --- /dev/null +++ b/mblock/src/lib/qa_timeouts.cc @@ -0,0 +1,292 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static pmt_t s_timeout = pmt_intern("%timeout"); +static pmt_t s_done = pmt_intern("done"); + + +// ------------------------------------------------------------------------ +// Exercise the priority queue used to implement timeouts. +// ------------------------------------------------------------------------ +void +qa_timeouts::test_timer_queue() +{ + mb_timer_queue tq; + mb_msg_accepter_sptr accepter; + + mb_timeout_sptr t1000_000 = + mb_timeout_sptr(new mb_timeout(mb_time(1000,0), PMT_F, accepter)); + + mb_timeout_sptr t2000_000 = + mb_timeout_sptr(new mb_timeout(mb_time(2000,0), PMT_F, accepter)); + + mb_timeout_sptr t3000_000 = + mb_timeout_sptr(new mb_timeout(mb_time(3000,0), PMT_F, accepter)); + + mb_timeout_sptr t3000_125 = + mb_timeout_sptr(new mb_timeout(mb_time(3000,125), PMT_F, accepter)); + + mb_timeout_sptr t3000_250 = + mb_timeout_sptr(new mb_timeout(mb_time(3000,250), PMT_F, accepter)); + + mb_timeout_sptr t4000_000 = + mb_timeout_sptr(new mb_timeout(mb_time(4000,0), PMT_F, accepter)); + + // insert in pseudo-random order + + tq.push(t3000_125); + tq.push(t1000_000); + tq.push(t4000_000); + tq.push(t3000_250); + tq.push(t2000_000); + tq.push(t3000_000); + + CPPUNIT_ASSERT_EQUAL(t1000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t2000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t3000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t3000_125, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t3000_250, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t4000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT(tq.empty()); + + // insert in pseudo-random order + + tq.push(t3000_000); + tq.push(t4000_000); + tq.push(t3000_125); + tq.push(t1000_000); + tq.push(t2000_000); + tq.push(t3000_250); + + tq.cancel(t1000_000->handle()); + + CPPUNIT_ASSERT_EQUAL(t2000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t3000_000, tq.top()); + tq.pop(); + + tq.cancel(t3000_250->handle()); + + CPPUNIT_ASSERT_EQUAL(t3000_125, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT_EQUAL(t4000_000, tq.top()); + tq.pop(); + + CPPUNIT_ASSERT(tq.empty()); +} + +// ------------------------------------------------------------------------ +// Test one-shot timeouts +// ------------------------------------------------------------------------ + +// FWIW, on SuSE 10.1 for x86-64, clock_getres returns 0.004 seconds. + +#define TIMING_MARGIN 0.010 // seconds + +class qa_timeouts_1_top : public mb_mblock +{ + int d_nleft; + int d_nerrors; + mb_time d_t0; + +public: + qa_timeouts_1_top(mb_runtime *runtime, + const std::string &instance_name, pmt_t user_arg); + + void initial_transition(); + void handle_message(mb_message_sptr msg); +}; + +qa_timeouts_1_top::qa_timeouts_1_top(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_nleft(0), d_nerrors(0) +{ +} + +void +qa_timeouts_1_top::initial_transition() +{ + d_t0 = mb_time::time(); // now + + schedule_one_shot_timeout(d_t0 + 0.200, pmt_from_double(0.200)); + schedule_one_shot_timeout(d_t0 + 0.125, pmt_from_double(0.125)); + schedule_one_shot_timeout(d_t0 + 0.075, pmt_from_double(0.075)); + schedule_one_shot_timeout(d_t0 + 0.175, pmt_from_double(0.175)); + + d_nleft = 4; +} + +void +qa_timeouts_1_top::handle_message(mb_message_sptr msg) +{ + if (pmt_eq(msg->signal(), s_timeout)){ + mb_time t_now = mb_time::time(); + double expected_delta_t = pmt_to_double(msg->data()); + double actual_delta_t = (t_now - d_t0).double_time(); + double delta = expected_delta_t - actual_delta_t; + + if (fabs(delta) > TIMING_MARGIN){ + std::cerr << "qa_timeouts_1_top: expected_delta_t = " << expected_delta_t + << " actual_delta_t = " << actual_delta_t << std::endl; + d_nerrors++; + } + + if (--d_nleft <= 0) + shutdown_all(d_nerrors == 0 ? PMT_T : PMT_F); + } +} + +REGISTER_MBLOCK_CLASS(qa_timeouts_1_top); + +void +qa_timeouts::test_timeouts_1() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "qa_timeouts_1_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +// ------------------------------------------------------------------------ +// Test periodic timeouts +// ------------------------------------------------------------------------ + +class qa_timeouts_2_top : public mb_mblock +{ + int d_nhandled; + int d_nerrors; + double d_delta_t; + mb_time d_t0; + +public: + qa_timeouts_2_top(mb_runtime *runtime, + const std::string &instance_name, pmt_t user_arg); + + void initial_transition(); + void handle_message(mb_message_sptr msg); +}; + +qa_timeouts_2_top::qa_timeouts_2_top(mb_runtime *runtime, + const std::string &instance_name, + pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg), + d_nhandled(0), d_nerrors(0), d_delta_t(0.075) +{ +} + +void +qa_timeouts_2_top::initial_transition() +{ + d_t0 = mb_time::time(); // now + + schedule_periodic_timeout(d_t0 + d_delta_t, mb_time(d_delta_t), PMT_T); +} + +void +qa_timeouts_2_top::handle_message(mb_message_sptr msg) +{ + static const int NMSGS_TO_HANDLE = 5; + + if (pmt_eq(msg->signal(), s_timeout) + && !pmt_eq(msg->data(), s_done)){ + + mb_time t_now = mb_time::time(); + + d_nhandled++; + + double expected_delta_t = d_delta_t * d_nhandled; + double actual_delta_t = (t_now - d_t0).double_time(); + double delta = expected_delta_t - actual_delta_t; + + if (fabs(delta) > TIMING_MARGIN){ + std::cerr << "qa_timeouts_2_top: expected_delta_t = " << expected_delta_t + << " actual_delta_t = " << actual_delta_t << std::endl; + d_nerrors++; + } + + if (d_nhandled == NMSGS_TO_HANDLE){ + cancel_timeout(msg->metadata()); // test cancel_timeout... + schedule_one_shot_timeout(d_t0 + (d_delta_t * (d_nhandled + 2)), s_done); + } + } + + if (pmt_eq(msg->signal(), s_timeout) + && pmt_eq(msg->data(), s_done)){ + if (d_nhandled != NMSGS_TO_HANDLE){ + std::cerr << "qa_timeouts_2_top: d_nhandled = " << d_nhandled + << " expected d_nhandled = " << NMSGS_TO_HANDLE + << " (cancel_timeout didn't work)\n"; + d_nerrors++; + } + shutdown_all(d_nerrors == 0 ? PMT_T : PMT_F); + } +} + +REGISTER_MBLOCK_CLASS(qa_timeouts_2_top); + +void +qa_timeouts::test_timeouts_2() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_NIL; + + rt->run("top", "qa_timeouts_2_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} diff --git a/mblock/src/lib/qa_timeouts.h b/mblock/src/lib/qa_timeouts.h new file mode 100644 index 00000000..736c4c2d --- /dev/null +++ b/mblock/src/lib/qa_timeouts.h @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006,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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef INCLUDED_QA_TIMEOUTS_H +#define INCLUDED_QA_TIMEOUTS_H + +#include +#include + +class qa_timeouts : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_timeouts); + CPPUNIT_TEST(test_timer_queue); + CPPUNIT_TEST(test_timeouts_1); + CPPUNIT_TEST(test_timeouts_2); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_timer_queue(); + void test_timeouts_1(); + void test_timeouts_2(); +}; + +#endif /* INCLUDED_QA_TIMEOUTS_H */ + diff --git a/mblock/src/scheme/Makefile.am b/mblock/src/scheme/Makefile.am new file mode 100644 index 00000000..7700a1fc --- /dev/null +++ b/mblock/src/scheme/Makefile.am @@ -0,0 +1,21 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +SUBDIRS = gnuradio diff --git a/mblock/src/scheme/gnuradio/Makefile.am b/mblock/src/scheme/gnuradio/Makefile.am new file mode 100644 index 00000000..f217f685 --- /dev/null +++ b/mblock/src/scheme/gnuradio/Makefile.am @@ -0,0 +1,19 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# diff --git a/mblock/src/scheme/gnuradio/compile-mbh.scm b/mblock/src/scheme/gnuradio/compile-mbh.scm new file mode 100755 index 00000000..fbad90d7 --- /dev/null +++ b/mblock/src/scheme/gnuradio/compile-mbh.scm @@ -0,0 +1,231 @@ +#!/usr/bin/guile \ +-e main -s +!# +;; -*-scheme-*- +;; +;; 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 2, or (at your option) +;; any later version. +;; +;; GNU Radio is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License along +;; with this program; if not, write to the Free Software Foundation, Inc., +;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;; + +;; usage: compile-mbh + +(use-modules (ice-9 getopt-long)) +(use-modules (ice-9 format)) +(use-modules (ice-9 pretty-print)) +;(use-modules (ice-9 slib)) +(use-modules (gnuradio pmt-serialize)) +(use-modules (gnuradio macros-etc)) + +(debug-enable 'backtrace) + +;; ---------------------------------------------------------------- + +(define (main args) + + (define (usage) + (format 0 "usage: ~a input-file output-file~%" (car args))) + + (when (not (= (length args) 3)) + (usage) + (exit 1)) + + (let ((input-filename (cadr args)) + (output-filename (caddr args))) + (if (compile-mbh-file input-filename output-filename) + (exit 0) + (exit 1)))) + + +;; ---------------------------------------------------------------- +;; constructor and accessors for protocol-class + +(define %protocol-class-tag (string->symbol "[PROTOCOL-CLASS-TAG]")) + +(define (make-protocol-class name incoming outgoing) + (vector %protocol-class-tag name incoming outgoing)) + +(define (protocol-class? obj) + (and (vector? obj) (eq? %protocol-class-tag (vector-ref obj 0)))) + +(define (protocol-class-name pc) + (vector-ref pc 1)) + +(define (protocol-class-incoming pc) + (vector-ref pc 2)) + +(define (protocol-class-outgoing pc) + (vector-ref pc 3)) + + +;; ---------------------------------------------------------------- + +(define (syntax-error msg e) + (throw 'syntax-error msg e)) + +(define (unrecognized-form form) + (syntax-error "Unrecognized form" form)) + + +(define (mbh-chk-length= e y n) + (cond ((and (null? y)(zero? n)) + #f) + ((null? y) + (syntax-error "Expression has too few subexpressions" e)) + ((atom? y) + (syntax-error (if (atom? e) + "List expected" + "Expression ends with `dotted' atom") + e)) + ((zero? n) + (syntax-error "Expression has too many subexpressions" e)) + (else + (mbh-chk-length= e (cdr y) (- n 1))))) + +(define (mbh-chk-length>= e y n) + (cond ((and (null? y)(< n 1)) + #f) + ((atom? y) + (mbh-chk-length= e y -1)) + (else + (mbh-chk-length>= e (cdr y) (- n 1))))) + + +(define (compile-mbh-file input-filename output-filename) + (let ((i-port (open-input-file input-filename)) + (o-port (open-output-file output-filename))) + + (letrec + ((protocol-classes '()) ; alist + + (lookup-protocol-class ; returns protocol-class or #f + (lambda (name) + (cond ((assq name protocol-classes) => cdr) + (else #f)))) + + (register-protocol-class + (lambda (pc) + (set! protocol-classes (acons (protocol-class-name pc) + pc protocol-classes)) + pc)) + + (parse-top-level-form + (lambda (form) + (mbh-chk-length>= form form 1) + (case (car form) + ((define-protocol-class) (parse-define-protocol-class form)) + (else (syntax-error form))))) + + (parse-define-protocol-class + (lambda (form) + (mbh-chk-length>= form form 2) + ;; form => (define-protocol-class name + ;; (:include protocol-class-name) + ;; (:incoming list-of-msgs) + ;; (:outgoing list-of-msgs)) + (let ((name (cadr form)) + (incoming '()) + (outgoing '())) + (if (lookup-protocol-class name) + (syntax-error "Duplicate protocol-class name" name)) + (for-each + (lambda (sub-form) + (mbh-chk-length>= sub-form sub-form 1) + (case (car sub-form) + ((:include) + (mbh-chk-length>= sub-form sub-form 2) + (cond ((lookup-protocol-class (cadr sub-form)) => + (lambda (pc) + (set! incoming (append incoming (protocol-class-incoming pc))) + (set! outgoing (append outgoing (protocol-class-outgoing pc))))) + (else + (syntax-error "Unknown protocol-class-name" (cadr sub-form))))) + ((:incoming) + (set! incoming (append incoming (cdr sub-form)))) + ((:outgoing) + (set! outgoing (append outgoing (cdr sub-form)))) + (else + (unrecognized-form (car sub-form))))) + (cddr form)) + + (register-protocol-class (make-protocol-class name incoming outgoing))))) + + ) ; end of bindings + + (for-each-in-file i-port parse-top-level-form) + + ;; generate the output here... + + (letrec ((classes (map cdr protocol-classes)) + (so-stream (make-serial-output-stream)) + (format-output-for-c++ + (lambda (output) + (format o-port "//~%") + (format o-port "// Machine generated by compile-mbh from ~a~%" input-filename) + (format o-port "//~%") + (format o-port "// protocol-classes: ~{~a ~}~%" (map car protocol-classes)) + (format o-port "//~%") + + (format o-port "#include ~%") + (format o-port "#include ~%") + (format o-port + "static const char~%protocol_class_init_data[~d] = {~% " + (length output)) + + (do ((lst output (cdr lst)) + (i 0 (+ i 1))) + ((null? lst) #t) + (format o-port "~a, " (car lst)) + (when (= 15 (modulo i 16)) + (format o-port "~% "))) + + (format o-port "~&};~%") + (format o-port "static mb_protocol_class_init _init_(protocol_class_init_data, sizeof(protocol_class_init_data));~%") + ))) + + + (map (lambda (pc) + (let ((obj-to-dump + (list (protocol-class-name pc) ; class name + (map car (protocol-class-incoming pc)) ; incoming msg names + (map car (protocol-class-outgoing pc)) ; outgoing msg names + ;;(protocol-class-incoming pc) ; full incoming msg descriptions + ;;(protocol-class-outgoing pc) ; full outgoing msg descriptions + ))) + ;;(pretty-print obj-to-dump) + (pmt-serialize obj-to-dump (so-stream 'put-byte)))) + classes) + + (format-output-for-c++ ((so-stream 'get-output))) + + #t)))) + + +(define (make-serial-output-stream) + (letrec ((output '()) + (put-byte + (lambda (byte) + (set! output (cons byte output)))) + (get-output + (lambda () + (reverse output)))) + (lambda (key) + (case key + ((put-byte) put-byte) + ((get-output) get-output) + (else (error "Unknown key" key)))))) + diff --git a/omnithread/omnithread.h b/omnithread/omnithread.h index bd916561..26e42b0e 100644 --- a/omnithread/omnithread.h +++ b/omnithread/omnithread.h @@ -391,11 +391,15 @@ protected: // execute the run() or run_undetached() member functions depending on // whether start() or start_undetached() is called respectively. +public: + void start_undetached(void); // can be used with the above constructor in a derived class to cause // the thread to be undetached. In this case the thread executes the // run_undetached member function. +protected: + virtual ~omni_thread(void); // destructor cannot be called by user (except via a derived class). // Use exit() or cancel() instead. This also means a thread object must diff --git a/omnithread/posix.cc b/omnithread/posix.cc index b82e86df..7325166c 100644 --- a/omnithread/posix.cc +++ b/omnithread/posix.cc @@ -51,6 +51,10 @@ #include #include +#if (PthreadDraftVersion == 0) +#error "PthreadDraftVersion not defined. If not sure, define it to 10" +#endif + #ifdef HAVE_NANOSLEEP #undef NoNanoSleep #else @@ -73,8 +77,14 @@ #endif #endif +#if 1 #define DB(x) // x -//#include or #include if DB is on. +#else +#define DB(x) x +#include +using std::cerr; +using std::endl; +#endif #if (PthreadDraftVersion <= 6) #define ERRNO(x) (((x) != 0) ? (errno) : 0) diff --git a/pmt/src/Makefile.am b/pmt/src/Makefile.am index bcbab245..df73ba51 100644 --- a/pmt/src/Makefile.am +++ b/pmt/src/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2004,2006 Free Software Foundation, Inc. +# Copyright 2006,2007 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -19,4 +19,4 @@ # Boston, MA 02110-1301, USA. # -SUBDIRS = lib +SUBDIRS = lib scheme diff --git a/pmt/src/lib/Makefile.am b/pmt/src/lib/Makefile.am index a738a169..b77f6201 100644 --- a/pmt/src/lib/Makefile.am +++ b/pmt/src/lib/Makefile.am @@ -21,7 +21,7 @@ include $(top_srcdir)/Makefile.common -INCLUDES = $(BOOST_CFLAGS) $(CPPUNIT_INCLUDES) +INCLUDES = $(DEFINES) $(OMNITHREAD_INCLUDES) $(BOOST_CFLAGS) $(CPPUNIT_INCLUDES) TESTS = test_pmt @@ -48,18 +48,22 @@ GENERATED_CC = \ $(GENERATED_H) $(GENERATED_I) $(GENERATED_CC): $(CODE_GENERATOR) PYTHONPATH=$(top_srcdir)/pmt/src/lib srcdir=$(srcdir) $(PYTHON) $(srcdir)/generate_unv.py -BUILT_SOURCES = $(GENERATED_H) $(GENERATED_CC) +pmt_serial_tags.h: $(srcdir)/../scheme/gnuradio/gen-serial-tags.scm $(srcdir)/../scheme/gnuradio/pmt-serial-tags.scm + $(RUN_GUILE) $(srcdir)/../scheme/gnuradio/gen-serial-tags.scm $(srcdir)/../scheme/gnuradio/pmt-serial-tags.scm pmt_serial_tags.h + +BUILT_SOURCES = $(GENERATED_H) $(GENERATED_CC) pmt_serial_tags.h # ---------------------------------------------------------------- EXTRA_DIST = \ - $(CODE_GENERATOR) - + $(CODE_GENERATOR) # These are the source files that go into the pmt shared library libpmt_la_SOURCES = \ pmt.cc \ pmt_io.cc \ + pmt_pool.cc \ + pmt_serialize.cc \ pmt_unv.cc # magic flags @@ -67,10 +71,13 @@ libpmt_la_LDFLAGS = $(NO_UNDEFINED) -avoid-version # link the library against the c++ standard library libpmt_la_LIBADD = \ + $(OMNITHREAD_LA) \ -lstdc++ include_HEADERS = \ - pmt.h + pmt.h \ + pmt_pool.h \ + pmt_serial_tags.h noinst_HEADERS = \ $(GENERATED_H) \ diff --git a/pmt/src/lib/pmt.cc b/pmt/src/lib/pmt.cc index cf6547b5..9378953c 100644 --- a/pmt/src/lib/pmt.cc +++ b/pmt/src/lib/pmt.cc @@ -26,6 +26,33 @@ #include #include #include "pmt_int.h" +#include +#include + +static const int CACHE_LINE_SIZE = 64; // good guess + +# if (PMT_LOCAL_ALLOCATOR) + +static pmt_pool global_pmt_pool(sizeof(pmt_pair), CACHE_LINE_SIZE); + +void * +pmt_base::operator new(size_t size) +{ + void *p = global_pmt_pool.malloc(); + + // fprintf(stderr, "pmt_base::new p = %p\n", p); + assert((reinterpret_cast(p) & (CACHE_LINE_SIZE - 1)) == 0); + return p; +} + +void +pmt_base::operator delete(void *p, size_t size) +{ + global_pmt_pool.free(p); +} + +#endif + pmt_base::~pmt_base() { @@ -108,25 +135,37 @@ _dict(pmt_t x) return dynamic_cast(x.get()); } +static pmt_any * +_any(pmt_t x) +{ + return dynamic_cast(x.get()); +} + //////////////////////////////////////////////////////////////////////////// -// Booleans +// Globals //////////////////////////////////////////////////////////////////////////// -const pmt_t PMT_BOOL_T = pmt_t(new pmt_bool()); // singleton -const pmt_t PMT_BOOL_F = pmt_t(new pmt_bool()); // singleton +const pmt_t PMT_T = pmt_t(new pmt_bool()); // singleton +const pmt_t PMT_F = pmt_t(new pmt_bool()); // singleton +const pmt_t PMT_NIL = pmt_t(new pmt_null()); // singleton +const pmt_t PMT_EOF = pmt_cons(PMT_NIL, PMT_NIL); // singleton + +//////////////////////////////////////////////////////////////////////////// +// Booleans +//////////////////////////////////////////////////////////////////////////// pmt_bool::pmt_bool(){} bool pmt_is_true(pmt_t obj) { - return obj != PMT_BOOL_F; + return obj != PMT_F; } bool pmt_is_false(pmt_t obj) { - return obj == PMT_BOOL_F; + return obj == PMT_F; } bool @@ -138,15 +177,15 @@ pmt_is_bool(pmt_t obj) pmt_t pmt_from_bool(bool val) { - return val ? PMT_BOOL_T : PMT_BOOL_F; + return val ? PMT_T : PMT_F; } bool pmt_to_bool(pmt_t val) { - if (val == PMT_BOOL_T) + if (val == PMT_T) return true; - if (val == PMT_BOOL_F) + if (val == PMT_F) return false; throw pmt_wrong_type("pmt_to_bool", val); } @@ -167,7 +206,7 @@ hash_string(const std::string &s) unsigned int h = 0; unsigned int g = 0; - for (std::string::const_iterator p = s.begin(); p != s.end(); p++){ + for (std::string::const_iterator p = s.begin(); p != s.end(); ++p){ h = (h << 4) + (*p & 0xff); g = h & 0xf0000000; if (g){ @@ -322,8 +361,6 @@ pmt_to_complex(pmt_t x) // Pairs //////////////////////////////////////////////////////////////////////////// -const pmt_t PMT_NIL = pmt_t(new pmt_null()); // singleton - pmt_null::pmt_null() {} pmt_pair::pmt_pair(pmt_t car, pmt_t cdr) : d_car(car), d_cdr(cdr) {} @@ -598,6 +635,40 @@ pmt_dict_values(pmt_t dict) return _dict(dict)->values(); } +//////////////////////////////////////////////////////////////////////////// +// Any +//////////////////////////////////////////////////////////////////////////// + +pmt_any::pmt_any(const boost::any &any) : d_any(any) {} + +bool +pmt_is_any(pmt_t obj) +{ + return obj->is_any(); +} + +pmt_t +pmt_make_any(const boost::any &any) +{ + return pmt_t(new pmt_any(any)); +} + +boost::any +pmt_any_ref(pmt_t obj) +{ + if (!obj->is_any()) + throw pmt_wrong_type("pmt_any_ref", obj); + return _any(obj)->ref(); +} + +void +pmt_any_set(pmt_t obj, const boost::any &any) +{ + if (!obj->is_any()) + throw pmt_wrong_type("pmt_any_set", obj); + _any(obj)->set(any); +} + //////////////////////////////////////////////////////////////////////////// // General Functions //////////////////////////////////////////////////////////////////////////// @@ -689,14 +760,14 @@ pmt_assq(pmt_t obj, pmt_t alist) while (pmt_is_pair(alist)){ pmt_t p = pmt_car(alist); if (!pmt_is_pair(p)) // malformed alist - return PMT_BOOL_F; + return PMT_F; if (pmt_eq(obj, pmt_car(p))) return p; alist = pmt_cdr(alist); } - return PMT_BOOL_F; + return PMT_F; } pmt_t @@ -705,14 +776,14 @@ pmt_assv(pmt_t obj, pmt_t alist) while (pmt_is_pair(alist)){ pmt_t p = pmt_car(alist); if (!pmt_is_pair(p)) // malformed alist - return PMT_BOOL_F; + return PMT_F; if (pmt_eqv(obj, pmt_car(p))) return p; alist = pmt_cdr(alist); } - return PMT_BOOL_F; + return PMT_F; } pmt_t @@ -721,14 +792,14 @@ pmt_assoc(pmt_t obj, pmt_t alist) while (pmt_is_pair(alist)){ pmt_t p = pmt_car(alist); if (!pmt_is_pair(p)) // malformed alist - return PMT_BOOL_F; + return PMT_F; if (pmt_equal(obj, pmt_car(p))) return p; alist = pmt_cdr(alist); } - return PMT_BOOL_F; + return PMT_F; } pmt_t @@ -805,7 +876,7 @@ pmt_memq(pmt_t obj, pmt_t list) return list; list = pmt_cdr(list); } - return PMT_BOOL_F; + return PMT_F; } pmt_t @@ -816,7 +887,7 @@ pmt_memv(pmt_t obj, pmt_t list) return list; list = pmt_cdr(list); } - return PMT_BOOL_F; + return PMT_F; } pmt_t @@ -827,7 +898,7 @@ pmt_member(pmt_t obj, pmt_t list) return list; list = pmt_cdr(list); } - return PMT_BOOL_F; + return PMT_F; } bool @@ -865,3 +936,62 @@ pmt_list4(pmt_t x1, pmt_t x2, pmt_t x3, pmt_t x4) { return pmt_cons(x1, pmt_cons(x2, pmt_cons(x3, pmt_cons(x4, PMT_NIL)))); } + +pmt_t +pmt_caar(pmt_t pair) +{ + return (pmt_car(pmt_car(pair))); +} + +pmt_t +pmt_cadr(pmt_t pair) +{ + return pmt_car(pmt_cdr(pair)); +} + +pmt_t +pmt_cdar(pmt_t pair) +{ + return pmt_cdr(pmt_car(pair)); +} + +pmt_t +pmt_cddr(pmt_t pair) +{ + return pmt_cdr(pmt_cdr(pair)); +} + +pmt_t +pmt_caddr(pmt_t pair) +{ + return pmt_car(pmt_cdr(pmt_cdr(pair))); +} + +pmt_t +pmt_cadddr(pmt_t pair) +{ + return pmt_car(pmt_cdr(pmt_cdr(pmt_cdr(pair)))); +} + +bool +pmt_is_eof_object(pmt_t obj) +{ + return pmt_eq(obj, PMT_EOF); +} + +void +pmt_dump_sizeof() +{ + printf("sizeof(pmt_t) = %3zd\n", sizeof(pmt_t)); + printf("sizeof(pmt_base) = %3zd\n", sizeof(pmt_base)); + printf("sizeof(pmt_bool) = %3zd\n", sizeof(pmt_bool)); + printf("sizeof(pmt_symbol) = %3zd\n", sizeof(pmt_symbol)); + printf("sizeof(pmt_integer) = %3zd\n", sizeof(pmt_integer)); + printf("sizeof(pmt_real) = %3zd\n", sizeof(pmt_real)); + printf("sizeof(pmt_complex) = %3zd\n", sizeof(pmt_complex)); + printf("sizeof(pmt_null) = %3zd\n", sizeof(pmt_null)); + printf("sizeof(pmt_pair) = %3zd\n", sizeof(pmt_pair)); + printf("sizeof(pmt_vector) = %3zd\n", sizeof(pmt_vector)); + printf("sizeof(pmt_dict) = %3zd\n", sizeof(pmt_dict)); + printf("sizeof(pmt_uniform_vector) = %3zd\n", sizeof(pmt_uniform_vector)); +} diff --git a/pmt/src/lib/pmt.h b/pmt/src/lib/pmt.h index 030637c4..dcf1ce66 100644 --- a/pmt/src/lib/pmt.h +++ b/pmt/src/lib/pmt.h @@ -24,10 +24,11 @@ #define INCLUDED_PMT_H #include +#include #include #include #include -#include +#include #include /*! @@ -82,8 +83,8 @@ public: * I.e., there is a single false value, #f. * ------------------------------------------------------------------------ */ -extern const pmt_t PMT_BOOL_T; //< #t : boolean true constant -extern const pmt_t PMT_BOOL_F; //< #f : boolean false constant +extern const pmt_t PMT_T; //< #t : boolean true constant +extern const pmt_t PMT_F; //< #f : boolean false constant //! Return true if obj is #t or #f, else return false. bool pmt_is_bool(pmt_t obj); @@ -97,7 +98,7 @@ bool pmt_is_false(pmt_t obj); //! Return #f is val is false, else return #t. pmt_t pmt_from_bool(bool val); -//! Return true if val is PMT_BOOL_T, return false when val is PMT_BOOL_F, +//! Return true if val is PMT_T, return false when val is PMT_F, // else raise wrong_type exception. bool pmt_to_bool(pmt_t val); @@ -225,6 +226,13 @@ void pmt_set_car(pmt_t pair, pmt_t value); //! Stores \p value in the cdr field of \p pair. void pmt_set_cdr(pmt_t pair, pmt_t value); +pmt_t pmt_caar(pmt_t pair); +pmt_t pmt_cadr(pmt_t pair); +pmt_t pmt_cdar(pmt_t pair); +pmt_t pmt_cddr(pmt_t pair); +pmt_t pmt_caddr(pmt_t pair); +pmt_t pmt_cadddr(pmt_t pair); + /* * ------------------------------------------------------------------------ * Vectors @@ -412,6 +420,28 @@ pmt_t pmt_dict_keys(pmt_t dict); //! Return list of values pmt_t pmt_dict_values(pmt_t dict); +/* + * ------------------------------------------------------------------------ + * Any (wraps boost::any -- can be used to wrap pretty much anything) + * + * Cannot be serialized or used across process boundaries. + * See http://www.boost.org/doc/html/any.html + * ------------------------------------------------------------------------ + */ + +//! Return true if \p obj is an any +bool pmt_is_any(pmt_t obj); + +//! make an any +pmt_t pmt_make_any(const boost::any &any); + +//! Return underlying boost::any +boost::any pmt_any_ref(pmt_t obj); + +//! Store \p any in \p obj +void pmt_any_set(pmt_t obj, const boost::any &any); + + /* * ------------------------------------------------------------------------ * General functions @@ -618,11 +648,14 @@ std::ostream& operator<<(std::ostream &os, pmt_t obj); /*! * \brief Write portable byte-serial representation of \p obj to \p sink */ -void pmt_serialize(pmt_t obj, std::ostream &sink); +bool pmt_serialize(pmt_t obj, std::streambuf &sink); /*! * \brief Create obj from portable byte-serial representation */ -pmt_t pmt_deserialize(std::istream &source); +pmt_t pmt_deserialize(std::streambuf &source); + + +void pmt_dump_sizeof(); // debugging #endif /* INCLUDED_PMT_H */ diff --git a/pmt/src/lib/pmt_int.h b/pmt/src/lib/pmt_int.h index 458443ea..e82ce50e 100644 --- a/pmt/src/lib/pmt_int.h +++ b/pmt/src/lib/pmt_int.h @@ -31,6 +31,8 @@ * See pmt.h for the public interface */ +#define PMT_LOCAL_ALLOCATOR 0 // define to 0 or 1 + class pmt_base : boost::noncopyable { protected: pmt_base(){}; @@ -47,6 +49,7 @@ public: virtual bool is_pair() const { return false; } virtual bool is_vector() const { return false; } virtual bool is_dict() const { return false; } + virtual bool is_any() const { return false; } virtual bool is_uniform_vector() const { return false; } virtual bool is_u8vector() const { return false; } @@ -62,6 +65,10 @@ public: virtual bool is_c32vector() const { return false; } virtual bool is_c64vector() const { return false; } +# if (PMT_LOCAL_ALLOCATOR) + void *operator new(size_t); + void operator delete(void *, size_t); +#endif }; class pmt_bool : public pmt_base @@ -189,6 +196,20 @@ public: pmt_t values() const; }; +class pmt_any : public pmt_base +{ + boost::any d_any; + +public: + pmt_any(const boost::any &any); + //~pmt_any(); + + bool is_any() const { return true; } + const boost::any &ref() const { return d_any; } + void set(const boost::any &any) { d_any = any; } +}; + + class pmt_uniform_vector : public pmt_base { public: diff --git a/pmt/src/lib/pmt_pool.cc b/pmt/src/lib/pmt_pool.cc new file mode 100644 index 00000000..16ab9c61 --- /dev/null +++ b/pmt/src/lib/pmt_pool.cc @@ -0,0 +1,96 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include + +static inline size_t +ROUNDUP(size_t x, size_t stride) +{ + return ((((x) + (stride) - 1)/(stride)) * (stride)); +} + +pmt_pool::pmt_pool(size_t itemsize, size_t alignment, size_t allocation_size) + : d_itemsize(ROUNDUP(itemsize, alignment)), + d_alignment(alignment), + d_allocation_size(std::max(allocation_size, 16 * itemsize)), + d_freelist(0) +{ +} + +pmt_pool::~pmt_pool() +{ + for (unsigned int i = 0; i < d_allocations.size(); i++){ + delete [] d_allocations[i]; + } +} + +void * +pmt_pool::malloc() +{ + omni_mutex_lock l(d_mutex); + item *p; + + if (d_freelist){ // got something? + p = d_freelist; + d_freelist = p->d_next; + return p; + } + + // allocate a new chunk + char *alloc = new char[d_allocation_size + d_alignment - 1]; + d_allocations.push_back(alloc); + + // get the alignment we require + char *start = (char *)(((uintptr_t)alloc + d_alignment-1) & -d_alignment); + char *end = alloc + d_allocation_size + d_alignment - 1; + size_t n = (end - start) / d_itemsize; + + // link the new items onto the free list. + p = (item *) start; + for (size_t i = 0; i < n; i++){ + p->d_next = d_freelist; + d_freelist = p; + p = (item *)((char *) p + d_itemsize); + } + + // now return the first one + p = d_freelist; + d_freelist = p->d_next; + return p; +} + +void +pmt_pool::free(void *foo) +{ + if (!foo) + return; + + omni_mutex_lock l(d_mutex); + + item *p = (item *) foo; + p->d_next = d_freelist; + d_freelist = p; +} diff --git a/pmt/src/lib/pmt_pool.h b/pmt/src/lib/pmt_pool.h new file mode 100644 index 00000000..fd9cc063 --- /dev/null +++ b/pmt/src/lib/pmt_pool.h @@ -0,0 +1,61 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_PMT_POOL_H +#define INCLUDED_PMT_POOL_H + +#include +#include +#include + +/*! + * \brief very simple thread-safe fixed-size allocation pool + * + * FIXME may want to go to global allocation with per-thread free list. + * This would eliminate virtually all lock contention. + */ +class pmt_pool { + + struct item { + struct item *d_next; + }; + + omni_mutex d_mutex; + + size_t d_itemsize; + size_t d_alignment; + size_t d_allocation_size; + item *d_freelist; + std::vector d_allocations; + +public: + /*! + * \param itemsize size in bytes of the items to be allocated. + * \param alignment alignment in bytes of all objects to be allocated (must be power-of-2). + * \param allocation_size number of bytes to allocate at a time from the underlying allocator. + */ + pmt_pool(size_t itemsize, size_t alignment = 16, size_t allocation_size = 4096); + ~pmt_pool(); + + void *malloc(); + void free(void *p); +}; + +#endif /* INCLUDED_PMT_POOL_H */ diff --git a/pmt/src/lib/pmt_serialize.cc b/pmt/src/lib/pmt_serialize.cc new file mode 100644 index 00000000..45f688be --- /dev/null +++ b/pmt/src/lib/pmt_serialize.cc @@ -0,0 +1,353 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include "pmt_int.h" +#include "pmt_serial_tags.h" + +static pmt_t parse_pair(std::streambuf &sb); + +// ---------------------------------------------------------------- +// output primitives +// ---------------------------------------------------------------- + +static bool +serialize_untagged_u8(unsigned int i, std::streambuf &sb) +{ + return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof(); +} + +// always writes big-endian +static bool +serialize_untagged_u16(unsigned int i, std::streambuf &sb) +{ + sb.sputc((i >> 8) & 0xff); + return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof(); +} + +// always writes big-endian +static bool +serialize_untagged_u32(unsigned int i, std::streambuf &sb) +{ + sb.sputc((i >> 24) & 0xff); + sb.sputc((i >> 16) & 0xff); + sb.sputc((i >> 8) & 0xff); + return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof(); +} + +#if 0 +// always writes big-endian +static bool +serialize_untagged_u64(uint64_t i, std::streambuf &sb) +{ + sb.sputc((i >> 56) & 0xff); + sb.sputc((i >> 48) & 0xff); + sb.sputc((i >> 40) & 0xff); + sb.sputc((i >> 32) & 0xff); + sb.sputc((i >> 24) & 0xff); + sb.sputc((i >> 16) & 0xff); + sb.sputc((i >> 8) & 0xff); + return sb.sputc((i >> 0) & 0xff) != std::streambuf::traits_type::eof(); +} +#endif + +// ---------------------------------------------------------------- +// input primitives +// ---------------------------------------------------------------- + + +// always reads big-endian +static bool +deserialize_untagged_u8(uint8_t *ip, std::streambuf &sb) +{ + std::streambuf::traits_type::int_type t; + int i; + + t = sb.sbumpc(); + i = t & 0xff; + + *ip = i; + return t != std::streambuf::traits_type::eof(); +} + +// always reads big-endian +static bool +deserialize_untagged_u16(uint16_t *ip, std::streambuf &sb) +{ + std::streambuf::traits_type::int_type t; + int i; + + t = sb.sbumpc(); + i = t & 0xff; + + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + + *ip = i; + return t != std::streambuf::traits_type::eof(); +} + +// always reads big-endian +static bool +deserialize_untagged_u32(uint32_t *ip, std::streambuf &sb) +{ + std::streambuf::traits_type::int_type t; + int i; + + t = sb.sbumpc(); + i = t & 0xff; + + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + + *ip = i; + return t != std::streambuf::traits_type::eof(); +} + +#if 0 +// always reads big-endian +static bool +deserialize_untagged_u64(uint64_t *ip, std::streambuf &sb) +{ + std::streambuf::traits_type::int_type t; + uint64_t i; + + t = sb.sbumpc(); + i = t & 0xff; + + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + t = sb.sbumpc(); + i = (i << 8) | (t & 0xff); + + *ip = i; + return t != std::streambuf::traits_type::eof(); +} +#endif + +/* + * Write portable byte-serial representation of \p obj to \p sb + * + * N.B., Circular structures cause infinite recursion. + */ +bool +pmt_serialize(pmt_t obj, std::streambuf &sb) +{ + bool ok = true; + + tail_recursion: + + if (pmt_is_bool(obj)){ + if (pmt_eq(obj, PMT_T)) + return serialize_untagged_u8(PST_TRUE, sb); + else + return serialize_untagged_u8(PST_FALSE, sb); + } + + if (pmt_is_null(obj)) + return serialize_untagged_u8(PST_NULL, sb); + + if (pmt_is_symbol(obj)){ + const std::string s = pmt_symbol_to_string(obj); + size_t len = s.size(); + ok = serialize_untagged_u8(PST_SYMBOL, sb); + ok &= serialize_untagged_u16(len, sb); + for (size_t i = 0; i < len; i++) + ok &= serialize_untagged_u8(s[i], sb); + return ok; + } + + if (pmt_is_pair(obj)){ + ok = serialize_untagged_u8(PST_PAIR, sb); + ok &= pmt_serialize(pmt_car(obj), sb); + if (!ok) + return false; + obj = pmt_cdr(obj); + goto tail_recursion; + } + + if (pmt_is_number(obj)){ + + if (pmt_is_integer(obj)){ + long i = pmt_to_long(obj); + if (sizeof(long) > 4){ + if (i < -2147483647 || i > 2147483647) + throw pmt_notimplemented("pmt_serialize (64-bit integers)", obj); + } + ok = serialize_untagged_u8(PST_INT32, sb); + ok &= serialize_untagged_u32(i, sb); + return ok; + } + + if (pmt_is_real(obj)) + throw pmt_notimplemented("pmt_serialize (real)", obj); + + if (pmt_is_complex(obj)) + throw pmt_notimplemented("pmt_serialize (complex)", obj); + } + + if (pmt_is_vector(obj)) + throw pmt_notimplemented("pmt_serialize (vector)", obj); + + if (pmt_is_uniform_vector(obj)) + throw pmt_notimplemented("pmt_serialize (uniform-vector)", obj); + + if (pmt_is_dict(obj)) + throw pmt_notimplemented("pmt_serialize (dict)", obj); + + + throw pmt_notimplemented("pmt_serialize (?)", obj); +} + +/* + * Create obj from portable byte-serial representation + * + * Returns next obj from streambuf, or PMT_EOF at end of file. + * Throws exception on malformed input. + */ +pmt_t +pmt_deserialize(std::streambuf &sb) +{ + uint8_t tag; + //uint8_t u8; + uint16_t u16; + uint32_t u32; + //uint32_t u64; + static char tmpbuf[1024]; + + if (!deserialize_untagged_u8(&tag, sb)) + return PMT_EOF; + + switch (tag){ + case PST_TRUE: + return PMT_T; + + case PST_FALSE: + return PMT_F; + + case PST_NULL: + return PMT_NIL; + + case PST_SYMBOL: + if (!deserialize_untagged_u16(&u16, sb)) + goto error; + if (u16 > sizeof(tmpbuf)) + throw pmt_notimplemented("pmt_deserialize: very long symbol", + PMT_F); + if (sb.sgetn(tmpbuf, u16) != u16) + goto error; + return pmt_intern(std::string(tmpbuf, u16)); + + case PST_INT32: + if (!deserialize_untagged_u32(&u32, sb)) + goto error; + return pmt_from_long((int32_t) u32); + + case PST_PAIR: + return parse_pair(sb); + + case PST_DOUBLE: + case PST_COMPLEX: + case PST_VECTOR: + case PST_DICT: + case PST_UNIFORM_VECTOR: + case PST_COMMENT: + throw pmt_notimplemented("pmt_deserialize: tag value = ", + pmt_from_long(tag)); + + default: + throw pmt_exception("pmt_deserialize: malformed input stream, tag value = ", + pmt_from_long(tag)); + } + + error: + throw pmt_exception("pmt_deserialize: malformed input stream", PMT_F); +} + +/* + * This is a mostly non-recursive implementation that allows us to + * deserialize very long lists w/o exhausting the evaluation stack. + * + * On entry we've already eaten the PST_PAIR tag. + */ +pmt_t +parse_pair(std::streambuf &sb) +{ + uint8_t tag; + pmt_t val, expr, lastnptr, nptr; + + // + // Keep appending nodes until we get a non-PAIR cdr. + // + lastnptr = PMT_NIL; + while (1){ + expr = pmt_deserialize(sb); // read the car + + nptr = pmt_cons(expr, PMT_NIL); // build new cell + if (pmt_is_null(lastnptr)) + val = nptr; + else + pmt_set_cdr(lastnptr, nptr); + lastnptr = nptr; + + if (!deserialize_untagged_u8(&tag, sb)) // get tag of cdr + throw pmt_exception("pmt_deserialize: malformed input stream", PMT_F); + + if (tag == PST_PAIR) + continue; // keep on looping... + + if (tag == PST_NULL){ + expr = PMT_NIL; + break; + } + + // + // default: push tag back and use pmt_deserialize to get the cdr + // + sb.sungetc(); + expr = pmt_deserialize(sb); + break; + } + + // + // At this point, expr contains the value of the final cdr in the list. + // + pmt_set_cdr(lastnptr, expr); + return val; +} diff --git a/pmt/src/lib/qa_pmt_prims.cc b/pmt/src/lib/qa_pmt_prims.cc index cb687b0e..4a665fe5 100644 --- a/pmt/src/lib/qa_pmt_prims.cc +++ b/pmt/src/lib/qa_pmt_prims.cc @@ -24,13 +24,14 @@ #include #include #include +#include void qa_pmt_prims::test_symbols() { - CPPUNIT_ASSERT(!pmt_is_symbol(PMT_BOOL_T)); - CPPUNIT_ASSERT(!pmt_is_symbol(PMT_BOOL_F)); - CPPUNIT_ASSERT_THROW(pmt_symbol_to_string(PMT_BOOL_F), pmt_wrong_type); + CPPUNIT_ASSERT(!pmt_is_symbol(PMT_T)); + CPPUNIT_ASSERT(!pmt_is_symbol(PMT_F)); + CPPUNIT_ASSERT_THROW(pmt_symbol_to_string(PMT_F), pmt_wrong_type); pmt_t sym1 = pmt_string_to_symbol("test"); CPPUNIT_ASSERT(pmt_is_symbol(sym1)); @@ -76,13 +77,13 @@ void qa_pmt_prims::test_booleans() { pmt_t sym = pmt_string_to_symbol("test"); - CPPUNIT_ASSERT(pmt_is_bool(PMT_BOOL_T)); - CPPUNIT_ASSERT(pmt_is_bool(PMT_BOOL_F)); + CPPUNIT_ASSERT(pmt_is_bool(PMT_T)); + CPPUNIT_ASSERT(pmt_is_bool(PMT_F)); CPPUNIT_ASSERT(!pmt_is_bool(sym)); - CPPUNIT_ASSERT_EQUAL(pmt_from_bool(false), PMT_BOOL_F); - CPPUNIT_ASSERT_EQUAL(pmt_from_bool(true), PMT_BOOL_T); - CPPUNIT_ASSERT_EQUAL(false, pmt_to_bool(PMT_BOOL_F)); - CPPUNIT_ASSERT_EQUAL(true, pmt_to_bool(PMT_BOOL_T)); + CPPUNIT_ASSERT_EQUAL(pmt_from_bool(false), PMT_F); + CPPUNIT_ASSERT_EQUAL(pmt_from_bool(true), PMT_T); + CPPUNIT_ASSERT_EQUAL(false, pmt_to_bool(PMT_F)); + CPPUNIT_ASSERT_EQUAL(true, pmt_to_bool(PMT_T)); CPPUNIT_ASSERT_THROW(pmt_to_bool(sym), pmt_wrong_type); } @@ -91,10 +92,10 @@ qa_pmt_prims::test_integers() { pmt_t p1 = pmt_from_long(1); pmt_t m1 = pmt_from_long(-1); - CPPUNIT_ASSERT(!pmt_is_integer(PMT_BOOL_T)); + CPPUNIT_ASSERT(!pmt_is_integer(PMT_T)); CPPUNIT_ASSERT(pmt_is_integer(p1)); CPPUNIT_ASSERT(pmt_is_integer(m1)); - CPPUNIT_ASSERT_THROW(pmt_to_long(PMT_BOOL_T), pmt_wrong_type); + CPPUNIT_ASSERT_THROW(pmt_to_long(PMT_T), pmt_wrong_type); CPPUNIT_ASSERT_EQUAL(-1L, pmt_to_long(m1)); CPPUNIT_ASSERT_EQUAL(1L, pmt_to_long(p1)); } @@ -104,10 +105,10 @@ qa_pmt_prims::test_reals() { pmt_t p1 = pmt_from_double(1); pmt_t m1 = pmt_from_double(-1); - CPPUNIT_ASSERT(!pmt_is_real(PMT_BOOL_T)); + CPPUNIT_ASSERT(!pmt_is_real(PMT_T)); CPPUNIT_ASSERT(pmt_is_real(p1)); CPPUNIT_ASSERT(pmt_is_real(m1)); - CPPUNIT_ASSERT_THROW(pmt_to_double(PMT_BOOL_T), pmt_wrong_type); + CPPUNIT_ASSERT_THROW(pmt_to_double(PMT_T), pmt_wrong_type); CPPUNIT_ASSERT_EQUAL(-1.0, pmt_to_double(m1)); CPPUNIT_ASSERT_EQUAL(1.0, pmt_to_double(p1)); CPPUNIT_ASSERT_EQUAL(1.0, pmt_to_double(pmt_from_long(1))); @@ -118,10 +119,10 @@ qa_pmt_prims::test_complexes() { pmt_t p1 = pmt_make_rectangular(2, -3); pmt_t m1 = pmt_make_rectangular(-3, 2); - CPPUNIT_ASSERT(!pmt_is_complex(PMT_BOOL_T)); + CPPUNIT_ASSERT(!pmt_is_complex(PMT_T)); CPPUNIT_ASSERT(pmt_is_complex(p1)); CPPUNIT_ASSERT(pmt_is_complex(m1)); - CPPUNIT_ASSERT_THROW(pmt_to_complex(PMT_BOOL_T), pmt_wrong_type); + CPPUNIT_ASSERT_THROW(pmt_to_complex(PMT_T), pmt_wrong_type); CPPUNIT_ASSERT_EQUAL(std::complex(2, -3), pmt_to_complex(p1)); CPPUNIT_ASSERT_EQUAL(std::complex(-3, 2), pmt_to_complex(m1)); CPPUNIT_ASSERT_EQUAL(std::complex(1.0, 0), pmt_to_complex(pmt_from_long(1))); @@ -242,7 +243,7 @@ qa_pmt_prims::test_misc() pmt_t alist = pmt_cons(p0, pmt_cons(p1, pmt_cons(p2, PMT_NIL))); CPPUNIT_ASSERT(pmt_eq(p1, pmt_assv(k1, alist))); - CPPUNIT_ASSERT(pmt_eq(PMT_BOOL_F, pmt_assv(k3, alist))); + CPPUNIT_ASSERT(pmt_eq(PMT_F, pmt_assv(k3, alist))); pmt_t keys = pmt_cons(k0, pmt_cons(k1, pmt_cons(k2, PMT_NIL))); pmt_t vals = pmt_cons(v0, pmt_cons(v1, pmt_cons(v2, PMT_NIL))); @@ -293,3 +294,102 @@ qa_pmt_prims::test_io() CPPUNIT_ASSERT_EQUAL(std::string("k0"), pmt_write_string(k0)); } + +// ------------------------------------------------------------------------ + +// class foo is used in test_any below. +// It can't be declared in the scope of test_any because of template +// namespace problems. + +class foo { +public: + double d_double; + int d_int; + foo(double d=0, int i=0) : d_double(d), d_int(i) {} +}; + +bool operator==(const foo &a, const foo &b) +{ + return a.d_double == b.d_double && a.d_int == b.d_int; +} + +std::ostream& operator<<(std::ostream &os, const foo obj) +{ + os << ""; + return os; +} + +void +qa_pmt_prims::test_any() +{ + boost::any a0; + boost::any a1; + boost::any a2; + + a0 = std::string("Hello!"); + a1 = 42; + a2 = foo(3.250, 21); + + pmt_t p0 = pmt_make_any(a0); + pmt_t p1 = pmt_make_any(a1); + pmt_t p2 = pmt_make_any(a2); + + CPPUNIT_ASSERT_EQUAL(std::string("Hello!"), + boost::any_cast(pmt_any_ref(p0))); + + CPPUNIT_ASSERT_EQUAL(42, + boost::any_cast(pmt_any_ref(p1))); + + CPPUNIT_ASSERT_EQUAL(foo(3.250, 21), + boost::any_cast(pmt_any_ref(p2))); +} + +// ------------------------------------------------------------------------ + +void +qa_pmt_prims::test_serialize() +{ + std::stringbuf sb; // fake channel + pmt_t a = pmt_intern("a"); + pmt_t b = pmt_intern("b"); + pmt_t c = pmt_intern("c"); + + sb.str(""); // reset channel to empty + + // write stuff to channel + + pmt_serialize(PMT_NIL, sb); + pmt_serialize(pmt_intern("foobarvia"), sb); + pmt_serialize(pmt_from_long(123456789), sb); + pmt_serialize(pmt_from_long(-123456789), sb); + pmt_serialize(pmt_cons(PMT_NIL, PMT_NIL), sb); + pmt_serialize(pmt_cons(a, b), sb); + pmt_serialize(pmt_list1(a), sb); + pmt_serialize(pmt_list2(a, b), sb); + pmt_serialize(pmt_list3(a, b, c), sb); + pmt_serialize(pmt_list3(a, pmt_list3(c, b, a), c), sb); + pmt_serialize(PMT_T, sb); + pmt_serialize(PMT_F, sb); + + // read it back + + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), PMT_NIL)); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_intern("foobarvia"))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_from_long(123456789))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_from_long(-123456789))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_cons(PMT_NIL, PMT_NIL))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_cons(a, b))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_list1(a))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_list2(a, b))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_list3(a, b, c))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), pmt_list3(a, pmt_list3(c, b, a), c))); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), PMT_T)); + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), PMT_F)); + + CPPUNIT_ASSERT(pmt_equal(pmt_deserialize(sb), PMT_EOF)); // last item + + + // FIXME add tests for real, complex, vector, uniform-vector, dict + // FIXME add tests for malformed input too. + +} diff --git a/pmt/src/lib/qa_pmt_prims.h b/pmt/src/lib/qa_pmt_prims.h index 8d379c5a..a390343b 100644 --- a/pmt/src/lib/qa_pmt_prims.h +++ b/pmt/src/lib/qa_pmt_prims.h @@ -38,7 +38,9 @@ class qa_pmt_prims : public CppUnit::TestCase { CPPUNIT_TEST(test_equivalence); CPPUNIT_TEST(test_misc); CPPUNIT_TEST(test_dict); + CPPUNIT_TEST(test_any); CPPUNIT_TEST(test_io); + CPPUNIT_TEST(test_serialize); CPPUNIT_TEST_SUITE_END(); private: @@ -52,7 +54,9 @@ class qa_pmt_prims : public CppUnit::TestCase { void test_equivalence(); void test_misc(); void test_dict(); + void test_any(); void test_io(); + void test_serialize(); }; #endif /* INCLUDED_QA_PMT_PRIMS_H */ diff --git a/pmt/src/scheme/Makefile.am b/pmt/src/scheme/Makefile.am new file mode 100644 index 00000000..7700a1fc --- /dev/null +++ b/pmt/src/scheme/Makefile.am @@ -0,0 +1,21 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +SUBDIRS = gnuradio diff --git a/pmt/src/scheme/gnuradio/Makefile.am b/pmt/src/scheme/gnuradio/Makefile.am new file mode 100644 index 00000000..12bc9b11 --- /dev/null +++ b/pmt/src/scheme/gnuradio/Makefile.am @@ -0,0 +1,32 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +#pkgdatadir = $(datadir)/gnuradio + +EXTRA_DIST = \ + gen-serial-tags.scm \ + pmt-serial-tags.scm \ + pmt-serialize.scm + + +# really scheme source files +pkgdata_DATA = \ + pmt-serial-tags.scm \ + pmt-serialize.scm diff --git a/pmt/src/scheme/gnuradio/gen-serial-tags.scm b/pmt/src/scheme/gnuradio/gen-serial-tags.scm new file mode 100755 index 00000000..5bcf8015 --- /dev/null +++ b/pmt/src/scheme/gnuradio/gen-serial-tags.scm @@ -0,0 +1,118 @@ +#!/usr/bin/guile \ +-e main -s +!# +;;; -*-scheme-*- +;;; +;;; 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 2, or (at your option) +;;; any later version. +;;; +;;; GNU Radio is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License along +;;; with this program; if not, write to the Free Software Foundation, Inc., +;;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;;; + +(use-modules (ice-9 format)) + +(defmacro when (pred . body) + `(if ,pred (begin ,@body) #f)) + +;; ---------------------------------------------------------------- + +(define (main args) + + (define (usage) + (format 0 + "usage: ~a ~%" + (car args))) + + (when (not (= (length args) 3)) + (usage) + (format 0 "args: ~s~%" args) + (exit 1)) + + (let ((i-file (open-input-file (cadr args))) + (h-file (open-output-file (caddr args)))) + + (write-header-comment h-file "// ") + (display "#ifndef INCLUDED_PMT_SERIAL_TAGS_H\n" h-file) + (display "#define INCLUDED_PMT_SERIAL_TAGS_H\n" h-file) + (newline h-file) + (display "enum pst_tags {\n" h-file) + + (for-each-in-file i-file + (lambda (form) + (let* ((name (cadr form)) + (c-name (string-upcase (c-ify name))) + (value (caddr form))) + ;;(format h-file "static const int ~a\t= 0x~x;~%" c-name value) + (format h-file " ~a\t= 0x~x,~%" c-name value)))) + + (display "};\n" h-file) + (display "#endif\n" h-file))) + +(define (c-ify name) + (list->string (map (lambda (c) + (if (eqv? c #\-) #\_ c)) + (string->list (symbol->string name))))) + + +(define (write-header-comment o-port prefix) + (for-each (lambda (comment) + (format o-port "~a~a~%" prefix comment)) + header-comment)) + +(define header-comment + '( + "" + "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 2, or (at your option)" + "any later version." + "" + "GNU Radio is distributed in the hope that it will be useful," + "but WITHOUT ANY WARRANTY; without even the implied warranty of" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" + "GNU General Public License for more details." + "" + "You should have received a copy of the GNU General Public License along" + "with this program; if not, write to the Free Software Foundation, Inc.," + "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA." + "" + "" + "THIS FILE IS MACHINE GENERATED FROM pmt-serial-tags.scm. DO NOT EDIT BY HAND." + "See pmt-serial-tags.scm for additional commentary." + "")) + + + +(define (for-each-in-file file f) + (let ((port (if (port? file) + file + (open-input-file file)))) + (letrec + ((loop + (lambda (port form) + (cond ((eof-object? form) + (when (not (eq? port file)) + (close-input-port port)) + #t) + (else + (f form) + (set! form #f) ; for GC + (loop port (read port))))))) + (loop port (read port))))) diff --git a/pmt/src/scheme/gnuradio/macros-etc.scm b/pmt/src/scheme/gnuradio/macros-etc.scm new file mode 100644 index 00000000..518c1ffe --- /dev/null +++ b/pmt/src/scheme/gnuradio/macros-etc.scm @@ -0,0 +1,50 @@ +;;; -*- scheme -*- +;;; +;;; 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 2, or (at your option) +;;; any later version. +;;; +;;; GNU Radio is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License along +;;; with this program; if not, write to the Free Software Foundation, Inc., +;;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;;; + +(define-module (gnuradio macros-etc) + :export (atom? when unless for-each-in-file)) + +(define (atom? obj) + (not (pair? obj))) + +(defmacro when (pred . body) + `(if ,pred (begin ,@body) #f)) + +(defmacro unless (pred . body) + `(if (not ,pred) (begin ,@body) #f)) + + +(define (for-each-in-file file f) + (let ((port (if (port? file) + file + (open-input-file file)))) + (letrec + ((loop + (lambda (port form) + (cond ((eof-object? form) + (when (not (eq? port file)) + (close-input-port port)) + #t) + (else + (f form) + (set! form #f) ; for GC + (loop port (read port))))))) + (loop port (read port))))) diff --git a/pmt/src/scheme/gnuradio/pmt-serial-tags.scm b/pmt/src/scheme/gnuradio/pmt-serial-tags.scm new file mode 100644 index 00000000..4dd18303 --- /dev/null +++ b/pmt/src/scheme/gnuradio/pmt-serial-tags.scm @@ -0,0 +1,75 @@ +;;; -*-scheme-*- +;;; +;;; 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 2, or (define 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. +;;; + +;;; definitions of tag values used for marshalling pmt data + +(define pst-true #x00) +(define pst-false #x01) +(define pst-symbol #x02) ; untagged-int16 n; followed by n bytes of symbol name +(define pst-int32 #x03) +(define pst-double #x04) +(define pst-complex #x05) ; complex: real, imag +(define pst-null #x06) +(define pst-pair #x07) ; followed by two objects +(define pst-vector #x08) ; untagged-int32 n; followed by n objects +(define pst-dict #x09) ; untagged-int32 n; followed by n key/value tuples + +(define pst-uniform-vector #x0a) + +;; u8, s8, u16, s16, u32, s32, u64, s64, f32, f64, c32, c64 +;; +;; untagged-uint8 tag +;; untagged-uint8 uvi (define uniform vector info, see below) +;; untagged-int32 n-items +;; untagged-uint8 npad +;; npad bytes of zeros to align binary data +;; n-items binary numeric items +;; +;; uvi: +;; +-+-+-+-+-+-+-+-+ +;; |B| subtype | +;; +-+-+-+-+-+-+-+-+ +;; +;; B == 0, numeric data is little-endian. +;; B == 1, numeric data is big-endian. + + (define uvi-endian-mask #x80) + (define uvi-subtype-mask #x7f) + + (define uvi-little-endian #x00) + (define uvi-big-endian #x80) + + (define uvi-u8 #x00) + (define uvi-s8 #x01) + (define uvi-u16 #x02) + (define uvi-s16 #x03) + (define uvi-u32 #x04) + (define uvi-s32 #x05) + (define uvi-u64 #x06) + (define uvi-s64 #x07) + (define uvi-f32 #x08) + (define uvi-f64 #x09) + (define uvi-c32 #x0a) + (define uvi-c64 #x0b) + + +(define pst-comment #x3b) ; ascii ';' +(define pst-comment-end #x0a) ; ascii '\n' diff --git a/pmt/src/scheme/gnuradio/pmt-serialize.scm b/pmt/src/scheme/gnuradio/pmt-serialize.scm new file mode 100644 index 00000000..a39f9cf5 --- /dev/null +++ b/pmt/src/scheme/gnuradio/pmt-serialize.scm @@ -0,0 +1,48 @@ +;;; +;;; 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 2, or (at your option) +;;; any later version. +;;; +;;; GNU Radio is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License along +;;; with this program; if not, write to the Free Software Foundation, Inc., +;;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;;; + +;;; An implementation of pmt_serialize in scheme. +;;; Currently handles only symbols and pairs. They're all we need for now. + +(define-module (gnuradio pmt-serialize) + :export (pmt-serialize)) + +(load-from-path "gnuradio/pmt-serial-tags") + +(define (pmt-serialize obj put-byte) + (define (put-u16 x) + (put-byte (logand (ash x -8) #xff)) + (put-byte (logand x #xff))) + + (cond ((null? obj) + (put-byte pst-null)) + ((symbol? obj) + (let* ((sym-as-bytes (map char->integer (string->list (symbol->string obj)))) + (len (length sym-as-bytes))) + (put-byte pst-symbol) + (put-u16 len) + (for-each put-byte sym-as-bytes))) + + ((pair? obj) + (put-byte pst-pair) + (pmt-serialize (car obj) put-byte) + (pmt-serialize (cdr obj) put-byte)) + (else + (throw 'not-implemented "pmt-serialize" obj)))) diff --git a/usrp/doc/inband-signaling-usb-host b/usrp/doc/inband-signaling-usb-host index ca306fe6..1a5d8707 100644 --- a/usrp/doc/inband-signaling-usb-host +++ b/usrp/doc/inband-signaling-usb-host @@ -18,241 +18,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # -This is preliminary design document on the organization of the host -component of the USRP inband signaling implementation over USB. -Assumptions: we'll have a single usrp_usb_daemon, implemented as an -mblock, that will provide the high-level message based interface to -the USRP. The daemon will handle all resource allocation, muxing and -demuxing for the control and status messages from multiple clients. - -The underlying cross-process IPC mechanism is not yet specified. -All communication will be via messages. - -The external interface to the usrp_usb_daemon will consist of two sets -of replicated ports, one set for the transmit, and one set for -receive. - - -// ------------------------------------------------------------------------ - -// Protocol class usrp_usb_common -// Defined from client's point-of-view -// -// This protocol class is shared by the usrp_usb_tx and usrp_usb_rx -// protocol classes - -// outgoing: - -cmd_allocate_channel(invocation_handle, channel, capacity_reservation) - -invocation_handle -Type Handle -Use The identifier provided by the client to tag the method invocation. - The identifier will be returned with the response, to provide - the client with a mechanism to match asynchronous responses with - commands that generated them. - -channel -Type integer -Use Specifies the Tx channel to allocate - -capacity_reservation -Type float, units are bytes/s -Use Specifies the number of bytes/s of USB capacity to reserve for - this channel. - - -// incoming: - -response_allocate_channel(invocation_handle, status) - -Type Handle -Use The identifier provided by the client in the prompting invocation. - The identifier is returned with the response, so that the - client has a mechanism to match asynchronous responses with - commands that generated them. The value of the invocation_handle - is opaque to the server, and is not required by the server to be unique. - -Type Status -Use Contains the status code for the operation, per FIXME, and - an optional message. - -Preconditions: - a) None - -Postconditions: - a) If successful, the requested channel and USB capacity are - allocated for the client's exclusive use. - - -// outgoing: - -cmd_deallocate_channel(invocation_handle, channel) - -invocation_handle -Type Handle -Use The identifier provided by the client to tag the method invocation. - The identifier will be returned with the response, to provide - the client with a mechanism to match asynchronous responses with - commands that generated them. - -channel -Type integer -Use Specifies the Tx channel to deallocate - - -// incoming: - -response_deallocate_channel(invocation_handle, status) - -Type Handle -Use The identifier provided by the client in the prompting invocation. - The identifier is returned with the response, so that the - client has a mechanism to match asynchronous responses with - commands that generated them. The value of the invocation_handle - is opaque to the server, and is not required by the server to be unique. - -Type Status -Use Contains the status code for the operation, per FIXME, and - an optional message. - -Preconditions: - a) None - -Postconditions: - a) If successful, the requested channel and associated USB capacity - are deallocated, and return to the pool of available resources. - - -// ------------------------------------------------------------------------ - -// Protocol class usrp_usb_tx inherits from usrp_usb_common -// Defined from client's point-of-view - -// outgoing: - -cmd_xmit_raw_frame(invocation_handle, channel, samples, timestamp) - -invocation_handle -Type Handle -Use The identifier provided by the client to tag the method invocation. - The identifier will be returned with the response, to provide - the client with a mechanism to match asynchronous responses with - commands that generated them. - -channel -Type integer -Use Specifies the channel that the frame of samples shall be transmitted on. - -samples -Type homogenous vector of unsigned char -Use These samples must already be in the appropriate format for parsing - by the USRP FPGA. The required format depends on the - configuration associated with this channel. No conversion of - the samples takes place in this method. Samples will be split - into multiple USB data packets as required for transport - across the USB. - -timestamp -Type 32-bit integer -Use Specifies the time at which the first sample in samples - shall be sent to the D/A converter. The format and - interpretation of time is as specified in - inband-signaling-usb. - - -// incoming: - -response_xmit_raw_frame(invocation_handle, status) - -Type Handle -Use The identifier provided by the client in the prompting invocation. - The identifier is returned with the response, so that the - client has a mechanism to match asynchronous responses with - commands that generated them. The value of the invocation_handle - is opaque to the server, and is not required by the server to be unique. - -Type Status -Use Contains the status code for the operation, per FIXME, and - an optional message. - -Preconditions: - a) The specified channel is allocated to this client - -Postconditions: - a) If successful, the samples of the frame have been queued for - transmission to the USRP. - - -// ------------------------------------------------------------------------ - -// Protocol class usrp_usb_rx inherits from usrp_usb_common -// Defined from client's point-of-view - - -// outgoing: - -cmd_recv_raw_samples(invocation_handle, channel) - -invocation_handle -Type Handle -Use The identifier provided by the client to tag the method invocation. - The identifier will be returned with the response, to provide - the client with a mechanism to match asynchronous responses with - commands that generated them. - -channel -Type integer -Use Specifies the channel that the samples shall be received from. - -The client may issue multiple cmd_recv_raw_samples commands to ensure -an uninterrupted flow of samples. The appropriate number of -outstanding cmds to issue is TBD, but is at least 2. - -[Discussion: I'm not sure if this is really the interface we want. -We may want to provide an enable/disable method, and then just begin -streaming samples. Or, we may wan to implement packet boundary -detection in the FPGA, and then return a frame's worth of samples -(potentially in multiple pieces). - -It may be that we need to different modes, one for packet based -reception and one for continuous streaming.] - - -// incoming: - -response_recv_raw_samples(invocation_handle, samples, timestamp, properties) - -Type Handle -Use The identifier provided by the client in the prompting invocation. - The identifier is returned with the response, so that the - client has a mechanism to match asynchronous responses with - commands that generated them. The value of the invocation_handle - is opaque to the server, and is not required by the server to be unique. - -samples -Type homogenous vector of unsigned char -Use Samples as returned by the USRP FPGA. The format of the - returned samples depends on the configuration associated with - this channel. No conversion or unpacking of samples takes place in - this method. - -timestamp -Type 32-bit integer -Use Specifies the time at which the first sample in samples - was received from the A/D converter. The format and - interpretation of time is as specified in - inband-signaling-usb. - -properties -Type Map -Use Returns additional (key, value) pairs associated with the - reception of these samples. In particular, the map may contain - the Received Strength Signal Indication (RSSI) reported by the - front end at the time the first sample was received from the A/D. - -// ------------------------------------------------------------------------ - -To do: control and configuration messages... +See usrp/host/lib/inband/usrp_server.mbh for interface diff --git a/usrp/fpga/inband_lib/chan_fifo_reader.v b/usrp/fpga/inband_lib/chan_fifo_reader.v new file mode 100755 index 00000000..2b3178da --- /dev/null +++ b/usrp/fpga/inband_lib/chan_fifo_reader.v @@ -0,0 +1,197 @@ +module chan_fifo_reader + ( input reset, + input tx_clock, + input tx_strobe, + input [31:0]adc_clock, + input [3:0] samples_format, + input [15:0] fifodata, + input pkt_waiting, + output reg rdreq, + output reg skip, + output reg [15:0]tx_q, + output reg [15:0]tx_i, + output reg overrun, + output reg underrun) ; + + // Should not be needed if adc clock rate < tx clock rate + `define JITTER 5 + + //Samples format + // 16 bits interleaved complex samples + `define QI16 4'b0 + + // States + `define IDLE 4'd0 + `define READ 4'd1 + `define HEADER1 4'd2 + `define HEADER2 4'd3 + `define TIMESTAMP1 4'd4 + `define TIMESTAMP2 4'd5 + `define WAIT 4'd6 + `define WAITSTROBE 4'd7 + `define SENDWAIT 4'd8 + `define SEND 4'd9 + `define FEED 4'd10 + `define DISCARD 4'd11 + + // State registers + reg[3:0] reader_state; + reg[3:0] reader_next_state; + + //Variables + reg[8:0] payload_len; + reg[8:0] read_len; + reg[31:0] timestamp; + reg burst; + reg qsample; + always @(posedge tx_clock) + begin + if (reset) + begin + reader_state <= `IDLE; + reader_next_state <= `IDLE; + rdreq <= 0; + skip <= 0; + overrun <= 0; + underrun <= 0; + burst <= 0; + qsample <= 1; + end + else + begin + reader_state = reader_next_state; + case (reader_state) + `IDLE: + begin + if (pkt_waiting == 1) + begin + reader_next_state <= `READ; + rdreq <= 1; + underrun <= 0; + end + else if (burst == 1) + underrun <= 1; + end + + // Just wait for the fifo data to arrive + `READ: + begin + reader_next_state <= `HEADER1; + end + + // First part of the header + `HEADER1: + begin + reader_next_state <= `HEADER2; + + //Check Start burst flag + if (fifodata[3] == 1) + burst <= 1; + + if (fifodata[4] == 1) + burst <= 0; + end + + // Read payload length + `HEADER2: + begin + payload_len <= (fifodata & 16'h1FF); + read_len <= 9'd0; + reader_next_state <= `TIMESTAMP1; + end + + `TIMESTAMP1: + begin + timestamp <= {fifodata, 16'b0}; + rdreq <= 0; + reader_next_state <= `TIMESTAMP2; + end + + `TIMESTAMP2: + begin + timestamp <= timestamp + fifodata; + reader_next_state <= `WAIT; + end + + // Decide if we wait, send or discard samples + `WAIT: + begin + // Wait a little bit more + if (timestamp > adc_clock + `JITTER) + reader_next_state <= `WAIT; + // Let's send it + else if ((timestamp < adc_clock + `JITTER + && timestamp > adc_clock) + || timestamp == 32'hFFFFFFFF) + begin + reader_next_state <= `WAITSTROBE; + end + // Outdated + else if (timestamp < adc_clock) + begin + reader_next_state <= `DISCARD; + skip <= 1; + end + end + + // Wait for the transmit chain to be ready + `WAITSTROBE: + begin + // If end of payload... + if (read_len == payload_len) + begin + reader_next_state <= `DISCARD; + skip <= (payload_len < 508); + end + + if (tx_strobe == 1) + reader_next_state <= `SENDWAIT; + end + + `SENDWAIT: + begin + rdreq <= 1; + reader_next_state <= `SEND; + end + + // Send the samples to the tx_chain + `SEND: + begin + reader_next_state <= `WAITSTROBE; + rdreq <= 0; + read_len <= read_len + 2; + case(samples_format) + `QI16: + begin + tx_q <= qsample ? fifodata : 16'bZ; + tx_i <= ~qsample ? fifodata : 16'bZ; + qsample <= ~ qsample; + end + default: + begin + // Assume 16 bits complex samples by default + $display ("Error unknown samples format"); + tx_q <= qsample ? fifodata : 16'bZ; + tx_i <= ~qsample ? fifodata : 16'bZ; + qsample <= ~ qsample; + end + endcase + end + + `DISCARD: + begin + skip <= 0; + reader_next_state <= `IDLE; + end + + default: + begin + $display ("Error unknown state"); + reader_state <= `IDLE; + reader_next_state <= `IDLE; + end + endcase + end + end + +endmodule \ No newline at end of file diff --git a/usrp/fpga/inband_lib/data_packet_fifo.v b/usrp/fpga/inband_lib/data_packet_fifo.v new file mode 100755 index 00000000..5b37b14e --- /dev/null +++ b/usrp/fpga/inband_lib/data_packet_fifo.v @@ -0,0 +1,128 @@ +module data_packet_fifo + ( input reset, + input clock, + input [15:0]ram_data_in, + input write_enable, + output reg have_space, + output reg [15:0]ram_data_out, + output reg pkt_waiting, + input read_enable, + input pkt_complete, + input skip_packet) ; + + /* Some parameters for usage later on */ + parameter DATA_WIDTH = 16 ; + parameter NUM_PACKETS = 4 ; + + /* Create the RAM here */ + reg [DATA_WIDTH-1:0] usb_ram [256*NUM_PACKETS-1:0] ; + + /* Create the address signals */ + reg [7:0] usb_ram_offset_out ; + reg [1:0] usb_ram_packet_out ; + reg [7:0] usb_ram_offset_in ; + reg [1:0] usb_ram_packet_in ; + + wire [7-2+NUM_PACKETS:0] usb_ram_aout ; + wire [7-2+NUM_PACKETS:0] usb_ram_ain ; + reg isfull; + + assign usb_ram_aout = {usb_ram_packet_out, usb_ram_offset_out} ; + assign usb_ram_ain = {usb_ram_packet_in, usb_ram_offset_in} ; + + // Check if there is one full packet to process + always @(usb_ram_ain, usb_ram_aout) + begin + if (reset) + pkt_waiting <= 0; + else if (usb_ram_ain >= usb_ram_aout) + pkt_waiting <= usb_ram_ain - usb_ram_aout >= 256; + else + pkt_waiting <= (usb_ram_ain + 10'b1111111111 - usb_ram_aout) >= 256; + end + + // Check if there is room + always @(usb_ram_ain, usb_ram_aout) + begin + if (reset) + have_space <= 1; + else if (usb_ram_ain == usb_ram_aout) + have_space <= ~isfull; + else if (usb_ram_ain > usb_ram_aout) + have_space <= (usb_ram_ain - usb_ram_aout) <= 256 * (NUM_PACKETS - 1); + else + have_space <= (usb_ram_aout - usb_ram_ain) >= 256; + end + + /* RAM Write Address process */ + always @(posedge clock) + begin + if( reset ) + begin + usb_ram_offset_in <= 0 ; + usb_ram_packet_in <= 0 ; + end + else + if( pkt_complete ) + begin + usb_ram_packet_in <= usb_ram_packet_in + 1; + usb_ram_offset_in <= 0; + end + else if( write_enable ) + begin + if (usb_ram_offset_in == 8'b11111111) + begin + usb_ram_offset_in <= 0; + usb_ram_packet_in <= usb_ram_packet_in + 1; + end + else + usb_ram_offset_in <= usb_ram_offset_in + 1 ; + if (usb_ram_ain + 1 == usb_ram_aout) + isfull <= 1; + end + end + + /* RAM Writing process */ + always @(posedge clock) + begin + if( write_enable ) + begin + usb_ram[usb_ram_ain] <= ram_data_in ; + end + end + + /* RAM Read Address process */ + always @(posedge clock) + begin + if( reset ) + begin + usb_ram_packet_out <= 0 ; + usb_ram_offset_out <= 0 ; + isfull <= 0; + end + else + if( skip_packet ) + begin + usb_ram_packet_out <= usb_ram_packet_out + 1 ; + usb_ram_offset_out <= 0 ; + end + else if(read_enable) begin + if( usb_ram_offset_out == 8'b11111111 ) + begin + usb_ram_offset_out <= 0 ; + usb_ram_packet_out <= usb_ram_packet_out + 1 ; + end + else + usb_ram_offset_out <= usb_ram_offset_out + 1 ; + end + if (usb_ram_ain == usb_ram_aout) + isfull <= 0; + end + + /* RAM Reading Process */ + always @(posedge clock) + begin + ram_data_out <= usb_ram[usb_ram_aout] ; + end + +endmodule diff --git a/usrp/fpga/inband_lib/tx_buffer_inband.v b/usrp/fpga/inband_lib/tx_buffer_inband.v new file mode 100755 index 00000000..56c07807 --- /dev/null +++ b/usrp/fpga/inband_lib/tx_buffer_inband.v @@ -0,0 +1,183 @@ +module tx_buffer_inband + ( input usbclk, + input bus_reset, // Used here for the 257-Hack to fix the FX2 bug + input reset, // standard DSP-side reset + input [15:0] usbdata, + input wire WR, + output wire have_space, + output reg tx_underrun, + input wire [3:0] channels, + output [15:0] tx_i_0, + output [15:0] tx_q_0, + output [15:0] tx_i_1, + output [15:0] tx_q_1, + //NOT USED + output reg [15:0] tx_i_2, + output reg [15:0] tx_q_2, + output reg [15:0] tx_i_3, + output reg [15:0] tx_q_3, + input txclk, + input txstrobe, + input clear_status, + output wire tx_empty, + output [11:0] debugbus + ); + + wire [15:0] tx_data_bus; + + wire WR_chan_0; + wire chan_0_done; + wire OR0; + wire UR0; + + wire WR_chan_1; + wire chan_1_done; + wire OR1; + wire UR1; + + // NOT USED yet + wire WR_cmd; + wire cmd_done; + + //EXTERNAL REGISTER + //TODO: increment it + reg [31:0] time_counter; + reg [7:0] txstrobe_rate_0; + reg [7:0] txstrobe_rate_1; + + + //Usb block + wire [15:0] tupf_fifodata; + wire tupf_pkt_waiting; + wire tupf_rdreq; + wire tupf_skip; + wire tupf_have_space; + + usb_packet_fifo2 tx_usb_packet_fifo + ( .reset (reset), + .usb_clock (usbclk), + .fpga_clock (txclk), + .write_data (usbdata), + .write_enable (WR), + .read_data (tupf_fifodata), + .pkt_waiting (tupf_pkt_waiting), + .read_enable (tupf_rdreq), + .skip_packet (tupf_skip), + .have_space (tupf_have_space), + .tx_empty (tx_empty) + ); + + usb_fifo_reader tx_usb_packet_reader ( + .reset(reset), + .tx_clock(txclk), + .tx_data_bus(tx_data_bus), + .WR_chan_0(WR_chan_0), + .WR_chan_1(WR_chan_1), + .WR_cmd(WR_cmd), + .chan_0_done(chan_0_done), + .chan_1_done(chan_1_done), + .cmd_done(cmd_done), + .rdreq(tupf_rdreq), + .skip(tupf_skip), + .pkt_waiting(tupf_pkt_waiting), + .fifodata(tupf_fifodata) + ); + + + //Channel 0 block + wire [15:0] tdpf_fifodata_0; + wire tdpf_pkt_waiting_0; + wire tdpf_rdreq_0; + wire tdpf_skip_0; + wire tdpf_have_space_0; + wire txstrobe_chan_0; + + data_packet_fifo tx_data_packet_fifo_0 + ( .reset(reset), + .clock(txclk), + .ram_data_in(tx_data_bus), + .write_enable(WR_chan_0), + .ram_data_out(tdpf_fifodata_0), + .pkt_waiting(tdpf_pkt_waiting_0), + .read_enable(tdpf_rdreq_0), + .pkt_complete(chan_0_done), + .skip_packet(tdpf_skip_0), + .have_space(tdpf_have_space_0) + ); + + strobe_gen strobe_gen_0 + ( .clock(txclk), + .reset(reset), + .enable(1'b1), + .rate(txstrobe_rate_0), + .strobe_in(txstrobe), + .strobe(txstrobe_chan_0) + ); + + chan_fifo_reader tx_chan_0_reader ( + .reset(reset), + .tx_clock(txclk), + .tx_strobe(txstrobe), + //.tx_strobe(txstrobe_chan_0), + .adc_clock(time_counter), + .samples_format(4'b0), + .tx_q(tx_q_0), + .tx_i(tx_i_0), + .overrun(OR0), + .underrun(UR0), + .skip(tdpf_skip_0), + .rdreq(tdpf_rdreq_0), + .fifodata(tdpf_fifodata_0), + .pkt_waiting(tdpf_pkt_waiting_0) + ); + + + //Channel 1 block + wire [15:0] tdpf_fifodata_1; + wire tdpf_pkt_waiting_1; + wire tdpf_rdreq_1; + wire tdpf_skip_1; + wire tdpf_have_space_1; + wire txstrobe_chan_1; + + data_packet_fifo tx_data_packet_fifo_1 + ( .reset(reset), + .clock(txclk), + .ram_data_in(tx_data_bus), + .write_enable(WR_chan_1), + .ram_data_out(tdpf_fifodata_1), + .pkt_waiting(tdpf_pkt_waiting_1), + .read_enable(tdpf_rdreq_1), + .pkt_complete(chan_1_done), + .skip_packet(tdpf_skip_1), + .have_space(tdpf_have_space_1) + ); + + strobe_gen strobe_gen_1 + ( .clock(txclk), + .reset(reset), + .enable(1'b1), + .rate(txstrobe_rate_1), + .strobe_in(txstrobe), + .strobe(txstrobe_chan_1) + ); + + chan_fifo_reader tx_chan_1_reader ( + .reset(reset), + .tx_clock(txclk), + .tx_strobe(txstrobe), + //.tx_strobe(txstrobe_chan_1), + .adc_clock(time_counter), + .samples_format(4'b0), + .tx_q(tx_q_1), + .tx_i(tx_i_1), + .overrun(OR1), + .underrun(UR1), + .skip(tdpf_skip_1), + .rdreq(tdpf_rdreq_1), + .fifodata(tdpf_fifodata_1), + .pkt_waiting(tdpf_pkt_waiting_1) + ); + +endmodule // tx_buffer + diff --git a/usrp/fpga/inband_lib/usb_fifo_reader.v b/usrp/fpga/inband_lib/usb_fifo_reader.v new file mode 100755 index 00000000..170c70fd --- /dev/null +++ b/usrp/fpga/inband_lib/usb_fifo_reader.v @@ -0,0 +1,135 @@ +module usb_fifo_reader (tx_clock, fifodata, pkt_waiting, reset, + rdreq, skip, done_chan, WR_chan, tx_data_bus); + + /* Module parameters */ + parameter NUM_CHAN = 2 ; + parameter WIDTH = 32 ; + + input wire tx_clock ; + input wire reset ; + input wire [WIDTH-1:0] fifodata ; + input wire pkt_waiting ; + output reg rdreq ; + output reg skip ; + output reg [NUM_CHAN:0] done_chan ; + output reg [NUM_CHAN:0] WR_chan ; + output reg [WIDTH-1:0] tx_data_bus ; + + + + /* States definition */ + `define IDLE 3'd0 + `define WAIT 3'd1 + `define READ_HEADER 3'd2 + `define FORWARD_DATA 3'd3 + `define SKIP_REST 3'd4 + + /* Channel Ids */ + `define TXCHAN0 5'h0 + `define TXCHAN1 5'h1 + `define TXCMD 5'h1F + + /* Local registers */ + reg [2:0] reader_state ; + reg [2:0] reader_next_state ; + reg [4:0] channel ; + reg [8:0] pkt_length ; + reg [8:0] read_length ; + + /* State Machine */ + always @(posedge tx_clock) + begin + if (reset) + begin + reader_state <= `IDLE ; + reader_next_state <= `IDLE ; + rdreq <= 0 ; + skip <= 0 ; + WR_chan <= {NUM_CHAN+1{1'b0}} ; + done_chan <= {NUM_CHAN+1{1'b0}} ; + end + else + begin + reader_state = reader_next_state ; + + case(reader_state) + `IDLE: + begin + reader_next_state <= pkt_waiting ? `WAIT : `IDLE ; + rdreq <= pkt_waiting ; + end + + /* Wait for the fifo's data to show up */ + `WAIT: + begin + reader_next_state <= `READ_HEADER ; + end + + `READ_HEADER: + begin + reader_next_state <= `FORWARD_DATA ; + + /* Read header fields */ + channel <= (fifodata & 32'h1F0000) ; + pkt_length <= (fifodata & 16'h1FF) + 4 ; + read_length <= 9'd0 ; + + /* Forward data */ + case (channel) + `TXCHAN0: WR_chan[0] <= 1 ; + `TXCHAN1: WR_chan[1] <= 1 ; + `TXCMD: WR_chan[2] <= 1 ; + default: WR_chan <= 1 ; + endcase + tx_data_bus <= fifodata ; + end + + `FORWARD_DATA: + begin + read_length <= read_length + 4 ; + + // If end of payload... + if (read_length == pkt_length) + begin + reader_next_state <= `SKIP_REST ; + /* If the packet is 512 bytes, don't skip */ + skip <= pkt_length < 506 ; + + /* Data pushing done */ + WR_chan <= {NUM_CHAN+1{1'b0}} ; + + /* Notify next block */ + case (channel) + `TXCHAN0: done_chan[0] <= 1 ; + `TXCHAN1: done_chan[1] <= 1 ; + `TXCMD: done_chan[2] <= 1 ; + default: done_chan[0] <= 1 ; + endcase + end + else if (read_length == pkt_length - 4) + rdreq <= 0 ; + + /* Forward data */ + tx_data_bus <= fifodata ; + end + + `SKIP_REST: + begin + reader_next_state <= pkt_waiting ? `READ_HEADER : `IDLE ; + done_chan <= {NUM_CHAN+1{1'b0}} ; + rdreq <= pkt_waiting ; + skip <= 0 ; + end + + default: + begin + reader_state <= `IDLE; + reader_next_state <= `IDLE; + end + endcase + end + end +endmodule + + + \ No newline at end of file diff --git a/usrp/fpga/inband_lib/usb_packet_fifo.v b/usrp/fpga/inband_lib/usb_packet_fifo.v new file mode 100755 index 00000000..c416e2bd --- /dev/null +++ b/usrp/fpga/inband_lib/usb_packet_fifo.v @@ -0,0 +1,112 @@ +module usb_packet_fifo + ( input reset, + input clock_in, + input clock_out, + input [15:0]ram_data_in, + input write_enable, + output reg [15:0]ram_data_out, + output reg pkt_waiting, + output reg have_space, + input read_enable, + input skip_packet ) ; + + /* Some parameters for usage later on */ + parameter DATA_WIDTH = 16 ; + parameter NUM_PACKETS = 4 ; + + /* Create the RAM here */ + reg [DATA_WIDTH-1:0] usb_ram [256*NUM_PACKETS-1:0] ; + + /* Create the address signals */ + reg [7-2+NUM_PACKETS:0] usb_ram_ain ; + reg [7:0] usb_ram_offset ; + reg [1:0] usb_ram_packet ; + + wire [7-2+NUM_PACKETS:0] usb_ram_aout ; + reg isfull; + + assign usb_ram_aout = {usb_ram_packet,usb_ram_offset} ; + + // Check if there is one full packet to process + always @(usb_ram_ain, usb_ram_aout) + begin + if (reset) + pkt_waiting <= 0; + else if (usb_ram_ain == usb_ram_aout) + pkt_waiting <= isfull; + else if (usb_ram_ain > usb_ram_aout) + pkt_waiting <= (usb_ram_ain - usb_ram_aout) >= 256; + else + pkt_waiting <= (usb_ram_ain + 10'b1111111111 - usb_ram_aout) >= 256; + end + + // Check if there is room + always @(usb_ram_ain, usb_ram_aout) + begin + if (reset) + have_space <= 1; + else if (usb_ram_ain == usb_ram_aout) + have_space <= ~isfull; + else if (usb_ram_ain > usb_ram_aout) + have_space <= (usb_ram_ain - usb_ram_aout) <= 256 * (NUM_PACKETS - 1); + else + have_space <= (usb_ram_aout - usb_ram_ain) >= 256; + end + + /* RAM Write Address process */ + always @(posedge clock_in) + begin + if( reset ) + usb_ram_ain <= 0 ; + else + if( write_enable ) + begin + usb_ram_ain <= usb_ram_ain + 1 ; + if (usb_ram_ain + 1 == usb_ram_aout) + isfull <= 1; + end + end + + /* RAM Writing process */ + always @(posedge clock_in) + begin + if( write_enable ) + begin + usb_ram[usb_ram_ain] <= ram_data_in ; + end + end + + /* RAM Read Address process */ + always @(posedge clock_out) + begin + if( reset ) + begin + usb_ram_packet <= 0 ; + usb_ram_offset <= 0 ; + isfull <= 0; + end + else + if( skip_packet ) + begin + usb_ram_packet <= usb_ram_packet + 1 ; + usb_ram_offset <= 0 ; + end + else if(read_enable) + if( usb_ram_offset == 8'b11111111 ) + begin + usb_ram_offset <= 0 ; + usb_ram_packet <= usb_ram_packet + 1 ; + end + else + usb_ram_offset <= usb_ram_offset + 1 ; + if (usb_ram_ain == usb_ram_aout) + isfull <= 0; + end + + /* RAM Reading Process */ + always @(posedge clock_out) + begin + ram_data_out <= usb_ram[usb_ram_aout] ; + end + +endmodule \ No newline at end of file diff --git a/usrp/fpga/inband_lib/usb_packet_fifo2.v b/usrp/fpga/inband_lib/usb_packet_fifo2.v new file mode 100755 index 00000000..d815e4e3 --- /dev/null +++ b/usrp/fpga/inband_lib/usb_packet_fifo2.v @@ -0,0 +1,119 @@ +`default_nettype none + +module usb_packet_fifo2(reset, usb_clock, fpga_clock, write_enable, write_data, + read_enable, skip_packet, read_data, have_space, pkt_waiting, tx_empty) ; + + /* Module parameters */ + parameter LOG2_N = 2 ; + parameter BUS_WIDTH = 16 ; + parameter FIFO_WIDTH = 32 ; + + input wire reset; + input wire usb_clock ; + input wire fpga_clock ; + input wire write_enable ; + input wire [BUS_WIDTH-1:0] write_data ; + input wire read_enable ; + input wire skip_packet ; + output wire [FIFO_WIDTH-1:0] read_data ; + output wire have_space ; + output wire pkt_waiting ; + output wire tx_empty; + + + /* Variable for generate statement */ + genvar i ; + + /* Local wires for FIFO connections */ + wire [2**LOG2_N-1:0] fifo_resets ; + reg [2**LOG2_N-1:0] fifo_we ; + wire [2**LOG2_N-1:0] fifo_re ; + reg [FIFO_WIDTH-1:0] fifo_wdata[2**LOG2_N-1:0] ; + wire [FIFO_WIDTH-1:0] fifo_rdata[2**LOG2_N-1:0] ; + wire [2**LOG2_N-1:0] fifo_rempty ; + wire [2**LOG2_N-1:0] fifo_rfull ; + wire [2**LOG2_N-1:0] fifo_wempty ; + wire [2**LOG2_N-1:0] fifo_wfull ; + + /* FIFO Select for read and write ports */ + reg [LOG2_N-1:0] fifo_rselect ; + reg [LOG2_N-1:0] fifo_wselect ; + + /* Used to convert 16 bits usbdata to the 32 bits wide fifo */ + reg word_complete ; + reg [BUS_WIDTH-1:0] write_data_delayed ; + + /* Assign have_space to empty flag of currently selected write FIFO */ + assign have_space = fifo_wempty[fifo_wselect] ; + + /* Assign pkt_waiting to full flag of currently selected read FIFO */ + assign pkt_waiting = fifo_rfull[fifo_rselect] ; + + /* Assign the read_data to the output of the currently selected FIFO */ + assign read_data = fifo_rdata[fifo_rselect] ; + + /* Figure out if we're all empty */ + assign tx_empty = !(~fifo_rempty) ; + + /* Increment fifo_rselect here */ + always @(posedge fpga_clock) + begin + if (reset) + fifo_rselect <= {2**LOG2_N{1'b0}} ; + + if (fifo_rempty[fifo_rselect]) + fifo_rselect <= fifo_rselect + 1 ; + + if (skip_packet) + fifo_rselect <= fifo_rselect + 1 ; + end + + /* Increment fifo_wselect and pack data into 32 bits block */ + always @(posedge usb_clock, reset) + begin + if (reset) + begin + fifo_wselect <= {2**LOG2_N{1'b0}} ; + word_complete <= 0; + end + + if (fifo_wfull[fifo_wselect]) + fifo_wselect <= fifo_wselect + 1 ; + + if (write_enable) + begin + word_complete <= ~word_complete ; + + if (word_complete) + fifo_wdata[fifo_wselect] <= {write_data_delayed, write_data} ; + else + write_data_delayed <= write_data ; + + /* Avoid to continue to write in the previous fifo when we have + just swichted to the next one */ + fifo_we[fifo_wselect-1] <= 0 ; + + fifo_we[fifo_wselect] <= write_enable & word_complete ; + end + end + + /* Generate all the single packet FIFOs */ + generate + for( i = 0 ; i < 2**LOG2_N ; i = i + 1 ) + begin : generate_single_packet_fifos + assign fifo_re[i] = (fifo_rselect == i) ? read_enable : 1'b0 ; + assign fifo_resets[i] = (fifo_rselect == i) ? skip_packet : 1'b0 ; + fifo_512 single_packet_fifo(.wrclk ( usb_clock ), + .rdclk ( fpga_clock ), + .aclr ( fifo_resets[i] ), + .wrreq ( fifo_we[i] ), + .data ( fifo_wdata[i] ), + .rdreq ( fifo_re[i] ), + .q ( fifo_rdata[i] ), + .rdfull ( fifo_rfull[i] ), + .rdempty( fifo_rempty[i] ), + .wrfull ( fifo_wfull[i] ), + .wrempty( fifo_wempty[i] ) ) ; + end + endgenerate +endmodule \ No newline at end of file diff --git a/usrp/fpga/megacells/fifo_512.bsf b/usrp/fpga/megacells/fifo_512.bsf new file mode 100755 index 00000000..a955b565 --- /dev/null +++ b/usrp/fpga/megacells/fifo_512.bsf @@ -0,0 +1,116 @@ +/* +WARNING: Do NOT edit the input and output ports in this file in a text +editor if you plan to continue editing the block that represents it in +the Block Editor! File corruption is VERY likely to occur. +*/ +/* +Copyright (C) 1991-2006 Altera Corporation +Your use of Altera Corporation's design tools, logic functions +and other software and tools, and its AMPP partner logic +functions, and any output files any of the foregoing +(including device programming or simulation files), and any +associated documentation or information are expressly subject +to the terms and conditions of the Altera Program License +Subscription Agreement, Altera MegaCore Function License +Agreement, or other applicable license agreement, including, +without limitation, that your use is for the sole purpose of +programming logic devices manufactured by Altera and sold by +Altera or its authorized distributors. Please refer to the +applicable agreement for further details. +*/ +(header "symbol" (version "1.1")) +(symbol + (rect 0 0 160 184) + (text "fifo_512" (rect 58 1 109 17)(font "Arial" (font_size 10))) + (text "inst" (rect 8 168 25 180)(font "Arial" )) + (port + (pt 0 32) + (input) + (text "data[31..0]" (rect 0 0 60 14)(font "Arial" (font_size 8))) + (text "data[31..0]" (rect 20 26 71 39)(font "Arial" (font_size 8))) + (line (pt 0 32)(pt 16 32)(line_width 3)) + ) + (port + (pt 0 56) + (input) + (text "wrreq" (rect 0 0 35 14)(font "Arial" (font_size 8))) + (text "wrreq" (rect 20 50 45 63)(font "Arial" (font_size 8))) + (line (pt 0 56)(pt 16 56)(line_width 1)) + ) + (port + (pt 0 72) + (input) + (text "wrclk" (rect 0 0 31 14)(font "Arial" (font_size 8))) + (text "wrclk" (rect 26 66 48 79)(font "Arial" (font_size 8))) + (line (pt 0 72)(pt 16 72)(line_width 1)) + ) + (port + (pt 0 104) + (input) + (text "rdreq" (rect 0 0 30 14)(font "Arial" (font_size 8))) + (text "rdreq" (rect 20 98 44 111)(font "Arial" (font_size 8))) + (line (pt 0 104)(pt 16 104)(line_width 1)) + ) + (port + (pt 0 120) + (input) + (text "rdclk" (rect 0 0 27 14)(font "Arial" (font_size 8))) + (text "rdclk" (rect 26 114 47 127)(font "Arial" (font_size 8))) + (line (pt 0 120)(pt 16 120)(line_width 1)) + ) + (port + (pt 0 160) + (input) + (text "aclr" (rect 0 0 21 14)(font "Arial" (font_size 8))) + (text "aclr" (rect 20 154 37 167)(font "Arial" (font_size 8))) + (line (pt 0 160)(pt 16 160)(line_width 1)) + ) + (port + (pt 160 40) + (output) + (text "wrfull" (rect 0 0 33 14)(font "Arial" (font_size 8))) + (text "wrfull" (rect 113 34 138 47)(font "Arial" (font_size 8))) + (line (pt 160 40)(pt 144 40)(line_width 1)) + ) + (port + (pt 160 56) + (output) + (text "wrempty" (rect 0 0 50 14)(font "Arial" (font_size 8))) + (text "wrempty" (rect 98 50 137 63)(font "Arial" (font_size 8))) + (line (pt 160 56)(pt 144 56)(line_width 1)) + ) + (port + (pt 160 96) + (output) + (text "q[31..0]" (rect 0 0 42 14)(font "Arial" (font_size 8))) + (text "q[31..0]" (rect 105 90 141 103)(font "Arial" (font_size 8))) + (line (pt 160 96)(pt 144 96)(line_width 3)) + ) + (port + (pt 160 120) + (output) + (text "rdfull" (rect 0 0 28 14)(font "Arial" (font_size 8))) + (text "rdfull" (rect 117 114 141 127)(font "Arial" (font_size 8))) + (line (pt 160 120)(pt 144 120)(line_width 1)) + ) + (port + (pt 160 136) + (output) + (text "rdempty" (rect 0 0 46 14)(font "Arial" (font_size 8))) + (text "rdempty" (rect 102 130 140 143)(font "Arial" (font_size 8))) + (line (pt 160 136)(pt 144 136)(line_width 1)) + ) + (drawing + (text "32 bits x 128 words" (rect 63 156 144 168)(font "Arial" )) + (line (pt 16 16)(pt 144 16)(line_width 1)) + (line (pt 144 16)(pt 144 168)(line_width 1)) + (line (pt 144 168)(pt 16 168)(line_width 1)) + (line (pt 16 168)(pt 16 16)(line_width 1)) + (line (pt 16 84)(pt 144 84)(line_width 1)) + (line (pt 16 148)(pt 144 148)(line_width 1)) + (line (pt 16 66)(pt 22 72)(line_width 1)) + (line (pt 22 72)(pt 16 78)(line_width 1)) + (line (pt 16 114)(pt 22 120)(line_width 1)) + (line (pt 22 120)(pt 16 126)(line_width 1)) + ) +) diff --git a/usrp/fpga/megacells/fifo_512.cmp b/usrp/fpga/megacells/fifo_512.cmp new file mode 100755 index 00000000..86fc0784 --- /dev/null +++ b/usrp/fpga/megacells/fifo_512.cmp @@ -0,0 +1,31 @@ +--Copyright (C) 1991-2006 Altera Corporation +--Your use of Altera Corporation's design tools, logic functions +--and other software and tools, and its AMPP partner logic +--functions, and any output files any of the foregoing +--(including device programming or simulation files), and any +--associated documentation or information are expressly subject +--to the terms and conditions of the Altera Program License +--Subscription Agreement, Altera MegaCore Function License +--Agreement, or other applicable license agreement, including, +--without limitation, that your use is for the sole purpose of +--programming logic devices manufactured by Altera and sold by +--Altera or its authorized distributors. Please refer to the +--applicable agreement for further details. + + +component fifo_512 + PORT + ( + aclr : IN STD_LOGIC := '0'; + data : IN STD_LOGIC_VECTOR (31 DOWNTO 0); + rdclk : IN STD_LOGIC ; + rdreq : IN STD_LOGIC ; + wrclk : IN STD_LOGIC ; + wrreq : IN STD_LOGIC ; + q : OUT STD_LOGIC_VECTOR (31 DOWNTO 0); + rdempty : OUT STD_LOGIC ; + rdfull : OUT STD_LOGIC ; + wrempty : OUT STD_LOGIC ; + wrfull : OUT STD_LOGIC + ); +end component; diff --git a/usrp/fpga/megacells/fifo_512.inc b/usrp/fpga/megacells/fifo_512.inc new file mode 100755 index 00000000..9ae1e3af --- /dev/null +++ b/usrp/fpga/megacells/fifo_512.inc @@ -0,0 +1,32 @@ +--Copyright (C) 1991-2006 Altera Corporation +--Your use of Altera Corporation's design tools, logic functions +--and other software and tools, and its AMPP partner logic +--functions, and any output files any of the foregoing +--(including device programming or simulation files), and any +--associated documentation or information are expressly subject +--to the terms and conditions of the Altera Program License +--Subscription Agreement, Altera MegaCore Function License +--Agreement, or other applicable license agreement, including, +--without limitation, that your use is for the sole purpose of +--programming logic devices manufactured by Altera and sold by +--Altera or its authorized distributors. Please refer to the +--applicable agreement for further details. + + +FUNCTION fifo_512 +( + aclr, + data[31..0], + rdclk, + rdreq, + wrclk, + wrreq +) + +RETURNS ( + q[31..0], + rdempty, + rdfull, + wrempty, + wrfull +); diff --git a/usrp/fpga/megacells/fifo_512.v b/usrp/fpga/megacells/fifo_512.v new file mode 100755 index 00000000..b034b4dd --- /dev/null +++ b/usrp/fpga/megacells/fifo_512.v @@ -0,0 +1,180 @@ +// megafunction wizard: %LPM_FIFO+% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: dcfifo + +// ============================================================ +// File Name: fifo_512.v +// Megafunction Name(s): +// dcfifo +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 5.1 Build 213 01/19/2006 SP 1 SJ Web Edition +// ************************************************************ + + +//Copyright (C) 1991-2006 Altera Corporation +//Your use of Altera Corporation's design tools, logic functions +//and other software and tools, and its AMPP partner logic +//functions, and any output files any of the foregoing +//(including device programming or simulation files), and any +//associated documentation or information are expressly subject +//to the terms and conditions of the Altera Program License +//Subscription Agreement, Altera MegaCore Function License +//Agreement, or other applicable license agreement, including, +//without limitation, that your use is for the sole purpose of +//programming logic devices manufactured by Altera and sold by +//Altera or its authorized distributors. Please refer to the +//applicable agreement for further details. + + +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on +module fifo_512 ( + aclr, + data, + rdclk, + rdreq, + wrclk, + wrreq, + q, + rdempty, + rdfull, + wrempty, + wrfull); + + input aclr; + input [31:0] data; + input rdclk; + input rdreq; + input wrclk; + input wrreq; + output [31:0] q; + output rdempty; + output rdfull; + output wrempty; + output wrfull; + + wire sub_wire0; + wire sub_wire1; + wire sub_wire2; + wire sub_wire3; + wire [31:0] sub_wire4; + wire rdfull = sub_wire0; + wire rdempty = sub_wire1; + wire wrfull = sub_wire2; + wire wrempty = sub_wire3; + wire [31:0] q = sub_wire4[31:0]; + + dcfifo dcfifo_component ( + .wrclk (wrclk), + .rdreq (rdreq), + .aclr (aclr), + .rdclk (rdclk), + .wrreq (wrreq), + .data (data), + .rdfull (sub_wire0), + .rdempty (sub_wire1), + .wrfull (sub_wire2), + .wrempty (sub_wire3), + .q (sub_wire4) + // synopsys translate_off + , + .rdusedw (), + .wrusedw () + // synopsys translate_on + ); + defparam + dcfifo_component.add_ram_output_register = "OFF", + dcfifo_component.clocks_are_synchronized = "FALSE", + dcfifo_component.intended_device_family = "Cyclone", + dcfifo_component.lpm_hint = "RAM_BLOCK_TYPE=M4K", + dcfifo_component.lpm_numwords = 128, + dcfifo_component.lpm_showahead = "OFF", + dcfifo_component.lpm_type = "dcfifo", + dcfifo_component.lpm_width = 32, + dcfifo_component.lpm_widthu = 7, + dcfifo_component.overflow_checking = "ON", + dcfifo_component.underflow_checking = "ON", + dcfifo_component.use_eab = "ON"; + + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: AlmostEmpty NUMERIC "0" +// Retrieval info: PRIVATE: AlmostEmptyThr NUMERIC "-1" +// Retrieval info: PRIVATE: AlmostFull NUMERIC "0" +// Retrieval info: PRIVATE: AlmostFullThr NUMERIC "-1" +// Retrieval info: PRIVATE: CLOCKS_ARE_SYNCHRONIZED NUMERIC "0" +// Retrieval info: PRIVATE: Clock NUMERIC "4" +// Retrieval info: PRIVATE: Depth NUMERIC "128" +// Retrieval info: PRIVATE: Empty NUMERIC "1" +// Retrieval info: PRIVATE: Full NUMERIC "1" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone" +// Retrieval info: PRIVATE: LE_BasedFIFO NUMERIC "0" +// Retrieval info: PRIVATE: LegacyRREQ NUMERIC "1" +// Retrieval info: PRIVATE: MAX_DEPTH_BY_9 NUMERIC "0" +// Retrieval info: PRIVATE: OVERFLOW_CHECKING NUMERIC "0" +// Retrieval info: PRIVATE: Optimize NUMERIC "2" +// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "2" +// Retrieval info: PRIVATE: UNDERFLOW_CHECKING NUMERIC "0" +// Retrieval info: PRIVATE: UsedW NUMERIC "1" +// Retrieval info: PRIVATE: Width NUMERIC "32" +// Retrieval info: PRIVATE: dc_aclr NUMERIC "1" +// Retrieval info: PRIVATE: rsEmpty NUMERIC "1" +// Retrieval info: PRIVATE: rsFull NUMERIC "1" +// Retrieval info: PRIVATE: rsUsedW NUMERIC "0" +// Retrieval info: PRIVATE: sc_aclr NUMERIC "0" +// Retrieval info: PRIVATE: sc_sclr NUMERIC "0" +// Retrieval info: PRIVATE: wsEmpty NUMERIC "1" +// Retrieval info: PRIVATE: wsFull NUMERIC "1" +// Retrieval info: PRIVATE: wsUsedW NUMERIC "0" +// Retrieval info: CONSTANT: ADD_RAM_OUTPUT_REGISTER STRING "OFF" +// Retrieval info: CONSTANT: CLOCKS_ARE_SYNCHRONIZED STRING "FALSE" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone" +// Retrieval info: CONSTANT: LPM_HINT STRING "RAM_BLOCK_TYPE=M4K" +// Retrieval info: CONSTANT: LPM_NUMWORDS NUMERIC "128" +// Retrieval info: CONSTANT: LPM_SHOWAHEAD STRING "OFF" +// Retrieval info: CONSTANT: LPM_TYPE STRING "dcfifo" +// Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "32" +// Retrieval info: CONSTANT: LPM_WIDTHU NUMERIC "7" +// Retrieval info: CONSTANT: OVERFLOW_CHECKING STRING "ON" +// Retrieval info: CONSTANT: UNDERFLOW_CHECKING STRING "ON" +// Retrieval info: CONSTANT: USE_EAB STRING "ON" +// Retrieval info: USED_PORT: aclr 0 0 0 0 INPUT GND aclr +// Retrieval info: USED_PORT: data 0 0 32 0 INPUT NODEFVAL data[31..0] +// Retrieval info: USED_PORT: q 0 0 32 0 OUTPUT NODEFVAL q[31..0] +// Retrieval info: USED_PORT: rdclk 0 0 0 0 INPUT NODEFVAL rdclk +// Retrieval info: USED_PORT: rdempty 0 0 0 0 OUTPUT NODEFVAL rdempty +// Retrieval info: USED_PORT: rdfull 0 0 0 0 OUTPUT NODEFVAL rdfull +// Retrieval info: USED_PORT: rdreq 0 0 0 0 INPUT NODEFVAL rdreq +// Retrieval info: USED_PORT: wrclk 0 0 0 0 INPUT NODEFVAL wrclk +// Retrieval info: USED_PORT: wrempty 0 0 0 0 OUTPUT NODEFVAL wrempty +// Retrieval info: USED_PORT: wrfull 0 0 0 0 OUTPUT NODEFVAL wrfull +// Retrieval info: USED_PORT: wrreq 0 0 0 0 INPUT NODEFVAL wrreq +// Retrieval info: CONNECT: @data 0 0 32 0 data 0 0 32 0 +// Retrieval info: CONNECT: q 0 0 32 0 @q 0 0 32 0 +// Retrieval info: CONNECT: @wrreq 0 0 0 0 wrreq 0 0 0 0 +// Retrieval info: CONNECT: @rdreq 0 0 0 0 rdreq 0 0 0 0 +// Retrieval info: CONNECT: @rdclk 0 0 0 0 rdclk 0 0 0 0 +// Retrieval info: CONNECT: @wrclk 0 0 0 0 wrclk 0 0 0 0 +// Retrieval info: CONNECT: rdfull 0 0 0 0 @rdfull 0 0 0 0 +// Retrieval info: CONNECT: rdempty 0 0 0 0 @rdempty 0 0 0 0 +// Retrieval info: CONNECT: wrfull 0 0 0 0 @wrfull 0 0 0 0 +// Retrieval info: CONNECT: wrempty 0 0 0 0 @wrempty 0 0 0 0 +// Retrieval info: CONNECT: @aclr 0 0 0 0 aclr 0 0 0 0 +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.inc TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.cmp TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.bsf TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_inst.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_bb.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_waveforms.html TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_wave*.jpg FALSE diff --git a/usrp/fpga/megacells/fifo_512_bb.v b/usrp/fpga/megacells/fifo_512_bb.v new file mode 100755 index 00000000..b1180315 --- /dev/null +++ b/usrp/fpga/megacells/fifo_512_bb.v @@ -0,0 +1,131 @@ +// megafunction wizard: %LPM_FIFO+%VBB% +// GENERATION: STANDARD +// VERSION: WM1.0 +// MODULE: dcfifo + +// ============================================================ +// File Name: fifo_512.v +// Megafunction Name(s): +// dcfifo +// ============================================================ +// ************************************************************ +// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +// +// 5.1 Build 213 01/19/2006 SP 1 SJ Web Edition +// ************************************************************ + +//Copyright (C) 1991-2006 Altera Corporation +//Your use of Altera Corporation's design tools, logic functions +//and other software and tools, and its AMPP partner logic +//functions, and any output files any of the foregoing +//(including device programming or simulation files), and any +//associated documentation or information are expressly subject +//to the terms and conditions of the Altera Program License +//Subscription Agreement, Altera MegaCore Function License +//Agreement, or other applicable license agreement, including, +//without limitation, that your use is for the sole purpose of +//programming logic devices manufactured by Altera and sold by +//Altera or its authorized distributors. Please refer to the +//applicable agreement for further details. + +module fifo_512 ( + aclr, + data, + rdclk, + rdreq, + wrclk, + wrreq, + q, + rdempty, + rdfull, + wrempty, + wrfull); + + input aclr; + input [31:0] data; + input rdclk; + input rdreq; + input wrclk; + input wrreq; + output [31:0] q; + output rdempty; + output rdfull; + output wrempty; + output wrfull; + +endmodule + +// ============================================================ +// CNX file retrieval info +// ============================================================ +// Retrieval info: PRIVATE: AlmostEmpty NUMERIC "0" +// Retrieval info: PRIVATE: AlmostEmptyThr NUMERIC "-1" +// Retrieval info: PRIVATE: AlmostFull NUMERIC "0" +// Retrieval info: PRIVATE: AlmostFullThr NUMERIC "-1" +// Retrieval info: PRIVATE: CLOCKS_ARE_SYNCHRONIZED NUMERIC "0" +// Retrieval info: PRIVATE: Clock NUMERIC "4" +// Retrieval info: PRIVATE: Depth NUMERIC "128" +// Retrieval info: PRIVATE: Empty NUMERIC "1" +// Retrieval info: PRIVATE: Full NUMERIC "1" +// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone" +// Retrieval info: PRIVATE: LE_BasedFIFO NUMERIC "0" +// Retrieval info: PRIVATE: LegacyRREQ NUMERIC "1" +// Retrieval info: PRIVATE: MAX_DEPTH_BY_9 NUMERIC "0" +// Retrieval info: PRIVATE: OVERFLOW_CHECKING NUMERIC "0" +// Retrieval info: PRIVATE: Optimize NUMERIC "2" +// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "2" +// Retrieval info: PRIVATE: UNDERFLOW_CHECKING NUMERIC "0" +// Retrieval info: PRIVATE: UsedW NUMERIC "1" +// Retrieval info: PRIVATE: Width NUMERIC "32" +// Retrieval info: PRIVATE: dc_aclr NUMERIC "1" +// Retrieval info: PRIVATE: rsEmpty NUMERIC "1" +// Retrieval info: PRIVATE: rsFull NUMERIC "1" +// Retrieval info: PRIVATE: rsUsedW NUMERIC "0" +// Retrieval info: PRIVATE: sc_aclr NUMERIC "0" +// Retrieval info: PRIVATE: sc_sclr NUMERIC "0" +// Retrieval info: PRIVATE: wsEmpty NUMERIC "1" +// Retrieval info: PRIVATE: wsFull NUMERIC "1" +// Retrieval info: PRIVATE: wsUsedW NUMERIC "0" +// Retrieval info: CONSTANT: ADD_RAM_OUTPUT_REGISTER STRING "OFF" +// Retrieval info: CONSTANT: CLOCKS_ARE_SYNCHRONIZED STRING "FALSE" +// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone" +// Retrieval info: CONSTANT: LPM_HINT STRING "RAM_BLOCK_TYPE=M4K" +// Retrieval info: CONSTANT: LPM_NUMWORDS NUMERIC "128" +// Retrieval info: CONSTANT: LPM_SHOWAHEAD STRING "OFF" +// Retrieval info: CONSTANT: LPM_TYPE STRING "dcfifo" +// Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "32" +// Retrieval info: CONSTANT: LPM_WIDTHU NUMERIC "7" +// Retrieval info: CONSTANT: OVERFLOW_CHECKING STRING "ON" +// Retrieval info: CONSTANT: UNDERFLOW_CHECKING STRING "ON" +// Retrieval info: CONSTANT: USE_EAB STRING "ON" +// Retrieval info: USED_PORT: aclr 0 0 0 0 INPUT GND aclr +// Retrieval info: USED_PORT: data 0 0 32 0 INPUT NODEFVAL data[31..0] +// Retrieval info: USED_PORT: q 0 0 32 0 OUTPUT NODEFVAL q[31..0] +// Retrieval info: USED_PORT: rdclk 0 0 0 0 INPUT NODEFVAL rdclk +// Retrieval info: USED_PORT: rdempty 0 0 0 0 OUTPUT NODEFVAL rdempty +// Retrieval info: USED_PORT: rdfull 0 0 0 0 OUTPUT NODEFVAL rdfull +// Retrieval info: USED_PORT: rdreq 0 0 0 0 INPUT NODEFVAL rdreq +// Retrieval info: USED_PORT: wrclk 0 0 0 0 INPUT NODEFVAL wrclk +// Retrieval info: USED_PORT: wrempty 0 0 0 0 OUTPUT NODEFVAL wrempty +// Retrieval info: USED_PORT: wrfull 0 0 0 0 OUTPUT NODEFVAL wrfull +// Retrieval info: USED_PORT: wrreq 0 0 0 0 INPUT NODEFVAL wrreq +// Retrieval info: CONNECT: @data 0 0 32 0 data 0 0 32 0 +// Retrieval info: CONNECT: q 0 0 32 0 @q 0 0 32 0 +// Retrieval info: CONNECT: @wrreq 0 0 0 0 wrreq 0 0 0 0 +// Retrieval info: CONNECT: @rdreq 0 0 0 0 rdreq 0 0 0 0 +// Retrieval info: CONNECT: @rdclk 0 0 0 0 rdclk 0 0 0 0 +// Retrieval info: CONNECT: @wrclk 0 0 0 0 wrclk 0 0 0 0 +// Retrieval info: CONNECT: rdfull 0 0 0 0 @rdfull 0 0 0 0 +// Retrieval info: CONNECT: rdempty 0 0 0 0 @rdempty 0 0 0 0 +// Retrieval info: CONNECT: wrfull 0 0 0 0 @wrfull 0 0 0 0 +// Retrieval info: CONNECT: wrempty 0 0 0 0 @wrempty 0 0 0 0 +// Retrieval info: CONNECT: @aclr 0 0 0 0 aclr 0 0 0 0 +// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.inc TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.cmp TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512.bsf TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_inst.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_bb.v TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_waveforms.html TRUE +// Retrieval info: GEN_FILE: TYPE_NORMAL fifo_512_wave*.jpg FALSE diff --git a/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.qsf b/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.qsf index 2646d831..8296a453 100644 --- a/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.qsf +++ b/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.qsf @@ -27,7 +27,7 @@ # ======================== set_global_assignment -name ORIGINAL_QUARTUS_VERSION 3.0 set_global_assignment -name PROJECT_CREATION_TIME_DATE "00:14:04 JULY 13, 2003" -set_global_assignment -name LAST_QUARTUS_VERSION 6.1 +set_global_assignment -name LAST_QUARTUS_VERSION "5.1 SP1" # Pin & Location Assignments # ========================== @@ -371,6 +371,13 @@ set_instance_assignment -name CLOCK_SETTINGS master_clk -to master_clk set_instance_assignment -name PARTITION_HIERARCHY no_file_for_top_partition -to | -section_id Top set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name FITTER_AUTO_EFFORT_DESIRED_SLACK_MARGIN "100 ps" +set_global_assignment -name VERILOG_FILE ../../inband_lib/tx_buffer_inband.v +set_global_assignment -name VERILOG_FILE ../../inband_lib/usb_packet_fifo.v +set_global_assignment -name VERILOG_FILE ../../inband_lib/chan_fifo_reader.v +set_global_assignment -name VERILOG_FILE ../../inband_lib/data_packet_fifo.v +set_global_assignment -name VERILOG_FILE ../../inband_lib/usb_fifo_reader.v +set_global_assignment -name VERILOG_FILE ../../sdr_lib/cic_dec_shifter.v set_global_assignment -name VERILOG_FILE ../../sdr_lib/rssi.v set_global_assignment -name VERILOG_FILE ../../sdr_lib/ram16.v set_global_assignment -name VERILOG_FILE ../../megacells/fifo_4k.v @@ -405,5 +412,4 @@ set_global_assignment -name VERILOG_FILE usrp_inband_usb.v set_global_assignment -name VERILOG_FILE ../../sdr_lib/clk_divider.v set_global_assignment -name VERILOG_FILE ../../sdr_lib/serial_io.v set_global_assignment -name VERILOG_FILE ../../sdr_lib/strobe_gen.v -set_global_assignment -name VERILOG_FILE ../../sdr_lib/sign_extend.v -set_global_assignment -name FITTER_AUTO_EFFORT_DESIRED_SLACK_MARGIN "100 ps" \ No newline at end of file +set_global_assignment -name VERILOG_FILE ../../sdr_lib/sign_extend.v \ No newline at end of file diff --git a/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.v b/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.v index 55701b89..cc7490c5 100644 --- a/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.v +++ b/usrp/fpga/toplevel/usrp_inband_usb/usrp_inband_usb.v @@ -19,6 +19,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA // +`define IN_BAND `include "config.vh" `include "../../../firmware/include/fpga_regs_common.v" @@ -122,6 +123,20 @@ module usrp_inband_usb assign bb_tx_i1 = ch2tx; assign bb_tx_q1 = ch3tx; +`ifdef IN_BAND + tx_buffer_inband tx_buffer + ( .usbclk(usbclk),.bus_reset(tx_bus_reset),.reset(tx_dsp_reset), + .usbdata(usbdata),.WR(WR),.have_space(have_space),.tx_underrun(tx_underrun), + .channels({tx_numchan,1'b0}), + .tx_i_0(ch0tx),.tx_q_0(ch1tx), + .tx_i_1(ch2tx),.tx_q_1(ch3tx), + .tx_i_2(),.tx_q_2(), + .tx_i_3(),.tx_q_3(), + .txclk(clk64),.txstrobe(strobe_interp), + .clear_status(clear_status), + .tx_empty(tx_empty), + .debugbus(tx_debugbus) ); +`else tx_buffer tx_buffer ( .usbclk(usbclk),.bus_reset(tx_bus_reset),.reset(tx_dsp_reset), .usbdata(usbdata),.WR(WR),.have_space(have_space),.tx_underrun(tx_underrun), @@ -134,6 +149,7 @@ module usrp_inband_usb .clear_status(clear_status), .tx_empty(tx_empty), .debugbus(tx_debugbus) ); +`endif `ifdef TX_EN_0 tx_chain tx_chain_0 diff --git a/usrp/host/apps/Makefile.am b/usrp/host/apps/Makefile.am index 8e0768e8..035a23ab 100644 --- a/usrp/host/apps/Makefile.am +++ b/usrp/host/apps/Makefile.am @@ -23,8 +23,6 @@ include $(top_srcdir)/Makefile.common INCLUDES = $(USRP_INCLUDES) $(BOOST_CFLAGS) -USRP_LIB = $(top_builddir)/usrp/host/lib/libusrp.la - bin_PROGRAMS = \ usrper \ usrp_cal_dc_offset @@ -45,13 +43,13 @@ noinst_PYTHON = \ check_order_quickly_SOURCES = check_order_quickly.cc test_usrp_standard_rx_SOURCES = test_usrp_standard_rx.cc time_stuff.c -test_usrp_standard_rx_LDADD = $(USRP_LIB) +test_usrp_standard_rx_LDADD = $(USRP_LA) test_usrp_standard_tx_SOURCES = test_usrp_standard_tx.cc time_stuff.c -test_usrp_standard_tx_LDADD = $(USRP_LIB) +test_usrp_standard_tx_LDADD = $(USRP_LA) usrper_SOURCES = usrper.cc -usrper_LDADD = $(USRP_LIB) +usrper_LDADD = $(USRP_LA) usrp_cal_dc_offset_SOURCES = usrp_cal_dc_offset.cc -usrp_cal_dc_offset_LDADD = $(USRP_LIB) +usrp_cal_dc_offset_LDADD = $(USRP_LA) diff --git a/usrp/host/lib/Makefile.am b/usrp/host/lib/Makefile.am index 7d17a380..cd37a9b2 100644 --- a/usrp/host/lib/Makefile.am +++ b/usrp/host/lib/Makefile.am @@ -20,133 +20,5 @@ include $(top_srcdir)/Makefile.common -INCLUDES = $(USRP_INCLUDES) +SUBDIRS = legacy inband -lib_LTLIBRARIES = libusrp.la - - -EXTRA_DIST = \ - std_paths.h.in \ - usrp_dbid.dat - - -BUILT_SOURCES = \ - usrp_dbid.h \ - usrp_dbid.cc \ - usrp_dbid.py - - -# ---------------------------------------------------------------- -# FUSB_TECH is set at configure time by way of -# usrp/config/usrp_fusb_tech.m4. -# It indicates which fast usb strategy we should be building. -# We currently implement "generic", "darwin", "win32" and "linux" - - -generic_CODE = \ - fusb_generic.cc \ - fusb_sysconfig_generic.cc - -darwin_CODE = \ - fusb_darwin.cc \ - fusb_sysconfig_darwin.cc \ - README_OSX \ - circular_buffer.h \ - circular_linked_list.h \ - darwin_libusb.h \ - mld_threads.h - -win32_CODE = \ - fusb_win32.cc \ - fusb_sysconfig_win32.cc - -linux_CODE = \ - fusb_linux.cc \ - fusb_sysconfig_linux.cc - -ra_wb_CODE = \ - fusb_ra_wb.cc \ - fusb_sysconfig_ra_wb.cc - - -# -# include each _CODE entry here... -# -EXTRA_libusrp_la_SOURCES = \ - $(generic_CODE) \ - $(darwin_CODE) \ - $(win32_CODE) \ - $(linux_CODE) \ - $(ra_wb_CODE) - - -# work around automake deficiency -libusrp_la_common_SOURCES = \ - fusb.cc \ - md5.c \ - usrp_basic.cc \ - usrp_config.cc \ - usrp_dbid.cc \ - usrp_local_sighandler.cc \ - usrp_prims.cc \ - usrp_standard.cc - - -if FUSB_TECH_generic -libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(generic_CODE) -endif - -if FUSB_TECH_darwin -libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(darwin_CODE) -endif - -if FUSB_TECH_win32 -libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(win32_CODE) -endif - -if FUSB_TECH_linux -libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(linux_CODE) -endif - -if FUSB_TECH_ra_wb -libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(ra_wb_CODE) -endif - - -libusrp_la_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 -libusrp_la_LIBADD = $(USB_LIBS) ../misc/libmisc.la - -include_HEADERS = \ - usrp_basic.h \ - usrp_bytesex.h \ - usrp_config.h \ - usrp_dbid.h \ - usrp_prims.h \ - usrp_slots.h \ - usrp_standard.h - -noinst_HEADERS = \ - ad9862.h \ - fusb.h \ - fusb_darwin.h \ - fusb_win32.h \ - fusb_generic.h \ - fusb_linux.h \ - fusb_ra_wb.h \ - md5.h \ - rate_to_regval.h \ - usrp_local_sighandler.h - -usrppython_PYTHON = \ - usrp_dbid.py - -noinst_PYTHON = \ - gen_usrp_dbid.py \ - check_data.py \ - dump_data.py - -usrp_dbid.py usrp_dbid.h usrp_dbid.cc: gen_usrp_dbid.py usrp_dbid.dat - PYTHONPATH=$(top_srcdir)/usrp/src srcdir=$(srcdir) $(PYTHON) $(srcdir)/gen_usrp_dbid.py $(srcdir)/usrp_dbid.dat - -MOSTLYCLEANFILES = \ - $(BUILT_SOURCES) *~ *.pyc diff --git a/usrp/host/lib/README_OSX b/usrp/host/lib/README_OSX deleted file mode 100644 index 20230f12..00000000 --- a/usrp/host/lib/README_OSX +++ /dev/null @@ -1,63 +0,0 @@ -USRP Darwin Fast USB Changes -Version 0.2 of 2006-04-27 -Michael Dickens - -The files included in this archive are: - -circular_buffer.h -circular_linked_list.h -darwin_libusb.h -fusb_darwin.cc -fusb_darwin.h -mld_threads.h - -These files allow GNURadio code for Darwin / MaxOS X to talk to the -USRP via USB 2.0 at rates up to around 30 Mega-Bytes/sec (MBps), up -from 4-8 MBps without the changes. - -I implemented the buffering myself; there are probably GR buffers -available which would do the work but as this is "beta" software it's -a good place to start. Speed improvements are made by porting -LIBUSB's non-true async bulk read and write functions into USRP's -"fusb", and upgrading them to handle -true- async transfers. -Unfortunately, the easiest way to do this is to spawn a thread or 2 to -handle the "async" part of the transfers. This implementation uses -Darwin's pthreads to do the work for mutexes, conditions, and threads. -Previous implementations (0.1 and before) used "omni_threads" as -provided by gnuradio-core, which caused issues with compiling and -execution ... I'm glad that this is no longer the case. - -As far as I have tested, there is no way to improve the throughput to -32+ MBps without moving into Darwin's "port"s ... a kernel-level data -transport method with a user/application layer for USB-specific -functions. Unfortunately, Apple's documentation for these "port"s is -minimal; I have learned more from reading the Darwin source code -< http://darwinsource.opendarwin.org/ > than by reading Apple's -documents! This would also require -not- using LIBUSB, of which the -removal from the rest of the USRP code would be potentially tedious. - -If you run into issues either compiling or testing the USRP on -OSX, please send me a note. - -(1) Go through the bootstrap, configure, compile, and install as -usual (e.g. see < http://www.nd.edu/~mdickens/GNURadio/ > for my -usual). - -(2) from .../usrp/host/apps : run the scripts -++++++++++++++++ -./test_usrp_standard_tx -./test_usrp_standard_rx -++++++++++++++++ - -For -all- systems I've tested on thus far, both of these return -exactly 41 overruns / underruns, and -most- systems start out with a -stalled pipe. This stall comes in a usb_control funciton call to -LIBUSB; one would have to change the LIBUSB code to handle this issue. - -(3) from gr-build/gnuradio-examples/python/usrp : -++++++++++++++++ -./benchmark_usb.py -++++++++++++++++ - -(4) If you get to here, the try doing the FM receiver (gui or not). -If that sounds correct, then the USB is working. Yay! \ No newline at end of file diff --git a/usrp/host/lib/ad9862.h b/usrp/host/lib/ad9862.h deleted file mode 100644 index d1895bbe..00000000 --- a/usrp/host/lib/ad9862.h +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_AD9862_H -#define INCLUDED_AD9862_H - -/* - * Analog Devices AD9862 registers and some fields - */ - -#define BEGIN_AD9862 namespace ad9862 { -#define END_AD962 } -#define DEF static const int - -BEGIN_AD9862; - -DEF REG_GENERAL = 0; -DEF REG_RX_PWR_DN = 1; -DEF RX_PWR_DN_VREF_DIFF = (1 << 7); -DEF RX_PWR_DN_VREF = (1 << 6); -DEF RX_PWR_DN_RX_DIGIGAL = (1 << 5); -DEF RX_PWR_DN_RX_B = (1 << 4); -DEF RX_PWR_DN_RX_A = (1 << 3); -DEF RX_PWR_DN_BUF_B = (1 << 2); -DEF RX_PWR_DN_BUF_A = (1 << 1); -DEF RX_PWR_DN_ALL = (1 << 0); - -DEF REG_RX_A = 2; // bypass input buffer / RxPGA -DEF REG_RX_B = 3; // pypass input buffer / RxPGA -DEF RX_X_BYPASS_INPUT_BUFFER = (1 << 7); - -DEF REG_RX_MISC = 4; -DEF RX_MISC_HS_DUTY_CYCLE = (1 << 2); -DEF RX_MISC_SHARED_REF = (1 << 1); -DEF RX_MISC_CLK_DUTY = (1 << 0); - -DEF REG_RX_IF = 5; -DEF RX_IF_THREE_STATE = (1 << 4); -DEF RX_IF_USE_CLKOUT1 = (0 << 3); -DEF RX_IF_USE_CLKOUT2 = (1 << 3); // aka Rx Retime -DEF RX_IF_2S_COMP = (1 << 2); -DEF RX_IF_INV_RX_SYNC = (1 << 1); -DEF RX_IF_MUX_OUT = (1 << 0); - -DEF REG_RX_DIGITAL = 6; -DEF RX_DIGITAL_2_CHAN = (1 << 3); -DEF RX_DIGITAL_KEEP_MINUS_VE = (1 << 2); -DEF RX_DIGITAL_HILBERT = (1 << 1); -DEF RX_DIGITAL_DECIMATE = (1 << 0); - -DEF REG_RESERVED_7 = 7; - -DEF REG_TX_PWR_DN = 8; -DEF TX_PWR_DN_ALT_TIMING_MODE = (1 << 5); -DEF TX_PWR_DN_TX_OFF_ENABLE = (1 << 4); -DEF TX_PWR_DN_TX_DIGITAL = (1 << 3); -DEF TX_PWR_DN_TX_ANALOG_B = 0x4; -DEF TX_PWR_DN_TX_ANALOG_A = 0x2; -DEF TX_PWR_DN_TX_ANALOG_BOTH = 0x7; - -DEF REG_RESERVED_9 = 9; - -DEF REG_TX_A_OFFSET_LO = 10; -DEF REG_TX_A_OFFSET_HI = 11; -DEF REG_TX_B_OFFSET_LO = 12; -DEF REG_TX_B_OFFSET_HI = 13; - -DEF REG_TX_A_GAIN = 14; // fine trim for matching -DEF REG_TX_B_GAIN = 15; // fine trim for matching -DEF TX_X_GAIN_COARSE_FULL = (3 << 6); -DEF TX_X_GAIN_COARSE_1_HALF = (1 << 6); -DEF TX_X_GAIN_COARSE_1_ELEVENTH = (0 << 6); - -DEF REG_TX_PGA = 16; // 20 dB continuous gain in 0.1 dB steps - // 0x00 = min gain (-20 dB) - // 0xff = max gain ( 0 dB) - -DEF REG_TX_MISC = 17; -DEF TX_MISC_SLAVE_ENABLE = (1 << 1); -DEF TX_MISC_TX_PGA_FAST = (1 << 0); - -DEF REG_TX_IF = 18; -DEF TX_IF_USE_CLKOUT2 = (0 << 6); -DEF TX_IF_USE_CLKOUT1 = (1 << 6); // aka Tx Retime -DEF TX_IF_I_FIRST = (0 << 5); -DEF TX_IF_Q_FIRST = (1 << 5); -DEF TX_IF_INV_TX_SYNC = (1 << 4); -DEF TX_IF_2S_COMP = (1 << 3); -DEF TX_IF_INVERSE_SAMPLE = (1 << 2); -DEF TX_IF_TWO_EDGES = (1 << 1); -DEF TX_IF_INTERLEAVED = (1 << 0); - -DEF REG_TX_DIGITAL = 19; -DEF TX_DIGITAL_2_DATA_PATHS = (1 << 4); -DEF TX_DIGITAL_KEEP_NEGATIVE = (1 << 3); -DEF TX_DIGITAL_HILBERT = (1 << 2); -DEF TX_DIGITAL_INTERPOLATE_NONE = 0x0; -DEF TX_DIGITAL_INTERPOLATE_2X = 0x1; -DEF TX_DIGITAL_INTERPOLATE_4X = 0x2; - -DEF REG_TX_MODULATOR = 20; -DEF TX_MODULATOR_NEG_FINE_TUNE = (1 << 5); -DEF TX_MODULATOR_DISABLE_NCO = (0 << 4); -DEF TX_MODULATOR_ENABLE_NCO = (1 << 4); // aka Fine Mode -DEF TX_MODULATOR_REAL_MIX_MODE = (1 << 3); -DEF TX_MODULATOR_NEG_COARSE_TUNE = (1 << 2); -DEF TX_MODULATOR_COARSE_MODULATION_NONE = 0x0; -DEF TX_MODULATOR_COARSE_MODULATION_F_OVER_4 = 0x1; -DEF TX_MODULATOR_COARSE_MODULATION_F_OVER_8 = 0x2; -DEF TX_MODULATOR_CM_MASK = 0x7; - - -DEF REG_TX_NCO_FTW_7_0 = 21; -DEF REG_TX_NCO_FTW_15_8 = 22; -DEF REG_TX_NCO_FTW_23_16= 23; - -DEF REG_DLL = 24; -DEF DLL_DISABLE_INTERNAL_XTAL_OSC = (1 << 6); // aka Input Clock Ctrl -DEF DLL_ADC_DIV2 = (1 << 5); -DEF DLL_MULT_1X = (0 << 3); -DEF DLL_MULT_2X = (1 << 3); -DEF DLL_MULT_4X = (2 << 3); -DEF DLL_PWR_DN = (1 << 2); -// undefined bit = (1 << 1); -DEF DLL_FAST = (1 << 0); - -DEF REG_CLKOUT = 25; -DEF CLKOUT2_EQ_DLL = (0 << 6); -DEF CLKOUT2_EQ_DLL_OVER_2 = (1 << 6); -DEF CLKOUT2_EQ_DLL_OVER_4 = (2 << 6); -DEF CLKOUT2_EQ_DLL_OVER_8 = (3 << 6); -DEF CLKOUT_INVERT_CLKOUT2 = (1 << 5); -DEF CLKOUT_DISABLE_CLKOUT2 = (1 << 4); -// undefined bit = (1 << 3); -// undefined bit = (1 << 2); -DEF CLKOUT_INVERT_CLKOUT1 = (1 << 1); -DEF CLKOUT_DISABLE_CLKOUT1 = (1 << 0); - -DEF REG_AUX_ADC_A2_LO = 26; -DEF REG_AUX_ADC_A2_HI = 27; -DEF REG_AUX_ADC_A1_LO = 28; -DEF REG_AUX_ADC_A1_HI = 29; -DEF REG_AUX_ADC_B2_LO = 30; -DEF REG_AUX_ADC_B2_HI = 31; -DEF REG_AUX_ADC_B1_LO = 32; -DEF REG_AUX_ADC_B1_HI = 33; - -DEF REG_AUX_ADC_CTRL = 34; -DEF AUX_ADC_CTRL_AUX_SPI = (1 << 7); -DEF AUX_ADC_CTRL_SELBNOTA = (1 << 6); -DEF AUX_ADC_CTRL_REFSEL_B = (1 << 5); -DEF AUX_ADC_CTRL_SELECT_B2 = (0 << 4); -DEF AUX_ADC_CTRL_SELECT_B1 = (1 << 4); -DEF AUX_ADC_CTRL_START_B = (1 << 3); -DEF AUX_ADC_CTRL_REFSEL_A = (1 << 2); -DEF AUX_ADC_CTRL_SELECT_A2 = (0 << 1); -DEF AUX_ADC_CTRL_SELECT_A1 = (1 << 1); -DEF AUX_ADC_CTRL_START_A = (1 << 0); - -DEF REG_AUX_ADC_CLK = 35; -DEF AUX_ADC_CLK_CLK_OVER_4 = (1 << 0); - -DEF REG_AUX_DAC_A = 36; -DEF REG_AUX_DAC_B = 37; -DEF REG_AUX_DAC_C = 38; - -DEF REG_AUX_DAC_UPDATE = 39; -DEF AUX_DAC_UPDATE_SLAVE_ENABLE = (1 << 7); -DEF AUX_DAC_UPDATE_C = (1 << 2); -DEF AUX_DAC_UPDATE_B = (1 << 1); -DEF AUX_DAC_UPDATE_A = (1 << 0); - -DEF REG_AUX_DAC_PWR_DN = 40; -DEF AUX_DAC_PWR_DN_C = (1 << 2); -DEF AUX_DAC_PWR_DN_B = (1 << 1); -DEF AUX_DAC_PWR_DN_A = (1 << 0); - -DEF REG_AUX_DAC_CTRL = 41; -DEF AUX_DAC_CTRL_INV_C = (1 << 4); -DEF AUX_DAC_CTRL_INV_B = (1 << 2); -DEF AUX_DAC_CTRL_INV_A = (1 << 0); - -DEF REG_SIGDELT_LO = 42; -DEF REG_SIGDELT_HI = 43; - -// 44 to 48 reserved - -DEF REG_ADC_LOW_PWR_LO = 49; -DEF REG_ADC_LOW_PWR_HI = 50; - -// 51 to 62 reserved - -DEF REG_CHIP_ID = 63; - - -END_AD962; - -#undef DEF -#undef BEGIN_AD9862 -#undef END_AD962 - -#endif /* INCLUDED_AD9862_H */ diff --git a/usrp/host/lib/check_data.py b/usrp/host/lib/check_data.py deleted file mode 100755 index 655ca150..00000000 --- a/usrp/host/lib/check_data.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. - -import sys -import struct - -fin = sys.stdin - -count = 0 -expected = 0 -last_correction = 0 - -while 1: - s = fin.read(2) - if not s or len(s) != 2: - break - - v, = struct.unpack ('H', s) - iv = int(v) & 0xffff - # print "%8d %6d 0x%04x" % (count, iv, iv) - if count & 0x1: # only counting on the Q channel - if (expected & 0xffff) != iv: - print "%8d (%6d) %6d 0x%04x" % (count, count - last_correction, iv, iv) - expected = iv # reset expected sequence - last_correction = count - expected = (expected + 1) & 0xffff - - count += 1 - - - - diff --git a/usrp/host/lib/circular_buffer.h b/usrp/host/lib/circular_buffer.h deleted file mode 100644 index 913360df..00000000 --- a/usrp/host/lib/circular_buffer.h +++ /dev/null @@ -1,325 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _CIRCULAR_BUFFER_H_ -#define _CIRCULAR_BUFFER_H_ - -#include "mld_threads.h" -#include - -#define DO_DEBUG 0 - -template class circular_buffer -{ -private: -// the buffer to use - T* d_buffer; - -// the following are in Items (type T) - UInt32 d_bufLen_I, d_readNdx_I, d_writeNdx_I; - UInt32 d_n_avail_write_I, d_n_avail_read_I; - -// stuff to control access to class internals - mld_mutex_ptr d_internal; - mld_condition_ptr d_readBlock, d_writeBlock; - -// booleans to decide how to control reading, writing, and aborting - bool d_doWriteBlock, d_doFullRead, d_doAbort; - - void delete_mutex_cond () { - if (d_internal) { - delete d_internal; - d_internal = NULL; - } - if (d_readBlock) { - delete d_readBlock; - d_readBlock = NULL; - } - if (d_writeBlock) { - delete d_writeBlock; - d_writeBlock = NULL; - } - }; - -public: - circular_buffer (UInt32 bufLen_I, - bool doWriteBlock = true, bool doFullRead = false) { - if (bufLen_I == 0) - throw std::runtime_error ("circular_buffer(): " - "Number of items to buffer must be > 0.\n"); - d_bufLen_I = bufLen_I; - d_buffer = (T*) new T[d_bufLen_I]; - d_doWriteBlock = doWriteBlock; - d_doFullRead = doFullRead; - d_internal = NULL; - d_readBlock = d_writeBlock = NULL; - reset (); -#if DO_DEBUG - fprintf (stderr, "c_b(): buf len (items) = %ld, " - "doWriteBlock = %s, doFullRead = %s\n", d_bufLen_I, - (d_doWriteBlock ? "true" : "false"), - (d_doFullRead ? "true" : "false")); -#endif - }; - - ~circular_buffer () { - delete_mutex_cond (); - delete [] d_buffer; - }; - - inline UInt32 n_avail_write_items () { - d_internal->lock (); - UInt32 retVal = d_n_avail_write_I; - d_internal->unlock (); - return (retVal); - }; - - inline UInt32 n_avail_read_items () { - d_internal->lock (); - UInt32 retVal = d_n_avail_read_I; - d_internal->unlock (); - return (retVal); - }; - - inline UInt32 buffer_length_items () {return (d_bufLen_I);}; - inline bool do_write_block () {return (d_doWriteBlock);}; - inline bool do_full_read () {return (d_doFullRead);}; - - void reset () { - d_doAbort = false; - bzero (d_buffer, d_bufLen_I * sizeof (T)); - d_readNdx_I = d_writeNdx_I = d_n_avail_read_I = 0; - d_n_avail_write_I = d_bufLen_I; - delete_mutex_cond (); - d_internal = new mld_mutex (); - d_readBlock = new mld_condition (); - d_writeBlock = new mld_condition (); - }; - -/* - * enqueue: add the given buffer of item-length to the queue, - * first-in-first-out (FIFO). - * - * inputs: - * buf: a pointer to the buffer holding the data - * - * bufLen_I: the buffer length in items (of the instantiated type) - * - * returns: - * -1: on overflow (write is not blocking, and data is being - * written faster than it is being read) - * 0: if nothing to do (0 length buffer) - * 1: if success - * 2: in the process of aborting, do doing nothing - * - * will throw runtime errors if inputs are improper: - * buffer pointer is NULL - * buffer length is larger than the instantiated buffer length - */ - - int enqueue (T* buf, UInt32 bufLen_I) { -#if DO_DEBUG - fprintf (stderr, "enqueue: buf = %X, bufLen = %ld.\n", - (unsigned int)buf, bufLen_I); -#endif - if (bufLen_I > d_bufLen_I) { - fprintf (stderr, "cannot add buffer longer (%ld" - ") than instantiated length (%ld" - ").\n", bufLen_I, d_bufLen_I); - throw std::runtime_error ("circular_buffer::enqueue()"); - } - - if (bufLen_I == 0) - return (0); - if (!buf) - throw std::runtime_error ("circular_buffer::enqueue(): " - "input buffer is NULL.\n"); - d_internal->lock (); - if (d_doAbort) { - d_internal->unlock (); - return (2); - } - if (bufLen_I > d_n_avail_write_I) { - if (d_doWriteBlock) { - while (bufLen_I > d_n_avail_write_I) { -#if DO_DEBUG - fprintf (stderr, "enqueue: #len > #a, waiting.\n"); -#endif - d_internal->unlock (); - d_writeBlock->wait (); - d_internal->lock (); - if (d_doAbort) { - d_internal->unlock (); -#if DO_DEBUG - fprintf (stderr, "enqueue: #len > #a, aborting.\n"); -#endif - return (2); - } -#if DO_DEBUG - fprintf (stderr, "enqueue: #len > #a, done waiting.\n"); -#endif - } - } else { - d_n_avail_read_I = d_bufLen_I - bufLen_I; - d_n_avail_write_I = bufLen_I; -#if DO_DEBUG - fprintf (stderr, "circular_buffer::enqueue: overflow\n"); -#endif - return (-1); - } - } - UInt32 n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0; - if (n_now_I > bufLen_I) - n_now_I = bufLen_I; - else if (n_now_I < bufLen_I) - n_start_I = bufLen_I - n_now_I; - bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T)); - if (n_start_I) { - bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T)); - d_writeNdx_I = n_start_I; - } else - d_writeNdx_I += n_now_I; - d_n_avail_read_I += bufLen_I; - d_n_avail_write_I -= bufLen_I; - d_readBlock->signal (); - d_internal->unlock (); - return (1); - }; - -/* - * dequeue: removes from the queue the number of items requested, or - * available, into the given buffer on a FIFO basis. - * - * inputs: - * buf: a pointer to the buffer into which to copy the data - * - * bufLen_I: pointer to the number of items to remove in items - * (of the instantiated type) - * - * returns: - * 0: if nothing to do (0 length buffer) - * 1: if success - * 2: in the process of aborting, do doing nothing - * - * will throw runtime errors if inputs are improper: - * buffer pointer is NULL - * buffer length pointer is NULL - * buffer length is larger than the instantiated buffer length - */ - - - int dequeue (T* buf, UInt32* bufLen_I) { -#if DO_DEBUG - fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld.\n", - (unsigned int)buf, *bufLen_I); -#endif - if (!bufLen_I) - throw std::runtime_error ("circular_buffer::dequeue(): " - "input bufLen pointer is NULL.\n"); - if (!buf) - throw std::runtime_error ("circular_buffer::dequeue(): " - "input buffer pointer is NULL.\n"); - UInt32 l_bufLen_I = *bufLen_I; - if (l_bufLen_I == 0) - return (0); - if (l_bufLen_I > d_bufLen_I) { - fprintf (stderr, "cannot remove buffer longer (%ld" - ") than instantiated length (%ld" - ").\n", l_bufLen_I, d_bufLen_I); - throw std::runtime_error ("circular_buffer::dequeue()"); - } - - d_internal->lock (); - if (d_doAbort) { - d_internal->unlock (); - return (2); - } - if (d_doFullRead) { - while (d_n_avail_read_I < l_bufLen_I) { -#if DO_DEBUG - fprintf (stderr, "dequeue: #a < #len, waiting.\n"); -#endif - d_internal->unlock (); - d_readBlock->wait (); - d_internal->lock (); - if (d_doAbort) { - d_internal->unlock (); -#if DO_DEBUG - fprintf (stderr, "dequeue: #a < #len, aborting.\n"); -#endif - return (2); - } -#if DO_DEBUG - fprintf (stderr, "dequeue: #a < #len, done waiting.\n"); -#endif - } - } else { - while (d_n_avail_read_I == 0) { -#if DO_DEBUG - fprintf (stderr, "dequeue: #a == 0, waiting.\n"); -#endif - d_internal->unlock (); - d_readBlock->wait (); - d_internal->lock (); - if (d_doAbort) { - d_internal->unlock (); -#if DO_DEBUG - fprintf (stderr, "dequeue: #a == 0, aborting.\n"); -#endif - return (2); - } -#if DO_DEBUG - fprintf (stderr, "dequeue: #a == 0, done waiting.\n"); -#endif - } - } - if (l_bufLen_I > d_n_avail_read_I) - l_bufLen_I = d_n_avail_read_I; - UInt32 n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0; - if (n_now_I > l_bufLen_I) - n_now_I = l_bufLen_I; - else if (n_now_I < l_bufLen_I) - n_start_I = l_bufLen_I - n_now_I; - bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T)); - if (n_start_I) { - bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T)); - d_readNdx_I = n_start_I; - } else - d_readNdx_I += n_now_I; - *bufLen_I = l_bufLen_I; - d_n_avail_read_I -= l_bufLen_I; - d_n_avail_write_I += l_bufLen_I; - d_writeBlock->signal (); - d_internal->unlock (); - return (1); - }; - - void abort () { - d_internal->lock (); - d_doAbort = true; - d_writeBlock->signal (); - d_readBlock->signal (); - d_internal->unlock (); - }; -}; - -#endif /* _CIRCULAR_BUFFER_H_ */ diff --git a/usrp/host/lib/circular_linked_list.h b/usrp/host/lib/circular_linked_list.h deleted file mode 100644 index 0e460a26..00000000 --- a/usrp/host/lib/circular_linked_list.h +++ /dev/null @@ -1,267 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _CIRCULAR_LINKED_LIST_H_ -#define _CIRCULAR_LINKED_LIST_H_ - -#include -#include - -#define __INLINE__ inline - -template class s_both; - -template class s_node -{ - typedef s_node* s_node_ptr; - -private: - T d_object; - bool d_available; - s_node_ptr d_prev, d_next; - s_both* d_both; - -public: - s_node (T l_object, - s_node_ptr l_prev = NULL, - s_node_ptr l_next = NULL) - : d_object (l_object), d_available (TRUE), d_prev (l_prev), - d_next (l_next), d_both (0) {}; - - __INLINE__ s_node (s_node_ptr l_prev, s_node_ptr l_next = NULL) { - s_node ((T) NULL, l_prev, l_next); }; - __INLINE__ s_node () { s_node (NULL, NULL, NULL); }; - __INLINE__ ~s_node () {}; - - void remove () { - d_prev->next (d_next); - d_next->prev (d_prev); - d_prev = d_next = this; - }; - - void insert_before (s_node_ptr l_next) { - if (l_next) { - s_node_ptr l_prev = l_next->prev (); - d_next = l_next; - d_prev = l_prev; - l_prev->next (this); - l_next->prev (this); - } else - d_next = d_prev = this; - }; - - void insert_after (s_node_ptr l_prev) { - if (l_prev) { - s_node_ptr l_next = l_prev->next (); - d_prev = l_prev; - d_next = l_next; - l_next->prev (this); - l_prev->next (this); - } else - d_prev = d_next = this; - }; - - __INLINE__ T object () { return (d_object); }; - __INLINE__ void object (T l_object) { d_object = l_object; }; - __INLINE__ bool available () { return (d_available); }; - __INLINE__ void set_available () { d_available = TRUE; }; - __INLINE__ void set_available (bool l_avail) { d_available = l_avail; }; - __INLINE__ void set_not_available () { d_available = FALSE; }; - __INLINE__ s_node_ptr next () { return (d_next); }; - __INLINE__ s_node_ptr prev () { return (d_prev); }; - __INLINE__ s_both* both () { return (d_both); }; - __INLINE__ void next (s_node_ptr l_next) { d_next = l_next; }; - __INLINE__ void prev (s_node_ptr l_prev) { d_prev = l_prev; }; - __INLINE__ void both (s_both* l_both) { d_both = l_both; }; -}; - -template class circular_linked_list { - typedef s_node* s_node_ptr; - -private: - s_node_ptr d_current, d_iterate, d_available, d_inUse; - UInt32 d_n_nodes, d_n_used; - mld_mutex_ptr d_internal; - mld_condition_ptr d_ioBlock; - -public: - circular_linked_list (UInt32 n_nodes) { - if (n_nodes == 0) - throw std::runtime_error ("circular_linked_list(): n_nodes == 0"); - - d_iterate = NULL; - d_n_nodes = n_nodes; - d_n_used = 0; - s_node_ptr l_prev, l_next; - d_inUse = d_current = l_next = l_prev = NULL; - - l_prev = new s_node (); - l_prev->set_available (); - l_prev->next (l_prev); - l_prev->prev (l_prev); - if (n_nodes > 1) { - l_next = new s_node (l_prev, l_prev); - l_next->set_available (); - l_next->next (l_prev); - l_next->prev (l_prev); - l_prev->next (l_next); - l_prev->prev (l_next); - if (n_nodes > 2) { - UInt32 n = n_nodes - 2; - while (n-- > 0) { - d_current = new s_node (l_prev, l_next); - d_current->set_available (); - d_current->prev (l_prev); - d_current->next (l_next); - l_prev->next (d_current); - l_next->prev (d_current); - l_next = d_current; - d_current = NULL; - } - } - } - d_available = d_current = l_prev; - d_internal = new mld_mutex (); - d_ioBlock = new mld_condition (); - }; - - ~circular_linked_list () { - iterate_start (); - s_node_ptr l_node = iterate_next (); - while (l_node) { - delete l_node; - l_node = iterate_next (); - } - delete d_internal; - d_internal = NULL; - delete d_ioBlock; - d_ioBlock = NULL; - d_available = d_inUse = d_iterate = d_current = NULL; - d_n_used = d_n_nodes = 0; - }; - - s_node_ptr find_next_available_node () { - d_internal->lock (); -// find an available node - s_node_ptr l_node = d_available; - while (! l_node) { - d_internal->unlock (); - d_ioBlock->wait (); - d_internal->lock (); - l_node = d_available; - } -// fprintf (stderr, "::f_n_a_n: #u = %ld, node = %p\n", num_used(), l_node); -// remove this one from the current available list - if (num_available () == 1) { -// last one, just set available to NULL - d_available = NULL; - } else - d_available = l_node->next (); - l_node->remove (); -// add is to the inUse list - if (! d_inUse) - d_inUse = l_node; - else - l_node->insert_before (d_inUse); - d_n_used++; - l_node->set_not_available (); - d_internal->unlock (); - return (l_node); - }; - - void make_node_available (s_node_ptr l_node) { - if (!l_node) return; - d_internal->lock (); -// fprintf (stderr, "::m_n_a: #u = %ld, node = %p\n", num_used(), l_node); -// remove this node from the inUse list - if (num_used () == 1) { -// last one, just set inUse to NULL - d_inUse = NULL; - } else - d_inUse = l_node->next (); - l_node->remove (); -// add this node to the available list - if (! d_available) - d_available = l_node; - else - l_node->insert_before (d_available); - d_n_used--; -// signal the condition when new data arrives - d_ioBlock->signal (); -// unlock the mutex for thread safety - d_internal->unlock (); - }; - - __INLINE__ void iterate_start () { d_iterate = d_current; }; - - s_node_ptr iterate_next () { -#if 0 -// lock the mutex for thread safety - d_internal->lock (); -#endif - s_node_ptr l_this = NULL; - if (d_iterate) { - l_this = d_iterate; - d_iterate = d_iterate->next (); - if (d_iterate == d_current) - d_iterate = NULL; - } -#if 0 -// unlock the mutex for thread safety - d_internal->unlock (); -#endif - return (l_this); - }; - - __INLINE__ T object () { return (d_current->d_object); }; - __INLINE__ void object (T l_object) { d_current->d_object = l_object; }; - __INLINE__ UInt32 num_nodes () { return (d_n_nodes); }; - __INLINE__ UInt32 num_used () { return (d_n_used); }; - __INLINE__ void num_used (UInt32 l_n_used) { d_n_used = l_n_used; }; - __INLINE__ UInt32 num_available () { return (d_n_nodes - d_n_used); }; - __INLINE__ void num_used_inc (void) { - if (d_n_used < d_n_nodes) ++d_n_used; - }; - __INLINE__ void num_used_dec (void) { - if (d_n_used != 0) --d_n_used; -// signal the condition that new data has arrived - d_ioBlock->signal (); - }; - __INLINE__ bool in_use () { return (d_n_used != 0); }; -}; - -template class s_both -{ -private: - s_node* d_node; - void* d_this; -public: - __INLINE__ s_both (s_node* l_node, void* l_this) - : d_node (l_node), d_this (l_this) {}; - __INLINE__ ~s_both () {}; - __INLINE__ s_node* node () { return (d_node); }; - __INLINE__ void* This () { return (d_this); }; - __INLINE__ void set (s_node* l_node, void* l_this) { - d_node = l_node; d_this = l_this;}; -}; - -#endif /* _CIRCULAR_LINKED_LIST_H_ */ diff --git a/usrp/host/lib/darwin_libusb.h b/usrp/host/lib/darwin_libusb.h deleted file mode 100644 index bdbdd344..00000000 --- a/usrp/host/lib/darwin_libusb.h +++ /dev/null @@ -1,190 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -/* - * The following code was taken from LIBUSB verion 0.1.10a, - * and makes the fusb_darwin codes do-able in the current GR - * programming framework. Parts and pieces were taken from - * usbi.h, darwin.c, and error.h . - * - * LIBUSB version 0.1.10a is covered by the LGPL, version 2; - * These codes are used with permission from: - * (c) 2000-2003 Johannes Erdfelt - * (c) 2002-2005 Nathan Hjelm - * All rights reserved. - */ - -#ifndef __DARWIN_LIBUSB_H__ -#define __DARWIN_LIBUSB_H__ - -#include -#include -#include -#include - -extern "C" { -static char * -darwin_error_str (int result) -{ - switch (result) { - case kIOReturnSuccess: - return "no error"; - case kIOReturnNotOpen: - return "device not opened for exclusive access"; - case kIOReturnNoDevice: - return "no connection to an IOService"; - case kIOUSBNoAsyncPortErr: - return "no asyc port has been opened for interface"; - case kIOReturnExclusiveAccess: - return "another process has device opened for exclusive access"; - case kIOUSBPipeStalled: - return "pipe is stalled"; - case kIOReturnError: - return "could not establish a connection to Darin kernel"; - case kIOReturnBadArgument: - return "invalid argument"; - default: - return "unknown error"; - } -} - -/* not a valid errorno outside darwin.c */ -#define LUSBDARWINSTALL (ELAST+1) - -static int -darwin_to_errno (int result) -{ - switch (result) { - case kIOReturnSuccess: - return 0; - case kIOReturnNotOpen: - return EBADF; - case kIOReturnNoDevice: - case kIOUSBNoAsyncPortErr: - return ENXIO; - case kIOReturnExclusiveAccess: - return EBUSY; - case kIOUSBPipeStalled: - return LUSBDARWINSTALL; - case kIOReturnBadArgument: - return EINVAL; - case kIOReturnError: - default: - return 1; - } -} - -typedef enum { - USB_ERROR_TYPE_NONE = 0, - USB_ERROR_TYPE_STRING, - USB_ERROR_TYPE_ERRNO, -} usb_error_type_t; - -extern char usb_error_str[1024]; -extern int usb_error_errno; -extern usb_error_type_t usb_error_type; - -#define USB_ERROR(r, x) \ - do { \ - usb_error_type = USB_ERROR_TYPE_ERRNO; \ - usb_error_errno = x; \ - return r; \ - } while (0) - -#define USB_ERROR_STR(r, x, format, args...) \ - do { \ - usb_error_type = USB_ERROR_TYPE_STRING; \ - snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ - if (usb_debug) \ - fprintf(stderr, "USB error: %s\n", usb_error_str); \ - return r; \ - } while (0) - -#define USB_ERROR_STR_ORIG(x, format, args...) \ - do { \ - usb_error_type = USB_ERROR_TYPE_STRING; \ - snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ - if (usb_debug) \ - fprintf(stderr, "USB error: %s\n", usb_error_str); \ - return x; \ - } while (0) - -#define USB_ERROR_STR_NO_RET(x, format, args...) \ - do { \ - usb_error_type = USB_ERROR_TYPE_STRING; \ - snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ - if (usb_debug) \ - fprintf(stderr, "USB error: %s\n", usb_error_str); \ - } while (0) - -/* simple function that figures out what pipeRef is associated with an endpoint */ -static int ep_to_pipeRef (darwin_dev_handle *device, int ep) -{ - io_return_t ret; - UInt8 numep, direction, number; - UInt8 dont_care1, dont_care3; - UInt16 dont_care2; - int i; - - if (usb_debug > 3) - fprintf(stderr, "Converting ep address to pipeRef.\n"); - - /* retrieve the total number of endpoints on this interface */ - ret = (*(device->interface))->GetNumEndpoints(device->interface, &numep); - if ( ret ) { - if ( usb_debug > 3 ) - fprintf ( stderr, "ep_to_pipeRef: interface is %p\n", device->interface ); - USB_ERROR_STR_ORIG ( -ret, "ep_to_pipeRef: can't get number of endpoints for interface" ); - } - - /* iterate through the pipeRefs until we find the correct one */ - for (i = 1 ; i <= numep ; i++) { - ret = (*(device->interface))->GetPipeProperties(device->interface, i, &direction, &number, - &dont_care1, &dont_care2, &dont_care3); - - if (ret != kIOReturnSuccess) { - fprintf (stderr, "ep_to_pipeRef: an error occurred getting pipe information on pipe %d\n", - i ); - USB_ERROR_STR_ORIG (-darwin_to_errno(ret), "ep_to_pipeRef(GetPipeProperties): %s", darwin_error_str(ret)); - } - - if (usb_debug > 3) - fprintf (stderr, "ep_to_pipeRef: Pipe %i: DIR: %i number: %i\n", i, direction, number); - - /* calculate the endpoint of the pipe and check it versus the requested endpoint */ - if ( ((direction << 7 & USB_ENDPOINT_DIR_MASK) | (number & USB_ENDPOINT_ADDRESS_MASK)) == ep ) { - if (usb_debug > 3) - fprintf(stderr, "ep_to_pipeRef: pipeRef for ep address 0x%02x found: 0x%02x\n", ep, i); - - return i; - } - } - - if (usb_debug > 3) - fprintf(stderr, "ep_to_pipeRef: No pipeRef found with endpoint address 0x%02x.\n", ep); - - /* none of the found pipes match the requested endpoint */ - return -1; -} - -} -#endif /* __DARWIN_LIBUSB_H__ */ diff --git a/usrp/host/lib/dump_data.py b/usrp/host/lib/dump_data.py deleted file mode 100755 index 34dde3dc..00000000 --- a/usrp/host/lib/dump_data.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2003 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. - -import sys -import struct - -fin = sys.stdin - -count = 0 - -while 1: - s = fin.read(2) - if not s or len(s) != 2: - break - - v, = struct.unpack ('H', s) - iv = int(v) & 0xffff - print "%8d %6d 0x%04x" % (count, iv, iv) - count += 1 - - - diff --git a/usrp/host/lib/dxc-io-assignments.gnumeric b/usrp/host/lib/dxc-io-assignments.gnumeric deleted file mode 100644 index 85e1a881..00000000 Binary files a/usrp/host/lib/dxc-io-assignments.gnumeric and /dev/null differ diff --git a/usrp/host/lib/fusb.cc b/usrp/host/lib/fusb.cc deleted file mode 100644 index 01ced9e2..00000000 --- a/usrp/host/lib/fusb.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - - -// ------------------------------------------------------------------------ -// device handle -// ------------------------------------------------------------------------ - -fusb_devhandle::fusb_devhandle (usb_dev_handle *udh) - : d_udh (udh) -{ - // that's it -}; - -fusb_devhandle::~fusb_devhandle () -{ - // nop -} - -// ------------------------------------------------------------------------ -// end point handle -// ------------------------------------------------------------------------ - -fusb_ephandle::fusb_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) - : d_endpoint (endpoint), d_input_p (input_p), - d_block_size (block_size), d_nblocks (nblocks), d_started (false) -{ - // that't it -} - -fusb_ephandle::~fusb_ephandle () -{ - // nop -} diff --git a/usrp/host/lib/fusb.h b/usrp/host/lib/fusb.h deleted file mode 100644 index da1b7c0a..00000000 --- a/usrp/host/lib/fusb.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -// Fast USB interface - -#ifndef _FUSB_H_ -#define _FUSB_H_ - - -struct usb_dev_handle; -class fusb_ephandle; - -/*! - * \brief abstract usb device handle - */ -class fusb_devhandle { -private: - // NOT IMPLEMENTED - fusb_devhandle (const fusb_devhandle &rhs); // no copy constructor - fusb_devhandle &operator= (const fusb_devhandle &rhs); // no assignment operator - -protected: - usb_dev_handle *d_udh; - -public: - // CREATORS - fusb_devhandle (usb_dev_handle *udh); - virtual ~fusb_devhandle (); - - // MANIPULATORS - - /*! - * \brief return an ephandle of the correct subtype - */ - virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0) = 0; - - // ACCESSORS - usb_dev_handle *get_usb_dev_handle () const { return d_udh; } -}; - - -/*! - * \brief abstract usb end point handle - */ -class fusb_ephandle { -private: - // NOT IMPLEMENTED - fusb_ephandle (const fusb_ephandle &rhs); // no copy constructor - fusb_ephandle &operator= (const fusb_ephandle &rhs); // no assignment operator - -protected: - int d_endpoint; - bool d_input_p; - int d_block_size; - int d_nblocks; - bool d_started; - -public: - fusb_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle (); - - virtual bool start () = 0; //!< begin streaming i/o - virtual bool stop () = 0; //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void *buffer, int nbytes) = 0; - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void *buffer, int nbytes) = 0; - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion () = 0; - - /*! - * \brief returns current block size. - */ - int block_size () { return d_block_size; }; -}; - - -/*! - * \brief factory for creating concrete instances of the appropriate subtype. - */ -class fusb_sysconfig { -public: - /*! - * \brief returns fusb_devhandle or throws if trouble - */ - static fusb_devhandle *make_devhandle (usb_dev_handle *udh); - - /*! - * \brief returns max block size hard limit - */ - static int max_block_size (); - - /*! - * \brief returns the default buffer size - */ - static int default_buffer_size (); - -}; - -#endif /* _FUSB_H_ */ diff --git a/usrp/host/lib/fusb_darwin.cc b/usrp/host/lib/fusb_darwin.cc deleted file mode 100644 index 8be54537..00000000 --- a/usrp/host/lib/fusb_darwin.cc +++ /dev/null @@ -1,499 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -// tell mld_threads to NOT use omni_threads, -// but rather Darwin's pthreads -#undef _USE_OMNI_THREADS_ - -#include -#include "fusb.h" -#include "fusb_darwin.h" -#include "darwin_libusb.h" - -static const int USB_TIMEOUT = 100; // in milliseconds -static const UInt8 NUM_QUEUE_ITEMS = 20; - -fusb_devhandle_darwin::fusb_devhandle_darwin (usb_dev_handle* udh) - : fusb_devhandle (udh) -{ - // that's it -} - -fusb_devhandle_darwin::~fusb_devhandle_darwin () -{ - // nop -} - -fusb_ephandle* -fusb_devhandle_darwin::make_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) -{ - return new fusb_ephandle_darwin (this, endpoint, input_p, - block_size, nblocks); -} - -// ---------------------------------------------------------------- - -fusb_ephandle_darwin::fusb_ephandle_darwin (fusb_devhandle_darwin* dh, - int endpoint, bool input_p, - int block_size, int nblocks) - : fusb_ephandle (endpoint, input_p, block_size, nblocks), - d_devhandle (dh), d_pipeRef (0), d_transferType (0), - d_interfaceRef (0), d_interface (0), d_queue (0), - d_buffer (0), d_bufLenBytes (0) -{ - d_bufLenBytes = fusb_sysconfig::max_block_size(); - -// create circular buffer - d_buffer = new circular_buffer (NUM_QUEUE_ITEMS * d_bufLenBytes, - !d_input_p, d_input_p); - -// create the queue - d_queue = new circular_linked_list (NUM_QUEUE_ITEMS); - d_queue->iterate_start (); - s_node_ptr l_node = d_queue->iterate_next (); - while (l_node) { - l_node->both (new s_both (l_node, this)); - s_buffer_ptr l_buf = new s_buffer (d_bufLenBytes); - l_node->object (l_buf); - l_node = d_queue->iterate_next (); - l_buf = NULL; - } - - d_readRunning = new mld_mutex (); - d_runThreadRunning = new mld_mutex (); - d_runBlock = new mld_condition (); - d_readBlock = new mld_condition (); -} - -fusb_ephandle_darwin::~fusb_ephandle_darwin () -{ - stop (); - - d_queue->iterate_start (); - s_node_ptr l_node = d_queue->iterate_next (); - while (l_node) { - s_both_ptr l_both = l_node->both (); - delete l_both; - l_both = NULL; - l_node->both (NULL); - s_buffer_ptr l_buf = l_node->object (); - delete l_buf; - l_buf = NULL; - l_node->object (NULL); - l_node = d_queue->iterate_next (); - } - delete d_queue; - d_queue = NULL; - delete d_buffer; - d_buffer = NULL; - delete d_readRunning; - d_readRunning = NULL; - delete d_runThreadRunning; - d_runThreadRunning = NULL; - delete d_runBlock; - d_runBlock = NULL; - delete d_readBlock; - d_readBlock = NULL; -} - -bool -fusb_ephandle_darwin::start () -{ - UInt8 direction, number, interval; - UInt16 maxPacketSize; - -// reset circular buffer - d_buffer->reset (); - -// reset the queue - d_queue->num_used (0); - d_queue->iterate_start (); - s_node_ptr l_node = d_queue->iterate_next (); - while (l_node) { - l_node->both()->set (l_node, this); - l_node->object()->reset (); - l_node->set_available (); - l_node = d_queue->iterate_next (); - } - - d_pipeRef = d_transferType = 0; - - usb_dev_handle* dev = d_devhandle->get_usb_dev_handle (); - if (! dev) - USB_ERROR_STR (false, -ENXIO, "fusb_ephandle_darwin::start: " - "null device"); - - darwin_dev_handle* device = (darwin_dev_handle*) dev->impl_info; - if (! device) - USB_ERROR_STR (false, -ENOENT, "fusb_ephandle_darwin::start: " - "device not initialized"); - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::start: " - "dev = %p, device = %p\n", dev, device); - - d_interfaceRef = device->interface; - if (! d_interfaceRef) - USB_ERROR_STR (false, -EACCES, "fusb_ephandle_darwin::start: " - "interface used without being claimed"); - d_interface = *d_interfaceRef; - -// get read or write pipe info (depends on "d_input_p") - - if (usb_debug > 3) - fprintf (stderr, "fusb_ephandle_darwin::start " - "d_endpoint = %d, d_input_p = %s\n", - d_endpoint, d_input_p ? "TRUE" : "FALSE"); - - int l_endpoint = (d_input_p ? USB_ENDPOINT_IN : USB_ENDPOINT_OUT); - int pipeRef = ep_to_pipeRef (device, d_endpoint | l_endpoint); - if (pipeRef < 0) - USB_ERROR_STR (false, -EINVAL, "fusb_ephandle_darwin::start " - " invalid pipeRef.\n"); - - d_pipeRef = pipeRef; - d_interface->GetPipeProperties (d_interfaceRef, - d_pipeRef, - &direction, - &number, - &d_transferType, - &maxPacketSize, - &interval); - if (usb_debug == 3) - fprintf (stderr, "fusb_ephandle_darwin::start: %s: ep = 0x%02x, " - "pipeRef = %d, d_i = %p, d_iR = %p, if_dir = %d, if_# = %d, " - "if_int = %d, if_maxPS = %d\n", d_input_p ? "read" : "write", - d_endpoint, d_pipeRef, d_interface, d_interfaceRef, direction, - number, interval, maxPacketSize); - -// set global start boolean - d_started = true; - -// create the run thread, which allows OSX to process I/O separately - d_runThread = new mld_thread (run_thread, this); - -// wait until the threads are -really- going - d_runBlock->wait (); - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::start: %s started.\n", - d_input_p ? "read" : "write"); - - return (true); -} - -void -fusb_ephandle_darwin::run_thread (void* arg) -{ - fusb_ephandle_darwin* This = static_cast(arg); - mld_mutex_ptr l_runThreadRunning = This->d_runThreadRunning; - l_runThreadRunning->lock (); - - mld_mutex_ptr l_readRunning = This->d_readRunning; - mld_condition_ptr l_readBlock = This->d_readBlock; - - bool l_input_p = This->d_input_p; - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::run_thread: " - "starting for %s.\n", - l_input_p ? "read" : "write"); - - usb_interface_t** l_interfaceRef = This->d_interfaceRef; - usb_interface_t* l_interface = This->d_interface; - CFRunLoopSourceRef l_cfSource; - -// create async run loop - l_interface->CreateInterfaceAsyncEventSource (l_interfaceRef, &l_cfSource); - CFRunLoopAddSource (CFRunLoopGetCurrent (), l_cfSource, - kCFRunLoopDefaultMode); -// get run loop reference, to allow other threads to stop - This->d_CFRunLoopRef = CFRunLoopGetCurrent (); - - mld_thread_ptr l_rwThread = NULL; - - if (l_input_p) { - l_rwThread = new mld_thread (read_thread, arg); -// wait until the the rwThread is -really- going - l_readBlock->wait (); - } - -// now signal the run condition to release and finish ::start() - This->d_runBlock->signal (); - -// run the loop - CFRunLoopRun (); - - if (l_input_p) { -// wait for read_thread () to finish - l_readRunning->lock (); - l_readRunning->unlock (); - } - -// remove run loop stuff - CFRunLoopRemoveSource (CFRunLoopGetCurrent (), - l_cfSource, kCFRunLoopDefaultMode); - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::run_thread: finished for %s.\n", - l_input_p ? "read" : "write"); - - l_runThreadRunning->unlock (); -} - -void -fusb_ephandle_darwin::read_thread (void* arg) -{ - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::read_thread: starting.\n"); - - fusb_ephandle_darwin* This = static_cast(arg); - - mld_mutex_ptr l_readRunning = This->d_readRunning; - l_readRunning->lock (); - -// signal the read condition from run_thread() to continue - mld_condition_ptr l_readBlock = This->d_readBlock; - l_readBlock->signal (); - - s_queue_ptr l_queue = This->d_queue; - l_queue->iterate_start (); - s_node_ptr l_node = l_queue->iterate_next (); - while (l_node) { - This->read_issue (l_node->both ()); - l_node = l_queue->iterate_next (); - } - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::read_thread: finished.\n"); - - l_readRunning->unlock (); -} - -void -fusb_ephandle_darwin::read_issue (s_both_ptr l_both) -{ - if ((! l_both) || (! d_started)) - return; - -// set the node and buffer from the input "both" - s_node_ptr l_node = l_both->node (); - s_buffer_ptr l_buf = l_node->object (); - void* v_buffer = (void*) l_buf->buffer (); - -// read up to d_bufLenBytes - UInt32 bufLen = d_bufLenBytes; - l_buf->n_used (bufLen); - -// setup system call result - io_return_t result = kIOReturnSuccess; - - if (d_transferType == kUSBInterrupt) -/* This is an interrupt pipe. We can't specify a timeout. */ - result = d_interface->ReadPipeAsync - (d_interfaceRef, d_pipeRef, v_buffer, bufLen, - (IOAsyncCallback1) read_completed, (void*) l_both); - else - result = d_interface->ReadPipeAsyncTO - (d_interfaceRef, d_pipeRef, v_buffer, bufLen, 0, USB_TIMEOUT, - (IOAsyncCallback1) read_completed, (void*) l_both); - - if (result != kIOReturnSuccess) - USB_ERROR_STR_NO_RET (- darwin_to_errno (result), - "fusb_ephandle_darwin::read_issue " - "(ReadPipeAsync%s): %s", - d_transferType == kUSBInterrupt ? "" : "TO", - darwin_error_str (result)); -} - -void -fusb_ephandle_darwin::read_completed (void* refCon, - io_return_t result, - void* io_size) -{ - UInt32 l_size = (UInt32) io_size; - s_both_ptr l_both = static_cast(refCon); - fusb_ephandle_darwin* This = static_cast(l_both->This ()); - s_node_ptr l_node = l_both->node (); - circular_buffer* l_buffer = This->d_buffer; - s_buffer_ptr l_buf = l_node->object (); - UInt32 l_i_size = l_buf->n_used (); - - if (This->d_started && (l_i_size != l_size)) - fprintf (stderr, "fusb_ephandle_darwin::read_completed: " - "Expected %ld bytes; read %ld.\n", - l_i_size, l_size); - -// add this read to the transfer buffer - if (l_buffer->enqueue (l_buf->buffer (), l_size) == -1) { - fputs ("iU", stderr); - fflush (stderr); - } - -// set buffer's # data to 0 - l_buf->n_used (0); - -// issue another read for this "both" - This->read_issue (l_both); -} - -int -fusb_ephandle_darwin::read (void* buffer, int nbytes) -{ - UInt32 l_nbytes = (UInt32) nbytes; - d_buffer->dequeue ((char*) buffer, &l_nbytes); - return ((int) l_nbytes); -} - -int -fusb_ephandle_darwin::write (const void* buffer, int nbytes) -{ - UInt32 l_nbytes = (UInt32) nbytes; - - if (! d_started) return (0); - - while (l_nbytes != 0) { -// find out how much data to copy; limited to "d_bufLenBytes" per node - UInt32 t_nbytes = (l_nbytes > d_bufLenBytes) ? d_bufLenBytes : l_nbytes; - -// get next available node to write into; -// blocks internally if none available - s_node_ptr l_node = d_queue->find_next_available_node (); - -// copy the input into the node's buffer - s_buffer_ptr l_buf = l_node->object (); - l_buf->buffer ((char*) buffer, t_nbytes); - void* v_buffer = (void*) l_buf->buffer (); - -// setup callback parameter & system call return - s_both_ptr l_both = l_node->both (); - io_return_t result = kIOReturnSuccess; - - if (d_transferType == kUSBInterrupt) -/* This is an interrupt pipe ... can't specify a timeout. */ - result = d_interface->WritePipeAsync - (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes, - (IOAsyncCallback1) write_completed, (void*) l_both); - else - result = d_interface->WritePipeAsyncTO - (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes, 0, USB_TIMEOUT, - (IOAsyncCallback1) write_completed, (void*) l_both); - - if (result != kIOReturnSuccess) - USB_ERROR_STR (-1, - darwin_to_errno (result), - "fusb_ephandle_darwin::write_thread " - "(WritePipeAsync%s): %s", - d_transferType == kUSBInterrupt ? "" : "TO", - darwin_error_str (result)); - l_nbytes -= t_nbytes; - } - - return (nbytes); -} - -void -fusb_ephandle_darwin::write_completed (void* refCon, - io_return_t result, - void* io_size) -{ - s_both_ptr l_both = static_cast(refCon); - fusb_ephandle_darwin* This = static_cast(l_both->This ()); - UInt32 l_size = (UInt32) io_size; - s_node_ptr l_node = l_both->node (); - s_queue_ptr l_queue = This->d_queue; - s_buffer_ptr l_buf = l_node->object (); - UInt32 l_i_size = l_buf->n_used (); - - if (This->d_started && (l_i_size != l_size)) - fprintf (stderr, "fusb_ephandle_darwin::write_completed: " - "Expected %ld bytes written; wrote %ld.\n", - l_i_size, l_size); - -// set buffer's # data to 0 - l_buf->n_used (0); -// make the node available for reuse - l_queue->make_node_available (l_node); -} - -void -fusb_ephandle_darwin::abort () -{ - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::abort: starting.\n"); - - io_return_t result = d_interface->AbortPipe (d_interfaceRef, d_pipeRef); - - if (result != kIOReturnSuccess) - USB_ERROR_STR_NO_RET (- darwin_to_errno (result), - "fusb_ephandle_darwin::abort " - "(AbortPipe): %s", darwin_error_str (result)); - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::abort: finished.\n"); -} - -bool -fusb_ephandle_darwin::stop () -{ - if (! d_started) - return (true); - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::stop: stopping %s.\n", - d_input_p ? "read" : "write"); - - d_started = false; - -// abort any pending IO transfers - abort (); - -// wait for write transfer to finish - wait_for_completion (); - -// tell IO buffer to abort any waiting conditions - d_buffer->abort (); - -// stop the run loop - CFRunLoopStop (d_CFRunLoopRef); - -// wait for the runThread to stop - d_runThreadRunning->lock (); - d_runThreadRunning->unlock (); - - if (usb_debug) - fprintf (stderr, "fusb_ephandle_darwin::stop: %s stopped.\n", - d_input_p ? "read" : "write"); - - return (true); -} - -void -fusb_ephandle_darwin::wait_for_completion () -{ - if (d_queue) - while (d_queue->in_use ()) - usleep (1000); -} diff --git a/usrp/host/lib/fusb_darwin.h b/usrp/host/lib/fusb_darwin.h deleted file mode 100644 index 9254c7f7..00000000 --- a/usrp/host/lib/fusb_darwin.h +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _FUSB_DARWIN_H_ -#define _FUSB_DARWIN_H_ - -#include -#include "fusb.h" -#include -#include -#include -#include -#include "circular_linked_list.h" -#include "circular_buffer.h" - -// for MacOS X 10.4.[0-3] -#define usb_interface_t IOUSBInterfaceInterface220 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 -#define InterfaceVersion 220 - -// for MacOS X 10.3.[0-9] and 10.4.[0-3] -#define usb_device_t IOUSBDeviceInterface197 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 -#define DeviceVersion 197 - -extern "C" { -typedef struct usb_dev_handle { - int fd; - - struct usb_bus *bus; - struct usb_device *device; - - int config; - int interface; - int altsetting; - - /* Added by RMT so implementations can store other per-open-device data */ - void *impl_info; -} usb_dev_handle; - -/* Darwin/OS X impl does not use fd field, instead it uses this */ -typedef struct darwin_dev_handle { - usb_device_t** device; - usb_interface_t** interface; - int open; -} darwin_dev_handle; - -typedef IOReturn io_return_t; -typedef IOCFPlugInInterface *io_cf_plugin_ref_t; - -static int ep_to_pipeRef (darwin_dev_handle* device, int ep); -extern int usb_debug; -} - -class s_buffer -{ -private: - char* d_buffer; - UInt32 d_n_used, d_n_alloc; - -public: - inline s_buffer (UInt32 n_alloc = 0) { - d_n_used = 0; - d_n_alloc = n_alloc; - if (n_alloc) { - d_buffer = (char*) new char [n_alloc]; - } else { - d_buffer = 0; - } - }; - inline ~s_buffer () { - if (d_n_alloc) { - delete [] d_buffer; - } - }; - inline UInt32 n_used () { return (d_n_used); }; - inline void n_used (UInt32 bufLen) { - d_n_used = (bufLen > d_n_alloc) ? d_n_alloc : bufLen; }; - inline UInt32 n_alloc () { return (d_n_alloc); }; - void buffer (char* l_buffer, UInt32 bufLen) { - if (bufLen > d_n_alloc) { - fprintf (stderr, "s_buffer::set: Copying only allocated bytes.\n"); - bufLen = d_n_alloc; - } - if (!l_buffer) { - fprintf (stderr, "s_buffer::set: NULL buffer.\n"); - return; - } - bcopy (l_buffer, d_buffer, bufLen); - d_n_used = bufLen; - }; - inline char* buffer () { return (d_buffer); }; - inline void reset () { - bzero (d_buffer, d_n_alloc); - d_n_used = 0; - }; -}; - -typedef s_buffer* s_buffer_ptr; -typedef s_node* s_node_ptr; -typedef circular_linked_list* s_queue_ptr; -typedef s_both* s_both_ptr; - -/*! - * \brief darwin implementation of fusb_devhandle - * - * This is currently identical to the generic implementation - * and is intended as a starting point for whatever magic is - * required to make usb fly. - */ -class fusb_devhandle_darwin : public fusb_devhandle -{ -public: - // CREATORS - fusb_devhandle_darwin (usb_dev_handle* udh); - virtual ~fusb_devhandle_darwin (); - - // MANIPULATORS - virtual fusb_ephandle* make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); -}; - -/*! - * \brief darwin implementation of fusb_ephandle - * - * This is currently identical to the generic implementation - * and is intended as a starting point for whatever magic is - * required to make usb fly. - */ -class fusb_ephandle_darwin : public fusb_ephandle -{ -private: - fusb_devhandle_darwin* d_devhandle; - mld_thread_ptr d_runThread; - mld_mutex_ptr d_runThreadRunning; - - CFRunLoopRef d_CFRunLoopRef; - - static void write_completed (void* ret_io_size, - io_return_t result, - void* io_size); - static void read_completed (void* ret_io_size, - io_return_t result, - void* io_size); - static void run_thread (void* arg); - static void read_thread (void* arg); - - void read_issue (s_both_ptr l_both); - -public: - // variables, for now - UInt8 d_pipeRef, d_transferType; - usb_interface_t** d_interfaceRef; - usb_interface_t* d_interface; - s_queue_ptr d_queue; - circular_buffer* d_buffer; - UInt32 d_bufLenBytes; - mld_mutex_ptr d_readRunning; - mld_condition_ptr d_runBlock, d_readBlock; - -// CREATORS - - fusb_ephandle_darwin (fusb_devhandle_darwin *dh, int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle_darwin (); - -// MANIPULATORS - - virtual bool start (); //!< begin streaming i/o - virtual bool stop (); //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void* buffer, int nbytes); - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void* buffer, int nbytes); - - /* - * abort any pending IO transfers - */ - void abort (); - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion (); -}; - -#endif /* _FUSB_DARWIN_H_ */ diff --git a/usrp/host/lib/fusb_generic.cc b/usrp/host/lib/fusb_generic.cc deleted file mode 100644 index f9f3c29d..00000000 --- a/usrp/host/lib/fusb_generic.cc +++ /dev/null @@ -1,108 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - - -static const int USB_TIMEOUT = 1000; // in milliseconds - - -fusb_devhandle_generic::fusb_devhandle_generic (usb_dev_handle *udh) - : fusb_devhandle (udh) -{ - // that's it -} - -fusb_devhandle_generic::~fusb_devhandle_generic () -{ - // nop -} - -fusb_ephandle * -fusb_devhandle_generic::make_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) -{ - return new fusb_ephandle_generic (this, endpoint, input_p, - block_size, nblocks); -} - -// ---------------------------------------------------------------- - -fusb_ephandle_generic::fusb_ephandle_generic (fusb_devhandle_generic *dh, - int endpoint, bool input_p, - int block_size, int nblocks) - : fusb_ephandle (endpoint, input_p, block_size, nblocks), - d_devhandle (dh) -{ - // that's it -} - -fusb_ephandle_generic::~fusb_ephandle_generic () -{ - // nop -} - -bool -fusb_ephandle_generic::start () -{ - d_started = true; - return true; -} - -bool -fusb_ephandle_generic::stop () -{ - d_started = false; - return true; -} - -int -fusb_ephandle_generic::write (const void *buffer, int nbytes) -{ - if (!d_started) // doesn't matter here, but keeps semantics constant - return -1; - - if (d_input_p) - return -1; - - return usb_bulk_write (d_devhandle->get_usb_dev_handle (), - d_endpoint, (char *) buffer, nbytes, USB_TIMEOUT); -} - -int -fusb_ephandle_generic::read (void *buffer, int nbytes) -{ - if (!d_started) // doesn't matter here, but keeps semantics constant - return -1; - - if (!d_input_p) - return -1; - - return usb_bulk_read (d_devhandle->get_usb_dev_handle (), - d_endpoint|USB_ENDPOINT_IN, (char *) buffer, nbytes, - USB_TIMEOUT); -} diff --git a/usrp/host/lib/fusb_generic.h b/usrp/host/lib/fusb_generic.h deleted file mode 100644 index 8c3a4564..00000000 --- a/usrp/host/lib/fusb_generic.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _FUSB_GENERIC_H_ -#define _FUSB_GENERIC_H_ - -#include - -/*! - * \brief generic implementation of fusb_devhandle using only libusb - */ -class fusb_devhandle_generic : public fusb_devhandle -{ -public: - // CREATORS - fusb_devhandle_generic (usb_dev_handle *udh); - virtual ~fusb_devhandle_generic (); - - // MANIPULATORS - virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); -}; - - -/*! - * \brief generic implementation of fusb_ephandle using only libusb - */ -class fusb_ephandle_generic : public fusb_ephandle -{ -private: - fusb_devhandle_generic *d_devhandle; - -public: - // CREATORS - fusb_ephandle_generic (fusb_devhandle_generic *dh, int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle_generic (); - - // MANIPULATORS - - virtual bool start (); //!< begin streaming i/o - virtual bool stop (); //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void *buffer, int nbytes); - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void *buffer, int nbytes); - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion () { }; -}; - -#endif /* _FUSB_GENERIC_H_ */ - diff --git a/usrp/host/lib/fusb_linux.cc b/usrp/host/lib/fusb_linux.cc deleted file mode 100644 index 1eaba4e5..00000000 --- a/usrp/host/lib/fusb_linux.cc +++ /dev/null @@ -1,686 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include // libusb header -#include -#ifdef HAVE_LINUX_COMPILER_H -#include -#endif -#include // interface to kernel portion of user mode usb driver -#include -#include -#include -#include -#include -#include - -#define MINIMIZE_TX_BUFFERING 1 // must be defined to 0 or 1 - - -static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); // hard limit -static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE; -static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20); // 4 MB / endpoint - - -// Totally evil and fragile extraction of file descriptor from -// guts of libusb. They don't install usbi.h, which is what we'd need -// to do this nicely. -// -// FIXME if everything breaks someday in the future, look here... - -static int -fd_from_usb_dev_handle (usb_dev_handle *udh) -{ - return *((int *) udh); -} - -inline static void -urb_set_ephandle (usbdevfs_urb *urb, fusb_ephandle_linux *handle) -{ - urb->usercontext = handle; -} - -inline static fusb_ephandle_linux * -urb_get_ephandle (usbdevfs_urb *urb) -{ - return (fusb_ephandle_linux *) urb->usercontext; -} - -// ------------------------------------------------------------------------ -// USB request block (urb) allocation -// ------------------------------------------------------------------------ - -static usbdevfs_urb * -alloc_urb (fusb_ephandle_linux *self, int buffer_length, int endpoint, - bool input_p, unsigned char *write_buffer) -{ - usbdevfs_urb *urb = new usbdevfs_urb; - memset (urb, 0, sizeof (*urb)); - - urb->buffer_length = buffer_length; - - // We allocate dedicated memory only for input buffers. - // For output buffers we reuse the same buffer (the kernel - // copies the data at submital time) - - if (input_p) - urb->buffer = new unsigned char [buffer_length]; - else - urb->buffer = write_buffer; - - // init common values - - urb->type = USBDEVFS_URB_TYPE_BULK; - urb->endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0); - - // USBDEVFS_URB_QUEUE_BULK goes away in linux 2.5, but is needed if - // we are using a 2.4 usb-uhci host controller driver. This is - // unlikely since we're almost always going to be plugged into a - // high speed host controller (ehci) -#if 0 && defined (USBDEVFS_URB_QUEUE_BULK) - urb->flags = USBDEVFS_URB_QUEUE_BULK; -#endif - - urb->signr = 0; - urb_set_ephandle (urb, self); - - return urb; -} - -static void -free_urb (usbdevfs_urb *urb) -{ - // if this was an input urb, free the buffer - if (urb->endpoint & 0x80) - delete [] ((unsigned char *) urb->buffer); - - delete urb; -} - -// ------------------------------------------------------------------------ -// device handle -// ------------------------------------------------------------------------ - -fusb_devhandle_linux::fusb_devhandle_linux (usb_dev_handle *udh) - : fusb_devhandle (udh) -{ - // that's all -} - -fusb_devhandle_linux::~fusb_devhandle_linux () -{ - // if there are any pending requests, cancel them and free the urbs. - - std::list::reverse_iterator it; - - for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){ - _cancel_urb (*it); - free_urb (*it); - } -} - -fusb_ephandle * -fusb_devhandle_linux::make_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) -{ - return new fusb_ephandle_linux (this, endpoint, input_p, - block_size, nblocks); -} - - -// Attempt to cancel all transactions associated with eph. - -void -fusb_devhandle_linux::_cancel_pending_rqsts (fusb_ephandle_linux *eph) -{ - std::list::reverse_iterator it; - - for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){ - if (urb_get_ephandle (*it) == eph) - _cancel_urb (*it); - } -} - -void -fusb_devhandle_linux::pending_add (usbdevfs_urb *urb) -{ - d_pending_rqsts.push_back (urb); -} - -usbdevfs_urb * -fusb_devhandle_linux::pending_get () -{ - if (d_pending_rqsts.empty ()) - return 0; - - usbdevfs_urb *urb = d_pending_rqsts.front (); - d_pending_rqsts.pop_front (); - return urb; -} - -bool -fusb_devhandle_linux::pending_remove (usbdevfs_urb *urb) -{ - std::list::iterator result = find (d_pending_rqsts.begin (), - d_pending_rqsts.end (), - urb); - if (result == d_pending_rqsts.end ()){ - fprintf (stderr, "fusb::pending_remove: failed to find urb in pending_rqsts: %p\n", urb); - return false; - } - d_pending_rqsts.erase (result); - return true; -} - -/* - * Submit the urb to the kernel. - * iff successful, the urb will be placed on the devhandle's pending list. - */ -bool -fusb_devhandle_linux::_submit_urb (usbdevfs_urb *urb) -{ - int ret; - - ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_SUBMITURB, urb); - if (ret < 0){ - perror ("fusb::_submit_urb"); - return false; - } - - pending_add (urb); - return true; -} - -/* - * Attempt to cancel the in pending or in-progress urb transaction. - * Return true iff transaction was sucessfully cancelled. - * - * Failure to cancel should not be considered a problem. This frequently - * occurs if the transaction has already completed in the kernel but hasn't - * yet been reaped by the user mode code. - * - * urbs which were cancelled have their status field set to -ENOENT when - * they are reaped. - */ -bool -fusb_devhandle_linux::_cancel_urb (usbdevfs_urb *urb) -{ - int ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_DISCARDURB, urb); - if (ret < 0){ - // perror ("fusb::_cancel_urb"); - return false; - } - return true; -} - -/* - * Check with the kernel and see if any of our outstanding requests - * have completed. For each completed transaction, remove it from the - * devhandle's pending list and append it to the completed list for - * the corresponding endpoint. - * - * If any transactions are reaped return true. - * - * If ok_to_block_p is true, then this will block until at least one - * transaction completes. - */ -bool -fusb_devhandle_linux::_reap (bool ok_to_block_p) -{ - int ret; - int nreaped = 0; - usbdevfs_urb *urb = 0; - - int fd = fd_from_usb_dev_handle (d_udh); - - // try to reap as many as possible without blocking... - - while ((ret = ioctl (fd, USBDEVFS_REAPURBNDELAY, &urb)) == 0){ - if (urb->status != 0 && urb->status != -ENOENT){ - fprintf (stderr, "_reap: usb->status = %d, actual_length = %5d\n", - urb->status, urb->actual_length); - } - pending_remove (urb); - urb_get_ephandle (urb)->completed_list_add (urb); - nreaped++; - } - - if (nreaped > 0) // if we got any, return w/o blocking - return true; - - if (!ok_to_block_p) - return false; - - ret = ioctl (fd, USBDEVFS_REAPURB, &urb); - if (ret < 0){ - perror ("fusb::_reap"); - return false; - } - - pending_remove (urb); - urb_get_ephandle (urb)->completed_list_add (urb); - return true; -} - -void -fusb_devhandle_linux::_wait_for_completion () -{ - while (!d_pending_rqsts.empty ()) - _reap (true); -} - // ------------------------------------------------------------------------ -// end point handle -// ------------------------------------------------------------------------ - -fusb_ephandle_linux::fusb_ephandle_linux (fusb_devhandle_linux *devhandle, - int endpoint, - bool input_p, - int block_size, int nblocks) - : fusb_ephandle (endpoint, input_p, block_size, nblocks), - d_devhandle (devhandle), - d_write_work_in_progress (0), d_write_buffer (0), - d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0) -{ - - if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE) - throw std::out_of_range ("fusb_ephandle_linux: block_size"); - - if (d_nblocks < 0) - throw std::out_of_range ("fusb_ephandle_linux: nblocks"); - - if (d_block_size == 0) - d_block_size = DEFAULT_BLOCK_SIZE; - - if (d_nblocks == 0) - d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size); - - if (!d_input_p) - if (!MINIMIZE_TX_BUFFERING) - d_write_buffer = new unsigned char [d_block_size]; - - if (0) - fprintf(stderr, "fusb_ephandle_linux::ctor: d_block_size = %d d_nblocks = %d\n", - d_block_size, d_nblocks); - - // allocate urbs - - for (int i = 0; i < d_nblocks; i++) - d_free_list.push_back (alloc_urb (this, d_block_size, d_endpoint, - d_input_p, d_write_buffer)); -} - -fusb_ephandle_linux::~fusb_ephandle_linux () -{ - stop (); - - usbdevfs_urb *urb; - - while ((urb = free_list_get ()) != 0) - free_urb (urb); - - while ((urb = completed_list_get ()) != 0) - free_urb (urb); - - if (d_write_work_in_progress) - free_urb (d_write_work_in_progress); - - delete [] d_write_buffer; - - if (d_read_work_in_progress) - free_urb (d_read_work_in_progress); -} - -// ---------------------------------------------------------------- - -bool -fusb_ephandle_linux::start () -{ - if (d_started) - return true; // already running - - d_started = true; - - if (d_input_p){ // fire off all the reads - usbdevfs_urb *urb; - - int nerrors = 0; - while ((urb = free_list_get ()) != 0 && nerrors < d_nblocks){ - if (!submit_urb (urb)) - nerrors++; - } - } - - return true; -} - -// -// kill all i/o in progress. -// kill any completed but unprocessed transactions. -// -bool -fusb_ephandle_linux::stop () -{ - if (!d_started) - return true; - - d_devhandle->_cancel_pending_rqsts (this); - d_devhandle->_reap (false); - - - usbdevfs_urb *urb; - while ((urb = completed_list_get ()) != 0) - free_list_add (urb); - - if (d_write_work_in_progress){ - free_list_add (d_write_work_in_progress); - d_write_work_in_progress = 0; - } - - if (d_read_work_in_progress){ - free_list_add (d_read_work_in_progress); - d_read_work_in_progress = 0; - d_read_buffer = 0; - d_read_buffer_end = 0; - } - - if (d_free_list.size () != (unsigned) d_nblocks) - fprintf (stderr, "d_free_list.size () = %d, d_nblocks = %d\n", - d_free_list.size (), d_nblocks); - - assert (d_free_list.size () == (unsigned) d_nblocks); - - d_started = false; - return true; -} - -// ---------------------------------------------------------------- -// routines for writing -// ---------------------------------------------------------------- - -#if (MINIMIZE_TX_BUFFERING) - -int -fusb_ephandle_linux::write(const void *buffer, int nbytes) -{ - if (!d_started) - return -1; - - if (d_input_p) - return -1; - - assert(nbytes % 512 == 0); - - unsigned char *src = (unsigned char *) buffer; - - int n = 0; - while (n < nbytes){ - - usbdevfs_urb *urb = get_write_work_in_progress(); - assert(urb->actual_length == 0); - int m = std::min(nbytes - n, MAX_BLOCK_SIZE); - urb->buffer = src; - urb->buffer_length = m; - - n += m; - src += m; - - if (!submit_urb(urb)) - return -1; - - d_write_work_in_progress = 0; - } - - return n; -} - -#else - -int -fusb_ephandle_linux::write (const void *buffer, int nbytes) -{ - if (!d_started) - return -1; - - if (d_input_p) - return -1; - - unsigned char *src = (unsigned char *) buffer; - - int n = 0; - while (n < nbytes){ - - usbdevfs_urb *urb = get_write_work_in_progress (); - unsigned char *dst = (unsigned char *) urb->buffer; - int m = std::min (nbytes - n, urb->buffer_length - urb->actual_length); - - memcpy (&dst[urb->actual_length], &src[n], m); - urb->actual_length += m; - n += m; - - if (urb->actual_length == urb->buffer_length){ - if (!submit_urb (urb)) - return -1; - d_write_work_in_progress = 0; - } - } - - return n; -} - -#endif - -usbdevfs_urb * -fusb_ephandle_linux::get_write_work_in_progress () -{ - // if we've already got some work in progress, return it - - if (d_write_work_in_progress) - return d_write_work_in_progress; - - while (1){ - - reap_complete_writes (); - - usbdevfs_urb *urb = free_list_get (); - - if (urb != 0){ - assert (urb->actual_length == 0); - d_write_work_in_progress = urb; - return urb; - } - - // The free list is empty. Tell the device handle to reap. - // Anything it reaps for us will end up on our completed list. - - d_devhandle->_reap (true); - } -} - -void -fusb_ephandle_linux::reap_complete_writes () -{ - // take a look at the completed_list and xfer to free list after - // checking for errors. - - usbdevfs_urb *urb; - - while ((urb = completed_list_get ()) != 0){ - - // Check for any errors or short writes that were reported in the urb. - // The kernel sets status, actual_length and error_count. - // error_count is only used for ISO xfers. - // status is 0 if successful, else is an errno kind of thing - - if (urb->status != 0){ - fprintf (stderr, "fusb: (status %d) %s\n", urb->status, strerror (-urb->status)); - } - else if (urb->actual_length != urb->buffer_length){ - fprintf (stderr, "fusb: short write xfer: %d != %d\n", - urb->actual_length, urb->buffer_length); - } - - free_list_add (urb); - } -} - -void -fusb_ephandle_linux::wait_for_completion () -{ - d_devhandle->_wait_for_completion (); -} - -// ---------------------------------------------------------------- -// routines for reading -// ---------------------------------------------------------------- - -int -fusb_ephandle_linux::read (void *buffer, int nbytes) -{ - if (!d_started) - return -1; - - if (!d_input_p) - return -1; - - unsigned char *dst = (unsigned char *) buffer; - - int n = 0; - while (n < nbytes){ - - if (d_read_buffer >= d_read_buffer_end) - if (!reload_read_buffer ()) - return -1; - - int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer)); - - memcpy (&dst[n], d_read_buffer, m); - d_read_buffer += m; - n += m; - } - - return n; -} - -bool -fusb_ephandle_linux::reload_read_buffer () -{ - assert (d_read_buffer >= d_read_buffer_end); - - usbdevfs_urb *urb; - - if (d_read_work_in_progress){ - // We're done with this urb. Fire off a read to refill it. - urb = d_read_work_in_progress; - d_read_work_in_progress = 0; - d_read_buffer = 0; - d_read_buffer_end = 0; - urb->actual_length = 0; - if (!submit_urb (urb)) - return false; - } - - while (1){ - - while ((urb = completed_list_get ()) == 0) - d_devhandle->_reap (true); - - // check result of completed read - - if (urb->status != 0){ - // We've got a problem. - // Report the problem and resubmit. - fprintf (stderr, "fusb: (rd status %d) %s\n", urb->status, strerror (-urb->status)); - urb->actual_length = 0; - if (!submit_urb (urb)) - return false; - - continue; - } - - // we've got a happy urb, full of data... - - d_read_work_in_progress = urb; - d_read_buffer = (unsigned char *) urb->buffer; - d_read_buffer_end = d_read_buffer + urb->actual_length; - - return true; - } -} - -// ---------------------------------------------------------------- - -void -fusb_ephandle_linux::free_list_add (usbdevfs_urb *urb) -{ - assert (urb_get_ephandle (urb) == this); - urb->actual_length = 0; - d_free_list.push_back (urb); -} - -usbdevfs_urb * -fusb_ephandle_linux::free_list_get () -{ - if (d_free_list.empty ()) - return 0; - - usbdevfs_urb *urb = d_free_list.front (); - d_free_list.pop_front (); - return urb; -} - -void -fusb_ephandle_linux::completed_list_add (usbdevfs_urb *urb) -{ - assert (urb_get_ephandle (urb) == this); - d_completed_list.push_back (urb); -} - -usbdevfs_urb * -fusb_ephandle_linux::completed_list_get () -{ - if (d_completed_list.empty ()) - return 0; - - usbdevfs_urb *urb = d_completed_list.front (); - d_completed_list.pop_front (); - return urb; -} - -/* - * Submit the urb. If successful the urb ends up on the devhandle's - * pending list, otherwise, it's back on our free list. - */ -bool -fusb_ephandle_linux::submit_urb (usbdevfs_urb *urb) -{ - if (!d_devhandle->_submit_urb (urb)){ // FIXME record the problem somewhere - fprintf (stderr, "_submit_urb failed\n"); - free_list_add (urb); - return false; - } - return true; -} diff --git a/usrp/host/lib/fusb_linux.h b/usrp/host/lib/fusb_linux.h deleted file mode 100644 index b2d46a51..00000000 --- a/usrp/host/lib/fusb_linux.h +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -// Fast USB interface - -#ifndef _FUSB_LINUX_H_ -#define _FUSB_LINUX_H_ - -#include -#include - -struct usbdevfs_urb; -class fusb_ephandle_linux; - -/*! - * \brief linux specific implementation of fusb_devhandle using usbdevice_fs - */ -class fusb_devhandle_linux : public fusb_devhandle { -private: - std::list d_pending_rqsts; - - void pending_add (usbdevfs_urb *urb); - bool pending_remove (usbdevfs_urb *urb); - usbdevfs_urb * pending_get (); - - -public: - // CREATORS - fusb_devhandle_linux (usb_dev_handle *udh); - virtual ~fusb_devhandle_linux (); - - // MANIPULATORS - virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - - // internal use only - bool _submit_urb (usbdevfs_urb *urb); - bool _cancel_urb (usbdevfs_urb *urb); - void _cancel_pending_rqsts (fusb_ephandle_linux *eph); - bool _reap (bool ok_to_block_p); - void _wait_for_completion (); -}; - - /*! - * \brief linux specific implementation of fusb_ephandle using usbdevice_fs - */ - -class fusb_ephandle_linux : public fusb_ephandle { -private: - fusb_devhandle_linux *d_devhandle; - std::list d_free_list; - std::list d_completed_list; - usbdevfs_urb *d_write_work_in_progress; - unsigned char *d_write_buffer; - usbdevfs_urb *d_read_work_in_progress; - unsigned char *d_read_buffer; - unsigned char *d_read_buffer_end; - - usbdevfs_urb *get_write_work_in_progress (); - void reap_complete_writes (); - bool reload_read_buffer (); - bool submit_urb (usbdevfs_urb *urb); - -public: - fusb_ephandle_linux (fusb_devhandle_linux *dh, int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle_linux (); - - virtual bool start (); //!< begin streaming i/o - virtual bool stop (); //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void *buffer, int nbytes); - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void *buffer, int nbytes); - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion (); - - // internal use only - void free_list_add (usbdevfs_urb *urb); - void completed_list_add (usbdevfs_urb *urb); - usbdevfs_urb *free_list_get (); // pop and return head of list or 0 - usbdevfs_urb *completed_list_get (); // pop and return head of list or 0 -}; - -#endif /* _FUSB_LINUX_H_ */ diff --git a/usrp/host/lib/fusb_ra_wb.cc b/usrp/host/lib/fusb_ra_wb.cc deleted file mode 100644 index c95f4afb..00000000 --- a/usrp/host/lib/fusb_ra_wb.cc +++ /dev/null @@ -1,258 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include -#include -#include - -#include -#include - -static const int USB_TIMEOUT = 1000; // in milliseconds - -// the following comment and function is from fusb_linux.cc -#if 0 -// Totally evil and fragile extraction of file descriptor from -// guts of libusb. They don't install usbi.h, which is what we'd need -// to do this nicely. -// -// FIXME if everything breaks someday in the future, look here... - -static int -fd_from_usb_dev_handle (usb_dev_handle *udh) -{ - return *((int *) udh); -} -#endif - -// the control endpoint doesn't actually do us any good so here is a -// new "fragile extraction" -static int -ep_fd_from_usb_dev_handle (usb_dev_handle *udh, int endpoint) -{ - struct usb_dev_handle_kludge2 { // see also usrp_prims.cc - int fd; - struct usb_bus *bus; - struct usb_device *device; - int config; - int interface; - int altsetting; - void *impl_info; - }; - struct bsd_usb_dev_handle_info_kludge { - int ep_fd[USB_MAX_ENDPOINTS]; - }; - struct bsd_usb_dev_handle_info_kludge *info - = (struct bsd_usb_dev_handle_info_kludge *) - ((struct usb_dev_handle_kludge2 *)udh)->impl_info; - return info->ep_fd[UE_GET_ADDR(endpoint)]; -} - - -fusb_devhandle_ra_wb::fusb_devhandle_ra_wb (usb_dev_handle *udh) - : fusb_devhandle (udh) -{ - // that's it -} - -fusb_devhandle_ra_wb::~fusb_devhandle_ra_wb () -{ - // nop -} - -fusb_ephandle * -fusb_devhandle_ra_wb::make_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) -{ - return new fusb_ephandle_ra_wb (this, endpoint, input_p, - block_size, nblocks); -} - -// ---------------------------------------------------------------- - -fusb_ephandle_ra_wb::fusb_ephandle_ra_wb (fusb_devhandle_ra_wb *dh, - int endpoint, bool input_p, - int block_size, int nblocks) - : fusb_ephandle (endpoint, input_p, block_size, nblocks), - d_devhandle (dh), d_ra_wb_on (false) -{ - // that's it -} - -fusb_ephandle_ra_wb::~fusb_ephandle_ra_wb () -{ - // nop -} - -bool -fusb_ephandle_ra_wb::start () -{ - d_started = true; - - char buf = '\0'; - int fd; - - // this is to cause libusb to open the endpoint - if (!d_input_p) { - write(&buf, 0); - fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), - d_endpoint); - } - else { - read(&buf, 0); - fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), - d_endpoint|USB_ENDPOINT_IN); - } - - // enable read ahead/write behind - int ret; - struct usb_bulk_ra_wb_opt opts; - int enable = 1; - - opts.ra_wb_buffer_size = d_block_size*d_nblocks; - opts.ra_wb_request_size = d_block_size; -// fprintf (stderr, "setting buffer size to %d, request size to %d\n", -// opts.ra_wb_buffer_size, opts.ra_wb_request_size); - if (!d_input_p) { - ret = ioctl (fd, USB_SET_BULK_WB_OPT, &opts); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_WB_OPT: %s\n", strerror(errno)); - else { - ret = ioctl (fd, USB_SET_BULK_WB, &enable); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno)); - else - d_ra_wb_on = true; - } - } - else { - ret = ioctl (fd, USB_SET_BULK_RA_OPT, &opts); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_RA_OPT: %s\n", strerror(errno)); - else { - ret = ioctl (fd, USB_SET_BULK_RA, &enable); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno)); - else - d_ra_wb_on = true; - } - } - - return true; -} - -bool -fusb_ephandle_ra_wb::stop () -{ - int fd; - int ret; - int enable = 0; - if (d_ra_wb_on) { - if (!d_input_p) { - fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), - d_endpoint); - ret = ioctl (fd, USB_SET_BULK_WB, &enable); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno)); - else - d_ra_wb_on = false; - } - else { - fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), - d_endpoint|USB_ENDPOINT_IN); - ret = ioctl (fd, USB_SET_BULK_RA, &enable); - if (ret < 0) - fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno)); - else - d_ra_wb_on = false; - } - } - - d_started = false; - return true; -} - -int -fusb_ephandle_ra_wb::write (const void *buffer, int nbytes) -{ - if (!d_started) - return -1; - - if (d_input_p) - return -1; - - return usb_bulk_write (d_devhandle->get_usb_dev_handle (), - d_endpoint, (char *) buffer, nbytes, USB_TIMEOUT); -} - -int -fusb_ephandle_ra_wb::read (void *buffer, int nbytes) -{ - if (!d_started) - return -1; - - if (!d_input_p) - return -1; - - return usb_bulk_read (d_devhandle->get_usb_dev_handle (), - d_endpoint|USB_ENDPOINT_IN, (char *) buffer, nbytes, - USB_TIMEOUT); -} - -void -fusb_ephandle_ra_wb::wait_for_completion () -{ - // as the driver is implemented this only makes sense for write - if (d_ra_wb_on && !d_input_p) { - int fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), - d_endpoint); - int kq = kqueue(); - if (kq < 0) - return; - struct kevent evt; - int nevents; - EV_SET (&evt, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, 0/*NULL*/); - nevents = kevent (kq, &evt, 1, &evt, 1, NULL); - if (nevents < 1) { - close(kq); - return; - } - while (!(evt.flags & EV_ERROR) && evt.data < (d_block_size*d_nblocks)) { - // it's a busy loop, but that's all I can do at the moment - nevents = kevent (kq, NULL, 0, &evt, 1, NULL); - // let's see if this improves the test_usrp_standard_tx throughput & - // "CPU usage" by looping less frequently - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 1000; // 1 ms - select (0, NULL, NULL, NULL, &timeout); - } - close (kq); - } -} diff --git a/usrp/host/lib/fusb_ra_wb.h b/usrp/host/lib/fusb_ra_wb.h deleted file mode 100644 index 09bf8ee6..00000000 --- a/usrp/host/lib/fusb_ra_wb.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _FUSB_RA_WB_H_ -#define _FUSB_RA_WB_H_ - -#include - -/*! - * \brief generic implementation of fusb_devhandle using only libusb - */ -class fusb_devhandle_ra_wb : public fusb_devhandle -{ -public: - // CREATORS - fusb_devhandle_ra_wb (usb_dev_handle *udh); - virtual ~fusb_devhandle_ra_wb (); - - // MANIPULATORS - virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); -}; - - -/*! - * \brief generic implementation of fusb_ephandle using only libusb - */ -class fusb_ephandle_ra_wb : public fusb_ephandle -{ -private: - fusb_devhandle_ra_wb *d_devhandle; - bool d_ra_wb_on; - -public: - // CREATORS - fusb_ephandle_ra_wb (fusb_devhandle_ra_wb *dh, int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle_ra_wb (); - - // MANIPULATORS - - virtual bool start (); //!< begin streaming i/o - virtual bool stop (); //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void *buffer, int nbytes); - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void *buffer, int nbytes); - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion (); -}; - -#endif /* _FUSB_RA_WB_H_ */ - diff --git a/usrp/host/lib/fusb_sysconfig_darwin.cc b/usrp/host/lib/fusb_sysconfig_darwin.cc deleted file mode 100644 index f66c298d..00000000 --- a/usrp/host/lib/fusb_sysconfig_darwin.cc +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include - -static const int MAX_BLOCK_SIZE = 32 * 1024; // hard limit -static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) - -fusb_devhandle * -fusb_sysconfig::make_devhandle (usb_dev_handle *udh) -{ - return new fusb_devhandle_darwin (udh); -} - -int fusb_sysconfig::max_block_size () -{ - return MAX_BLOCK_SIZE; -} - -int fusb_sysconfig::default_buffer_size () -{ - return FUSB_BUFFER_SIZE; -} - diff --git a/usrp/host/lib/fusb_sysconfig_generic.cc b/usrp/host/lib/fusb_sysconfig_generic.cc deleted file mode 100644 index d336cb94..00000000 --- a/usrp/host/lib/fusb_sysconfig_generic.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include - -static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit -static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) - -fusb_devhandle * -fusb_sysconfig::make_devhandle (usb_dev_handle *udh) -{ - return new fusb_devhandle_generic (udh); -} - -int fusb_sysconfig::max_block_size () -{ - return MAX_BLOCK_SIZE; -} - -int fusb_sysconfig::default_buffer_size () -{ - return FUSB_BUFFER_SIZE; -} diff --git a/usrp/host/lib/fusb_sysconfig_linux.cc b/usrp/host/lib/fusb_sysconfig_linux.cc deleted file mode 100644 index 468fa9f8..00000000 --- a/usrp/host/lib/fusb_sysconfig_linux.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include - -static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit -static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) - -fusb_devhandle * -fusb_sysconfig::make_devhandle (usb_dev_handle *udh) -{ - return new fusb_devhandle_linux (udh); -} - -int fusb_sysconfig::max_block_size () -{ - return MAX_BLOCK_SIZE; -} - -int fusb_sysconfig::default_buffer_size () -{ - return FUSB_BUFFER_SIZE; -} diff --git a/usrp/host/lib/fusb_sysconfig_ra_wb.cc b/usrp/host/lib/fusb_sysconfig_ra_wb.cc deleted file mode 100644 index 68eecedc..00000000 --- a/usrp/host/lib/fusb_sysconfig_ra_wb.cc +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include - -//static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit -// there's no hard limit, even before making any changes to the driver -// 64k is empirically a pretty good number -static const int MAX_BLOCK_SIZE = 64 * 1024; -// there is a limit of 1 MB in the driver for the buffer size -static const int FUSB_BUFFER_SIZE = 256 * (1L << 10); // 256 kB - -fusb_devhandle * -fusb_sysconfig::make_devhandle (usb_dev_handle *udh) -{ - return new fusb_devhandle_ra_wb (udh); -} - -int fusb_sysconfig::max_block_size () -{ - return MAX_BLOCK_SIZE; -} - -int fusb_sysconfig::default_buffer_size () -{ - return FUSB_BUFFER_SIZE; -} diff --git a/usrp/host/lib/fusb_sysconfig_win32.cc b/usrp/host/lib/fusb_sysconfig_win32.cc deleted file mode 100644 index 282e77ac..00000000 --- a/usrp/host/lib/fusb_sysconfig_win32.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2005 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include - -static const int MAX_BLOCK_SIZE = 64 * 1024; // Windows kernel hard limit -static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) - -fusb_devhandle * -fusb_sysconfig::make_devhandle (usb_dev_handle *udh) -{ - return new fusb_devhandle_win32 (udh); -} - -int fusb_sysconfig::max_block_size () -{ - return MAX_BLOCK_SIZE; -} - -int fusb_sysconfig::default_buffer_size () -{ - return FUSB_BUFFER_SIZE; -} diff --git a/usrp/host/lib/fusb_win32.cc b/usrp/host/lib/fusb_win32.cc deleted file mode 100644 index c6e3c972..00000000 --- a/usrp/host/lib/fusb_win32.cc +++ /dev/null @@ -1,265 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2005 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); -static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE; -static const int DEFAULT_BUFFER_SIZE = 16 * (1L << 20); // 16 MB / endpoint - - -static const int USB_TIMEOUT = 1000; // in milliseconds - - -fusb_devhandle_win32::fusb_devhandle_win32 (usb_dev_handle *udh) - : fusb_devhandle (udh) -{ - // that's it -} - -fusb_devhandle_win32::~fusb_devhandle_win32 () -{ - // nop -} - -fusb_ephandle * -fusb_devhandle_win32::make_ephandle (int endpoint, bool input_p, - int block_size, int nblocks) -{ - return new fusb_ephandle_win32 (this, endpoint, input_p, - block_size, nblocks); -} - -// ---------------------------------------------------------------- - -fusb_ephandle_win32::fusb_ephandle_win32 (fusb_devhandle_win32 *dh, - int endpoint, bool input_p, - int block_size, int nblocks) - : fusb_ephandle (endpoint, input_p, block_size, nblocks), - d_devhandle (dh), d_input_leftover(0),d_output_short(0) -{ - if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE) - throw std::out_of_range ("fusb_ephandle_win32: block_size"); - - if (d_nblocks < 0) - throw std::out_of_range ("fusb_ephandle_win32: nblocks"); - - if (d_block_size == 0) - d_block_size = DEFAULT_BLOCK_SIZE; - - if (d_nblocks == 0) - d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size); - - d_buffer = new char [d_block_size*d_nblocks]; - d_context = new void * [d_nblocks]; - - // allocate contexts - - usb_dev_handle *dev = dh->get_usb_dev_handle (); - int i; - - if (d_input_p) - endpoint |= USB_ENDPOINT_IN; - - for (i=0; i 0) { - d_curr = (d_curr+1)%d_nblocks; - buf = &d_buffer[d_curr*d_block_size]; - - if (d_outstanding_write != d_nblocks) { - d_outstanding_write++; - } else { - retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT); - if (retval < 0) { - fprintf(stderr, "%s: usb_reap_async: %s\n", - __FUNCTION__, usb_strerror()); - return retval; - } - } - - int ncopy = std::min(bytes_to_write, d_block_size); - memcpy(buf, (void *) &(((char*)buffer)[a]), ncopy); - bytes_to_write -= ncopy; - a += ncopy; - - d_output_short = d_block_size - ncopy; - if (d_output_short == 0) - usb_submit_async(d_context[d_curr], buf, d_block_size); - } - - return retval < 0 ? retval : nbytes; -} - -int -fusb_ephandle_win32::read (void *buffer, int nbytes) -{ - int retval=0; - char *buf; - - if (!d_started) // doesn't matter here, but keeps semantics constant - return -1; - - if (!d_input_p) - return -1; - - int bytes_to_read = nbytes; - - int a=0; - if (d_input_leftover != 0) { - - buf = &d_buffer[d_curr*d_block_size + d_block_size - d_input_leftover]; - a = std::min(nbytes, d_input_leftover); - memcpy(buffer, buf, a); - bytes_to_read -= a; - d_input_leftover -= a; - - if (d_input_leftover == 0) - usb_submit_async(d_context[d_curr], - &d_buffer[d_curr*d_block_size], d_block_size); - } - - while (bytes_to_read > 0) { - - d_curr = (d_curr+1)%d_nblocks; - buf = &d_buffer[d_curr*d_block_size]; - - retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT); - if (retval < 0) - fprintf(stderr, "%s: usb_reap_async: %s\n", - __FUNCTION__, usb_strerror()); - - int ncopy = std::min(bytes_to_read, d_block_size); - memcpy((void *) &(((char*)buffer)[a]), buf, ncopy); - bytes_to_read -= ncopy; - a += ncopy; - - d_input_leftover = d_block_size - ncopy; - if (d_input_leftover == 0) - usb_submit_async(d_context[d_curr], buf, d_block_size); - } - - return retval < 0 ? retval : nbytes; -} - -void -fusb_ephandle_win32::wait_for_completion () -{ - int i; - - for (i=0; i - -/*! - * \brief win32 implementation of fusb_devhandle using libusb-win32 - */ -class fusb_devhandle_win32 : public fusb_devhandle -{ -public: - // CREATORS - fusb_devhandle_win32 (usb_dev_handle *udh); - virtual ~fusb_devhandle_win32 (); - - // MANIPULATORS - virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); -}; - - -/*! - * \brief win32 implementation of fusb_ephandle using libusb-win32 - */ -class fusb_ephandle_win32 : public fusb_ephandle -{ -private: - fusb_devhandle_win32 *d_devhandle; - - unsigned d_curr; - unsigned d_outstanding_write; - int d_output_short; - int d_input_leftover; - void ** d_context; - char * d_buffer; - -public: - // CREATORS - fusb_ephandle_win32 (fusb_devhandle_win32 *dh, int endpoint, bool input_p, - int block_size = 0, int nblocks = 0); - virtual ~fusb_ephandle_win32 (); - - // MANIPULATORS - - virtual bool start (); //!< begin streaming i/o - virtual bool stop (); //!< stop streaming i/o - - /*! - * \returns \p nbytes if write was successfully enqueued, else -1. - * Will block if no free buffers available. - */ - virtual int write (const void *buffer, int nbytes); - - /*! - * \returns number of bytes read or -1 if error. - * number of bytes read will be <= nbytes. - * Will block if no input available. - */ - virtual int read (void *buffer, int nbytes); - - /* - * block until all outstanding writes have completed - */ - virtual void wait_for_completion (); -}; - -#endif /* _FUSB_WIN32_H_ */ - diff --git a/usrp/host/lib/gen_usrp_dbid.py b/usrp/host/lib/gen_usrp_dbid.py deleted file mode 100755 index 14d3ee55..00000000 --- a/usrp/host/lib/gen_usrp_dbid.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python - -import sys -import os -import os.path -import re -from optparse import OptionParser - -def write_header(f, comment_char): - f.write(comment_char); f.write('\n') - f.write(comment_char); f.write(' Machine generated by gen_usrp_dbid.py from usrp_dbid.dat\n') - f.write(comment_char); f.write(' Do not edit by hand. All edits will be overwritten.\n') - f.write(comment_char); f.write('\n') - f.write('\n') - -def gen_dbid_py(r): - f = open('usrp_dbid.py', 'w') - comment_char = '#' - write_header(f, comment_char) - f.write(comment_char); f.write('\n') - f.write(comment_char); f.write(" USRP Daughterboard ID's\n") - f.write(comment_char); f.write('\n') - f.write('\n') - for x in r: - f.write('%-16s = %s\n' % (x[1], x[2])) - -def gen_dbid_h(r): - f = open('usrp_dbid.h', 'w') - comment_char = '//' - write_header(f, comment_char) - f.write(comment_char); f.write('\n') - f.write(comment_char); f.write(" USRP Daughterboard ID's\n") - f.write(comment_char); f.write('\n') - f.write('\n') - f.write('#ifndef INCLUDED_USRP_DBID_H\n') - f.write('#define INCLUDED_USRP_DBID_H\n') - f.write('\n') - for x in r: - f.write('#define %-25s %s\n' % ('USRP_DBID_' + x[1], x[2])) - f.write('\n') - f.write('#endif /* INCLUDED_USRP_DBID_H */\n') - -def gen_dbid_cc(r): - f = open('usrp_dbid.cc', 'w') - write_header(f, '//') - head = '''/* - * Copyright 2005 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include -#include -#include - -#define NELEM(x) sizeof(x)/sizeof(x[0]) - -static struct { - unsigned short dbid; - const char *name; -} dbid_map[] = { -''' - - tail = '''}; - -const std::string -usrp_dbid_to_string (int dbid) -{ - if (dbid == -1) - return ""; - - if (dbid == -2) - return ""; - - for (unsigned i = 0; i < NELEM (dbid_map); i++) - if (dbid == dbid_map[i].dbid) - return dbid_map[i].name; - - char tmp[64]; - snprintf (tmp, sizeof (tmp), "Unknown (0x%04x)", dbid); - return tmp; -} -''' - f.write(head) - for x in r: - f.write(' { %-27s "%s" },\n' % ( - 'USRP_DBID_' + x[1] + ',', x[0])) - f.write(tail) - -def gen_all(src_filename): - src_file = open(src_filename, 'r') - r = [] - for line in src_file: - line = line.strip() - line = re.sub(r'\s*#.*$','', line) - if len(line) == 0: - continue - mo = re.match('"([^"]+)"\s*(0x[0-9a-fA-F]+)', line) - if mo: - str_name = mo.group(1) - id_name = str_name.upper().replace(' ', '_') - id_val = mo.group(2) - r.append((str_name, id_name, id_val)) - #sys.stdout.write('%-16s\t%-16s\t%s\n' % ('"'+str_name+'"', id_name, id_val)) - - gen_dbid_h(r) - gen_dbid_py(r) - gen_dbid_cc(r) - - -def main(): - usage = "usage: %prog [options] usrp_dbid.dat" - parser = OptionParser(usage=usage) - (options, args) = parser.parse_args() - if len(args) != 1: - parser.print_help() - sys.exit(1) - - gen_all(args[0]) - -if __name__ == '__main__': - main() diff --git a/usrp/host/lib/inband/Makefile.am b/usrp/host/lib/inband/Makefile.am new file mode 100644 index 00000000..9f6a7ab6 --- /dev/null +++ b/usrp/host/lib/inband/Makefile.am @@ -0,0 +1,92 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with 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 + +INCLUDES = \ + $(DEFINES) $(OMNITHREAD_INCLUDES) $(PMT_INCLUDES) $(MBLOCK_INCLUDES) \ + $(USRP_INCLUDES) $(BOOST_CFLAGS) $(CPPUNIT_INCLUDES) + +TESTS = test_inband + +EXTRA_DIST = + +lib_LTLIBRARIES = \ + libusrp_inband.la \ + libusrp_inband-qa.la + + +# ------------------------------------------------------------------------ +# Build the inband library + +BUILT_SOURCES = \ + usrp_server_mbh.cc + +usrp_server_mbh.cc : usrp_server.mbh + $(COMPILE_MBH) usrp_server.mbh usrp_server_mbh.cc + +libusrp_inband_la_SOURCES = \ + $(BUILT_SOURCES) \ + usrp_server.cc + +libusrp_inband_la_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 + +libusrp_inband_la_LIBADD = \ + $(MBLOCK_LA) \ + -lstdc++ + + +include_HEADERS = \ + usrp_server.h + +noinst_HEADERS = \ + qa_inband.h \ + qa_inband_packet_prims.h \ + qa_inband_usrp_server.h + +# ------------------------------------------------------------------------ +# Build the qa code in its own library + +libusrp_inband_qa_la_SOURCES = \ + qa_inband.cc \ + qa_inband_packet_prims.cc \ + qa_inband_usrp_server.cc + +# magic flags +libusrp_inband_qa_la_LDFLAGS = $(NO_UNDEFINED) -avoid-version + +# link against c++ standard library +libusrp_inband_qa_la_LIBADD = \ + libusrp_inband.la \ + $(CPPUNIT_LIBS) \ + -lstdc++ + +# ------------------------------------------------------------------------ + +noinst_PROGRAMS = \ + test_inband + +test_inband_SOURCES = test_inband.cc +test_inband_LDADD = libusrp_inband-qa.la + + +MOSTLYCLEANFILES = \ + $(BUILT_SOURCES) *~ *.pyc + diff --git a/usrp/host/lib/inband/dump_packets.py b/usrp/host/lib/inband/dump_packets.py new file mode 100755 index 00000000..ea27ca51 --- /dev/null +++ b/usrp/host/lib/inband/dump_packets.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import sys +import struct +from optparse import OptionParser + +from usb_packet import * + +def dump_packet(raw_pkt, outfile, dump_payload): + pkt = usb_packet(raw_pkt) + outfile.write(pkt.decoded_flags()) + outfile.write(' chan= %2d len= %3d timestamp= 0x%08x rssi= % 2d tag= %2d\n' % ( + pkt.chan(), pkt.payload_len(), pkt.timestamp(), pkt.rssi(), pkt.tag())) + if dump_payload: + assert pkt.payload_len() % 4 == 0 + shorts = struct.unpack('<%dh' % (pkt.payload_len() // 2), pkt.payload()) + for i in range(0, len(shorts), 2): + outfile.write(' %6d, %6d\n' % (shorts[i], shorts[i+1])) + + +def dump_packets(infile, outfile, dump_payload): + raw_pkt = infile.read(512) + while raw_pkt: + if len(raw_pkt) != 512: + sys.stderr.write("File length is not a multiple of 512 bytes") + raise SystemExit, 1 + + dump_packet(raw_pkt, outfile, dump_payload) + raw_pkt = infile.read(512) + + +def main(): + parser = OptionParser() + parser.add_option('-p', '--dump-payload', action='store_true', default=False, + help='dump payload in decimal and hex') + + (options, files) = parser.parse_args() + if len(files) == 0: + dump_packets(sys.stdin, sys.stdout, options.dump_payload) + else: + for f in files: + dump_packets(open(f, "r"), sys.stdout, options.dump_payload) + + +if __name__ == '__main__': + main() diff --git a/usrp/host/lib/inband/gen_test_packets.py b/usrp/host/lib/inband/gen_test_packets.py new file mode 100755 index 00000000..1e22722d --- /dev/null +++ b/usrp/host/lib/inband/gen_test_packets.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +import random +import struct +from pprint import pprint +from usb_packet import * + +MAX_PAYLOAD = 504 +TIME_NOW = 0xffffffff + + +class sequence_generator(object): + def __init__(self): + self.i = 0 + + def __call__(self): + t = self.i + self.i += 1 + return t + +def gen_shuffled_lengths(): + valid_lengths = range(0, MAX_PAYLOAD+1, 4) # [0, 4, 8, ... 504] + random.shuffle(valid_lengths) + return valid_lengths + + +class packet_sequence_generator(object): + def __init__(self, channel, lengths): + self.next = sequence_generator() + self.channel = channel + self.lengths = lengths + + def __call__(self, output_file): + gen_packet(output_file, self.channel, self.next, self.lengths[0]) + del self.lengths[0] + + +def gen_packet(output_file, channel, content_generator, payload_len): + assert (payload_len % 4) == 0 + payload = [] + n_iq = payload_len // 4 + for n in range(n_iq): + payload.append(content_generator()) # I + payload.append(content_generator()) # Q + for n in range(MAX_PAYLOAD // 4 - n_iq): + payload.append(0x0000) + payload.append(0xffff) + + assert (len(payload) == MAX_PAYLOAD // 2) + + #print "\npayload_len =", payload_len + #pprint(payload) + + output_file.write(make_header(FL_START_OF_BURST|FL_END_OF_BURST, + channel, payload_len, TIME_NOW)) + output_file.write(struct.pack('<252h', *payload)) + + +def gen_all_valid_packet_lengths_1_channel(output_file): + lengths = gen_shuffled_lengths() + npkts = len(lengths) # number of packets we'll generator on each stream + pkt_gen_0 = packet_sequence_generator(0, lengths) + for i in range(npkts): + pkt_gen_0(output_file) + + assert pkt_gen_0.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + + +def gen_all_valid_packet_lengths_2_channels(output_file): + lengths = gen_shuffled_lengths() + npkts = len(lengths) # number of packets we'll generator on each stream + pkt_gen_0 = packet_sequence_generator(0, lengths) + pkt_gen_1 = packet_sequence_generator(1, gen_shuffled_lengths()) + pkt_gen = (pkt_gen_0, pkt_gen_1) + + which_gen = (npkts * [0]) + (npkts * [1]) + random.shuffle(which_gen) + + for i in which_gen: + pkt_gen[i](output_file) + + assert pkt_gen_0.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + assert pkt_gen_1.next() == 16002 # 2*sum(1, 2, ..., 126) == 126 * 127 + +if __name__ == '__main__': + gen_all_valid_packet_lengths_1_channel(open("all_valid_packet_lengths_1_channel.dat", "w")) + gen_all_valid_packet_lengths_2_channels(open("all_valid_packet_lengths_2_channels.dat", "w")) diff --git a/usrp/host/lib/inband/qa_inband.cc b/usrp/host/lib/inband/qa_inband.cc new file mode 100644 index 00000000..00a821f3 --- /dev/null +++ b/usrp/host/lib/inband/qa_inband.cc @@ -0,0 +1,35 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +CppUnit::TestSuite * +qa_inband::suite() +{ + CppUnit::TestSuite *s = new CppUnit::TestSuite("inband"); + + s->addTest (qa_inband_packet_prims::suite()); + s->addTest (qa_inband_usrp_server::suite()); + + return s; +} diff --git a/usrp/host/lib/inband/qa_inband.h b/usrp/host/lib/inband/qa_inband.h new file mode 100644 index 00000000..92386f95 --- /dev/null +++ b/usrp/host/lib/inband/qa_inband.h @@ -0,0 +1,35 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_QA_INBAND_H +#define INCLUDED_QA_INBAND_H + +#include + +//! collect all the tests for the user server + +class qa_inband { + public: + //! return suite of tests for all of usrp server + static CppUnit::TestSuite *suite(); +}; + +#endif /* INCLUDED_QA_INBAND_H */ diff --git a/usrp/host/lib/inband/qa_inband_packet_prims.cc b/usrp/host/lib/inband/qa_inband_packet_prims.cc new file mode 100644 index 00000000..9d9a0c70 --- /dev/null +++ b/usrp/host/lib/inband/qa_inband_packet_prims.cc @@ -0,0 +1,161 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif + +#include +#include +#include +#include // will change on gigabit crossover + +typedef usrp_inband_usb_packet transport_pkt; + +void +qa_inband_packet_prims::test_flags() +{ + transport_pkt pkt; + + // Test each one of the flags while ensuring no other fields become set in the process + pkt.set_header(pkt.FL_START_OF_BURST,0,0,0); + CPPUNIT_ASSERT_EQUAL(1, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_END_OF_BURST,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_OVERRUN,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_UNDERRUN,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(pkt.FL_DROPPED,0,0,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + // test of all fields set + pkt.set_header( + pkt.FL_START_OF_BURST | + pkt.FL_END_OF_BURST | + pkt.FL_UNDERRUN | + pkt.FL_OVERRUN | + pkt.FL_DROPPED + ,0,0,0); + CPPUNIT_ASSERT_EQUAL(1, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(1, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(1, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + +} +////////////////////////////////////////////////////////////////////// + +void +qa_inband_packet_prims::test_fields() +{ + transport_pkt pkt; + void * payload; + + // test word0 field exclusiveness + // + // I want to test max values of each field to ensure field boundaries + // but these max values could change based on technology? The + // max payload is returned by a private method so the code is not + // technology dependent + pkt.set_header(0,16,0,0); + CPPUNIT_ASSERT_EQUAL(16, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + + pkt.set_header(0,0,8,0); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(8, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0,pkt.payload_len()); + + pkt.set_header(0,0,0,pkt.max_payload()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(pkt.max_payload(), pkt.payload_len()); + + // test timestamp, shouldn't have to test other fields since + // setting the timestamp only has the ability to affect one word + pkt.set_timestamp(54); + CPPUNIT_ASSERT_EQUAL(uint32_t(54), pkt.timestamp()); + + // test the payload, ensure no other fields overwritten + // + // is there a better test for this? + pkt.set_header(0,0,0,0); + payload = malloc(pkt.payload_len()); + memset(payload, 'f', pkt.payload_len()); + memcpy(pkt.payload(), payload, pkt.payload_len()); + CPPUNIT_ASSERT_EQUAL(0, memcmp(pkt.payload(), payload, pkt.payload_len())); + CPPUNIT_ASSERT_EQUAL(0, pkt.start_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.end_of_burst()); + CPPUNIT_ASSERT_EQUAL(0, pkt.overrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.underrun()); + CPPUNIT_ASSERT_EQUAL(0, pkt.dropped()); + CPPUNIT_ASSERT_EQUAL(0, pkt.chan()); + CPPUNIT_ASSERT_EQUAL(0, pkt.tag()); + CPPUNIT_ASSERT_EQUAL(0, pkt.payload_len()); + free(payload); + +} +////////////////////////////////////////////////////////////////////// diff --git a/usrp/host/lib/inband/qa_inband_packet_prims.h b/usrp/host/lib/inband/qa_inband_packet_prims.h new file mode 100644 index 00000000..5e60c60e --- /dev/null +++ b/usrp/host/lib/inband/qa_inband_packet_prims.h @@ -0,0 +1,41 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef QA_INBAND_PACKET_PRIMS_H +#define QA_INBAND_PACKET_PRIMS_H + +#include +#include + +class qa_inband_packet_prims : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_inband_packet_prims); + CPPUNIT_TEST(test_flags); + CPPUNIT_TEST(test_fields); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_flags(); + void test_fields(); + +}; + +#endif /* INCLUDED_QA_INBAND_PACKET_PRIMS_H */ diff --git a/usrp/host/lib/inband/qa_inband_usrp_server.cc b/usrp/host/lib/inband/qa_inband_usrp_server.cc new file mode 100644 index 00000000..2885a4d8 --- /dev/null +++ b/usrp/host/lib/inband/qa_inband_usrp_server.cc @@ -0,0 +1,455 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pmt_t s_cmd_allocate_channel = pmt_intern("cmd-allocate-channel"); +static pmt_t s_response_allocate_channel = pmt_intern("response-allocate-channel"); +static pmt_t s_send_allocate_channel = pmt_intern("send-allocate-channel"); +static pmt_t s_cmd_deallocate_channel = pmt_intern("cmd-deallocate-channel"); +static pmt_t s_response_deallocate_channel = pmt_intern("response-deallocate-channel"); +static pmt_t s_send_deallocate_channel = pmt_intern("send-deallocate-channel"); +static pmt_t s_cmd_max_capacity = pmt_intern("cmd-max-capacity"); +static pmt_t s_response_max_capacity = pmt_intern("response-max-capacity"); +static pmt_t s_cmd_ntx_chan = pmt_intern("cmd-ntx-chan"); +static pmt_t s_cmd_nrx_chan = pmt_intern("cmd-nrx-chan"); +static pmt_t s_response_ntx_chan = pmt_intern("response-ntx-chan"); +static pmt_t s_response_nrx_chan = pmt_intern("response-nrx-chan"); +static pmt_t s_cmd_current_capacity_allocation = pmt_intern("cmd-current-capacity-allocation"); +static pmt_t s_response_current_capacity_allocation = pmt_intern("response-current-capacity-allocation"); + + +// ---------------------------------------------------------------------------------------------- + +class qa_alloc_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_nmsgs_to_recv; + long d_nrecvd; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_nstatus; + long d_nstatus_to_recv; + + public: + qa_alloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_alloc_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_message(mb_message_sptr msg); + void run_tests(); +}; + +qa_alloc_top::qa_alloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_nrecvd=0; + d_nmsgs_to_recv = 7; + d_nstatus=0; + d_nstatus_to_recv = 3; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Test the TX side + define_component("server", "usrp_server", PMT_F); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); + +} + +qa_alloc_top::~qa_alloc_top(){} + +void +qa_alloc_top::initial_transition() +{ + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_max_capacity, pmt_list1(PMT_F)); + d_cs->send(s_cmd_ntx_chan, pmt_list1(PMT_F)); + d_cs->send(s_cmd_nrx_chan, pmt_list1(PMT_F)); +} + +void +qa_alloc_top::run_tests() +{ + std::cout << "[qa_alloc_top] Starting tests...\n"; + // should be able to allocate 1 byte + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + + // should not be able to allocate max capacity after 100 bytes were allocated + d_tx->send(s_cmd_allocate_channel, pmt_list2(pmt_from_long(usrp_server::RQSTD_CAPACITY_UNAVAIL), pmt_from_long(d_max_capacity))); + + // keep allocating a little more until all of the channels are used and test the error response + // we start at 1 since we've already allocated 1 channel + for(int i=1; i < d_ntx_chan; i++) { + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + d_nmsgs_to_recv++; + } + d_tx->send(s_cmd_allocate_channel, pmt_list2(pmt_from_long(usrp_server::CHANNEL_UNAVAIL), pmt_from_long(1))); + + // test out the same on the RX side + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + d_rx->send(s_cmd_allocate_channel, pmt_list2(pmt_from_long(usrp_server::RQSTD_CAPACITY_UNAVAIL), pmt_from_long(d_max_capacity))); + + for(int i=1; i < d_nrx_chan; i++) { + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + d_nmsgs_to_recv++; + } + d_rx->send(s_cmd_allocate_channel, pmt_list2(pmt_from_long(usrp_server::CHANNEL_UNAVAIL), pmt_from_long(1))); + + // when all is said and done, there should be d_ntx_chan+d_ntx_chan bytes allocated + d_cs->send(s_cmd_current_capacity_allocation, pmt_list1(pmt_from_long(d_ntx_chan+d_nrx_chan))); +} + +void +qa_alloc_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + if ((pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) + && pmt_eq(msg->signal(), s_response_allocate_channel)) + check_message(msg); + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_max_capacity)) { + d_max_capacity = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_alloc_top] USRP has max capacity of " << d_max_capacity << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_ntx_chan)) { + d_ntx_chan = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_alloc_top] USRP tx channels: " << d_ntx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_nrx_chan)) { + d_nrx_chan = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_alloc_top] USRP rx channels: " << d_nrx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_current_capacity_allocation)) { + check_message(msg); + } + + d_nstatus++; + + if(d_nstatus==d_nstatus_to_recv) + run_tests(); + } +} + +void +qa_alloc_top::check_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + pmt_t expected_result = pmt_nth(0, data); + pmt_t result = pmt_nth(1, data); + + d_nrecvd++; + + + if(!pmt_eqv(expected_result, result)) { + std::cout << "Got: " << result << " Expected: " << expected_result << "\n"; + shutdown_all(PMT_F); + } else { + std::cout << "[qa_alloc_top] Received expected response for message " << d_nrecvd << "\n"; + } + + if(d_nrecvd == d_nmsgs_to_recv) + shutdown_all(PMT_T); +} + +REGISTER_MBLOCK_CLASS(qa_alloc_top); + +// ---------------------------------------------------------------------------------------------- + +class qa_dealloc_top : public mb_mblock +{ + mb_port_sptr d_tx; + mb_port_sptr d_rx; + mb_port_sptr d_cs; + + long d_max_capacity; + long d_ntx_chan, d_nrx_chan; + + long d_nstatus; + long d_nstatus_to_recv; + + long d_nalloc_to_recv; + long d_nalloc_recvd; + + long d_ndealloc_to_recv; + long d_ndealloc_recvd; + + std::vector d_tx_chans; + std::vector d_rx_chans; + + public: + qa_dealloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg); + ~qa_dealloc_top(); + void initial_transition(); + void handle_message(mb_message_sptr msg); + + protected: + void check_allocation(mb_message_sptr msg); + void check_deallocation(mb_message_sptr msg); + void allocate_max(); + void deallocate_all(); +}; + +qa_dealloc_top::qa_dealloc_top(mb_runtime *runtime, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(runtime, instance_name, user_arg) +{ + d_ndealloc_recvd=0; + d_ndealloc_to_recv = 0; + d_nalloc_recvd=0; + d_nalloc_to_recv = 0; + d_nstatus=0; + d_nstatus_to_recv = 3; + + d_rx = define_port("rx0", "usrp-rx", false, mb_port::INTERNAL); + d_tx = define_port("tx0", "usrp-tx", false, mb_port::INTERNAL); + d_cs = define_port("cs", "usrp-server-cs", false, mb_port::INTERNAL); + + // Test the TX side + define_component("server", "usrp_server", PMT_F); + connect("self", "tx0", "server", "tx0"); + connect("self", "rx0", "server", "rx0"); + connect("self", "cs", "server", "cs"); +} + +qa_dealloc_top::~qa_dealloc_top(){} + +void +qa_dealloc_top::initial_transition() +{ + // Retrieve information about the USRP, then run tests + d_cs->send(s_cmd_max_capacity, pmt_list1(PMT_F)); + d_cs->send(s_cmd_ntx_chan, pmt_list1(PMT_F)); + d_cs->send(s_cmd_nrx_chan, pmt_list1(PMT_F)); +} + +void +qa_dealloc_top::allocate_max() +{ + std::cout << "[qa_dealloc_top] Max allocating...\n"; + + // Keep allocating until we hit the maximum number of channels + for(int i=0; i < d_ntx_chan; i++) { + d_tx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + d_nalloc_to_recv++; + } + for(int i=0; i < d_nrx_chan; i++) { + d_rx->send(s_cmd_allocate_channel, pmt_list2(PMT_T, pmt_from_long(1))); + d_nalloc_to_recv++; + } +} + +void +qa_dealloc_top::deallocate_all() { + + // Deallocate all of the channels that were allocated from allocate_max() + for(int i=0; i < (int)d_tx_chans.size(); i++) { + d_tx->send(s_cmd_deallocate_channel, pmt_list2(PMT_T, pmt_from_long(d_tx_chans[i]))); + d_ndealloc_to_recv++; + } + for(int i=0; i < (int)d_rx_chans.size(); i++) { + d_rx->send(s_cmd_deallocate_channel, pmt_list2(PMT_T, pmt_from_long(d_rx_chans[i]))); + d_ndealloc_to_recv++; + } + + // Should get permission denied errors trying to re-dealloc the channels, as we no + // longer have permission to them after deallocating + for(int i=0; i < (int)d_tx_chans.size(); i++) { + d_tx->send(s_cmd_deallocate_channel, pmt_list2(pmt_from_long(usrp_server::PERMISSION_DENIED), pmt_from_long(d_tx_chans[i]))); + d_ndealloc_to_recv++; + } + for(int i=0; i < (int)d_rx_chans.size(); i++) { + d_rx->send(s_cmd_deallocate_channel, pmt_list2(pmt_from_long(usrp_server::PERMISSION_DENIED), pmt_from_long(d_rx_chans[i]))); + d_ndealloc_to_recv++; + } + + // Try to deallocate a channel that doesn't exist on both sides, the last element in the vectors + // is the highest channel number, so we take that plus 1 + d_ndealloc_to_recv+=2; + d_tx->send(s_cmd_deallocate_channel, pmt_list2(pmt_from_long(usrp_server::CHANNEL_INVALID), pmt_from_long(d_rx_chans.back()+1))); + d_rx->send(s_cmd_deallocate_channel, pmt_list2(pmt_from_long(usrp_server::CHANNEL_INVALID), pmt_from_long(d_rx_chans.back()+1))); + + + // The used capacity should be back to 0 now that we've deallocated everything + d_cs->send(s_cmd_current_capacity_allocation, pmt_list1(pmt_from_long(0))); +} + +void +qa_dealloc_top::handle_message(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + if (pmt_eq(msg->port_id(), d_tx->port_symbol()) + || pmt_eq(msg->port_id(), d_rx->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_allocate_channel)) { + check_allocation(msg); + } + + if(pmt_eq(msg->signal(), s_response_deallocate_channel)){ + check_deallocation(msg); + } + } + + if (pmt_eq(msg->port_id(), d_cs->port_symbol())) { + + if(pmt_eq(msg->signal(), s_response_max_capacity)) { + d_max_capacity = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_dealloc_top] USRP has max capacity of " << d_max_capacity << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_ntx_chan)) { + d_ntx_chan = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_dealloc_top] USRP tx channels: " << d_ntx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_nrx_chan)) { + d_nrx_chan = pmt_to_long(pmt_nth(1, data)); + std::cout << "[qa_dealloc_top] USRP rx channels: " << d_nrx_chan << "\n"; + } + else if(pmt_eq(msg->signal(), s_response_current_capacity_allocation)) { + // the final command is a capacity check which should be 0, then we shutdown + pmt_t expected_result = pmt_nth(0, data); + pmt_t result = pmt_nth(1, data); + + if(pmt_eqv(expected_result, result)) + shutdown_all(PMT_T); + else + shutdown_all(PMT_F); + } + + d_nstatus++; + + if(d_nstatus==d_nstatus_to_recv) + allocate_max(); + } +} + +void +qa_dealloc_top::check_deallocation(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + pmt_t expected_result = pmt_nth(0, data); + pmt_t result = pmt_nth(1, data); + + d_ndealloc_recvd++; + + if(!pmt_eqv(expected_result, result)) { + std::cout << "Got: " << result << " Expected: " << expected_result << "\n"; + shutdown_all(PMT_F); + } else { + std::cout << "[qa_dealloc_top] Received expected deallocation response for message " << d_ndealloc_recvd << "\n"; + } +} + +void +qa_dealloc_top::check_allocation(mb_message_sptr msg) +{ + pmt_t data = msg->data(); + + pmt_t invocation_handle = pmt_nth(0, data); + pmt_t status = pmt_nth(1, data); + pmt_t channel = pmt_nth(2, data); + + d_nalloc_recvd++; + + if(pmt_eqv(status, PMT_F)) { + std::cout << "[qa_dealloc_top] Unexpected error response when allocating channels\n"; + shutdown_all(PMT_F); + } else { + // store all of the allocate channel numbers + if(pmt_eq(msg->port_id(), d_tx->port_symbol())) + d_tx_chans.push_back(pmt_to_long(channel)); + if(pmt_eq(msg->port_id(), d_rx->port_symbol())) + d_rx_chans.push_back(pmt_to_long(channel)); + } + + if(d_nalloc_recvd == d_nalloc_to_recv) { + + std::cout << "[qa_dealloc_top] Allocated TX channels: "; + for(int i=0; i < (int)d_tx_chans.size(); i++) + std::cout << d_tx_chans[i] << " "; + + std::cout << "\n[qa_dealloc_top] Allocated RX channels: "; + for(int i=0; i < (int)d_rx_chans.size(); i++) + std::cout << d_rx_chans[i] << " "; + std::cout << "\n"; + + deallocate_all(); // once we've allocated all of our channels, try to dealloc them + } +} + +REGISTER_MBLOCK_CLASS(qa_dealloc_top); + +// ---------------------------------------------------------------------------------------------- + +void +qa_inband_usrp_server::test_chan_allocation() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + rt->run("top", "qa_alloc_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_chan_deallocation() +{ + mb_runtime_sptr rt = mb_make_runtime(); + pmt_t result = PMT_T; + + rt->run("top", "qa_dealloc_top", PMT_F, &result); + + CPPUNIT_ASSERT(pmt_equal(PMT_T, result)); +} + +void +qa_inband_usrp_server::test_fragmentation() +{ + +} diff --git a/usrp/host/lib/inband/qa_inband_usrp_server.h b/usrp/host/lib/inband/qa_inband_usrp_server.h new file mode 100644 index 00000000..eb0f7a3e --- /dev/null +++ b/usrp/host/lib/inband/qa_inband_usrp_server.h @@ -0,0 +1,42 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef QA_INBAND_USRP_SERVER_H +#define QA_INBAND_USRP_SERVER_H + +#include +#include + +class qa_inband_usrp_server : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE(qa_inband_usrp_server); + CPPUNIT_TEST(test_chan_allocation); + CPPUNIT_TEST(test_chan_deallocation); + CPPUNIT_TEST(test_fragmentation); + CPPUNIT_TEST_SUITE_END(); + + private: + void test_chan_allocation(); + void test_chan_deallocation(); + void test_fragmentation(); +}; + +#endif /* INCLUDED_QA_INBAND_USRP_SERVER_H */ diff --git a/usrp/host/lib/inband/test_inband.cc b/usrp/host/lib/inband/test_inband.cc new file mode 100644 index 00000000..49619bef --- /dev/null +++ b/usrp/host/lib/inband/test_inband.cc @@ -0,0 +1,36 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +int +main(int argc, char **argv) +{ + + CppUnit::TextTestRunner runner; + + runner.addTest(qa_inband::suite ()); + + bool was_successful = runner.run("", false); + + return was_successful ? 0 : 1; +} diff --git a/usrp/host/lib/inband/usb_packet.py b/usrp/host/lib/inband/usb_packet.py new file mode 100644 index 00000000..5ca19b79 --- /dev/null +++ b/usrp/host/lib/inband/usb_packet.py @@ -0,0 +1,115 @@ +# +# 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 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import struct + + +FL_OVERRUN = 0x80000000 +FL_UNDERRUN = 0x40000000 +FL_DROPPED = 0x20000000 +FL_END_OF_BURST = 0x10000000 +FL_START_OF_BURST = 0x08000000 + +FL_ALL_FLAGS = 0xf8000000 + +FL_OVERRUN_SHIFT = 31 +FL_UNDERRUN_SHIFT = 30 +FL_DROPPED_SHIFT = 29 +FL_END_OF_BURST_SHIFT = 28 +FL_START_OF_BURST_SHIFT = 27 + +RSSI_MASK = 0x3f +RSSI_SHIFT = 21 + +CHAN_MASK = 0x1f +CHAN_SHIFT = 16 + +TAG_MASK = 0xf +TAG_SHIFT = 9 + +PAYLOAD_LEN_MASK = 0x1ff +PAYLOAD_LEN_SHIFT = 0 + +def make_header(flags, chan, payload_len, timestamp, rssi=0, tag=0): + word0 = ((flags & FL_ALL_FLAGS) + | ((rssi & RSSI_MASK) << RSSI_SHIFT) + | ((chan & CHAN_MASK) << CHAN_SHIFT) + | ((tag & TAG_MASK) << TAG_SHIFT) + | ((payload_len & PAYLOAD_LEN_MASK) << PAYLOAD_LEN_SHIFT)) + word1 = timestamp + return struct.pack('<2I', word0, word1) + + +def _decode(pred, indicator): + if pred: + return indicator + else: + return '-' + + +class usb_packet(object): + def __init__(self, raw_pkt): + assert isinstance(raw_pkt, str) and len(raw_pkt) == 512 + self._raw_pkt = raw_pkt; + (self._word0, self._word1) = struct.unpack('<2I', self._raw_pkt[0:8]) + + def timestamp(self): + return self._word1 + + def rssi(self): + return (self._word0 >> RSSI_SHIFT) & RSSI_MASK + + def chan(self): + return (self._word0 >> CHAN_SHIFT) & CHAN_MASK + + def tag(self): + return (self._word0 >> TAG_SHIFT) & TAG_MASK + + def payload_len(self): + return (self._word0 >> PAYLOAD_LEN_SHIFT) & PAYLOAD_LEN_MASK + + def flags(self): + return self._word0 & FL_ALL_FLAGS + + def overrun(self): + return (self._word0 >> FL_OVERRUN_SHIFT) & 0x1 + + def underrun(self): + return (self._word0 >> FL_UNDERRUN_SHIFT) & 0x1 + + def start_of_burst(self): + return (self._word0 >> FL_START_OF_BURST_SHIFT) & 0x1 + + def end_of_burst(self): + return (self._word0 >> FL_END_OF_BURST_SHIFT) & 0x1 + + def dropped(self): + return (self._word0 >> FL_DROPPED_SHIFT) & 0x1 + + def payload(self): + return self._raw_pkt[8:8+self.payload_len()] + + def decoded_flags(self): + s = (_decode(self.overrun(), 'O') + + _decode(self.underrun(), 'U') + + _decode(self.dropped(), 'D') + + _decode(self.end_of_burst(), 'E') + + _decode(self.start_of_burst(), 'S')) + return s diff --git a/usrp/host/lib/inband/usrp_inband_usb_packet.h b/usrp/host/lib/inband/usrp_inband_usb_packet.h new file mode 100644 index 00000000..471bfc66 --- /dev/null +++ b/usrp/host/lib/inband/usrp_inband_usb_packet.h @@ -0,0 +1,149 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef INCLUDED_USRP_INBAND_USB_PACKET_H_ +#define INCLUDED_USRP_INBAND_USB_PACKET_H_ + +#include +#include + +static const int USB_PKT_SIZE = 512; // bytes +static const int MAX_PAYLOAD = USB_PKT_SIZE-2*sizeof(uint32_t); + +class usrp_inband_usb_packet { + // + // keep raw packet in USRP-endian order + // + uint32_t d_word0; + uint32_t d_timestamp; + unsigned char d_payload[MAX_PAYLOAD]; + +public: + + enum flags { + FL_OVERRUN = 0x80000000, + FL_UNDERRUN = 0x40000000, + FL_DROPPED = 0x20000000, + FL_END_OF_BURST = 0x10000000, + FL_START_OF_BURST = 0x08000000, + + FL_ALL_FLAGS = 0xf8000000 + }; + + static const int FL_OVERRUN_SHIFT = 31; + static const int FL_UNDERRUN_SHIFT = 30; + static const int FL_DROPPED_SHIFT = 29; + static const int FL_END_OF_BURST_SHIFT = 28; + static const int FL_START_OF_BURST_SHIFT = 27; + + static const int RSSI_MASK = 0x3f; + static const int RSSI_SHIFT = 21; + + static const int CHAN_MASK = 0x1f; + static const int CHAN_SHIFT = 16; + + static const int TAG_MASK = 0xf; + static const int TAG_SHIFT = 9; + + static const int PAYLOAD_LEN_MASK = 0x1ff; + static const int PAYLOAD_LEN_SHIFT = 0; + +public: + + void set_timestamp(uint32_t timestamp){ + d_timestamp = host_to_usrp_u32(timestamp); + } + + void set_end_of_burst() { + uint32_t word0 = usrp_to_host_u32(d_word0); + word0 |= 1<> RSSI_SHIFT) & RSSI_MASK; + } + + int chan() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> CHAN_SHIFT) & CHAN_MASK; + } + + int tag() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> TAG_SHIFT) & TAG_MASK; + } + + int payload_len() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> PAYLOAD_LEN_SHIFT) & PAYLOAD_LEN_MASK; + } + + int flags() const { + return usrp_to_host_u32(d_word0) & FL_ALL_FLAGS; + } + + int overrun() const { + return (usrp_to_host_u32(d_word0) & FL_OVERRUN) >> FL_OVERRUN_SHIFT; + } + + + int underrun() const { + return (usrp_to_host_u32(d_word0) & FL_UNDERRUN) >> FL_UNDERRUN_SHIFT; + } + + + int start_of_burst() const { + return (usrp_to_host_u32(d_word0) & FL_START_OF_BURST) >> FL_START_OF_BURST_SHIFT; + } + + int end_of_burst() const { + return (usrp_to_host_u32(d_word0) & FL_END_OF_BURST) >> FL_END_OF_BURST_SHIFT; + } + + int dropped() const { + return (usrp_to_host_u32(d_word0) & FL_DROPPED) >> FL_DROPPED_SHIFT; + } + + unsigned char *payload() { + return d_payload; + } + + static int max_payload() { + return MAX_PAYLOAD; + } + +}; + +#endif diff --git a/usrp/host/lib/inband/usrp_server.cc b/usrp/host/lib/inband/usrp_server.cc new file mode 100644 index 00000000..5041a921 --- /dev/null +++ b/usrp/host/lib/inband/usrp_server.cc @@ -0,0 +1,394 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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 +#endif +#include +#include +#include +#include +#include + +typedef usrp_inband_usb_packet transport_pkt; // makes conversion to gigabit easy + +// FIXME We should machine generate these by a simple preprocessor run over this file +// +// These are all the messages that we expect to receive. +// +// We "intern" these here (make them into symbols) so that our +// comparisions below are effectively pointer comparisons. + +static pmt_t s_cmd_allocate_channel = pmt_intern("cmd-allocate-channel"); +static pmt_t s_cmd_close = pmt_intern("cmd-close"); +static pmt_t s_cmd_deallocate_channel = pmt_intern("cmd-deallocate-channel"); +static pmt_t s_cmd_open = pmt_intern("cmd-open"); +static pmt_t s_cmd_start_recv_raw_samples = pmt_intern("cmd-start-recv-raw-samples"); +static pmt_t s_cmd_stop_recv_raw_samples = pmt_intern("cmd-stop-recv-raw-samples"); +static pmt_t s_cmd_to_control_channel = pmt_intern("cmd-to-control-channel"); +static pmt_t s_cmd_xmit_raw_frame = pmt_intern("cmd-xmit-raw-frame"); +static pmt_t s_cmd_max_capacity = pmt_intern("cmd-max-capacity"); +static pmt_t s_cmd_ntx_chan = pmt_intern("cmd-ntx-chan"); +static pmt_t s_cmd_nrx_chan = pmt_intern("cmd-nrx-chan"); +static pmt_t s_cmd_current_capacity_allocation = pmt_intern("cmd-current-capacity-allocation"); +static pmt_t s_response_allocate_channel = pmt_intern("response-allocate-channel"); +static pmt_t s_response_close = pmt_intern("response-close"); +static pmt_t s_response_deallocate_channel = pmt_intern("response-deallocate-channel"); +static pmt_t s_response_from_control_channel = pmt_intern("response-from-control-channel"); +static pmt_t s_response_open = pmt_intern("response-open"); +static pmt_t s_response_recv_raw_samples = pmt_intern("response-recv-raw-samples"); +static pmt_t s_response_xmit_raw_frame = pmt_intern("response-xmit-raw-frame"); +static pmt_t s_response_max_capacity = pmt_intern("response-max-capacity"); +static pmt_t s_response_ntx_chan = pmt_intern("response-ntx-chan"); +static pmt_t s_response_nrx_chan = pmt_intern("response-nrx-chan"); +static pmt_t s_response_current_capacity_allocation = pmt_intern("response-current-capacity-allocation"); + +static std::string +str(long x) +{ + std::ostringstream s; + s << x; + return s.str(); +} + +usrp_server::usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg) + : mb_mblock(rt, instance_name, user_arg) +{ + // define our ports + + // control & status port + d_cs = define_port("cs", "usrp-server-cs", true, mb_port::EXTERNAL); + + // ports + // + // (if/when we do replicated ports, these will be replaced by a + // single replicated port) + for(int port=0; port < N_PORTS; port++) { + d_tx.push_back(define_port("tx"+str(port), "usrp-tx", true, mb_port::EXTERNAL)); + d_rx.push_back(define_port("rx"+str(port), "usrp-rx", true, mb_port::EXTERNAL)); + } + + // FIXME ... initializing to 2 channels on each for now, eventually we should + // query the FPGA to get these values + d_ntx_chan = 2; + d_nrx_chan = 2; + + // Initialize capacity on each channel to 0 and to no owner + for(int chan=0; chan < d_ntx_chan; chan++) { + d_chaninfo_tx[chan].assigned_capacity = 0; + d_chaninfo_tx[chan].owner = PMT_NIL; + } + for(int chan=0; chan < d_nrx_chan; chan++) { + d_chaninfo_rx[chan].assigned_capacity = 0; + d_chaninfo_rx[chan].owner = PMT_NIL; + } +} + +usrp_server::~usrp_server() +{ +} + + +void +usrp_server::initial_transition() +{ + // the initial transition +} + +void +usrp_server::handle_message(mb_message_sptr msg) +{ + pmt_t event = msg->signal(); // the "name" of the message + pmt_t port_id = msg->port_id(); // which port it came in on + pmt_t data = msg->data(); + pmt_t metadata = msg->metadata(); + pmt_t invocation_handle; + pmt_t reply_data; + pmt_t status; + + if (1){ + std::cout << "[USRP_SERVER] event: " << event << std::endl; + std::cout << "[USRP_SERVER] port_id: " << port_id << std::endl; + } + + // It would be nice if this were all table driven, and we could + // compute our state transition as f(current_state, port_id, signal) + + if (pmt_eq(port_id, d_cs->port_symbol())){ // message came in on our control/status port + + if (pmt_eq(event, s_cmd_open)){ + // extract args from data + invocation_handle = pmt_nth(0, data); + long which_usrp = pmt_to_long(pmt_nth(1, data)); // integer usrp id, usually 0 + + // Do the right thing.... + // build a reply + + // if everything OK + status = PMT_T; + reply_data = pmt_list2(invocation_handle, status); + + // ...and send it + d_cs->send(s_response_open, reply_data); + return; + } + else if (pmt_eq(event, s_cmd_close)){ + // ... + } + else if (pmt_eq(event, s_cmd_max_capacity)) { + invocation_handle = pmt_nth(0, data); + reply_data = pmt_list2(invocation_handle, pmt_from_long(max_capacity())); + d_cs->send(s_response_max_capacity, reply_data); + return; + } + else if (pmt_eq(event, s_cmd_ntx_chan)) { + invocation_handle = pmt_nth(0, data); + reply_data = pmt_list2(invocation_handle, pmt_from_long(d_ntx_chan)); + d_cs->send(s_response_ntx_chan, reply_data); + } + else if (pmt_eq(event, s_cmd_nrx_chan)) { + invocation_handle = pmt_nth(0, data); + reply_data = pmt_list2(invocation_handle, pmt_from_long(d_nrx_chan)); + d_cs->send(s_response_nrx_chan, reply_data); + } + else if (pmt_eq(event, s_cmd_current_capacity_allocation)) { + invocation_handle = pmt_nth(0, data); + reply_data = pmt_list2(invocation_handle, pmt_from_long(current_capacity_allocation())); + d_cs->send(s_response_current_capacity_allocation, reply_data); + } + goto unhandled; + } + + if (pmt_eq(event, s_cmd_allocate_channel)){ + handle_cmd_allocate_channel(port_id, data); + return; + } + + if (pmt_eq(event, s_cmd_deallocate_channel)) { + handle_cmd_deallocate_channel(port_id, data); + return; + } + + if (pmt_eq(event, s_cmd_xmit_raw_frame)){ + handle_cmd_xmit_raw_frame(data); + return; + } + + unhandled: + std::cout << "[USRP_SERVER] unhandled msg: " << msg << std::endl; +} + +// Return -1 if it is not an RX port, or an index +int usrp_server::tx_port_index(pmt_t port_id) { + + for(int i=0; i < (int) d_tx.size(); i++) + if(pmt_eq(d_tx[i]->port_symbol(), port_id)) + return i; + + return -1; +} + +// Return -1 if it is not an RX port, or an index +int usrp_server::rx_port_index(pmt_t port_id) { + + for(int i=0; i < (int) d_rx.size(); i++) + if(pmt_eq(d_rx[i]->port_symbol(), port_id)) + return i; + + return -1; +} + +// Go through all TX and RX channels, sum up the assigned capacity +// and return it +long usrp_server::current_capacity_allocation() { + long capacity = 0; + + for(int chan=0; chan < d_ntx_chan; chan++) + capacity += d_chaninfo_tx[chan].assigned_capacity; + + for(int chan=0; chan < d_nrx_chan; chan++) + capacity += d_chaninfo_rx[chan].assigned_capacity; + + return capacity; +} + +void usrp_server::handle_cmd_allocate_channel(pmt_t port_id, pmt_t data) { + + pmt_t invocation_handle = pmt_nth(0, data); + long rqstd_capacity = pmt_to_long(pmt_nth(1, data)); + long chan, port; + pmt_t reply_data; + + // If it's a TX port, allocate on a free channel, else check if it's a RX port + // and allocate. + if((port = tx_port_index(port_id)) != -1) { + + // Check capacity exists + if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { + reply_data = pmt_list3(invocation_handle, pmt_from_long(RQSTD_CAPACITY_UNAVAIL), PMT_NIL); // no capacity available + d_tx[port]->send(s_response_allocate_channel, reply_data); + return; + } + + // Find a free channel, assign the capacity and respond + for(chan=0; chan < d_ntx_chan; chan++) { + if(d_chaninfo_tx[chan].owner == PMT_NIL) { + d_chaninfo_tx[chan].owner = port_id; + d_chaninfo_tx[chan].assigned_capacity = rqstd_capacity; + reply_data = pmt_list3(invocation_handle, PMT_T, pmt_from_long(chan)); + d_tx[port]->send(s_response_allocate_channel, reply_data); + return; + } + } + + std::cout << "[USRP_SERVER] Couldnt find a TX chan\n"; + + reply_data = pmt_list3(invocation_handle, pmt_from_long(CHANNEL_UNAVAIL), PMT_NIL); // no free TX chan found + d_tx[port]->send(s_response_allocate_channel, reply_data); + return; + } + + // Repeat the same process on the RX side if the port was not determined to be TX + if((port = rx_port_index(port_id)) != -1) { + + if((D_USB_CAPACITY - current_capacity_allocation()) < rqstd_capacity) { + reply_data = pmt_list3(invocation_handle, pmt_from_long(RQSTD_CAPACITY_UNAVAIL), PMT_NIL); // no capacity available + d_rx[port]->send(s_response_allocate_channel, reply_data); + return; + } + + for(chan=0; chan < d_nrx_chan; chan++) { + if(d_chaninfo_rx[chan].owner == PMT_NIL) { + d_chaninfo_rx[chan].owner = port_id; + d_chaninfo_rx[chan].assigned_capacity = rqstd_capacity; + reply_data = pmt_list3(invocation_handle, PMT_T, pmt_from_long(chan)); + d_rx[port]->send(s_response_allocate_channel, reply_data); + return; + } + } + + std::cout << "[USRP_SERVER] Couldnt find a RX chan\n"; + reply_data = pmt_list3(invocation_handle, pmt_from_long(CHANNEL_UNAVAIL), PMT_NIL); // no free RX chan found + d_rx[port]->send(s_response_allocate_channel, reply_data); + return; + } +} + +// Check the port type and deallocate assigned capacity based on this, ensuring +// that the owner of the method invocation is the owner of the port and that +// the channel number is valid. +void usrp_server::handle_cmd_deallocate_channel(pmt_t port_id, pmt_t data) { + + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + long port; + pmt_t reply_data; + + // Check that the channel number is valid, and that the calling port is the owner + // of the channel, and if so remove the assigned capacity. + if((port = tx_port_index(port_id)) != -1) { + + if(channel >= d_ntx_chan) { + reply_data = pmt_list2(invocation_handle, pmt_from_long(CHANNEL_INVALID)); // not a legit channel number + d_tx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + + if(d_chaninfo_tx[channel].owner != port_id) { + reply_data = pmt_list2(invocation_handle, pmt_from_long(PERMISSION_DENIED)); // not the owner of the port + d_tx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + + d_chaninfo_tx[channel].assigned_capacity = 0; + d_chaninfo_tx[channel].owner = PMT_NIL; + + reply_data = pmt_list2(invocation_handle, PMT_T); + d_tx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + + // Repeated process on the RX side + if((port = rx_port_index(port_id)) != -1) { + + if(channel >= d_nrx_chan) { + reply_data = pmt_list2(invocation_handle, pmt_from_long(CHANNEL_INVALID)); // not a legit channel number + d_rx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + + if(d_chaninfo_rx[channel].owner != port_id) { + reply_data = pmt_list2(invocation_handle, pmt_from_long(PERMISSION_DENIED)); // not the owner of the port + d_rx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + + d_chaninfo_rx[channel].assigned_capacity = 0; + d_chaninfo_rx[channel].owner = PMT_NIL; + + reply_data = pmt_list2(invocation_handle, PMT_T); + d_rx[port]->send(s_response_deallocate_channel, reply_data); + return; + } + +} + +void usrp_server::handle_cmd_xmit_raw_frame(pmt_t data) { + + size_t n_bytes, psize; + long max_payload_len = transport_pkt::max_payload(); + + pmt_t invocation_handle = pmt_nth(0, data); + long channel = pmt_to_long(pmt_nth(1, data)); + const void *samples = pmt_uniform_vector_elements(pmt_nth(2, data), n_bytes); + long timestamp = pmt_to_long(pmt_nth(3, data)); + + // Determine the number of packets to allocate contiguous memory for bursting over the + // USB and get a pointer to the memory to be used in building the packets + long n_packets = static_cast(std::ceil(n_bytes / (double)max_payload_len)); + pmt_t v_packets = pmt_make_u8vector(sizeof(transport_pkt) * n_packets, 0); + + transport_pkt *pkts = + (transport_pkt *) pmt_u8vector_writeable_elements(v_packets, psize); + + for(int n=0; n < n_packets; n++) { + + long payload_len = std::min((long)(n_bytes-(n*max_payload_len)), (long)max_payload_len); + + if(n == 0) { // first packet gets start of burst flag and timestamp + pkts[n].set_header(pkts[n].FL_START_OF_BURST, channel, 0, payload_len); + pkts[n].set_timestamp(timestamp); + } else { + pkts[n].set_header(0, channel, 0, payload_len); + pkts[n].set_timestamp(0xffffffff); + } + + memcpy(pkts[n].payload(), (uint8_t *)samples+(max_payload_len * n), payload_len); + } + + pkts[n_packets-1].set_end_of_burst(); // set the last packet's end of burst + + // interface with the USRP to send the USB packet, since the memory is + // contiguous, this should be a serious of memory copies to the bus, each being + // USB_PKT_SIZE * MAX_PACKET_BURST bytes worth of data (given a full burst) +} + +REGISTER_MBLOCK_CLASS(usrp_server); diff --git a/usrp/host/lib/inband/usrp_server.h b/usrp/host/lib/inband/usrp_server.h new file mode 100644 index 00000000..87c2768e --- /dev/null +++ b/usrp/host/lib/inband/usrp_server.h @@ -0,0 +1,82 @@ +/* -*- 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 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_USRP_SERVER_H +#define INCLUDED_USRP_SERVER_H + +#include +#include + +/*! + * \brief Implements the lowest-level mblock interface to the USRP + */ +class usrp_server : public mb_mblock +{ +public: + + enum error_codes { + RQSTD_CAPACITY_UNAVAIL = 0, + CHANNEL_UNAVAIL = 1, + CHANNEL_INVALID = 2, + PERMISSION_DENIED = 3 + }; + + // our ports + enum port_types { + RX_PORT = 0, + TX_PORT = 1 + }; + static const int N_PORTS = 4; + std::vector d_tx, d_rx; + mb_port_sptr d_cs; + + static const int D_USB_CAPACITY = 32 * 1024 * 1024; + static const int D_MAX_CHANNELS = 16; + long d_ntx_chan; + long d_nrx_chan; + + struct channel_info { + long assigned_capacity; // the capacity currently assignedby the channel + pmt_t owner; // port ID of the owner of the channel + }; + + struct channel_info d_chaninfo_tx[D_MAX_CHANNELS]; + struct channel_info d_chaninfo_rx[D_MAX_CHANNELS]; + +public: + usrp_server(mb_runtime *rt, const std::string &instance_name, pmt_t user_arg); + ~usrp_server(); + + void initial_transition(); + void handle_message(mb_message_sptr msg); + +protected: + static int max_capacity() { return D_USB_CAPACITY; } + +private: + void handle_cmd_allocate_channel(pmt_t port_id, pmt_t data); + void handle_cmd_deallocate_channel(pmt_t port_id, pmt_t data); + void handle_cmd_xmit_raw_frame(pmt_t data); + int rx_port_index(pmt_t port_id); + int tx_port_index(pmt_t port_id); + long current_capacity_allocation(); +}; + +#endif /* INCLUDED_USRP_SERVER_H */ diff --git a/usrp/host/lib/inband/usrp_server.mbh b/usrp/host/lib/inband/usrp_server.mbh new file mode 100644 index 00000000..97bb0e66 --- /dev/null +++ b/usrp/host/lib/inband/usrp_server.mbh @@ -0,0 +1,256 @@ +;; -*- scheme -*- ; not really, but tells emacs how to format this +;; +;; 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 2, or (at your option) +;; any later version. +;; +;; GNU Radio is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License along +;; with this program; if not, write to the Free Software Foundation, Inc., +;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +;; + +;; ---------------------------------------------------------------- +;; This is an mblock header file +;; +;; The format is very much a work-in-progress. +;; It'll be compiled to C++. +;; ---------------------------------------------------------------- + +;; In the outgoing messages described below, invocation-handle is an +;; identifier provided by the client to tag the method invocation. +;; The identifier will be returned with the response, to provide the +;; client with a mechanism to match asynchronous responses with the +;; commands that generate them. The value of the invocation-handle is +;; opaque the the server, and is not required by the server to be +;; unique. +;; +;; In the incoming messages described below, invocation-handle is the +;; identifier provided by the client in the prompting invocation. The +;; identifier is returned with the response, so that the client has a +;; mechanism to match asynchronous responses with the commands that +;; generated them. +;; +;; status is either #t, indicating success, or a pair containing +;; (status-code . message), where status-code is a symbol and message +;; is a string. + + +;; ---------------------------------------------------------------- +;; usrp-channel +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-channel + + (:outgoing + + (cmd-allocate-channel invocation-handle capacity-reservation) + + ;; The cmd-allocate-channel message requests that the server + ;; allocates a logical channel in the FPGA for use. + ;; capacity-reservation specifies the number of bytes/s of + ;; interconnect capacity (USB or ethernet) to reserve for this + ;; channel. (The reservation is just a sanity check, no OS + ;; specific mechanism is used.) + + (cmd-deallocate-channel invocation-handle channel) + + ;; The integer channel specifies the channel to deallocate. + + ) + + (:incoming + + + (response-allocate-channel invocation-handle status channel) + + ;; If successful, a channel the specified capacity was allocated. + ;; channel, an integer, indicates which channel was allocated. + + (response-deallocate-channel invocation-handle status) + + ;; If successful, the specified channel and associated interconnect + ;; capacity were deallocated. + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-low-level-cs +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) +;; +;; This defines a low level control and status interface to the usrp. +;; This will probably be replaced (or at least augmented) with a +;; higher level interface. For now, this will allow us to get on +;; the air. +;; +;; The subpackets are lists containing the relevant parameters. The +;; server will marshall them appropriately. Below is a list of +;; subpackets. See inband-signaling-usb for details. The opcodes are +;; symbols; unless otherwise indicated the remaining parameters are +;; integers. rid values are limited to 3-bits. +;; +;; (op-ping-fixed rid ping-value) +;; (op-ping-fixed-reply rid ping-value) +;; (op-write-reg reg-number reg-value) +;; (op-write-reg-masked reg-number reg-value mask-value) +;; (op-read-reg rid reg-number reg-value) +;; (op-read-reg-reply rid reg-number reg-value) +;; (op-i2c-write i2c-addr u8-vec) +;; (op-i2c-read rid i2c-addr nbytes) +;; (op-i2c-read-reply rid i2c-addr u8-vec) +;; (op-spi-write enables format opt-header-bytes u8-vec) +;; (op-spi-read rid enables format opt-header-bytes nbytes) +;; (op-spi-read-reply rid u8-vec) +;; (op-delay ticks) + + +(define-protocol-class usrp-low-level-cs + + (:outgoing + + (cmd-to-control-channel invocation-handle list-of-subpackets) + + ) + + (:incoming + + (response-from-control-channel invocation-handle status list-of-subpackets) + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-tx +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-tx + (:include usrp-channel) + (:include usrp-low-level-cs) + + (:outgoing + + (cmd-xmit-raw-frame invocation-handle channel samples timestamp) + + ;; The argument channel must be an integer. It specifies the + ;; channel on which the frame of samples will be be sent. + ;; + ;; samples must be a uniform numeric vector. The contents of the + ;; sample vector is treated as opaque and is passed on to the FPGA + ;; unmodified. It is the responsibility of the sender to ensure + ;; that the binary format is sensible for the current FPGA + ;; configuration. + ;; + ;; timestamp is a 32-bit integer that specifies the time at which + ;; the first sample in samples shall be sent to the D/A converter. + ;; The format and interpration of time is specified in the file + ;; inband-signaling-usb + ) + + (:incoming + + (response-xmit-raw-frame invocation-handle status) + + ;; If successful, the samples of the associated frame have been + ;; transmitted to the USRP. This message may be used to implement + ;; Tx flow control. The client could for example implement a + ;; policy of never having more than 4 unacknowledged + ;; cmd-xmit-raw-frame's outstanding. + + ) + ) + +;; ---------------------------------------------------------------- +;; usrp-rx +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-rx + (:include usrp-channel) + (:include usrp-low-level-cs) + + (:outgoing + + (cmd-start-recv-raw-samples invocation-handle channel) + + ;; The argument channel must be an integer. It specifies the + ;; channel from which frames of samples will be be received. The + ;; server will return response-recv-raw-samples messages until a + ;; cmd-stop-recv-raw-samples message is received. + + (cmd-stop-recv-raw-samples invocation-handle channel) + + ;; The argument channel must be an integer. There is no reply to + ;; this message. + + ) + + (:incoming + + (response-recv-raw-samples invocation-handle status samples timestamp properties) + + ;; samples is a uniform numeric vector. The contents of the sample + ;; vector is treated as opaque and is passed from the FPGA + ;; unmodified. It is the responsibility of the receiver to decode + ;; the binary format as appropriate for the current FPGA + ;; configuration. + ;; + ;; timestamp is a 32-bit integer that specifies the time at which + ;; the first sample in samples was received from the A/D converter. + ;; The format and interpretation of time is as specified in the + ;; file inband-signaling-usb. + ;; + ;; properties is a dictionary containing additional (key, value) + ;; pairs associated with the reception of these samples. In + ;; particular, the map may contain the Received Signal Strength + ;; Indication (RSSI) reported by the front end at the time the + ;; first sample was received from the A/D. + + ) + ) + + +;; ---------------------------------------------------------------- +;; usrp-server-cs +;; +;; Control and status port for usrp-server +;; +;; The protocol class is defined from the client's point-of-view. +;; (The client port is unconjugated, the server port is conjugated.) + +(define-protocol-class usrp-server-cs + + (:outgoing + (cmd-open invocation-handle which-usrp) + (cmd-close invocation-handle) + (cmd-max-capacity invocation-handle) + (cmd-ntx-chan invocation-handle) + (cmd-nrx-chan invocation-handle) + (cmd-current-capacity-allocation invocation-handle) + ) + + (:incoming + (response-open invocation-handle status) + (response-close invocation-handle status) + (response-max-capacity invocation-handle capacity) + (response-ntx-chan invocation-handle ntx-chan) + (response-nrx-chan invocation-handle nrx-chan) + (response-current-capacity-allocation invocation-handle capacity) + ) + ) diff --git a/usrp/host/lib/legacy/Makefile.am b/usrp/host/lib/legacy/Makefile.am new file mode 100644 index 00000000..7303fb4c --- /dev/null +++ b/usrp/host/lib/legacy/Makefile.am @@ -0,0 +1,153 @@ +# +# USRP - Universal Software Radio Peripheral +# +# Copyright (C) 2003,2004,2006,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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA +# + +include $(top_srcdir)/Makefile.common + +INCLUDES = $(USRP_INCLUDES) + +lib_LTLIBRARIES = libusrp.la + +libusrp_la_LDFLAGS = $(NO_UNDEFINED) -version-info 0:0:0 + +libusrp_la_LIBADD = \ + $(USB_LIBS) \ + ../../misc/libmisc.la + +EXTRA_DIST = \ + std_paths.h.in \ + usrp_dbid.dat + + +BUILT_SOURCES = \ + usrp_dbid.h \ + usrp_dbid.cc \ + usrp_dbid.py + + +# ---------------------------------------------------------------- +# FUSB_TECH is set at configure time by way of +# usrp/config/usrp_fusb_tech.m4. +# It indicates which fast usb strategy we should be building. +# We currently implement "generic", "darwin", "win32" and "linux" + + +generic_CODE = \ + fusb_generic.cc \ + fusb_sysconfig_generic.cc + +darwin_CODE = \ + fusb_darwin.cc \ + fusb_sysconfig_darwin.cc \ + README_OSX \ + circular_buffer.h \ + circular_linked_list.h \ + darwin_libusb.h \ + mld_threads.h + +win32_CODE = \ + fusb_win32.cc \ + fusb_sysconfig_win32.cc + +linux_CODE = \ + fusb_linux.cc \ + fusb_sysconfig_linux.cc + +ra_wb_CODE = \ + fusb_ra_wb.cc \ + fusb_sysconfig_ra_wb.cc + + +# +# include each _CODE entry here... +# +EXTRA_libusrp_la_SOURCES = \ + $(generic_CODE) \ + $(darwin_CODE) \ + $(win32_CODE) \ + $(linux_CODE) \ + $(ra_wb_CODE) + + +# work around automake deficiency +libusrp_la_common_SOURCES = \ + fusb.cc \ + md5.c \ + usrp_basic.cc \ + usrp_config.cc \ + usrp_dbid.cc \ + usrp_local_sighandler.cc \ + usrp_prims.cc \ + usrp_standard.cc + + +if FUSB_TECH_generic +libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(generic_CODE) +endif + +if FUSB_TECH_darwin +libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(darwin_CODE) +endif + +if FUSB_TECH_win32 +libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(win32_CODE) +endif + +if FUSB_TECH_linux +libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(linux_CODE) +endif + +if FUSB_TECH_ra_wb +libusrp_la_SOURCES = $(libusrp_la_common_SOURCES) $(ra_wb_CODE) +endif + +include_HEADERS = \ + usrp_basic.h \ + usrp_bytesex.h \ + usrp_config.h \ + usrp_dbid.h \ + usrp_prims.h \ + usrp_slots.h \ + usrp_standard.h + +noinst_HEADERS = \ + ad9862.h \ + fusb.h \ + fusb_darwin.h \ + fusb_win32.h \ + fusb_generic.h \ + fusb_linux.h \ + fusb_ra_wb.h \ + md5.h \ + rate_to_regval.h \ + usrp_local_sighandler.h + +usrppython_PYTHON = \ + usrp_dbid.py + +noinst_PYTHON = \ + gen_usrp_dbid.py \ + check_data.py \ + dump_data.py + +usrp_dbid.py usrp_dbid.h usrp_dbid.cc: gen_usrp_dbid.py usrp_dbid.dat + PYTHONPATH=$(top_srcdir)/usrp/src srcdir=$(srcdir) $(PYTHON) $(srcdir)/gen_usrp_dbid.py $(srcdir)/usrp_dbid.dat + +MOSTLYCLEANFILES = \ + $(BUILT_SOURCES) *~ *.pyc diff --git a/usrp/host/lib/legacy/README_OSX b/usrp/host/lib/legacy/README_OSX new file mode 100644 index 00000000..20230f12 --- /dev/null +++ b/usrp/host/lib/legacy/README_OSX @@ -0,0 +1,63 @@ +USRP Darwin Fast USB Changes +Version 0.2 of 2006-04-27 +Michael Dickens + +The files included in this archive are: + +circular_buffer.h +circular_linked_list.h +darwin_libusb.h +fusb_darwin.cc +fusb_darwin.h +mld_threads.h + +These files allow GNURadio code for Darwin / MaxOS X to talk to the +USRP via USB 2.0 at rates up to around 30 Mega-Bytes/sec (MBps), up +from 4-8 MBps without the changes. + +I implemented the buffering myself; there are probably GR buffers +available which would do the work but as this is "beta" software it's +a good place to start. Speed improvements are made by porting +LIBUSB's non-true async bulk read and write functions into USRP's +"fusb", and upgrading them to handle -true- async transfers. +Unfortunately, the easiest way to do this is to spawn a thread or 2 to +handle the "async" part of the transfers. This implementation uses +Darwin's pthreads to do the work for mutexes, conditions, and threads. +Previous implementations (0.1 and before) used "omni_threads" as +provided by gnuradio-core, which caused issues with compiling and +execution ... I'm glad that this is no longer the case. + +As far as I have tested, there is no way to improve the throughput to +32+ MBps without moving into Darwin's "port"s ... a kernel-level data +transport method with a user/application layer for USB-specific +functions. Unfortunately, Apple's documentation for these "port"s is +minimal; I have learned more from reading the Darwin source code +< http://darwinsource.opendarwin.org/ > than by reading Apple's +documents! This would also require -not- using LIBUSB, of which the +removal from the rest of the USRP code would be potentially tedious. + +If you run into issues either compiling or testing the USRP on +OSX, please send me a note. + +(1) Go through the bootstrap, configure, compile, and install as +usual (e.g. see < http://www.nd.edu/~mdickens/GNURadio/ > for my +usual). + +(2) from .../usrp/host/apps : run the scripts +++++++++++++++++ +./test_usrp_standard_tx +./test_usrp_standard_rx +++++++++++++++++ + +For -all- systems I've tested on thus far, both of these return +exactly 41 overruns / underruns, and -most- systems start out with a +stalled pipe. This stall comes in a usb_control funciton call to +LIBUSB; one would have to change the LIBUSB code to handle this issue. + +(3) from gr-build/gnuradio-examples/python/usrp : +++++++++++++++++ +./benchmark_usb.py +++++++++++++++++ + +(4) If you get to here, the try doing the FM receiver (gui or not). +If that sounds correct, then the USB is working. Yay! \ No newline at end of file diff --git a/usrp/host/lib/legacy/ad9862.h b/usrp/host/lib/legacy/ad9862.h new file mode 100644 index 00000000..d1895bbe --- /dev/null +++ b/usrp/host/lib/legacy/ad9862.h @@ -0,0 +1,221 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_AD9862_H +#define INCLUDED_AD9862_H + +/* + * Analog Devices AD9862 registers and some fields + */ + +#define BEGIN_AD9862 namespace ad9862 { +#define END_AD962 } +#define DEF static const int + +BEGIN_AD9862; + +DEF REG_GENERAL = 0; +DEF REG_RX_PWR_DN = 1; +DEF RX_PWR_DN_VREF_DIFF = (1 << 7); +DEF RX_PWR_DN_VREF = (1 << 6); +DEF RX_PWR_DN_RX_DIGIGAL = (1 << 5); +DEF RX_PWR_DN_RX_B = (1 << 4); +DEF RX_PWR_DN_RX_A = (1 << 3); +DEF RX_PWR_DN_BUF_B = (1 << 2); +DEF RX_PWR_DN_BUF_A = (1 << 1); +DEF RX_PWR_DN_ALL = (1 << 0); + +DEF REG_RX_A = 2; // bypass input buffer / RxPGA +DEF REG_RX_B = 3; // pypass input buffer / RxPGA +DEF RX_X_BYPASS_INPUT_BUFFER = (1 << 7); + +DEF REG_RX_MISC = 4; +DEF RX_MISC_HS_DUTY_CYCLE = (1 << 2); +DEF RX_MISC_SHARED_REF = (1 << 1); +DEF RX_MISC_CLK_DUTY = (1 << 0); + +DEF REG_RX_IF = 5; +DEF RX_IF_THREE_STATE = (1 << 4); +DEF RX_IF_USE_CLKOUT1 = (0 << 3); +DEF RX_IF_USE_CLKOUT2 = (1 << 3); // aka Rx Retime +DEF RX_IF_2S_COMP = (1 << 2); +DEF RX_IF_INV_RX_SYNC = (1 << 1); +DEF RX_IF_MUX_OUT = (1 << 0); + +DEF REG_RX_DIGITAL = 6; +DEF RX_DIGITAL_2_CHAN = (1 << 3); +DEF RX_DIGITAL_KEEP_MINUS_VE = (1 << 2); +DEF RX_DIGITAL_HILBERT = (1 << 1); +DEF RX_DIGITAL_DECIMATE = (1 << 0); + +DEF REG_RESERVED_7 = 7; + +DEF REG_TX_PWR_DN = 8; +DEF TX_PWR_DN_ALT_TIMING_MODE = (1 << 5); +DEF TX_PWR_DN_TX_OFF_ENABLE = (1 << 4); +DEF TX_PWR_DN_TX_DIGITAL = (1 << 3); +DEF TX_PWR_DN_TX_ANALOG_B = 0x4; +DEF TX_PWR_DN_TX_ANALOG_A = 0x2; +DEF TX_PWR_DN_TX_ANALOG_BOTH = 0x7; + +DEF REG_RESERVED_9 = 9; + +DEF REG_TX_A_OFFSET_LO = 10; +DEF REG_TX_A_OFFSET_HI = 11; +DEF REG_TX_B_OFFSET_LO = 12; +DEF REG_TX_B_OFFSET_HI = 13; + +DEF REG_TX_A_GAIN = 14; // fine trim for matching +DEF REG_TX_B_GAIN = 15; // fine trim for matching +DEF TX_X_GAIN_COARSE_FULL = (3 << 6); +DEF TX_X_GAIN_COARSE_1_HALF = (1 << 6); +DEF TX_X_GAIN_COARSE_1_ELEVENTH = (0 << 6); + +DEF REG_TX_PGA = 16; // 20 dB continuous gain in 0.1 dB steps + // 0x00 = min gain (-20 dB) + // 0xff = max gain ( 0 dB) + +DEF REG_TX_MISC = 17; +DEF TX_MISC_SLAVE_ENABLE = (1 << 1); +DEF TX_MISC_TX_PGA_FAST = (1 << 0); + +DEF REG_TX_IF = 18; +DEF TX_IF_USE_CLKOUT2 = (0 << 6); +DEF TX_IF_USE_CLKOUT1 = (1 << 6); // aka Tx Retime +DEF TX_IF_I_FIRST = (0 << 5); +DEF TX_IF_Q_FIRST = (1 << 5); +DEF TX_IF_INV_TX_SYNC = (1 << 4); +DEF TX_IF_2S_COMP = (1 << 3); +DEF TX_IF_INVERSE_SAMPLE = (1 << 2); +DEF TX_IF_TWO_EDGES = (1 << 1); +DEF TX_IF_INTERLEAVED = (1 << 0); + +DEF REG_TX_DIGITAL = 19; +DEF TX_DIGITAL_2_DATA_PATHS = (1 << 4); +DEF TX_DIGITAL_KEEP_NEGATIVE = (1 << 3); +DEF TX_DIGITAL_HILBERT = (1 << 2); +DEF TX_DIGITAL_INTERPOLATE_NONE = 0x0; +DEF TX_DIGITAL_INTERPOLATE_2X = 0x1; +DEF TX_DIGITAL_INTERPOLATE_4X = 0x2; + +DEF REG_TX_MODULATOR = 20; +DEF TX_MODULATOR_NEG_FINE_TUNE = (1 << 5); +DEF TX_MODULATOR_DISABLE_NCO = (0 << 4); +DEF TX_MODULATOR_ENABLE_NCO = (1 << 4); // aka Fine Mode +DEF TX_MODULATOR_REAL_MIX_MODE = (1 << 3); +DEF TX_MODULATOR_NEG_COARSE_TUNE = (1 << 2); +DEF TX_MODULATOR_COARSE_MODULATION_NONE = 0x0; +DEF TX_MODULATOR_COARSE_MODULATION_F_OVER_4 = 0x1; +DEF TX_MODULATOR_COARSE_MODULATION_F_OVER_8 = 0x2; +DEF TX_MODULATOR_CM_MASK = 0x7; + + +DEF REG_TX_NCO_FTW_7_0 = 21; +DEF REG_TX_NCO_FTW_15_8 = 22; +DEF REG_TX_NCO_FTW_23_16= 23; + +DEF REG_DLL = 24; +DEF DLL_DISABLE_INTERNAL_XTAL_OSC = (1 << 6); // aka Input Clock Ctrl +DEF DLL_ADC_DIV2 = (1 << 5); +DEF DLL_MULT_1X = (0 << 3); +DEF DLL_MULT_2X = (1 << 3); +DEF DLL_MULT_4X = (2 << 3); +DEF DLL_PWR_DN = (1 << 2); +// undefined bit = (1 << 1); +DEF DLL_FAST = (1 << 0); + +DEF REG_CLKOUT = 25; +DEF CLKOUT2_EQ_DLL = (0 << 6); +DEF CLKOUT2_EQ_DLL_OVER_2 = (1 << 6); +DEF CLKOUT2_EQ_DLL_OVER_4 = (2 << 6); +DEF CLKOUT2_EQ_DLL_OVER_8 = (3 << 6); +DEF CLKOUT_INVERT_CLKOUT2 = (1 << 5); +DEF CLKOUT_DISABLE_CLKOUT2 = (1 << 4); +// undefined bit = (1 << 3); +// undefined bit = (1 << 2); +DEF CLKOUT_INVERT_CLKOUT1 = (1 << 1); +DEF CLKOUT_DISABLE_CLKOUT1 = (1 << 0); + +DEF REG_AUX_ADC_A2_LO = 26; +DEF REG_AUX_ADC_A2_HI = 27; +DEF REG_AUX_ADC_A1_LO = 28; +DEF REG_AUX_ADC_A1_HI = 29; +DEF REG_AUX_ADC_B2_LO = 30; +DEF REG_AUX_ADC_B2_HI = 31; +DEF REG_AUX_ADC_B1_LO = 32; +DEF REG_AUX_ADC_B1_HI = 33; + +DEF REG_AUX_ADC_CTRL = 34; +DEF AUX_ADC_CTRL_AUX_SPI = (1 << 7); +DEF AUX_ADC_CTRL_SELBNOTA = (1 << 6); +DEF AUX_ADC_CTRL_REFSEL_B = (1 << 5); +DEF AUX_ADC_CTRL_SELECT_B2 = (0 << 4); +DEF AUX_ADC_CTRL_SELECT_B1 = (1 << 4); +DEF AUX_ADC_CTRL_START_B = (1 << 3); +DEF AUX_ADC_CTRL_REFSEL_A = (1 << 2); +DEF AUX_ADC_CTRL_SELECT_A2 = (0 << 1); +DEF AUX_ADC_CTRL_SELECT_A1 = (1 << 1); +DEF AUX_ADC_CTRL_START_A = (1 << 0); + +DEF REG_AUX_ADC_CLK = 35; +DEF AUX_ADC_CLK_CLK_OVER_4 = (1 << 0); + +DEF REG_AUX_DAC_A = 36; +DEF REG_AUX_DAC_B = 37; +DEF REG_AUX_DAC_C = 38; + +DEF REG_AUX_DAC_UPDATE = 39; +DEF AUX_DAC_UPDATE_SLAVE_ENABLE = (1 << 7); +DEF AUX_DAC_UPDATE_C = (1 << 2); +DEF AUX_DAC_UPDATE_B = (1 << 1); +DEF AUX_DAC_UPDATE_A = (1 << 0); + +DEF REG_AUX_DAC_PWR_DN = 40; +DEF AUX_DAC_PWR_DN_C = (1 << 2); +DEF AUX_DAC_PWR_DN_B = (1 << 1); +DEF AUX_DAC_PWR_DN_A = (1 << 0); + +DEF REG_AUX_DAC_CTRL = 41; +DEF AUX_DAC_CTRL_INV_C = (1 << 4); +DEF AUX_DAC_CTRL_INV_B = (1 << 2); +DEF AUX_DAC_CTRL_INV_A = (1 << 0); + +DEF REG_SIGDELT_LO = 42; +DEF REG_SIGDELT_HI = 43; + +// 44 to 48 reserved + +DEF REG_ADC_LOW_PWR_LO = 49; +DEF REG_ADC_LOW_PWR_HI = 50; + +// 51 to 62 reserved + +DEF REG_CHIP_ID = 63; + + +END_AD962; + +#undef DEF +#undef BEGIN_AD9862 +#undef END_AD962 + +#endif /* INCLUDED_AD9862_H */ diff --git a/usrp/host/lib/legacy/check_data.py b/usrp/host/lib/legacy/check_data.py new file mode 100755 index 00000000..655ca150 --- /dev/null +++ b/usrp/host/lib/legacy/check_data.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# Copyright 2003 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +import sys +import struct + +fin = sys.stdin + +count = 0 +expected = 0 +last_correction = 0 + +while 1: + s = fin.read(2) + if not s or len(s) != 2: + break + + v, = struct.unpack ('H', s) + iv = int(v) & 0xffff + # print "%8d %6d 0x%04x" % (count, iv, iv) + if count & 0x1: # only counting on the Q channel + if (expected & 0xffff) != iv: + print "%8d (%6d) %6d 0x%04x" % (count, count - last_correction, iv, iv) + expected = iv # reset expected sequence + last_correction = count + expected = (expected + 1) & 0xffff + + count += 1 + + + + diff --git a/usrp/host/lib/legacy/circular_buffer.h b/usrp/host/lib/legacy/circular_buffer.h new file mode 100644 index 00000000..913360df --- /dev/null +++ b/usrp/host/lib/legacy/circular_buffer.h @@ -0,0 +1,325 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _CIRCULAR_BUFFER_H_ +#define _CIRCULAR_BUFFER_H_ + +#include "mld_threads.h" +#include + +#define DO_DEBUG 0 + +template class circular_buffer +{ +private: +// the buffer to use + T* d_buffer; + +// the following are in Items (type T) + UInt32 d_bufLen_I, d_readNdx_I, d_writeNdx_I; + UInt32 d_n_avail_write_I, d_n_avail_read_I; + +// stuff to control access to class internals + mld_mutex_ptr d_internal; + mld_condition_ptr d_readBlock, d_writeBlock; + +// booleans to decide how to control reading, writing, and aborting + bool d_doWriteBlock, d_doFullRead, d_doAbort; + + void delete_mutex_cond () { + if (d_internal) { + delete d_internal; + d_internal = NULL; + } + if (d_readBlock) { + delete d_readBlock; + d_readBlock = NULL; + } + if (d_writeBlock) { + delete d_writeBlock; + d_writeBlock = NULL; + } + }; + +public: + circular_buffer (UInt32 bufLen_I, + bool doWriteBlock = true, bool doFullRead = false) { + if (bufLen_I == 0) + throw std::runtime_error ("circular_buffer(): " + "Number of items to buffer must be > 0.\n"); + d_bufLen_I = bufLen_I; + d_buffer = (T*) new T[d_bufLen_I]; + d_doWriteBlock = doWriteBlock; + d_doFullRead = doFullRead; + d_internal = NULL; + d_readBlock = d_writeBlock = NULL; + reset (); +#if DO_DEBUG + fprintf (stderr, "c_b(): buf len (items) = %ld, " + "doWriteBlock = %s, doFullRead = %s\n", d_bufLen_I, + (d_doWriteBlock ? "true" : "false"), + (d_doFullRead ? "true" : "false")); +#endif + }; + + ~circular_buffer () { + delete_mutex_cond (); + delete [] d_buffer; + }; + + inline UInt32 n_avail_write_items () { + d_internal->lock (); + UInt32 retVal = d_n_avail_write_I; + d_internal->unlock (); + return (retVal); + }; + + inline UInt32 n_avail_read_items () { + d_internal->lock (); + UInt32 retVal = d_n_avail_read_I; + d_internal->unlock (); + return (retVal); + }; + + inline UInt32 buffer_length_items () {return (d_bufLen_I);}; + inline bool do_write_block () {return (d_doWriteBlock);}; + inline bool do_full_read () {return (d_doFullRead);}; + + void reset () { + d_doAbort = false; + bzero (d_buffer, d_bufLen_I * sizeof (T)); + d_readNdx_I = d_writeNdx_I = d_n_avail_read_I = 0; + d_n_avail_write_I = d_bufLen_I; + delete_mutex_cond (); + d_internal = new mld_mutex (); + d_readBlock = new mld_condition (); + d_writeBlock = new mld_condition (); + }; + +/* + * enqueue: add the given buffer of item-length to the queue, + * first-in-first-out (FIFO). + * + * inputs: + * buf: a pointer to the buffer holding the data + * + * bufLen_I: the buffer length in items (of the instantiated type) + * + * returns: + * -1: on overflow (write is not blocking, and data is being + * written faster than it is being read) + * 0: if nothing to do (0 length buffer) + * 1: if success + * 2: in the process of aborting, do doing nothing + * + * will throw runtime errors if inputs are improper: + * buffer pointer is NULL + * buffer length is larger than the instantiated buffer length + */ + + int enqueue (T* buf, UInt32 bufLen_I) { +#if DO_DEBUG + fprintf (stderr, "enqueue: buf = %X, bufLen = %ld.\n", + (unsigned int)buf, bufLen_I); +#endif + if (bufLen_I > d_bufLen_I) { + fprintf (stderr, "cannot add buffer longer (%ld" + ") than instantiated length (%ld" + ").\n", bufLen_I, d_bufLen_I); + throw std::runtime_error ("circular_buffer::enqueue()"); + } + + if (bufLen_I == 0) + return (0); + if (!buf) + throw std::runtime_error ("circular_buffer::enqueue(): " + "input buffer is NULL.\n"); + d_internal->lock (); + if (d_doAbort) { + d_internal->unlock (); + return (2); + } + if (bufLen_I > d_n_avail_write_I) { + if (d_doWriteBlock) { + while (bufLen_I > d_n_avail_write_I) { +#if DO_DEBUG + fprintf (stderr, "enqueue: #len > #a, waiting.\n"); +#endif + d_internal->unlock (); + d_writeBlock->wait (); + d_internal->lock (); + if (d_doAbort) { + d_internal->unlock (); +#if DO_DEBUG + fprintf (stderr, "enqueue: #len > #a, aborting.\n"); +#endif + return (2); + } +#if DO_DEBUG + fprintf (stderr, "enqueue: #len > #a, done waiting.\n"); +#endif + } + } else { + d_n_avail_read_I = d_bufLen_I - bufLen_I; + d_n_avail_write_I = bufLen_I; +#if DO_DEBUG + fprintf (stderr, "circular_buffer::enqueue: overflow\n"); +#endif + return (-1); + } + } + UInt32 n_now_I = d_bufLen_I - d_writeNdx_I, n_start_I = 0; + if (n_now_I > bufLen_I) + n_now_I = bufLen_I; + else if (n_now_I < bufLen_I) + n_start_I = bufLen_I - n_now_I; + bcopy (buf, &(d_buffer[d_writeNdx_I]), n_now_I * sizeof (T)); + if (n_start_I) { + bcopy (&(buf[n_now_I]), d_buffer, n_start_I * sizeof (T)); + d_writeNdx_I = n_start_I; + } else + d_writeNdx_I += n_now_I; + d_n_avail_read_I += bufLen_I; + d_n_avail_write_I -= bufLen_I; + d_readBlock->signal (); + d_internal->unlock (); + return (1); + }; + +/* + * dequeue: removes from the queue the number of items requested, or + * available, into the given buffer on a FIFO basis. + * + * inputs: + * buf: a pointer to the buffer into which to copy the data + * + * bufLen_I: pointer to the number of items to remove in items + * (of the instantiated type) + * + * returns: + * 0: if nothing to do (0 length buffer) + * 1: if success + * 2: in the process of aborting, do doing nothing + * + * will throw runtime errors if inputs are improper: + * buffer pointer is NULL + * buffer length pointer is NULL + * buffer length is larger than the instantiated buffer length + */ + + + int dequeue (T* buf, UInt32* bufLen_I) { +#if DO_DEBUG + fprintf (stderr, "dequeue: buf = %X, *bufLen = %ld.\n", + (unsigned int)buf, *bufLen_I); +#endif + if (!bufLen_I) + throw std::runtime_error ("circular_buffer::dequeue(): " + "input bufLen pointer is NULL.\n"); + if (!buf) + throw std::runtime_error ("circular_buffer::dequeue(): " + "input buffer pointer is NULL.\n"); + UInt32 l_bufLen_I = *bufLen_I; + if (l_bufLen_I == 0) + return (0); + if (l_bufLen_I > d_bufLen_I) { + fprintf (stderr, "cannot remove buffer longer (%ld" + ") than instantiated length (%ld" + ").\n", l_bufLen_I, d_bufLen_I); + throw std::runtime_error ("circular_buffer::dequeue()"); + } + + d_internal->lock (); + if (d_doAbort) { + d_internal->unlock (); + return (2); + } + if (d_doFullRead) { + while (d_n_avail_read_I < l_bufLen_I) { +#if DO_DEBUG + fprintf (stderr, "dequeue: #a < #len, waiting.\n"); +#endif + d_internal->unlock (); + d_readBlock->wait (); + d_internal->lock (); + if (d_doAbort) { + d_internal->unlock (); +#if DO_DEBUG + fprintf (stderr, "dequeue: #a < #len, aborting.\n"); +#endif + return (2); + } +#if DO_DEBUG + fprintf (stderr, "dequeue: #a < #len, done waiting.\n"); +#endif + } + } else { + while (d_n_avail_read_I == 0) { +#if DO_DEBUG + fprintf (stderr, "dequeue: #a == 0, waiting.\n"); +#endif + d_internal->unlock (); + d_readBlock->wait (); + d_internal->lock (); + if (d_doAbort) { + d_internal->unlock (); +#if DO_DEBUG + fprintf (stderr, "dequeue: #a == 0, aborting.\n"); +#endif + return (2); + } +#if DO_DEBUG + fprintf (stderr, "dequeue: #a == 0, done waiting.\n"); +#endif + } + } + if (l_bufLen_I > d_n_avail_read_I) + l_bufLen_I = d_n_avail_read_I; + UInt32 n_now_I = d_bufLen_I - d_readNdx_I, n_start_I = 0; + if (n_now_I > l_bufLen_I) + n_now_I = l_bufLen_I; + else if (n_now_I < l_bufLen_I) + n_start_I = l_bufLen_I - n_now_I; + bcopy (&(d_buffer[d_readNdx_I]), buf, n_now_I * sizeof (T)); + if (n_start_I) { + bcopy (d_buffer, &(buf[n_now_I]), n_start_I * sizeof (T)); + d_readNdx_I = n_start_I; + } else + d_readNdx_I += n_now_I; + *bufLen_I = l_bufLen_I; + d_n_avail_read_I -= l_bufLen_I; + d_n_avail_write_I += l_bufLen_I; + d_writeBlock->signal (); + d_internal->unlock (); + return (1); + }; + + void abort () { + d_internal->lock (); + d_doAbort = true; + d_writeBlock->signal (); + d_readBlock->signal (); + d_internal->unlock (); + }; +}; + +#endif /* _CIRCULAR_BUFFER_H_ */ diff --git a/usrp/host/lib/legacy/circular_linked_list.h b/usrp/host/lib/legacy/circular_linked_list.h new file mode 100644 index 00000000..0e460a26 --- /dev/null +++ b/usrp/host/lib/legacy/circular_linked_list.h @@ -0,0 +1,267 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _CIRCULAR_LINKED_LIST_H_ +#define _CIRCULAR_LINKED_LIST_H_ + +#include +#include + +#define __INLINE__ inline + +template class s_both; + +template class s_node +{ + typedef s_node* s_node_ptr; + +private: + T d_object; + bool d_available; + s_node_ptr d_prev, d_next; + s_both* d_both; + +public: + s_node (T l_object, + s_node_ptr l_prev = NULL, + s_node_ptr l_next = NULL) + : d_object (l_object), d_available (TRUE), d_prev (l_prev), + d_next (l_next), d_both (0) {}; + + __INLINE__ s_node (s_node_ptr l_prev, s_node_ptr l_next = NULL) { + s_node ((T) NULL, l_prev, l_next); }; + __INLINE__ s_node () { s_node (NULL, NULL, NULL); }; + __INLINE__ ~s_node () {}; + + void remove () { + d_prev->next (d_next); + d_next->prev (d_prev); + d_prev = d_next = this; + }; + + void insert_before (s_node_ptr l_next) { + if (l_next) { + s_node_ptr l_prev = l_next->prev (); + d_next = l_next; + d_prev = l_prev; + l_prev->next (this); + l_next->prev (this); + } else + d_next = d_prev = this; + }; + + void insert_after (s_node_ptr l_prev) { + if (l_prev) { + s_node_ptr l_next = l_prev->next (); + d_prev = l_prev; + d_next = l_next; + l_next->prev (this); + l_prev->next (this); + } else + d_prev = d_next = this; + }; + + __INLINE__ T object () { return (d_object); }; + __INLINE__ void object (T l_object) { d_object = l_object; }; + __INLINE__ bool available () { return (d_available); }; + __INLINE__ void set_available () { d_available = TRUE; }; + __INLINE__ void set_available (bool l_avail) { d_available = l_avail; }; + __INLINE__ void set_not_available () { d_available = FALSE; }; + __INLINE__ s_node_ptr next () { return (d_next); }; + __INLINE__ s_node_ptr prev () { return (d_prev); }; + __INLINE__ s_both* both () { return (d_both); }; + __INLINE__ void next (s_node_ptr l_next) { d_next = l_next; }; + __INLINE__ void prev (s_node_ptr l_prev) { d_prev = l_prev; }; + __INLINE__ void both (s_both* l_both) { d_both = l_both; }; +}; + +template class circular_linked_list { + typedef s_node* s_node_ptr; + +private: + s_node_ptr d_current, d_iterate, d_available, d_inUse; + UInt32 d_n_nodes, d_n_used; + mld_mutex_ptr d_internal; + mld_condition_ptr d_ioBlock; + +public: + circular_linked_list (UInt32 n_nodes) { + if (n_nodes == 0) + throw std::runtime_error ("circular_linked_list(): n_nodes == 0"); + + d_iterate = NULL; + d_n_nodes = n_nodes; + d_n_used = 0; + s_node_ptr l_prev, l_next; + d_inUse = d_current = l_next = l_prev = NULL; + + l_prev = new s_node (); + l_prev->set_available (); + l_prev->next (l_prev); + l_prev->prev (l_prev); + if (n_nodes > 1) { + l_next = new s_node (l_prev, l_prev); + l_next->set_available (); + l_next->next (l_prev); + l_next->prev (l_prev); + l_prev->next (l_next); + l_prev->prev (l_next); + if (n_nodes > 2) { + UInt32 n = n_nodes - 2; + while (n-- > 0) { + d_current = new s_node (l_prev, l_next); + d_current->set_available (); + d_current->prev (l_prev); + d_current->next (l_next); + l_prev->next (d_current); + l_next->prev (d_current); + l_next = d_current; + d_current = NULL; + } + } + } + d_available = d_current = l_prev; + d_internal = new mld_mutex (); + d_ioBlock = new mld_condition (); + }; + + ~circular_linked_list () { + iterate_start (); + s_node_ptr l_node = iterate_next (); + while (l_node) { + delete l_node; + l_node = iterate_next (); + } + delete d_internal; + d_internal = NULL; + delete d_ioBlock; + d_ioBlock = NULL; + d_available = d_inUse = d_iterate = d_current = NULL; + d_n_used = d_n_nodes = 0; + }; + + s_node_ptr find_next_available_node () { + d_internal->lock (); +// find an available node + s_node_ptr l_node = d_available; + while (! l_node) { + d_internal->unlock (); + d_ioBlock->wait (); + d_internal->lock (); + l_node = d_available; + } +// fprintf (stderr, "::f_n_a_n: #u = %ld, node = %p\n", num_used(), l_node); +// remove this one from the current available list + if (num_available () == 1) { +// last one, just set available to NULL + d_available = NULL; + } else + d_available = l_node->next (); + l_node->remove (); +// add is to the inUse list + if (! d_inUse) + d_inUse = l_node; + else + l_node->insert_before (d_inUse); + d_n_used++; + l_node->set_not_available (); + d_internal->unlock (); + return (l_node); + }; + + void make_node_available (s_node_ptr l_node) { + if (!l_node) return; + d_internal->lock (); +// fprintf (stderr, "::m_n_a: #u = %ld, node = %p\n", num_used(), l_node); +// remove this node from the inUse list + if (num_used () == 1) { +// last one, just set inUse to NULL + d_inUse = NULL; + } else + d_inUse = l_node->next (); + l_node->remove (); +// add this node to the available list + if (! d_available) + d_available = l_node; + else + l_node->insert_before (d_available); + d_n_used--; +// signal the condition when new data arrives + d_ioBlock->signal (); +// unlock the mutex for thread safety + d_internal->unlock (); + }; + + __INLINE__ void iterate_start () { d_iterate = d_current; }; + + s_node_ptr iterate_next () { +#if 0 +// lock the mutex for thread safety + d_internal->lock (); +#endif + s_node_ptr l_this = NULL; + if (d_iterate) { + l_this = d_iterate; + d_iterate = d_iterate->next (); + if (d_iterate == d_current) + d_iterate = NULL; + } +#if 0 +// unlock the mutex for thread safety + d_internal->unlock (); +#endif + return (l_this); + }; + + __INLINE__ T object () { return (d_current->d_object); }; + __INLINE__ void object (T l_object) { d_current->d_object = l_object; }; + __INLINE__ UInt32 num_nodes () { return (d_n_nodes); }; + __INLINE__ UInt32 num_used () { return (d_n_used); }; + __INLINE__ void num_used (UInt32 l_n_used) { d_n_used = l_n_used; }; + __INLINE__ UInt32 num_available () { return (d_n_nodes - d_n_used); }; + __INLINE__ void num_used_inc (void) { + if (d_n_used < d_n_nodes) ++d_n_used; + }; + __INLINE__ void num_used_dec (void) { + if (d_n_used != 0) --d_n_used; +// signal the condition that new data has arrived + d_ioBlock->signal (); + }; + __INLINE__ bool in_use () { return (d_n_used != 0); }; +}; + +template class s_both +{ +private: + s_node* d_node; + void* d_this; +public: + __INLINE__ s_both (s_node* l_node, void* l_this) + : d_node (l_node), d_this (l_this) {}; + __INLINE__ ~s_both () {}; + __INLINE__ s_node* node () { return (d_node); }; + __INLINE__ void* This () { return (d_this); }; + __INLINE__ void set (s_node* l_node, void* l_this) { + d_node = l_node; d_this = l_this;}; +}; + +#endif /* _CIRCULAR_LINKED_LIST_H_ */ diff --git a/usrp/host/lib/legacy/darwin_libusb.h b/usrp/host/lib/legacy/darwin_libusb.h new file mode 100644 index 00000000..bdbdd344 --- /dev/null +++ b/usrp/host/lib/legacy/darwin_libusb.h @@ -0,0 +1,190 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * The following code was taken from LIBUSB verion 0.1.10a, + * and makes the fusb_darwin codes do-able in the current GR + * programming framework. Parts and pieces were taken from + * usbi.h, darwin.c, and error.h . + * + * LIBUSB version 0.1.10a is covered by the LGPL, version 2; + * These codes are used with permission from: + * (c) 2000-2003 Johannes Erdfelt + * (c) 2002-2005 Nathan Hjelm + * All rights reserved. + */ + +#ifndef __DARWIN_LIBUSB_H__ +#define __DARWIN_LIBUSB_H__ + +#include +#include +#include +#include + +extern "C" { +static char * +darwin_error_str (int result) +{ + switch (result) { + case kIOReturnSuccess: + return "no error"; + case kIOReturnNotOpen: + return "device not opened for exclusive access"; + case kIOReturnNoDevice: + return "no connection to an IOService"; + case kIOUSBNoAsyncPortErr: + return "no asyc port has been opened for interface"; + case kIOReturnExclusiveAccess: + return "another process has device opened for exclusive access"; + case kIOUSBPipeStalled: + return "pipe is stalled"; + case kIOReturnError: + return "could not establish a connection to Darin kernel"; + case kIOReturnBadArgument: + return "invalid argument"; + default: + return "unknown error"; + } +} + +/* not a valid errorno outside darwin.c */ +#define LUSBDARWINSTALL (ELAST+1) + +static int +darwin_to_errno (int result) +{ + switch (result) { + case kIOReturnSuccess: + return 0; + case kIOReturnNotOpen: + return EBADF; + case kIOReturnNoDevice: + case kIOUSBNoAsyncPortErr: + return ENXIO; + case kIOReturnExclusiveAccess: + return EBUSY; + case kIOUSBPipeStalled: + return LUSBDARWINSTALL; + case kIOReturnBadArgument: + return EINVAL; + case kIOReturnError: + default: + return 1; + } +} + +typedef enum { + USB_ERROR_TYPE_NONE = 0, + USB_ERROR_TYPE_STRING, + USB_ERROR_TYPE_ERRNO, +} usb_error_type_t; + +extern char usb_error_str[1024]; +extern int usb_error_errno; +extern usb_error_type_t usb_error_type; + +#define USB_ERROR(r, x) \ + do { \ + usb_error_type = USB_ERROR_TYPE_ERRNO; \ + usb_error_errno = x; \ + return r; \ + } while (0) + +#define USB_ERROR_STR(r, x, format, args...) \ + do { \ + usb_error_type = USB_ERROR_TYPE_STRING; \ + snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ + if (usb_debug) \ + fprintf(stderr, "USB error: %s\n", usb_error_str); \ + return r; \ + } while (0) + +#define USB_ERROR_STR_ORIG(x, format, args...) \ + do { \ + usb_error_type = USB_ERROR_TYPE_STRING; \ + snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ + if (usb_debug) \ + fprintf(stderr, "USB error: %s\n", usb_error_str); \ + return x; \ + } while (0) + +#define USB_ERROR_STR_NO_RET(x, format, args...) \ + do { \ + usb_error_type = USB_ERROR_TYPE_STRING; \ + snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ + if (usb_debug) \ + fprintf(stderr, "USB error: %s\n", usb_error_str); \ + } while (0) + +/* simple function that figures out what pipeRef is associated with an endpoint */ +static int ep_to_pipeRef (darwin_dev_handle *device, int ep) +{ + io_return_t ret; + UInt8 numep, direction, number; + UInt8 dont_care1, dont_care3; + UInt16 dont_care2; + int i; + + if (usb_debug > 3) + fprintf(stderr, "Converting ep address to pipeRef.\n"); + + /* retrieve the total number of endpoints on this interface */ + ret = (*(device->interface))->GetNumEndpoints(device->interface, &numep); + if ( ret ) { + if ( usb_debug > 3 ) + fprintf ( stderr, "ep_to_pipeRef: interface is %p\n", device->interface ); + USB_ERROR_STR_ORIG ( -ret, "ep_to_pipeRef: can't get number of endpoints for interface" ); + } + + /* iterate through the pipeRefs until we find the correct one */ + for (i = 1 ; i <= numep ; i++) { + ret = (*(device->interface))->GetPipeProperties(device->interface, i, &direction, &number, + &dont_care1, &dont_care2, &dont_care3); + + if (ret != kIOReturnSuccess) { + fprintf (stderr, "ep_to_pipeRef: an error occurred getting pipe information on pipe %d\n", + i ); + USB_ERROR_STR_ORIG (-darwin_to_errno(ret), "ep_to_pipeRef(GetPipeProperties): %s", darwin_error_str(ret)); + } + + if (usb_debug > 3) + fprintf (stderr, "ep_to_pipeRef: Pipe %i: DIR: %i number: %i\n", i, direction, number); + + /* calculate the endpoint of the pipe and check it versus the requested endpoint */ + if ( ((direction << 7 & USB_ENDPOINT_DIR_MASK) | (number & USB_ENDPOINT_ADDRESS_MASK)) == ep ) { + if (usb_debug > 3) + fprintf(stderr, "ep_to_pipeRef: pipeRef for ep address 0x%02x found: 0x%02x\n", ep, i); + + return i; + } + } + + if (usb_debug > 3) + fprintf(stderr, "ep_to_pipeRef: No pipeRef found with endpoint address 0x%02x.\n", ep); + + /* none of the found pipes match the requested endpoint */ + return -1; +} + +} +#endif /* __DARWIN_LIBUSB_H__ */ diff --git a/usrp/host/lib/legacy/dump_data.py b/usrp/host/lib/legacy/dump_data.py new file mode 100755 index 00000000..34dde3dc --- /dev/null +++ b/usrp/host/lib/legacy/dump_data.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# Copyright 2003 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +import sys +import struct + +fin = sys.stdin + +count = 0 + +while 1: + s = fin.read(2) + if not s or len(s) != 2: + break + + v, = struct.unpack ('H', s) + iv = int(v) & 0xffff + print "%8d %6d 0x%04x" % (count, iv, iv) + count += 1 + + + diff --git a/usrp/host/lib/legacy/fusb.cc b/usrp/host/lib/legacy/fusb.cc new file mode 100644 index 00000000..01ced9e2 --- /dev/null +++ b/usrp/host/lib/legacy/fusb.cc @@ -0,0 +1,60 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + + +// ------------------------------------------------------------------------ +// device handle +// ------------------------------------------------------------------------ + +fusb_devhandle::fusb_devhandle (usb_dev_handle *udh) + : d_udh (udh) +{ + // that's it +}; + +fusb_devhandle::~fusb_devhandle () +{ + // nop +} + +// ------------------------------------------------------------------------ +// end point handle +// ------------------------------------------------------------------------ + +fusb_ephandle::fusb_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) + : d_endpoint (endpoint), d_input_p (input_p), + d_block_size (block_size), d_nblocks (nblocks), d_started (false) +{ + // that't it +} + +fusb_ephandle::~fusb_ephandle () +{ + // nop +} diff --git a/usrp/host/lib/legacy/fusb.h b/usrp/host/lib/legacy/fusb.h new file mode 100644 index 00000000..da1b7c0a --- /dev/null +++ b/usrp/host/lib/legacy/fusb.h @@ -0,0 +1,133 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +// Fast USB interface + +#ifndef _FUSB_H_ +#define _FUSB_H_ + + +struct usb_dev_handle; +class fusb_ephandle; + +/*! + * \brief abstract usb device handle + */ +class fusb_devhandle { +private: + // NOT IMPLEMENTED + fusb_devhandle (const fusb_devhandle &rhs); // no copy constructor + fusb_devhandle &operator= (const fusb_devhandle &rhs); // no assignment operator + +protected: + usb_dev_handle *d_udh; + +public: + // CREATORS + fusb_devhandle (usb_dev_handle *udh); + virtual ~fusb_devhandle (); + + // MANIPULATORS + + /*! + * \brief return an ephandle of the correct subtype + */ + virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0) = 0; + + // ACCESSORS + usb_dev_handle *get_usb_dev_handle () const { return d_udh; } +}; + + +/*! + * \brief abstract usb end point handle + */ +class fusb_ephandle { +private: + // NOT IMPLEMENTED + fusb_ephandle (const fusb_ephandle &rhs); // no copy constructor + fusb_ephandle &operator= (const fusb_ephandle &rhs); // no assignment operator + +protected: + int d_endpoint; + bool d_input_p; + int d_block_size; + int d_nblocks; + bool d_started; + +public: + fusb_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle (); + + virtual bool start () = 0; //!< begin streaming i/o + virtual bool stop () = 0; //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void *buffer, int nbytes) = 0; + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void *buffer, int nbytes) = 0; + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion () = 0; + + /*! + * \brief returns current block size. + */ + int block_size () { return d_block_size; }; +}; + + +/*! + * \brief factory for creating concrete instances of the appropriate subtype. + */ +class fusb_sysconfig { +public: + /*! + * \brief returns fusb_devhandle or throws if trouble + */ + static fusb_devhandle *make_devhandle (usb_dev_handle *udh); + + /*! + * \brief returns max block size hard limit + */ + static int max_block_size (); + + /*! + * \brief returns the default buffer size + */ + static int default_buffer_size (); + +}; + +#endif /* _FUSB_H_ */ diff --git a/usrp/host/lib/legacy/fusb_darwin.cc b/usrp/host/lib/legacy/fusb_darwin.cc new file mode 100644 index 00000000..8be54537 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_darwin.cc @@ -0,0 +1,499 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// tell mld_threads to NOT use omni_threads, +// but rather Darwin's pthreads +#undef _USE_OMNI_THREADS_ + +#include +#include "fusb.h" +#include "fusb_darwin.h" +#include "darwin_libusb.h" + +static const int USB_TIMEOUT = 100; // in milliseconds +static const UInt8 NUM_QUEUE_ITEMS = 20; + +fusb_devhandle_darwin::fusb_devhandle_darwin (usb_dev_handle* udh) + : fusb_devhandle (udh) +{ + // that's it +} + +fusb_devhandle_darwin::~fusb_devhandle_darwin () +{ + // nop +} + +fusb_ephandle* +fusb_devhandle_darwin::make_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) +{ + return new fusb_ephandle_darwin (this, endpoint, input_p, + block_size, nblocks); +} + +// ---------------------------------------------------------------- + +fusb_ephandle_darwin::fusb_ephandle_darwin (fusb_devhandle_darwin* dh, + int endpoint, bool input_p, + int block_size, int nblocks) + : fusb_ephandle (endpoint, input_p, block_size, nblocks), + d_devhandle (dh), d_pipeRef (0), d_transferType (0), + d_interfaceRef (0), d_interface (0), d_queue (0), + d_buffer (0), d_bufLenBytes (0) +{ + d_bufLenBytes = fusb_sysconfig::max_block_size(); + +// create circular buffer + d_buffer = new circular_buffer (NUM_QUEUE_ITEMS * d_bufLenBytes, + !d_input_p, d_input_p); + +// create the queue + d_queue = new circular_linked_list (NUM_QUEUE_ITEMS); + d_queue->iterate_start (); + s_node_ptr l_node = d_queue->iterate_next (); + while (l_node) { + l_node->both (new s_both (l_node, this)); + s_buffer_ptr l_buf = new s_buffer (d_bufLenBytes); + l_node->object (l_buf); + l_node = d_queue->iterate_next (); + l_buf = NULL; + } + + d_readRunning = new mld_mutex (); + d_runThreadRunning = new mld_mutex (); + d_runBlock = new mld_condition (); + d_readBlock = new mld_condition (); +} + +fusb_ephandle_darwin::~fusb_ephandle_darwin () +{ + stop (); + + d_queue->iterate_start (); + s_node_ptr l_node = d_queue->iterate_next (); + while (l_node) { + s_both_ptr l_both = l_node->both (); + delete l_both; + l_both = NULL; + l_node->both (NULL); + s_buffer_ptr l_buf = l_node->object (); + delete l_buf; + l_buf = NULL; + l_node->object (NULL); + l_node = d_queue->iterate_next (); + } + delete d_queue; + d_queue = NULL; + delete d_buffer; + d_buffer = NULL; + delete d_readRunning; + d_readRunning = NULL; + delete d_runThreadRunning; + d_runThreadRunning = NULL; + delete d_runBlock; + d_runBlock = NULL; + delete d_readBlock; + d_readBlock = NULL; +} + +bool +fusb_ephandle_darwin::start () +{ + UInt8 direction, number, interval; + UInt16 maxPacketSize; + +// reset circular buffer + d_buffer->reset (); + +// reset the queue + d_queue->num_used (0); + d_queue->iterate_start (); + s_node_ptr l_node = d_queue->iterate_next (); + while (l_node) { + l_node->both()->set (l_node, this); + l_node->object()->reset (); + l_node->set_available (); + l_node = d_queue->iterate_next (); + } + + d_pipeRef = d_transferType = 0; + + usb_dev_handle* dev = d_devhandle->get_usb_dev_handle (); + if (! dev) + USB_ERROR_STR (false, -ENXIO, "fusb_ephandle_darwin::start: " + "null device"); + + darwin_dev_handle* device = (darwin_dev_handle*) dev->impl_info; + if (! device) + USB_ERROR_STR (false, -ENOENT, "fusb_ephandle_darwin::start: " + "device not initialized"); + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::start: " + "dev = %p, device = %p\n", dev, device); + + d_interfaceRef = device->interface; + if (! d_interfaceRef) + USB_ERROR_STR (false, -EACCES, "fusb_ephandle_darwin::start: " + "interface used without being claimed"); + d_interface = *d_interfaceRef; + +// get read or write pipe info (depends on "d_input_p") + + if (usb_debug > 3) + fprintf (stderr, "fusb_ephandle_darwin::start " + "d_endpoint = %d, d_input_p = %s\n", + d_endpoint, d_input_p ? "TRUE" : "FALSE"); + + int l_endpoint = (d_input_p ? USB_ENDPOINT_IN : USB_ENDPOINT_OUT); + int pipeRef = ep_to_pipeRef (device, d_endpoint | l_endpoint); + if (pipeRef < 0) + USB_ERROR_STR (false, -EINVAL, "fusb_ephandle_darwin::start " + " invalid pipeRef.\n"); + + d_pipeRef = pipeRef; + d_interface->GetPipeProperties (d_interfaceRef, + d_pipeRef, + &direction, + &number, + &d_transferType, + &maxPacketSize, + &interval); + if (usb_debug == 3) + fprintf (stderr, "fusb_ephandle_darwin::start: %s: ep = 0x%02x, " + "pipeRef = %d, d_i = %p, d_iR = %p, if_dir = %d, if_# = %d, " + "if_int = %d, if_maxPS = %d\n", d_input_p ? "read" : "write", + d_endpoint, d_pipeRef, d_interface, d_interfaceRef, direction, + number, interval, maxPacketSize); + +// set global start boolean + d_started = true; + +// create the run thread, which allows OSX to process I/O separately + d_runThread = new mld_thread (run_thread, this); + +// wait until the threads are -really- going + d_runBlock->wait (); + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::start: %s started.\n", + d_input_p ? "read" : "write"); + + return (true); +} + +void +fusb_ephandle_darwin::run_thread (void* arg) +{ + fusb_ephandle_darwin* This = static_cast(arg); + mld_mutex_ptr l_runThreadRunning = This->d_runThreadRunning; + l_runThreadRunning->lock (); + + mld_mutex_ptr l_readRunning = This->d_readRunning; + mld_condition_ptr l_readBlock = This->d_readBlock; + + bool l_input_p = This->d_input_p; + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::run_thread: " + "starting for %s.\n", + l_input_p ? "read" : "write"); + + usb_interface_t** l_interfaceRef = This->d_interfaceRef; + usb_interface_t* l_interface = This->d_interface; + CFRunLoopSourceRef l_cfSource; + +// create async run loop + l_interface->CreateInterfaceAsyncEventSource (l_interfaceRef, &l_cfSource); + CFRunLoopAddSource (CFRunLoopGetCurrent (), l_cfSource, + kCFRunLoopDefaultMode); +// get run loop reference, to allow other threads to stop + This->d_CFRunLoopRef = CFRunLoopGetCurrent (); + + mld_thread_ptr l_rwThread = NULL; + + if (l_input_p) { + l_rwThread = new mld_thread (read_thread, arg); +// wait until the the rwThread is -really- going + l_readBlock->wait (); + } + +// now signal the run condition to release and finish ::start() + This->d_runBlock->signal (); + +// run the loop + CFRunLoopRun (); + + if (l_input_p) { +// wait for read_thread () to finish + l_readRunning->lock (); + l_readRunning->unlock (); + } + +// remove run loop stuff + CFRunLoopRemoveSource (CFRunLoopGetCurrent (), + l_cfSource, kCFRunLoopDefaultMode); + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::run_thread: finished for %s.\n", + l_input_p ? "read" : "write"); + + l_runThreadRunning->unlock (); +} + +void +fusb_ephandle_darwin::read_thread (void* arg) +{ + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::read_thread: starting.\n"); + + fusb_ephandle_darwin* This = static_cast(arg); + + mld_mutex_ptr l_readRunning = This->d_readRunning; + l_readRunning->lock (); + +// signal the read condition from run_thread() to continue + mld_condition_ptr l_readBlock = This->d_readBlock; + l_readBlock->signal (); + + s_queue_ptr l_queue = This->d_queue; + l_queue->iterate_start (); + s_node_ptr l_node = l_queue->iterate_next (); + while (l_node) { + This->read_issue (l_node->both ()); + l_node = l_queue->iterate_next (); + } + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::read_thread: finished.\n"); + + l_readRunning->unlock (); +} + +void +fusb_ephandle_darwin::read_issue (s_both_ptr l_both) +{ + if ((! l_both) || (! d_started)) + return; + +// set the node and buffer from the input "both" + s_node_ptr l_node = l_both->node (); + s_buffer_ptr l_buf = l_node->object (); + void* v_buffer = (void*) l_buf->buffer (); + +// read up to d_bufLenBytes + UInt32 bufLen = d_bufLenBytes; + l_buf->n_used (bufLen); + +// setup system call result + io_return_t result = kIOReturnSuccess; + + if (d_transferType == kUSBInterrupt) +/* This is an interrupt pipe. We can't specify a timeout. */ + result = d_interface->ReadPipeAsync + (d_interfaceRef, d_pipeRef, v_buffer, bufLen, + (IOAsyncCallback1) read_completed, (void*) l_both); + else + result = d_interface->ReadPipeAsyncTO + (d_interfaceRef, d_pipeRef, v_buffer, bufLen, 0, USB_TIMEOUT, + (IOAsyncCallback1) read_completed, (void*) l_both); + + if (result != kIOReturnSuccess) + USB_ERROR_STR_NO_RET (- darwin_to_errno (result), + "fusb_ephandle_darwin::read_issue " + "(ReadPipeAsync%s): %s", + d_transferType == kUSBInterrupt ? "" : "TO", + darwin_error_str (result)); +} + +void +fusb_ephandle_darwin::read_completed (void* refCon, + io_return_t result, + void* io_size) +{ + UInt32 l_size = (UInt32) io_size; + s_both_ptr l_both = static_cast(refCon); + fusb_ephandle_darwin* This = static_cast(l_both->This ()); + s_node_ptr l_node = l_both->node (); + circular_buffer* l_buffer = This->d_buffer; + s_buffer_ptr l_buf = l_node->object (); + UInt32 l_i_size = l_buf->n_used (); + + if (This->d_started && (l_i_size != l_size)) + fprintf (stderr, "fusb_ephandle_darwin::read_completed: " + "Expected %ld bytes; read %ld.\n", + l_i_size, l_size); + +// add this read to the transfer buffer + if (l_buffer->enqueue (l_buf->buffer (), l_size) == -1) { + fputs ("iU", stderr); + fflush (stderr); + } + +// set buffer's # data to 0 + l_buf->n_used (0); + +// issue another read for this "both" + This->read_issue (l_both); +} + +int +fusb_ephandle_darwin::read (void* buffer, int nbytes) +{ + UInt32 l_nbytes = (UInt32) nbytes; + d_buffer->dequeue ((char*) buffer, &l_nbytes); + return ((int) l_nbytes); +} + +int +fusb_ephandle_darwin::write (const void* buffer, int nbytes) +{ + UInt32 l_nbytes = (UInt32) nbytes; + + if (! d_started) return (0); + + while (l_nbytes != 0) { +// find out how much data to copy; limited to "d_bufLenBytes" per node + UInt32 t_nbytes = (l_nbytes > d_bufLenBytes) ? d_bufLenBytes : l_nbytes; + +// get next available node to write into; +// blocks internally if none available + s_node_ptr l_node = d_queue->find_next_available_node (); + +// copy the input into the node's buffer + s_buffer_ptr l_buf = l_node->object (); + l_buf->buffer ((char*) buffer, t_nbytes); + void* v_buffer = (void*) l_buf->buffer (); + +// setup callback parameter & system call return + s_both_ptr l_both = l_node->both (); + io_return_t result = kIOReturnSuccess; + + if (d_transferType == kUSBInterrupt) +/* This is an interrupt pipe ... can't specify a timeout. */ + result = d_interface->WritePipeAsync + (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes, + (IOAsyncCallback1) write_completed, (void*) l_both); + else + result = d_interface->WritePipeAsyncTO + (d_interfaceRef, d_pipeRef, v_buffer, l_nbytes, 0, USB_TIMEOUT, + (IOAsyncCallback1) write_completed, (void*) l_both); + + if (result != kIOReturnSuccess) + USB_ERROR_STR (-1, - darwin_to_errno (result), + "fusb_ephandle_darwin::write_thread " + "(WritePipeAsync%s): %s", + d_transferType == kUSBInterrupt ? "" : "TO", + darwin_error_str (result)); + l_nbytes -= t_nbytes; + } + + return (nbytes); +} + +void +fusb_ephandle_darwin::write_completed (void* refCon, + io_return_t result, + void* io_size) +{ + s_both_ptr l_both = static_cast(refCon); + fusb_ephandle_darwin* This = static_cast(l_both->This ()); + UInt32 l_size = (UInt32) io_size; + s_node_ptr l_node = l_both->node (); + s_queue_ptr l_queue = This->d_queue; + s_buffer_ptr l_buf = l_node->object (); + UInt32 l_i_size = l_buf->n_used (); + + if (This->d_started && (l_i_size != l_size)) + fprintf (stderr, "fusb_ephandle_darwin::write_completed: " + "Expected %ld bytes written; wrote %ld.\n", + l_i_size, l_size); + +// set buffer's # data to 0 + l_buf->n_used (0); +// make the node available for reuse + l_queue->make_node_available (l_node); +} + +void +fusb_ephandle_darwin::abort () +{ + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::abort: starting.\n"); + + io_return_t result = d_interface->AbortPipe (d_interfaceRef, d_pipeRef); + + if (result != kIOReturnSuccess) + USB_ERROR_STR_NO_RET (- darwin_to_errno (result), + "fusb_ephandle_darwin::abort " + "(AbortPipe): %s", darwin_error_str (result)); + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::abort: finished.\n"); +} + +bool +fusb_ephandle_darwin::stop () +{ + if (! d_started) + return (true); + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::stop: stopping %s.\n", + d_input_p ? "read" : "write"); + + d_started = false; + +// abort any pending IO transfers + abort (); + +// wait for write transfer to finish + wait_for_completion (); + +// tell IO buffer to abort any waiting conditions + d_buffer->abort (); + +// stop the run loop + CFRunLoopStop (d_CFRunLoopRef); + +// wait for the runThread to stop + d_runThreadRunning->lock (); + d_runThreadRunning->unlock (); + + if (usb_debug) + fprintf (stderr, "fusb_ephandle_darwin::stop: %s stopped.\n", + d_input_p ? "read" : "write"); + + return (true); +} + +void +fusb_ephandle_darwin::wait_for_completion () +{ + if (d_queue) + while (d_queue->in_use ()) + usleep (1000); +} diff --git a/usrp/host/lib/legacy/fusb_darwin.h b/usrp/host/lib/legacy/fusb_darwin.h new file mode 100644 index 00000000..9254c7f7 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_darwin.h @@ -0,0 +1,215 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FUSB_DARWIN_H_ +#define _FUSB_DARWIN_H_ + +#include +#include "fusb.h" +#include +#include +#include +#include +#include "circular_linked_list.h" +#include "circular_buffer.h" + +// for MacOS X 10.4.[0-3] +#define usb_interface_t IOUSBInterfaceInterface220 +#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 +#define InterfaceVersion 220 + +// for MacOS X 10.3.[0-9] and 10.4.[0-3] +#define usb_device_t IOUSBDeviceInterface197 +#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 +#define DeviceVersion 197 + +extern "C" { +typedef struct usb_dev_handle { + int fd; + + struct usb_bus *bus; + struct usb_device *device; + + int config; + int interface; + int altsetting; + + /* Added by RMT so implementations can store other per-open-device data */ + void *impl_info; +} usb_dev_handle; + +/* Darwin/OS X impl does not use fd field, instead it uses this */ +typedef struct darwin_dev_handle { + usb_device_t** device; + usb_interface_t** interface; + int open; +} darwin_dev_handle; + +typedef IOReturn io_return_t; +typedef IOCFPlugInInterface *io_cf_plugin_ref_t; + +static int ep_to_pipeRef (darwin_dev_handle* device, int ep); +extern int usb_debug; +} + +class s_buffer +{ +private: + char* d_buffer; + UInt32 d_n_used, d_n_alloc; + +public: + inline s_buffer (UInt32 n_alloc = 0) { + d_n_used = 0; + d_n_alloc = n_alloc; + if (n_alloc) { + d_buffer = (char*) new char [n_alloc]; + } else { + d_buffer = 0; + } + }; + inline ~s_buffer () { + if (d_n_alloc) { + delete [] d_buffer; + } + }; + inline UInt32 n_used () { return (d_n_used); }; + inline void n_used (UInt32 bufLen) { + d_n_used = (bufLen > d_n_alloc) ? d_n_alloc : bufLen; }; + inline UInt32 n_alloc () { return (d_n_alloc); }; + void buffer (char* l_buffer, UInt32 bufLen) { + if (bufLen > d_n_alloc) { + fprintf (stderr, "s_buffer::set: Copying only allocated bytes.\n"); + bufLen = d_n_alloc; + } + if (!l_buffer) { + fprintf (stderr, "s_buffer::set: NULL buffer.\n"); + return; + } + bcopy (l_buffer, d_buffer, bufLen); + d_n_used = bufLen; + }; + inline char* buffer () { return (d_buffer); }; + inline void reset () { + bzero (d_buffer, d_n_alloc); + d_n_used = 0; + }; +}; + +typedef s_buffer* s_buffer_ptr; +typedef s_node* s_node_ptr; +typedef circular_linked_list* s_queue_ptr; +typedef s_both* s_both_ptr; + +/*! + * \brief darwin implementation of fusb_devhandle + * + * This is currently identical to the generic implementation + * and is intended as a starting point for whatever magic is + * required to make usb fly. + */ +class fusb_devhandle_darwin : public fusb_devhandle +{ +public: + // CREATORS + fusb_devhandle_darwin (usb_dev_handle* udh); + virtual ~fusb_devhandle_darwin (); + + // MANIPULATORS + virtual fusb_ephandle* make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); +}; + +/*! + * \brief darwin implementation of fusb_ephandle + * + * This is currently identical to the generic implementation + * and is intended as a starting point for whatever magic is + * required to make usb fly. + */ +class fusb_ephandle_darwin : public fusb_ephandle +{ +private: + fusb_devhandle_darwin* d_devhandle; + mld_thread_ptr d_runThread; + mld_mutex_ptr d_runThreadRunning; + + CFRunLoopRef d_CFRunLoopRef; + + static void write_completed (void* ret_io_size, + io_return_t result, + void* io_size); + static void read_completed (void* ret_io_size, + io_return_t result, + void* io_size); + static void run_thread (void* arg); + static void read_thread (void* arg); + + void read_issue (s_both_ptr l_both); + +public: + // variables, for now + UInt8 d_pipeRef, d_transferType; + usb_interface_t** d_interfaceRef; + usb_interface_t* d_interface; + s_queue_ptr d_queue; + circular_buffer* d_buffer; + UInt32 d_bufLenBytes; + mld_mutex_ptr d_readRunning; + mld_condition_ptr d_runBlock, d_readBlock; + +// CREATORS + + fusb_ephandle_darwin (fusb_devhandle_darwin *dh, int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle_darwin (); + +// MANIPULATORS + + virtual bool start (); //!< begin streaming i/o + virtual bool stop (); //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void* buffer, int nbytes); + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void* buffer, int nbytes); + + /* + * abort any pending IO transfers + */ + void abort (); + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion (); +}; + +#endif /* _FUSB_DARWIN_H_ */ diff --git a/usrp/host/lib/legacy/fusb_generic.cc b/usrp/host/lib/legacy/fusb_generic.cc new file mode 100644 index 00000000..f9f3c29d --- /dev/null +++ b/usrp/host/lib/legacy/fusb_generic.cc @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + + +static const int USB_TIMEOUT = 1000; // in milliseconds + + +fusb_devhandle_generic::fusb_devhandle_generic (usb_dev_handle *udh) + : fusb_devhandle (udh) +{ + // that's it +} + +fusb_devhandle_generic::~fusb_devhandle_generic () +{ + // nop +} + +fusb_ephandle * +fusb_devhandle_generic::make_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) +{ + return new fusb_ephandle_generic (this, endpoint, input_p, + block_size, nblocks); +} + +// ---------------------------------------------------------------- + +fusb_ephandle_generic::fusb_ephandle_generic (fusb_devhandle_generic *dh, + int endpoint, bool input_p, + int block_size, int nblocks) + : fusb_ephandle (endpoint, input_p, block_size, nblocks), + d_devhandle (dh) +{ + // that's it +} + +fusb_ephandle_generic::~fusb_ephandle_generic () +{ + // nop +} + +bool +fusb_ephandle_generic::start () +{ + d_started = true; + return true; +} + +bool +fusb_ephandle_generic::stop () +{ + d_started = false; + return true; +} + +int +fusb_ephandle_generic::write (const void *buffer, int nbytes) +{ + if (!d_started) // doesn't matter here, but keeps semantics constant + return -1; + + if (d_input_p) + return -1; + + return usb_bulk_write (d_devhandle->get_usb_dev_handle (), + d_endpoint, (char *) buffer, nbytes, USB_TIMEOUT); +} + +int +fusb_ephandle_generic::read (void *buffer, int nbytes) +{ + if (!d_started) // doesn't matter here, but keeps semantics constant + return -1; + + if (!d_input_p) + return -1; + + return usb_bulk_read (d_devhandle->get_usb_dev_handle (), + d_endpoint|USB_ENDPOINT_IN, (char *) buffer, nbytes, + USB_TIMEOUT); +} diff --git a/usrp/host/lib/legacy/fusb_generic.h b/usrp/host/lib/legacy/fusb_generic.h new file mode 100644 index 00000000..8c3a4564 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_generic.h @@ -0,0 +1,83 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FUSB_GENERIC_H_ +#define _FUSB_GENERIC_H_ + +#include + +/*! + * \brief generic implementation of fusb_devhandle using only libusb + */ +class fusb_devhandle_generic : public fusb_devhandle +{ +public: + // CREATORS + fusb_devhandle_generic (usb_dev_handle *udh); + virtual ~fusb_devhandle_generic (); + + // MANIPULATORS + virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); +}; + + +/*! + * \brief generic implementation of fusb_ephandle using only libusb + */ +class fusb_ephandle_generic : public fusb_ephandle +{ +private: + fusb_devhandle_generic *d_devhandle; + +public: + // CREATORS + fusb_ephandle_generic (fusb_devhandle_generic *dh, int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle_generic (); + + // MANIPULATORS + + virtual bool start (); //!< begin streaming i/o + virtual bool stop (); //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void *buffer, int nbytes); + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void *buffer, int nbytes); + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion () { }; +}; + +#endif /* _FUSB_GENERIC_H_ */ + diff --git a/usrp/host/lib/legacy/fusb_linux.cc b/usrp/host/lib/legacy/fusb_linux.cc new file mode 100644 index 00000000..1eaba4e5 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_linux.cc @@ -0,0 +1,686 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include // libusb header +#include +#ifdef HAVE_LINUX_COMPILER_H +#include +#endif +#include // interface to kernel portion of user mode usb driver +#include +#include +#include +#include +#include +#include + +#define MINIMIZE_TX_BUFFERING 1 // must be defined to 0 or 1 + + +static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); // hard limit +static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE; +static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20); // 4 MB / endpoint + + +// Totally evil and fragile extraction of file descriptor from +// guts of libusb. They don't install usbi.h, which is what we'd need +// to do this nicely. +// +// FIXME if everything breaks someday in the future, look here... + +static int +fd_from_usb_dev_handle (usb_dev_handle *udh) +{ + return *((int *) udh); +} + +inline static void +urb_set_ephandle (usbdevfs_urb *urb, fusb_ephandle_linux *handle) +{ + urb->usercontext = handle; +} + +inline static fusb_ephandle_linux * +urb_get_ephandle (usbdevfs_urb *urb) +{ + return (fusb_ephandle_linux *) urb->usercontext; +} + +// ------------------------------------------------------------------------ +// USB request block (urb) allocation +// ------------------------------------------------------------------------ + +static usbdevfs_urb * +alloc_urb (fusb_ephandle_linux *self, int buffer_length, int endpoint, + bool input_p, unsigned char *write_buffer) +{ + usbdevfs_urb *urb = new usbdevfs_urb; + memset (urb, 0, sizeof (*urb)); + + urb->buffer_length = buffer_length; + + // We allocate dedicated memory only for input buffers. + // For output buffers we reuse the same buffer (the kernel + // copies the data at submital time) + + if (input_p) + urb->buffer = new unsigned char [buffer_length]; + else + urb->buffer = write_buffer; + + // init common values + + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0); + + // USBDEVFS_URB_QUEUE_BULK goes away in linux 2.5, but is needed if + // we are using a 2.4 usb-uhci host controller driver. This is + // unlikely since we're almost always going to be plugged into a + // high speed host controller (ehci) +#if 0 && defined (USBDEVFS_URB_QUEUE_BULK) + urb->flags = USBDEVFS_URB_QUEUE_BULK; +#endif + + urb->signr = 0; + urb_set_ephandle (urb, self); + + return urb; +} + +static void +free_urb (usbdevfs_urb *urb) +{ + // if this was an input urb, free the buffer + if (urb->endpoint & 0x80) + delete [] ((unsigned char *) urb->buffer); + + delete urb; +} + +// ------------------------------------------------------------------------ +// device handle +// ------------------------------------------------------------------------ + +fusb_devhandle_linux::fusb_devhandle_linux (usb_dev_handle *udh) + : fusb_devhandle (udh) +{ + // that's all +} + +fusb_devhandle_linux::~fusb_devhandle_linux () +{ + // if there are any pending requests, cancel them and free the urbs. + + std::list::reverse_iterator it; + + for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){ + _cancel_urb (*it); + free_urb (*it); + } +} + +fusb_ephandle * +fusb_devhandle_linux::make_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) +{ + return new fusb_ephandle_linux (this, endpoint, input_p, + block_size, nblocks); +} + + +// Attempt to cancel all transactions associated with eph. + +void +fusb_devhandle_linux::_cancel_pending_rqsts (fusb_ephandle_linux *eph) +{ + std::list::reverse_iterator it; + + for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){ + if (urb_get_ephandle (*it) == eph) + _cancel_urb (*it); + } +} + +void +fusb_devhandle_linux::pending_add (usbdevfs_urb *urb) +{ + d_pending_rqsts.push_back (urb); +} + +usbdevfs_urb * +fusb_devhandle_linux::pending_get () +{ + if (d_pending_rqsts.empty ()) + return 0; + + usbdevfs_urb *urb = d_pending_rqsts.front (); + d_pending_rqsts.pop_front (); + return urb; +} + +bool +fusb_devhandle_linux::pending_remove (usbdevfs_urb *urb) +{ + std::list::iterator result = find (d_pending_rqsts.begin (), + d_pending_rqsts.end (), + urb); + if (result == d_pending_rqsts.end ()){ + fprintf (stderr, "fusb::pending_remove: failed to find urb in pending_rqsts: %p\n", urb); + return false; + } + d_pending_rqsts.erase (result); + return true; +} + +/* + * Submit the urb to the kernel. + * iff successful, the urb will be placed on the devhandle's pending list. + */ +bool +fusb_devhandle_linux::_submit_urb (usbdevfs_urb *urb) +{ + int ret; + + ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_SUBMITURB, urb); + if (ret < 0){ + perror ("fusb::_submit_urb"); + return false; + } + + pending_add (urb); + return true; +} + +/* + * Attempt to cancel the in pending or in-progress urb transaction. + * Return true iff transaction was sucessfully cancelled. + * + * Failure to cancel should not be considered a problem. This frequently + * occurs if the transaction has already completed in the kernel but hasn't + * yet been reaped by the user mode code. + * + * urbs which were cancelled have their status field set to -ENOENT when + * they are reaped. + */ +bool +fusb_devhandle_linux::_cancel_urb (usbdevfs_urb *urb) +{ + int ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_DISCARDURB, urb); + if (ret < 0){ + // perror ("fusb::_cancel_urb"); + return false; + } + return true; +} + +/* + * Check with the kernel and see if any of our outstanding requests + * have completed. For each completed transaction, remove it from the + * devhandle's pending list and append it to the completed list for + * the corresponding endpoint. + * + * If any transactions are reaped return true. + * + * If ok_to_block_p is true, then this will block until at least one + * transaction completes. + */ +bool +fusb_devhandle_linux::_reap (bool ok_to_block_p) +{ + int ret; + int nreaped = 0; + usbdevfs_urb *urb = 0; + + int fd = fd_from_usb_dev_handle (d_udh); + + // try to reap as many as possible without blocking... + + while ((ret = ioctl (fd, USBDEVFS_REAPURBNDELAY, &urb)) == 0){ + if (urb->status != 0 && urb->status != -ENOENT){ + fprintf (stderr, "_reap: usb->status = %d, actual_length = %5d\n", + urb->status, urb->actual_length); + } + pending_remove (urb); + urb_get_ephandle (urb)->completed_list_add (urb); + nreaped++; + } + + if (nreaped > 0) // if we got any, return w/o blocking + return true; + + if (!ok_to_block_p) + return false; + + ret = ioctl (fd, USBDEVFS_REAPURB, &urb); + if (ret < 0){ + perror ("fusb::_reap"); + return false; + } + + pending_remove (urb); + urb_get_ephandle (urb)->completed_list_add (urb); + return true; +} + +void +fusb_devhandle_linux::_wait_for_completion () +{ + while (!d_pending_rqsts.empty ()) + _reap (true); +} + // ------------------------------------------------------------------------ +// end point handle +// ------------------------------------------------------------------------ + +fusb_ephandle_linux::fusb_ephandle_linux (fusb_devhandle_linux *devhandle, + int endpoint, + bool input_p, + int block_size, int nblocks) + : fusb_ephandle (endpoint, input_p, block_size, nblocks), + d_devhandle (devhandle), + d_write_work_in_progress (0), d_write_buffer (0), + d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0) +{ + + if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE) + throw std::out_of_range ("fusb_ephandle_linux: block_size"); + + if (d_nblocks < 0) + throw std::out_of_range ("fusb_ephandle_linux: nblocks"); + + if (d_block_size == 0) + d_block_size = DEFAULT_BLOCK_SIZE; + + if (d_nblocks == 0) + d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size); + + if (!d_input_p) + if (!MINIMIZE_TX_BUFFERING) + d_write_buffer = new unsigned char [d_block_size]; + + if (0) + fprintf(stderr, "fusb_ephandle_linux::ctor: d_block_size = %d d_nblocks = %d\n", + d_block_size, d_nblocks); + + // allocate urbs + + for (int i = 0; i < d_nblocks; i++) + d_free_list.push_back (alloc_urb (this, d_block_size, d_endpoint, + d_input_p, d_write_buffer)); +} + +fusb_ephandle_linux::~fusb_ephandle_linux () +{ + stop (); + + usbdevfs_urb *urb; + + while ((urb = free_list_get ()) != 0) + free_urb (urb); + + while ((urb = completed_list_get ()) != 0) + free_urb (urb); + + if (d_write_work_in_progress) + free_urb (d_write_work_in_progress); + + delete [] d_write_buffer; + + if (d_read_work_in_progress) + free_urb (d_read_work_in_progress); +} + +// ---------------------------------------------------------------- + +bool +fusb_ephandle_linux::start () +{ + if (d_started) + return true; // already running + + d_started = true; + + if (d_input_p){ // fire off all the reads + usbdevfs_urb *urb; + + int nerrors = 0; + while ((urb = free_list_get ()) != 0 && nerrors < d_nblocks){ + if (!submit_urb (urb)) + nerrors++; + } + } + + return true; +} + +// +// kill all i/o in progress. +// kill any completed but unprocessed transactions. +// +bool +fusb_ephandle_linux::stop () +{ + if (!d_started) + return true; + + d_devhandle->_cancel_pending_rqsts (this); + d_devhandle->_reap (false); + + + usbdevfs_urb *urb; + while ((urb = completed_list_get ()) != 0) + free_list_add (urb); + + if (d_write_work_in_progress){ + free_list_add (d_write_work_in_progress); + d_write_work_in_progress = 0; + } + + if (d_read_work_in_progress){ + free_list_add (d_read_work_in_progress); + d_read_work_in_progress = 0; + d_read_buffer = 0; + d_read_buffer_end = 0; + } + + if (d_free_list.size () != (unsigned) d_nblocks) + fprintf (stderr, "d_free_list.size () = %d, d_nblocks = %d\n", + d_free_list.size (), d_nblocks); + + assert (d_free_list.size () == (unsigned) d_nblocks); + + d_started = false; + return true; +} + +// ---------------------------------------------------------------- +// routines for writing +// ---------------------------------------------------------------- + +#if (MINIMIZE_TX_BUFFERING) + +int +fusb_ephandle_linux::write(const void *buffer, int nbytes) +{ + if (!d_started) + return -1; + + if (d_input_p) + return -1; + + assert(nbytes % 512 == 0); + + unsigned char *src = (unsigned char *) buffer; + + int n = 0; + while (n < nbytes){ + + usbdevfs_urb *urb = get_write_work_in_progress(); + assert(urb->actual_length == 0); + int m = std::min(nbytes - n, MAX_BLOCK_SIZE); + urb->buffer = src; + urb->buffer_length = m; + + n += m; + src += m; + + if (!submit_urb(urb)) + return -1; + + d_write_work_in_progress = 0; + } + + return n; +} + +#else + +int +fusb_ephandle_linux::write (const void *buffer, int nbytes) +{ + if (!d_started) + return -1; + + if (d_input_p) + return -1; + + unsigned char *src = (unsigned char *) buffer; + + int n = 0; + while (n < nbytes){ + + usbdevfs_urb *urb = get_write_work_in_progress (); + unsigned char *dst = (unsigned char *) urb->buffer; + int m = std::min (nbytes - n, urb->buffer_length - urb->actual_length); + + memcpy (&dst[urb->actual_length], &src[n], m); + urb->actual_length += m; + n += m; + + if (urb->actual_length == urb->buffer_length){ + if (!submit_urb (urb)) + return -1; + d_write_work_in_progress = 0; + } + } + + return n; +} + +#endif + +usbdevfs_urb * +fusb_ephandle_linux::get_write_work_in_progress () +{ + // if we've already got some work in progress, return it + + if (d_write_work_in_progress) + return d_write_work_in_progress; + + while (1){ + + reap_complete_writes (); + + usbdevfs_urb *urb = free_list_get (); + + if (urb != 0){ + assert (urb->actual_length == 0); + d_write_work_in_progress = urb; + return urb; + } + + // The free list is empty. Tell the device handle to reap. + // Anything it reaps for us will end up on our completed list. + + d_devhandle->_reap (true); + } +} + +void +fusb_ephandle_linux::reap_complete_writes () +{ + // take a look at the completed_list and xfer to free list after + // checking for errors. + + usbdevfs_urb *urb; + + while ((urb = completed_list_get ()) != 0){ + + // Check for any errors or short writes that were reported in the urb. + // The kernel sets status, actual_length and error_count. + // error_count is only used for ISO xfers. + // status is 0 if successful, else is an errno kind of thing + + if (urb->status != 0){ + fprintf (stderr, "fusb: (status %d) %s\n", urb->status, strerror (-urb->status)); + } + else if (urb->actual_length != urb->buffer_length){ + fprintf (stderr, "fusb: short write xfer: %d != %d\n", + urb->actual_length, urb->buffer_length); + } + + free_list_add (urb); + } +} + +void +fusb_ephandle_linux::wait_for_completion () +{ + d_devhandle->_wait_for_completion (); +} + +// ---------------------------------------------------------------- +// routines for reading +// ---------------------------------------------------------------- + +int +fusb_ephandle_linux::read (void *buffer, int nbytes) +{ + if (!d_started) + return -1; + + if (!d_input_p) + return -1; + + unsigned char *dst = (unsigned char *) buffer; + + int n = 0; + while (n < nbytes){ + + if (d_read_buffer >= d_read_buffer_end) + if (!reload_read_buffer ()) + return -1; + + int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer)); + + memcpy (&dst[n], d_read_buffer, m); + d_read_buffer += m; + n += m; + } + + return n; +} + +bool +fusb_ephandle_linux::reload_read_buffer () +{ + assert (d_read_buffer >= d_read_buffer_end); + + usbdevfs_urb *urb; + + if (d_read_work_in_progress){ + // We're done with this urb. Fire off a read to refill it. + urb = d_read_work_in_progress; + d_read_work_in_progress = 0; + d_read_buffer = 0; + d_read_buffer_end = 0; + urb->actual_length = 0; + if (!submit_urb (urb)) + return false; + } + + while (1){ + + while ((urb = completed_list_get ()) == 0) + d_devhandle->_reap (true); + + // check result of completed read + + if (urb->status != 0){ + // We've got a problem. + // Report the problem and resubmit. + fprintf (stderr, "fusb: (rd status %d) %s\n", urb->status, strerror (-urb->status)); + urb->actual_length = 0; + if (!submit_urb (urb)) + return false; + + continue; + } + + // we've got a happy urb, full of data... + + d_read_work_in_progress = urb; + d_read_buffer = (unsigned char *) urb->buffer; + d_read_buffer_end = d_read_buffer + urb->actual_length; + + return true; + } +} + +// ---------------------------------------------------------------- + +void +fusb_ephandle_linux::free_list_add (usbdevfs_urb *urb) +{ + assert (urb_get_ephandle (urb) == this); + urb->actual_length = 0; + d_free_list.push_back (urb); +} + +usbdevfs_urb * +fusb_ephandle_linux::free_list_get () +{ + if (d_free_list.empty ()) + return 0; + + usbdevfs_urb *urb = d_free_list.front (); + d_free_list.pop_front (); + return urb; +} + +void +fusb_ephandle_linux::completed_list_add (usbdevfs_urb *urb) +{ + assert (urb_get_ephandle (urb) == this); + d_completed_list.push_back (urb); +} + +usbdevfs_urb * +fusb_ephandle_linux::completed_list_get () +{ + if (d_completed_list.empty ()) + return 0; + + usbdevfs_urb *urb = d_completed_list.front (); + d_completed_list.pop_front (); + return urb; +} + +/* + * Submit the urb. If successful the urb ends up on the devhandle's + * pending list, otherwise, it's back on our free list. + */ +bool +fusb_ephandle_linux::submit_urb (usbdevfs_urb *urb) +{ + if (!d_devhandle->_submit_urb (urb)){ // FIXME record the problem somewhere + fprintf (stderr, "_submit_urb failed\n"); + free_list_add (urb); + return false; + } + return true; +} diff --git a/usrp/host/lib/legacy/fusb_linux.h b/usrp/host/lib/legacy/fusb_linux.h new file mode 100644 index 00000000..b2d46a51 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_linux.h @@ -0,0 +1,116 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +// Fast USB interface + +#ifndef _FUSB_LINUX_H_ +#define _FUSB_LINUX_H_ + +#include +#include + +struct usbdevfs_urb; +class fusb_ephandle_linux; + +/*! + * \brief linux specific implementation of fusb_devhandle using usbdevice_fs + */ +class fusb_devhandle_linux : public fusb_devhandle { +private: + std::list d_pending_rqsts; + + void pending_add (usbdevfs_urb *urb); + bool pending_remove (usbdevfs_urb *urb); + usbdevfs_urb * pending_get (); + + +public: + // CREATORS + fusb_devhandle_linux (usb_dev_handle *udh); + virtual ~fusb_devhandle_linux (); + + // MANIPULATORS + virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + + // internal use only + bool _submit_urb (usbdevfs_urb *urb); + bool _cancel_urb (usbdevfs_urb *urb); + void _cancel_pending_rqsts (fusb_ephandle_linux *eph); + bool _reap (bool ok_to_block_p); + void _wait_for_completion (); +}; + + /*! + * \brief linux specific implementation of fusb_ephandle using usbdevice_fs + */ + +class fusb_ephandle_linux : public fusb_ephandle { +private: + fusb_devhandle_linux *d_devhandle; + std::list d_free_list; + std::list d_completed_list; + usbdevfs_urb *d_write_work_in_progress; + unsigned char *d_write_buffer; + usbdevfs_urb *d_read_work_in_progress; + unsigned char *d_read_buffer; + unsigned char *d_read_buffer_end; + + usbdevfs_urb *get_write_work_in_progress (); + void reap_complete_writes (); + bool reload_read_buffer (); + bool submit_urb (usbdevfs_urb *urb); + +public: + fusb_ephandle_linux (fusb_devhandle_linux *dh, int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle_linux (); + + virtual bool start (); //!< begin streaming i/o + virtual bool stop (); //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void *buffer, int nbytes); + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void *buffer, int nbytes); + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion (); + + // internal use only + void free_list_add (usbdevfs_urb *urb); + void completed_list_add (usbdevfs_urb *urb); + usbdevfs_urb *free_list_get (); // pop and return head of list or 0 + usbdevfs_urb *completed_list_get (); // pop and return head of list or 0 +}; + +#endif /* _FUSB_LINUX_H_ */ diff --git a/usrp/host/lib/legacy/fusb_ra_wb.cc b/usrp/host/lib/legacy/fusb_ra_wb.cc new file mode 100644 index 00000000..c95f4afb --- /dev/null +++ b/usrp/host/lib/legacy/fusb_ra_wb.cc @@ -0,0 +1,258 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include + +#include +#include + +static const int USB_TIMEOUT = 1000; // in milliseconds + +// the following comment and function is from fusb_linux.cc +#if 0 +// Totally evil and fragile extraction of file descriptor from +// guts of libusb. They don't install usbi.h, which is what we'd need +// to do this nicely. +// +// FIXME if everything breaks someday in the future, look here... + +static int +fd_from_usb_dev_handle (usb_dev_handle *udh) +{ + return *((int *) udh); +} +#endif + +// the control endpoint doesn't actually do us any good so here is a +// new "fragile extraction" +static int +ep_fd_from_usb_dev_handle (usb_dev_handle *udh, int endpoint) +{ + struct usb_dev_handle_kludge2 { // see also usrp_prims.cc + int fd; + struct usb_bus *bus; + struct usb_device *device; + int config; + int interface; + int altsetting; + void *impl_info; + }; + struct bsd_usb_dev_handle_info_kludge { + int ep_fd[USB_MAX_ENDPOINTS]; + }; + struct bsd_usb_dev_handle_info_kludge *info + = (struct bsd_usb_dev_handle_info_kludge *) + ((struct usb_dev_handle_kludge2 *)udh)->impl_info; + return info->ep_fd[UE_GET_ADDR(endpoint)]; +} + + +fusb_devhandle_ra_wb::fusb_devhandle_ra_wb (usb_dev_handle *udh) + : fusb_devhandle (udh) +{ + // that's it +} + +fusb_devhandle_ra_wb::~fusb_devhandle_ra_wb () +{ + // nop +} + +fusb_ephandle * +fusb_devhandle_ra_wb::make_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) +{ + return new fusb_ephandle_ra_wb (this, endpoint, input_p, + block_size, nblocks); +} + +// ---------------------------------------------------------------- + +fusb_ephandle_ra_wb::fusb_ephandle_ra_wb (fusb_devhandle_ra_wb *dh, + int endpoint, bool input_p, + int block_size, int nblocks) + : fusb_ephandle (endpoint, input_p, block_size, nblocks), + d_devhandle (dh), d_ra_wb_on (false) +{ + // that's it +} + +fusb_ephandle_ra_wb::~fusb_ephandle_ra_wb () +{ + // nop +} + +bool +fusb_ephandle_ra_wb::start () +{ + d_started = true; + + char buf = '\0'; + int fd; + + // this is to cause libusb to open the endpoint + if (!d_input_p) { + write(&buf, 0); + fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), + d_endpoint); + } + else { + read(&buf, 0); + fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), + d_endpoint|USB_ENDPOINT_IN); + } + + // enable read ahead/write behind + int ret; + struct usb_bulk_ra_wb_opt opts; + int enable = 1; + + opts.ra_wb_buffer_size = d_block_size*d_nblocks; + opts.ra_wb_request_size = d_block_size; +// fprintf (stderr, "setting buffer size to %d, request size to %d\n", +// opts.ra_wb_buffer_size, opts.ra_wb_request_size); + if (!d_input_p) { + ret = ioctl (fd, USB_SET_BULK_WB_OPT, &opts); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_WB_OPT: %s\n", strerror(errno)); + else { + ret = ioctl (fd, USB_SET_BULK_WB, &enable); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno)); + else + d_ra_wb_on = true; + } + } + else { + ret = ioctl (fd, USB_SET_BULK_RA_OPT, &opts); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_RA_OPT: %s\n", strerror(errno)); + else { + ret = ioctl (fd, USB_SET_BULK_RA, &enable); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno)); + else + d_ra_wb_on = true; + } + } + + return true; +} + +bool +fusb_ephandle_ra_wb::stop () +{ + int fd; + int ret; + int enable = 0; + if (d_ra_wb_on) { + if (!d_input_p) { + fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), + d_endpoint); + ret = ioctl (fd, USB_SET_BULK_WB, &enable); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_WB: %s\n", strerror(errno)); + else + d_ra_wb_on = false; + } + else { + fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), + d_endpoint|USB_ENDPOINT_IN); + ret = ioctl (fd, USB_SET_BULK_RA, &enable); + if (ret < 0) + fprintf (stderr, "USB_SET_BULK_RA: %s\n", strerror(errno)); + else + d_ra_wb_on = false; + } + } + + d_started = false; + return true; +} + +int +fusb_ephandle_ra_wb::write (const void *buffer, int nbytes) +{ + if (!d_started) + return -1; + + if (d_input_p) + return -1; + + return usb_bulk_write (d_devhandle->get_usb_dev_handle (), + d_endpoint, (char *) buffer, nbytes, USB_TIMEOUT); +} + +int +fusb_ephandle_ra_wb::read (void *buffer, int nbytes) +{ + if (!d_started) + return -1; + + if (!d_input_p) + return -1; + + return usb_bulk_read (d_devhandle->get_usb_dev_handle (), + d_endpoint|USB_ENDPOINT_IN, (char *) buffer, nbytes, + USB_TIMEOUT); +} + +void +fusb_ephandle_ra_wb::wait_for_completion () +{ + // as the driver is implemented this only makes sense for write + if (d_ra_wb_on && !d_input_p) { + int fd = ep_fd_from_usb_dev_handle (d_devhandle->get_usb_dev_handle(), + d_endpoint); + int kq = kqueue(); + if (kq < 0) + return; + struct kevent evt; + int nevents; + EV_SET (&evt, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, 0/*NULL*/); + nevents = kevent (kq, &evt, 1, &evt, 1, NULL); + if (nevents < 1) { + close(kq); + return; + } + while (!(evt.flags & EV_ERROR) && evt.data < (d_block_size*d_nblocks)) { + // it's a busy loop, but that's all I can do at the moment + nevents = kevent (kq, NULL, 0, &evt, 1, NULL); + // let's see if this improves the test_usrp_standard_tx throughput & + // "CPU usage" by looping less frequently + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 1000; // 1 ms + select (0, NULL, NULL, NULL, &timeout); + } + close (kq); + } +} diff --git a/usrp/host/lib/legacy/fusb_ra_wb.h b/usrp/host/lib/legacy/fusb_ra_wb.h new file mode 100644 index 00000000..09bf8ee6 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_ra_wb.h @@ -0,0 +1,84 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _FUSB_RA_WB_H_ +#define _FUSB_RA_WB_H_ + +#include + +/*! + * \brief generic implementation of fusb_devhandle using only libusb + */ +class fusb_devhandle_ra_wb : public fusb_devhandle +{ +public: + // CREATORS + fusb_devhandle_ra_wb (usb_dev_handle *udh); + virtual ~fusb_devhandle_ra_wb (); + + // MANIPULATORS + virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); +}; + + +/*! + * \brief generic implementation of fusb_ephandle using only libusb + */ +class fusb_ephandle_ra_wb : public fusb_ephandle +{ +private: + fusb_devhandle_ra_wb *d_devhandle; + bool d_ra_wb_on; + +public: + // CREATORS + fusb_ephandle_ra_wb (fusb_devhandle_ra_wb *dh, int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle_ra_wb (); + + // MANIPULATORS + + virtual bool start (); //!< begin streaming i/o + virtual bool stop (); //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void *buffer, int nbytes); + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void *buffer, int nbytes); + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion (); +}; + +#endif /* _FUSB_RA_WB_H_ */ + diff --git a/usrp/host/lib/legacy/fusb_sysconfig_darwin.cc b/usrp/host/lib/legacy/fusb_sysconfig_darwin.cc new file mode 100644 index 00000000..f66c298d --- /dev/null +++ b/usrp/host/lib/legacy/fusb_sysconfig_darwin.cc @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +static const int MAX_BLOCK_SIZE = 32 * 1024; // hard limit +static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) + +fusb_devhandle * +fusb_sysconfig::make_devhandle (usb_dev_handle *udh) +{ + return new fusb_devhandle_darwin (udh); +} + +int fusb_sysconfig::max_block_size () +{ + return MAX_BLOCK_SIZE; +} + +int fusb_sysconfig::default_buffer_size () +{ + return FUSB_BUFFER_SIZE; +} + diff --git a/usrp/host/lib/legacy/fusb_sysconfig_generic.cc b/usrp/host/lib/legacy/fusb_sysconfig_generic.cc new file mode 100644 index 00000000..d336cb94 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_sysconfig_generic.cc @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit +static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) + +fusb_devhandle * +fusb_sysconfig::make_devhandle (usb_dev_handle *udh) +{ + return new fusb_devhandle_generic (udh); +} + +int fusb_sysconfig::max_block_size () +{ + return MAX_BLOCK_SIZE; +} + +int fusb_sysconfig::default_buffer_size () +{ + return FUSB_BUFFER_SIZE; +} diff --git a/usrp/host/lib/legacy/fusb_sysconfig_linux.cc b/usrp/host/lib/legacy/fusb_sysconfig_linux.cc new file mode 100644 index 00000000..468fa9f8 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_sysconfig_linux.cc @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit +static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) + +fusb_devhandle * +fusb_sysconfig::make_devhandle (usb_dev_handle *udh) +{ + return new fusb_devhandle_linux (udh); +} + +int fusb_sysconfig::max_block_size () +{ + return MAX_BLOCK_SIZE; +} + +int fusb_sysconfig::default_buffer_size () +{ + return FUSB_BUFFER_SIZE; +} diff --git a/usrp/host/lib/legacy/fusb_sysconfig_ra_wb.cc b/usrp/host/lib/legacy/fusb_sysconfig_ra_wb.cc new file mode 100644 index 00000000..68eecedc --- /dev/null +++ b/usrp/host/lib/legacy/fusb_sysconfig_ra_wb.cc @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +//static const int MAX_BLOCK_SIZE = 16 * 1024; // hard limit +// there's no hard limit, even before making any changes to the driver +// 64k is empirically a pretty good number +static const int MAX_BLOCK_SIZE = 64 * 1024; +// there is a limit of 1 MB in the driver for the buffer size +static const int FUSB_BUFFER_SIZE = 256 * (1L << 10); // 256 kB + +fusb_devhandle * +fusb_sysconfig::make_devhandle (usb_dev_handle *udh) +{ + return new fusb_devhandle_ra_wb (udh); +} + +int fusb_sysconfig::max_block_size () +{ + return MAX_BLOCK_SIZE; +} + +int fusb_sysconfig::default_buffer_size () +{ + return FUSB_BUFFER_SIZE; +} diff --git a/usrp/host/lib/legacy/fusb_sysconfig_win32.cc b/usrp/host/lib/legacy/fusb_sysconfig_win32.cc new file mode 100644 index 00000000..282e77ac --- /dev/null +++ b/usrp/host/lib/legacy/fusb_sysconfig_win32.cc @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +static const int MAX_BLOCK_SIZE = 64 * 1024; // Windows kernel hard limit +static const int FUSB_BUFFER_SIZE = 2 * (1L << 20); // 2 MB (was 8 MB) + +fusb_devhandle * +fusb_sysconfig::make_devhandle (usb_dev_handle *udh) +{ + return new fusb_devhandle_win32 (udh); +} + +int fusb_sysconfig::max_block_size () +{ + return MAX_BLOCK_SIZE; +} + +int fusb_sysconfig::default_buffer_size () +{ + return FUSB_BUFFER_SIZE; +} diff --git a/usrp/host/lib/legacy/fusb_win32.cc b/usrp/host/lib/legacy/fusb_win32.cc new file mode 100644 index 00000000..c6e3c972 --- /dev/null +++ b/usrp/host/lib/legacy/fusb_win32.cc @@ -0,0 +1,265 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); +static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE; +static const int DEFAULT_BUFFER_SIZE = 16 * (1L << 20); // 16 MB / endpoint + + +static const int USB_TIMEOUT = 1000; // in milliseconds + + +fusb_devhandle_win32::fusb_devhandle_win32 (usb_dev_handle *udh) + : fusb_devhandle (udh) +{ + // that's it +} + +fusb_devhandle_win32::~fusb_devhandle_win32 () +{ + // nop +} + +fusb_ephandle * +fusb_devhandle_win32::make_ephandle (int endpoint, bool input_p, + int block_size, int nblocks) +{ + return new fusb_ephandle_win32 (this, endpoint, input_p, + block_size, nblocks); +} + +// ---------------------------------------------------------------- + +fusb_ephandle_win32::fusb_ephandle_win32 (fusb_devhandle_win32 *dh, + int endpoint, bool input_p, + int block_size, int nblocks) + : fusb_ephandle (endpoint, input_p, block_size, nblocks), + d_devhandle (dh), d_input_leftover(0),d_output_short(0) +{ + if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE) + throw std::out_of_range ("fusb_ephandle_win32: block_size"); + + if (d_nblocks < 0) + throw std::out_of_range ("fusb_ephandle_win32: nblocks"); + + if (d_block_size == 0) + d_block_size = DEFAULT_BLOCK_SIZE; + + if (d_nblocks == 0) + d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size); + + d_buffer = new char [d_block_size*d_nblocks]; + d_context = new void * [d_nblocks]; + + // allocate contexts + + usb_dev_handle *dev = dh->get_usb_dev_handle (); + int i; + + if (d_input_p) + endpoint |= USB_ENDPOINT_IN; + + for (i=0; i 0) { + d_curr = (d_curr+1)%d_nblocks; + buf = &d_buffer[d_curr*d_block_size]; + + if (d_outstanding_write != d_nblocks) { + d_outstanding_write++; + } else { + retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT); + if (retval < 0) { + fprintf(stderr, "%s: usb_reap_async: %s\n", + __FUNCTION__, usb_strerror()); + return retval; + } + } + + int ncopy = std::min(bytes_to_write, d_block_size); + memcpy(buf, (void *) &(((char*)buffer)[a]), ncopy); + bytes_to_write -= ncopy; + a += ncopy; + + d_output_short = d_block_size - ncopy; + if (d_output_short == 0) + usb_submit_async(d_context[d_curr], buf, d_block_size); + } + + return retval < 0 ? retval : nbytes; +} + +int +fusb_ephandle_win32::read (void *buffer, int nbytes) +{ + int retval=0; + char *buf; + + if (!d_started) // doesn't matter here, but keeps semantics constant + return -1; + + if (!d_input_p) + return -1; + + int bytes_to_read = nbytes; + + int a=0; + if (d_input_leftover != 0) { + + buf = &d_buffer[d_curr*d_block_size + d_block_size - d_input_leftover]; + a = std::min(nbytes, d_input_leftover); + memcpy(buffer, buf, a); + bytes_to_read -= a; + d_input_leftover -= a; + + if (d_input_leftover == 0) + usb_submit_async(d_context[d_curr], + &d_buffer[d_curr*d_block_size], d_block_size); + } + + while (bytes_to_read > 0) { + + d_curr = (d_curr+1)%d_nblocks; + buf = &d_buffer[d_curr*d_block_size]; + + retval = usb_reap_async(d_context[d_curr], USB_TIMEOUT); + if (retval < 0) + fprintf(stderr, "%s: usb_reap_async: %s\n", + __FUNCTION__, usb_strerror()); + + int ncopy = std::min(bytes_to_read, d_block_size); + memcpy((void *) &(((char*)buffer)[a]), buf, ncopy); + bytes_to_read -= ncopy; + a += ncopy; + + d_input_leftover = d_block_size - ncopy; + if (d_input_leftover == 0) + usb_submit_async(d_context[d_curr], buf, d_block_size); + } + + return retval < 0 ? retval : nbytes; +} + +void +fusb_ephandle_win32::wait_for_completion () +{ + int i; + + for (i=0; i + +/*! + * \brief win32 implementation of fusb_devhandle using libusb-win32 + */ +class fusb_devhandle_win32 : public fusb_devhandle +{ +public: + // CREATORS + fusb_devhandle_win32 (usb_dev_handle *udh); + virtual ~fusb_devhandle_win32 (); + + // MANIPULATORS + virtual fusb_ephandle *make_ephandle (int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); +}; + + +/*! + * \brief win32 implementation of fusb_ephandle using libusb-win32 + */ +class fusb_ephandle_win32 : public fusb_ephandle +{ +private: + fusb_devhandle_win32 *d_devhandle; + + unsigned d_curr; + unsigned d_outstanding_write; + int d_output_short; + int d_input_leftover; + void ** d_context; + char * d_buffer; + +public: + // CREATORS + fusb_ephandle_win32 (fusb_devhandle_win32 *dh, int endpoint, bool input_p, + int block_size = 0, int nblocks = 0); + virtual ~fusb_ephandle_win32 (); + + // MANIPULATORS + + virtual bool start (); //!< begin streaming i/o + virtual bool stop (); //!< stop streaming i/o + + /*! + * \returns \p nbytes if write was successfully enqueued, else -1. + * Will block if no free buffers available. + */ + virtual int write (const void *buffer, int nbytes); + + /*! + * \returns number of bytes read or -1 if error. + * number of bytes read will be <= nbytes. + * Will block if no input available. + */ + virtual int read (void *buffer, int nbytes); + + /* + * block until all outstanding writes have completed + */ + virtual void wait_for_completion (); +}; + +#endif /* _FUSB_WIN32_H_ */ + diff --git a/usrp/host/lib/legacy/gen_usrp_dbid.py b/usrp/host/lib/legacy/gen_usrp_dbid.py new file mode 100755 index 00000000..14d3ee55 --- /dev/null +++ b/usrp/host/lib/legacy/gen_usrp_dbid.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +import sys +import os +import os.path +import re +from optparse import OptionParser + +def write_header(f, comment_char): + f.write(comment_char); f.write('\n') + f.write(comment_char); f.write(' Machine generated by gen_usrp_dbid.py from usrp_dbid.dat\n') + f.write(comment_char); f.write(' Do not edit by hand. All edits will be overwritten.\n') + f.write(comment_char); f.write('\n') + f.write('\n') + +def gen_dbid_py(r): + f = open('usrp_dbid.py', 'w') + comment_char = '#' + write_header(f, comment_char) + f.write(comment_char); f.write('\n') + f.write(comment_char); f.write(" USRP Daughterboard ID's\n") + f.write(comment_char); f.write('\n') + f.write('\n') + for x in r: + f.write('%-16s = %s\n' % (x[1], x[2])) + +def gen_dbid_h(r): + f = open('usrp_dbid.h', 'w') + comment_char = '//' + write_header(f, comment_char) + f.write(comment_char); f.write('\n') + f.write(comment_char); f.write(" USRP Daughterboard ID's\n") + f.write(comment_char); f.write('\n') + f.write('\n') + f.write('#ifndef INCLUDED_USRP_DBID_H\n') + f.write('#define INCLUDED_USRP_DBID_H\n') + f.write('\n') + for x in r: + f.write('#define %-25s %s\n' % ('USRP_DBID_' + x[1], x[2])) + f.write('\n') + f.write('#endif /* INCLUDED_USRP_DBID_H */\n') + +def gen_dbid_cc(r): + f = open('usrp_dbid.cc', 'w') + write_header(f, '//') + head = '''/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#define NELEM(x) sizeof(x)/sizeof(x[0]) + +static struct { + unsigned short dbid; + const char *name; +} dbid_map[] = { +''' + + tail = '''}; + +const std::string +usrp_dbid_to_string (int dbid) +{ + if (dbid == -1) + return ""; + + if (dbid == -2) + return ""; + + for (unsigned i = 0; i < NELEM (dbid_map); i++) + if (dbid == dbid_map[i].dbid) + return dbid_map[i].name; + + char tmp[64]; + snprintf (tmp, sizeof (tmp), "Unknown (0x%04x)", dbid); + return tmp; +} +''' + f.write(head) + for x in r: + f.write(' { %-27s "%s" },\n' % ( + 'USRP_DBID_' + x[1] + ',', x[0])) + f.write(tail) + +def gen_all(src_filename): + src_file = open(src_filename, 'r') + r = [] + for line in src_file: + line = line.strip() + line = re.sub(r'\s*#.*$','', line) + if len(line) == 0: + continue + mo = re.match('"([^"]+)"\s*(0x[0-9a-fA-F]+)', line) + if mo: + str_name = mo.group(1) + id_name = str_name.upper().replace(' ', '_') + id_val = mo.group(2) + r.append((str_name, id_name, id_val)) + #sys.stdout.write('%-16s\t%-16s\t%s\n' % ('"'+str_name+'"', id_name, id_val)) + + gen_dbid_h(r) + gen_dbid_py(r) + gen_dbid_cc(r) + + +def main(): + usage = "usage: %prog [options] usrp_dbid.dat" + parser = OptionParser(usage=usage) + (options, args) = parser.parse_args() + if len(args) != 1: + parser.print_help() + sys.exit(1) + + gen_all(args[0]) + +if __name__ == '__main__': + main() diff --git a/usrp/host/lib/legacy/md5.c b/usrp/host/lib/legacy/md5.c new file mode 100644 index 00000000..abee067f --- /dev/null +++ b/usrp/host/lib/legacy/md5.c @@ -0,0 +1,452 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Boston, MA 02110-1301, USA. */ + +/* Written by Ulrich Drepper , 1995. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "md5.h" + +#include + +#include +#include + +// #include "unlocked-io.h" + +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +/* We need to keep the namespace clean so define the MD5 function + protected using leading __ . */ +# define md5_init_ctx __md5_init_ctx +# define md5_process_block __md5_process_block +# define md5_process_bytes __md5_process_bytes +# define md5_finish_ctx __md5_finish_ctx +# define md5_read_ctx __md5_read_ctx +# define md5_stream __md5_stream +# define md5_buffer __md5_buffer +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + +#define BLOCKSIZE 4096 +/* Ensure that BLOCKSIZE is a multiple of 64. */ +#if BLOCKSIZE % 64 != 0 +/* FIXME-someday (soon?): use #error instead of this kludge. */ +"invalid BLOCKSIZE" +#endif + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (struct md5_ctx *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (FILE *stream, void *resblock) +{ + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + while (1) + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + + if (sum == BLOCKSIZE) + break; + + if (n == 0) + { + /* Check for the error flag IFF N == 0, so that we don't + exit the loop after a partial read due to e.g., EAGAIN + or EWOULDBLOCK. */ + if (ferror (stream)) + return 1; + goto process_partial_block; + } + + /* We've read at least one byte, so ignore errors. But always + check for EOF, since feof may be true even though N > 0. + Otherwise, we could end up calling fread after EOF. */ + if (feof (stream)) + goto process_partial_block; + } + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + process_partial_block:; + + /* Process any remaining bytes. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (const char *buffer, size_t len, void *resblock) +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +# if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) +# else +# define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0) +# endif + if (UNALIGNED_P (buffer)) + while (len > 64) + { + md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx) +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + a = rol (a, s); \ + a += b; \ + } \ + while (0) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or + perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + a = rol (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/usrp/host/lib/legacy/md5.h b/usrp/host/lib/legacy/md5.h new file mode 100644 index 00000000..709cefb9 --- /dev/null +++ b/usrp/host/lib/legacy/md5.h @@ -0,0 +1,129 @@ +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. + Copyright (C) 1995, 1996, 1999, 2000, 2003 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Boston, MA 02110-1301, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include +#include + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include +typedef uint32_t md5_uint32; +typedef uintptr_t md5_uintptr; +#else +# define UINT_MAX_32_BITS 4294967295U + +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +/* We have to make a guess about the integer type equivalent in size + to pointers which should always be correct. */ +typedef unsigned long int md5_uintptr; +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void md5_init_ctx (struct md5_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void md5_process_block (const void *buffer, size_t len, + struct md5_ctx *ctx); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void md5_process_bytes (const void *buffer, size_t len, + struct md5_ctx *ctx); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF be correctly + aligned for a 32 bits value. */ +extern void *md5_finish_ctx (struct md5_ctx *ctx, void *resbuf); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_read_ctx (const struct md5_ctx *ctx, void *resbuf); + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int md5_stream (FILE *stream, void *resblock); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *md5_buffer (const char *buffer, size_t len, void *resblock); + +#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) + +#endif diff --git a/usrp/host/lib/legacy/mld_threads.h b/usrp/host/lib/legacy/mld_threads.h new file mode 100644 index 00000000..08b3555a --- /dev/null +++ b/usrp/host/lib/legacy/mld_threads.h @@ -0,0 +1,257 @@ +/* -*- c++ -*- */ +/* + * Copyright 2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio. + * + * Primary Author: Michael Dickens, NCIP Lab, University of Notre Dame + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _INCLUDED_MLD_THREADS_H_ +#define _INCLUDED_MLD_THREADS_H_ + +/* classes which allow for either pthreads or omni_threads */ + +#ifdef _USE_OMNI_THREADS_ +#include +#else +#include +#endif + +#include + +#define __INLINE__ inline + +class mld_condition_t; + +class mld_mutex_t { +#ifdef _USE_OMNI_THREADS_ + typedef omni_mutex l_mutex, *l_mutex_ptr; +#else + typedef pthread_mutex_t l_mutex, *l_mutex_ptr; +#endif + + friend class mld_condition_t; + +private: + l_mutex_ptr d_mutex; + +protected: + inline l_mutex_ptr mutex () { return (d_mutex); }; + +public: + __INLINE__ mld_mutex_t () { +#ifdef _USE_OMNI_THREADS_ + d_mutex = new omni_mutex (); +#else + d_mutex = (l_mutex_ptr) new l_mutex; + int l_ret = pthread_mutex_init (d_mutex, NULL); + if (l_ret != 0) { + fprintf (stderr, "Error %d creating mutex.\n", l_ret); + throw std::runtime_error ("mld_mutex_t::mld_mutex_t()\n"); + } +#endif + }; + + __INLINE__ ~mld_mutex_t () { + unlock (); +#ifndef _USE_OMNI_THREADS_ + int l_ret = pthread_mutex_destroy (d_mutex); + if (l_ret != 0) { + fprintf (stderr, "mld_mutex_t::~mld_mutex_t(): " + "Error %d destroying mutex.\n", l_ret); + } +#endif + delete d_mutex; + d_mutex = NULL; + }; + + __INLINE__ void lock () { +#ifdef _USE_OMNI_THREADS_ + d_mutex->lock (); +#else + int l_ret = pthread_mutex_lock (d_mutex); + if (l_ret != 0) { + fprintf (stderr, "mld_mutex_t::lock(): " + "Error %d locking mutex.\n", l_ret); + } +#endif + }; + + __INLINE__ void unlock () { +#ifdef _USE_OMNI_THREADS_ + d_mutex->unlock (); +#else + int l_ret = pthread_mutex_unlock (d_mutex); + if (l_ret != 0) { + fprintf (stderr, "mld_mutex_t::unlock(): " + "Error %d locking mutex.\n", l_ret); + } +#endif + }; + + __INLINE__ bool trylock () { +#ifdef _USE_OMNI_THREADS_ + int l_ret = d_mutex->trylock (); +#else + int l_ret = pthread_mutex_unlock (d_mutex); +#endif + return (l_ret == 0 ? true : false); + }; + + inline void acquire () { lock(); }; + inline void release () { unlock(); }; + inline void wait () { lock(); }; + inline void post () { unlock(); }; +}; + +typedef mld_mutex_t mld_mutex, *mld_mutex_ptr; + +class mld_condition_t { +#ifdef _USE_OMNI_THREADS_ + typedef omni_condition l_condition, *l_condition_ptr; +#else + typedef pthread_cond_t l_condition, *l_condition_ptr; +#endif + +private: + l_condition_ptr d_condition; + mld_mutex_ptr d_mutex; + bool d_waiting; + +public: + __INLINE__ mld_condition_t () { + d_waiting = false; + d_mutex = new mld_mutex (); +#ifdef _USE_OMNI_THREADS_ + d_condition = new omni_condition (d_mutex->mutex ()); +#else + d_condition = (l_condition_ptr) new l_condition; + int l_ret = pthread_cond_init (d_condition, NULL); + if (l_ret != 0) { + fprintf (stderr, "Error %d creating condition.\n", l_ret); + throw std::runtime_error ("mld_condition_t::mld_condition_t()\n"); + } +#endif + }; + + __INLINE__ ~mld_condition_t () { + signal (); +#ifndef _USE_OMNI_THREADS_ + int l_ret = pthread_cond_destroy (d_condition); + if (l_ret != 0) { + fprintf (stderr, "mld_condition_t::mld_condition_t(): " + "Error %d destroying condition.\n", l_ret); + } +#endif + delete d_condition; + d_condition = NULL; + delete d_mutex; + d_mutex = NULL; + }; + + __INLINE__ void signal () { + if (d_waiting == true) { +#ifdef _USE_OMNI_THREADS_ + d_condition->signal (); +#else + int l_ret = pthread_cond_signal (d_condition); + if (l_ret != 0) { + fprintf (stderr, "mld_condition_t::signal(): " + "Error %d.\n", l_ret); + } +#endif + d_waiting = false; + } + }; + + __INLINE__ void wait () { + if (d_waiting == false) { + d_waiting = true; +#ifdef _USE_OMNI_THREADS_ + d_condition->wait (); +#else + int l_ret = pthread_cond_wait (d_condition, d_mutex->mutex ()); + if (l_ret != 0) { + fprintf (stderr, "mld_condition_t::wait(): " + "Error %d.\n", l_ret); + } +#endif + } + }; +}; + +typedef mld_condition_t mld_condition, *mld_condition_ptr; + +class mld_thread_t { +#ifdef _USE_OMNI_THREADS_ + typedef omni_thread l_thread, *l_thread_ptr; +#else + typedef pthread_t l_thread, *l_thread_ptr; +#endif + +private: +#ifndef _USE_OMNI_THREADS_ + l_thread d_thread; + void (*d_start_routine)(void*); + void *d_arg; +#else + l_thread_ptr d_thread; +#endif + +#ifndef _USE_OMNI_THREADS_ + static void* local_start_routine (void *arg) { + mld_thread_t* This = (mld_thread_t*) arg; + (*(This->d_start_routine))(This->d_arg); + return (NULL); + }; +#endif + +public: + __INLINE__ mld_thread_t (void (*start_routine)(void *), void *arg) { +#ifdef _USE_OMNI_THREADS_ + d_thread = new omni_thread (start_routine, arg); + d_thread->start (); +#else + d_start_routine = start_routine; + d_arg = arg; + int l_ret = pthread_create (&d_thread, NULL, local_start_routine, this); + if (l_ret != 0) { + fprintf (stderr, "Error %d creating thread.\n", l_ret); + throw std::runtime_error ("mld_thread_t::mld_thread_t()\n"); + } +#endif + }; + + __INLINE__ ~mld_thread_t () { +#ifdef _USE_OMNI_THREADS_ +// delete d_thread; + d_thread = NULL; +#else + int l_ret = pthread_detach (d_thread); + if (l_ret != 0) { + fprintf (stderr, "Error %d detaching thread.\n", l_ret); + throw std::runtime_error ("mld_thread_t::~mld_thread_t()\n"); + } +#endif + }; +}; + +typedef mld_thread_t mld_thread, *mld_thread_ptr; + +#endif /* _INCLUDED_MLD_THREADS_H_ */ diff --git a/usrp/host/lib/legacy/rate_to_regval.h b/usrp/host/lib/legacy/rate_to_regval.h new file mode 100644 index 00000000..1ffdc0f6 --- /dev/null +++ b/usrp/host/lib/legacy/rate_to_regval.h @@ -0,0 +1,97 @@ + { 1, 0x00 }, + { 2, 0x01 }, + { 3, 0x02 }, + { 4, 0x11 }, + { 5, 0x04 }, + { 6, 0x05 }, + { 7, 0x06 }, + { 8, 0x13 }, + { 9, 0x08 }, + { 10, 0x09 }, + { 11, 0x0a }, + { 12, 0x15 }, + { 13, 0x0c }, + { 14, 0x0d }, + { 15, 0x0e }, + { 16, 0x33 }, + { 18, 0x18 }, + { 20, 0x19 }, + { 21, 0x26 }, + { 22, 0x1a }, + { 24, 0x35 }, + { 25, 0x44 }, + { 26, 0x1c }, + { 27, 0x28 }, + { 28, 0x1d }, + { 30, 0x1e }, + { 32, 0x37 }, + { 33, 0x2a }, + { 35, 0x46 }, + { 36, 0x55 }, + { 39, 0x2c }, + { 40, 0x39 }, + { 42, 0x56 }, + { 44, 0x3a }, + { 45, 0x2e }, + { 48, 0x57 }, + { 49, 0x66 }, + { 50, 0x49 }, + { 52, 0x3c }, + { 54, 0x58 }, + { 55, 0x4a }, + { 56, 0x3d }, + { 60, 0x59 }, + { 63, 0x68 }, + { 64, 0x77 }, + { 65, 0x4c }, + { 66, 0x5a }, + { 70, 0x69 }, + { 72, 0x5b }, + { 75, 0x4e }, + { 77, 0x6a }, + { 78, 0x5c }, + { 80, 0x79 }, + { 81, 0x88 }, + { 84, 0x5d }, + { 88, 0x7a }, + { 90, 0x5e }, + { 91, 0x6c }, + { 96, 0x7b }, + { 98, 0x6d }, + { 99, 0x8a }, + { 100, 0x99 }, + { 104, 0x7c }, + { 105, 0x6e }, + { 108, 0x8b }, + { 110, 0x9a }, + { 112, 0x7d }, + { 117, 0x8c }, + { 120, 0x9b }, + { 121, 0xaa }, + { 126, 0x8d }, + { 128, 0x7f }, + { 130, 0x9c }, + { 132, 0xab }, + { 135, 0x8e }, + { 140, 0x9d }, + { 143, 0xac }, + { 144, 0xbb }, + { 150, 0x9e }, + { 154, 0xad }, + { 156, 0xbc }, + { 160, 0x9f }, + { 165, 0xae }, + { 168, 0xbd }, + { 169, 0xcc }, + { 176, 0xaf }, + { 180, 0xbe }, + { 182, 0xcd }, + { 192, 0xbf }, + { 195, 0xce }, + { 196, 0xdd }, + { 208, 0xcf }, + { 210, 0xde }, + { 224, 0xdf }, + { 225, 0xee }, + { 240, 0xef }, + { 256, 0xff } diff --git a/usrp/host/lib/legacy/std_paths.h.in b/usrp/host/lib/legacy/std_paths.h.in new file mode 100644 index 00000000..22a8ecbd --- /dev/null +++ b/usrp/host/lib/legacy/std_paths.h.in @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2005 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +static char *std_paths[] = { + "@prefix@/share/usrp", + "/usr/local/share/usrp", + 0 +}; diff --git a/usrp/host/lib/legacy/usrp_basic.cc b/usrp/host/lib/legacy/usrp_basic.cc new file mode 100644 index 00000000..2eef1472 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_basic.cc @@ -0,0 +1,1238 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "usrp_basic.h" +#include "usrp_prims.h" +#include "usrp_interfaces.h" +#include "fpga_regs_common.h" +#include "fusb.h" +#include +#include +#include +#include +#include + +using namespace ad9862; + +#define NELEM(x) (sizeof (x) / sizeof (x[0])) + +// These set the buffer size used for each end point using the fast +// usb interface. The kernel ends up locking down this much memory. + +static const int FUSB_BUFFER_SIZE = fusb_sysconfig::default_buffer_size(); +static const int FUSB_BLOCK_SIZE = fusb_sysconfig::max_block_size(); +static const int FUSB_NBLOCKS = FUSB_BUFFER_SIZE / FUSB_BLOCK_SIZE; + + +static const double POLLING_INTERVAL = 0.1; // seconds + +//////////////////////////////////////////////////////////////// + +static struct usb_dev_handle * +open_rx_interface (struct usb_device *dev) +{ + struct usb_dev_handle *udh = usrp_open_rx_interface (dev); + if (udh == 0){ + fprintf (stderr, "usrp_basic_rx: can't open rx interface\n"); + usb_strerror (); + } + return udh; +} + +static struct usb_dev_handle * +open_tx_interface (struct usb_device *dev) +{ + struct usb_dev_handle *udh = usrp_open_tx_interface (dev); + if (udh == 0){ + fprintf (stderr, "usrp_basic_tx: can't open tx interface\n"); + usb_strerror (); + } + return udh; +} + + +////////////////////////////////////////////////////////////////// +// +// usrp_basic +// +//////////////////////////////////////////////////////////////// + + +// Given: +// CLKIN = 64 MHz +// CLKSEL pin = high +// +// These settings give us: +// CLKOUT1 = CLKIN = 64 MHz +// CLKOUT2 = CLKIN = 64 MHz +// ADC is clocked at 64 MHz +// DAC is clocked at 128 MHz + +static unsigned char common_regs[] = { + REG_GENERAL, 0, + REG_DLL, (DLL_DISABLE_INTERNAL_XTAL_OSC + | DLL_MULT_2X + | DLL_FAST), + REG_CLKOUT, CLKOUT2_EQ_DLL_OVER_2, + REG_AUX_ADC_CLK, AUX_ADC_CLK_CLK_OVER_4 +}; + + +usrp_basic::usrp_basic (int which_board, + struct usb_dev_handle * + open_interface (struct usb_device *dev), + const std::string fpga_filename, + const std::string firmware_filename) + : d_udh (0), + d_usb_data_rate (16000000), // SWAG, see below + d_bytes_per_poll ((int) (POLLING_INTERVAL * d_usb_data_rate)), + d_verbose (false) +{ + /* + * SWAG: Scientific Wild Ass Guess. + * + * d_usb_data_rate is used only to determine how often to poll for over- and under-runs. + * We defualt it to 1/2 of our best case. Classes derived from usrp_basic (e.g., + * usrp_standard_tx and usrp_standard_rx) call set_usb_data_rate() to tell us the + * actual rate. This doesn't change our throughput, that's determined by the signal + * processing code in the FPGA (which we know nothing about), and the system limits + * determined by libusb, fusb_*, and the underlying drivers. + */ + memset (d_fpga_shadows, 0, sizeof (d_fpga_shadows)); + + usrp_one_time_init (); + + if (!usrp_load_standard_bits (which_board, false, fpga_filename, firmware_filename)) + throw std::runtime_error ("usrp_basic/usrp_load_standard_bits"); + + struct usb_device *dev = usrp_find_device (which_board); + if (dev == 0){ + fprintf (stderr, "usrp_basic: can't find usrp[%d]\n", which_board); + throw std::runtime_error ("usrp_basic/usrp_find_device"); + } + + if (!(usrp_usrp_p(dev) && usrp_hw_rev(dev) >= 1)){ + fprintf (stderr, "usrp_basic: sorry, this code only works with USRP revs >= 1\n"); + throw std::runtime_error ("usrp_basic/bad_rev"); + } + + if ((d_udh = open_interface (dev)) == 0) + throw std::runtime_error ("usrp_basic/open_interface"); + + // initialize registers that are common to rx and tx + + if (!usrp_9862_write_many_all (d_udh, common_regs, sizeof (common_regs))){ + fprintf (stderr, "usrp_basic: failed to init common AD9862 regs\n"); + throw std::runtime_error ("usrp_basic/init_9862"); + } + + _write_fpga_reg (FR_MODE, 0); // ensure we're in normal mode + _write_fpga_reg (FR_DEBUG_EN, 0); // disable debug outputs +} + +usrp_basic::~usrp_basic () +{ + if (d_udh) + usb_close (d_udh); +} + +bool +usrp_basic::start () +{ + return true; // nop +} + +bool +usrp_basic::stop () +{ + return true; // nop +} + +void +usrp_basic::set_usb_data_rate (int usb_data_rate) +{ + d_usb_data_rate = usb_data_rate; + d_bytes_per_poll = (int) (usb_data_rate * POLLING_INTERVAL); +} + +bool +usrp_basic::write_aux_dac (int slot, int which_dac, int value) +{ + return usrp_write_aux_dac (d_udh, slot, which_dac, value); +} + +bool +usrp_basic::read_aux_adc (int slot, int which_adc, int *value) +{ + return usrp_read_aux_adc (d_udh, slot, which_adc, value); +} + +int +usrp_basic::read_aux_adc (int slot, int which_adc) +{ + int value; + if (!read_aux_adc (slot, which_adc, &value)) + return READ_FAILED; + + return value; +} + +bool +usrp_basic::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) +{ + return usrp_eeprom_write (d_udh, i2c_addr, eeprom_offset, buf.data (), buf.size ()); +} + +std::string +usrp_basic::read_eeprom (int i2c_addr, int eeprom_offset, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_eeprom_read (d_udh, i2c_addr, eeprom_offset, buf, len)) + return ""; + + return std::string (buf, len); +} + +bool +usrp_basic::write_i2c (int i2c_addr, const std::string buf) +{ + return usrp_i2c_write (d_udh, i2c_addr, buf.data (), buf.size ()); +} + +std::string +usrp_basic::read_i2c (int i2c_addr, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_i2c_read (d_udh, i2c_addr, buf, len)) + return ""; + + return std::string (buf, len); +} + +std::string +usrp_basic::serial_number() +{ + return usrp_serial_number(d_udh); +} + +// ---------------------------------------------------------------- + +bool +usrp_basic::set_adc_offset (int which, int offset) +{ + if (which < 0 || which > 3) + return false; + + return _write_fpga_reg (FR_ADC_OFFSET_0 + which, offset); +} + +bool +usrp_basic::set_dac_offset (int which, int offset, int offset_pin) +{ + if (which < 0 || which > 3) + return false; + + int which_codec = which >> 1; + int tx_a = (which & 0x1) == 0; + int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1); + int hi = (offset >> 2); + bool ok; + + if (tx_a){ + ok = _write_9862 (which_codec, REG_TX_A_OFFSET_LO, lo); + ok &= _write_9862 (which_codec, REG_TX_A_OFFSET_HI, hi); + } + else { + ok = _write_9862 (which_codec, REG_TX_B_OFFSET_LO, lo); + ok &= _write_9862 (which_codec, REG_TX_B_OFFSET_HI, hi); + } + return ok; +} + +bool +usrp_basic::set_adc_buffer_bypass (int which, bool bypass) +{ + if (which < 0 || which > 3) + return false; + + int codec = which >> 1; + int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; + + unsigned char cur_rx; + unsigned char cur_pwr_dn; + + // If the input buffer is bypassed, we need to power it down too. + + bool ok = _read_9862 (codec, reg, &cur_rx); + ok &= _read_9862 (codec, REG_RX_PWR_DN, &cur_pwr_dn); + if (!ok) + return false; + + if (bypass){ + cur_rx |= RX_X_BYPASS_INPUT_BUFFER; + cur_pwr_dn |= ((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B; + } + else { + cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER; + cur_pwr_dn &= ~(((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); + } + + ok &= _write_9862 (codec, reg, cur_rx); + ok &= _write_9862 (codec, REG_RX_PWR_DN, cur_pwr_dn); + return ok; +} + +// ---------------------------------------------------------------- + +bool +usrp_basic::_write_fpga_reg (int regno, int value) +{ + if (d_verbose){ + fprintf (stdout, "_write_fpga_reg(%3d, 0x%08x)\n", regno, value); + fflush (stdout); + } + + if (regno >= 0 && regno < MAX_REGS) + d_fpga_shadows[regno] = value; + + return usrp_write_fpga_reg (d_udh, regno, value); +} + +bool +usrp_basic::_write_fpga_reg_masked (int regno, int value, int mask) +{ + //Only use this for registers who actually use a mask in the verilog firmware, like FR_RX_MASTER_SLAVE + //value is a 16 bits value and mask is a 16 bits mask + if (d_verbose){ + fprintf (stdout, "_write_fpga_reg_masked(%3d, 0x%04x,0x%04x)\n", regno, value, mask); + fflush (stdout); + } + + if (regno >= 0 && regno < MAX_REGS) + d_fpga_shadows[regno] = value; + + return usrp_write_fpga_reg (d_udh, regno, (value & 0xffff) | ((mask & 0xffff)<<16)); +} + + +bool +usrp_basic::_read_fpga_reg (int regno, int *value) +{ + return usrp_read_fpga_reg (d_udh, regno, value); +} + +int +usrp_basic::_read_fpga_reg (int regno) +{ + int value; + if (!_read_fpga_reg (regno, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::_write_9862 (int which_codec, int regno, unsigned char value) +{ + if (0 && d_verbose){ + // FIXME really want to enable logging in usrp_prims:usrp_9862_write + fprintf(stdout, "_write_9862(codec = %d, regno = %2d, val = 0x%02x)\n", which_codec, regno, value); + fflush(stdout); + } + + return usrp_9862_write (d_udh, which_codec, regno, value); +} + + +bool +usrp_basic::_read_9862 (int which_codec, int regno, unsigned char *value) const +{ + return usrp_9862_read (d_udh, which_codec, regno, value); +} + +int +usrp_basic::_read_9862 (int which_codec, int regno) const +{ + unsigned char value; + if (!_read_9862 (which_codec, regno, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::_write_spi (int optional_header, int enables, int format, std::string buf) +{ + return usrp_spi_write (d_udh, optional_header, enables, format, + buf.data(), buf.size()); +} + +std::string +usrp_basic::_read_spi (int optional_header, int enables, int format, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_spi_read (d_udh, optional_header, enables, format, buf, len)) + return ""; + + return std::string (buf, len); +} + + +bool +usrp_basic::_set_led (int which, bool on) +{ + return usrp_set_led (d_udh, which, on); +} + +//////////////////////////////////////////////////////////////// +// +// usrp_basic_rx +// +//////////////////////////////////////////////////////////////// + +static unsigned char rx_init_regs[] = { + REG_RX_PWR_DN, 0, + REG_RX_A, 0, // minimum gain = 0x00 (max gain = 0x14) + REG_RX_B, 0, // minimum gain = 0x00 (max gain = 0x14) + REG_RX_MISC, (RX_MISC_HS_DUTY_CYCLE | RX_MISC_CLK_DUTY), + REG_RX_IF, (RX_IF_USE_CLKOUT1 + | RX_IF_2S_COMP), + REG_RX_DIGITAL, (RX_DIGITAL_2_CHAN) +}; + + +usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) + : usrp_basic (which_board, open_rx_interface, fpga_filename, firmware_filename), + d_devhandle (0), d_ephandle (0), + d_bytes_seen (0), d_first_read (true), + d_rx_enable (false) +{ + // initialize rx specific registers + + if (!usrp_9862_write_many_all (d_udh, rx_init_regs, sizeof (rx_init_regs))){ + fprintf (stderr, "usrp_basic_rx: failed to init AD9862 RX regs\n"); + throw std::runtime_error ("usrp_basic_rx/init_9862"); + } + + if (0){ + // FIXME power down 2nd codec rx path + usrp_9862_write (d_udh, 1, REG_RX_PWR_DN, 0x1); // power down everything + } + + // Reset the rx path and leave it disabled. + set_rx_enable (false); + usrp_set_fpga_rx_reset (d_udh, true); + usrp_set_fpga_rx_reset (d_udh, false); + + set_fpga_rx_sample_rate_divisor (2); // usually correct + + set_dc_offset_cl_enable(0xf, 0xf); // enable DC offset removal control loops + + probe_rx_slots (false); + + // check fusb buffering parameters + + if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); + + if (fusb_nblocks < 0) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); + + if (fusb_block_size == 0) + fusb_block_size = FUSB_BLOCK_SIZE; + + if (fusb_nblocks == 0) + fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); + + d_devhandle = fusb_sysconfig::make_devhandle (d_udh); + d_ephandle = d_devhandle->make_ephandle (USRP_RX_ENDPOINT, true, + fusb_block_size, fusb_nblocks); + + _write_fpga_reg(FR_ATR_MASK_1, 0); // zero Rx side Auto Transmit/Receive regs + _write_fpga_reg(FR_ATR_TXVAL_1, 0); + _write_fpga_reg(FR_ATR_RXVAL_1, 0); + _write_fpga_reg(FR_ATR_MASK_3, 0); + _write_fpga_reg(FR_ATR_TXVAL_3, 0); + _write_fpga_reg(FR_ATR_RXVAL_3, 0); +} + +static unsigned char rx_fini_regs[] = { + REG_RX_PWR_DN, 0x1 // power down everything +}; + +usrp_basic_rx::~usrp_basic_rx () +{ + if (!set_rx_enable (false)){ + fprintf (stderr, "usrp_basic_rx: set_fpga_rx_enable failed\n"); + usb_strerror (); + } + + d_ephandle->stop (); + delete d_ephandle; + delete d_devhandle; + + if (!usrp_9862_write_many_all (d_udh, rx_fini_regs, sizeof (rx_fini_regs))){ + fprintf (stderr, "usrp_basic_rx: failed to fini AD9862 RX regs\n"); + } +} + + +bool +usrp_basic_rx::start () +{ + if (!usrp_basic::start ()) // invoke parent's method + return false; + + // fire off reads before asserting rx_enable + + if (!d_ephandle->start ()){ + fprintf (stderr, "usrp_basic_rx: failed to start end point streaming"); + usb_strerror (); + return false; + } + + if (!set_rx_enable (true)){ + fprintf (stderr, "usrp_basic_rx: set_rx_enable failed\n"); + usb_strerror (); + return false; + } + + return true; +} + +bool +usrp_basic_rx::stop () +{ + bool ok = usrp_basic::stop(); + + if (!d_ephandle->stop()){ + fprintf (stderr, "usrp_basic_rx: failed to stop end point streaming"); + usb_strerror (); + ok = false; + } + if (!set_rx_enable(false)){ + fprintf (stderr, "usrp_basic_rx: set_rx_enable(false) failed\n"); + usb_strerror (); + ok = false; + } + return false; +} + +usrp_basic_rx * +usrp_basic_rx::make (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) +{ + usrp_basic_rx *u = 0; + + try { + u = new usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_basic_rx::set_fpga_rx_sample_rate_divisor (unsigned int div) +{ + return _write_fpga_reg (FR_RX_SAMPLE_RATE_DIV, div - 1); +} + + +/* + * \brief read data from the D/A's via the FPGA. + * \p len must be a multiple of 512 bytes. + * + * \returns the number of bytes read, or -1 on error. + * + * If overrun is non-NULL it will be set true iff an RX overrun is detected. + */ +int +usrp_basic_rx::read (void *buf, int len, bool *overrun) +{ + int r; + + if (overrun) + *overrun = false; + + if (len < 0 || (len % 512) != 0){ + fprintf (stderr, "usrp_basic_rx::read: invalid length = %d\n", len); + return -1; + } + + r = d_ephandle->read (buf, len); + if (r > 0) + d_bytes_seen += r; + + /* + * In many cases, the FPGA reports an rx overrun right after we + * enable the Rx path. If this is our first read, check for the + * overrun to clear the condition, then ignore the result. + */ + if (0 && d_first_read){ // FIXME + d_first_read = false; + bool bogus_overrun; + usrp_check_rx_overrun (d_udh, &bogus_overrun); + } + + if (overrun != 0 && d_bytes_seen >= d_bytes_per_poll){ + d_bytes_seen = 0; + if (!usrp_check_rx_overrun (d_udh, overrun)){ + fprintf (stderr, "usrp_basic_rx: usrp_check_rx_overrun failed\n"); + usb_strerror (); + } + } + + return r; +} + +bool +usrp_basic_rx::set_rx_enable (bool on) +{ + d_rx_enable = on; + return usrp_set_fpga_rx_enable (d_udh, on); +} + +// conditional disable, return prev state +bool +usrp_basic_rx::disable_rx () +{ + bool enabled = rx_enable (); + if (enabled) + set_rx_enable (false); + return enabled; +} + +// conditional set +void +usrp_basic_rx::restore_rx (bool on) +{ + if (on != rx_enable ()) + set_rx_enable (on); +} + +bool +usrp_basic_rx::set_pga (int which, double gain) +{ + if (which < 0 || which > 3) + return false; + + gain = std::max (pga_min (), gain); + gain = std::min (pga_max (), gain); + + int codec = which >> 1; + int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; + + // read current value to get input buffer bypass flag. + unsigned char cur_rx; + if (!_read_9862 (codec, reg, &cur_rx)) + return false; + + int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); + + cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f); + return _write_9862 (codec, reg, cur_rx); +} + +double +usrp_basic_rx::pga (int which) const +{ + if (which < 0 || which > 3) + return READ_FAILED; + + int codec = which >> 1; + int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; + unsigned char v; + bool ok = _read_9862 (codec, reg, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * (v & 0x1f)) + pga_min(); +} + +static int +slot_id_to_oe_reg (int slot_id) +{ + static int reg[4] = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_io_reg (int slot_id) +{ + static int reg[4] = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +void +usrp_basic_rx::probe_rx_slots (bool verbose) +{ + struct usrp_dboard_eeprom eeprom; + static int slot_id_map[2] = { SLOT_RX_A, SLOT_RX_B }; + static const char *slot_name[2] = { "RX d'board A", "RX d'board B" }; + + for (int i = 0; i < 2; i++){ + int slot_id = slot_id_map [i]; + const char *msg = 0; + usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); + + switch (s){ + case UDBE_OK: + d_dbid[i] = eeprom.id; + msg = usrp_dbid_to_string (eeprom.id).c_str (); + set_adc_offset (2*i+0, eeprom.offset[0]); + set_adc_offset (2*i+1, eeprom.offset[1]); + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_NO_EEPROM: + d_dbid[i] = -1; + msg = ""; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_INVALID_EEPROM: + d_dbid[i] = -2; + msg = "Invalid EEPROM contents"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_BAD_SLOT: + default: + assert (0); + } + + if (verbose){ + fflush (stdout); + fprintf (stderr, "%s: %s\n", slot_name[i], msg); + } + } +} + +bool +usrp_basic_rx::_write_oe (int which_dboard, int value, int mask) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic_rx::write_io (int which_dboard, int value, int mask) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic_rx::read_io (int which_dboard, int *value) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + int t; + int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) + bool ok = _read_fpga_reg (reg, &t); + if (!ok) + return false; + + *value = (t >> 16) & 0xffff; // FIXME, more magic + return true; +} + +int +usrp_basic_rx::read_io (int which_dboard) +{ + int value; + if (!read_io (which_dboard, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic_rx::write_aux_dac (int which_dboard, int which_dac, int value) +{ + return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), + which_dac, value); +} + +bool +usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc, int *value) +{ + return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), + which_adc, value); +} + +int +usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc) +{ + return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); +} + +int +usrp_basic_rx::block_size () const { return d_ephandle->block_size(); } + +bool +usrp_basic_rx::set_dc_offset_cl_enable(int bits, int mask) +{ + return _write_fpga_reg(FR_DC_OFFSET_CL_EN, + (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); +} + +//////////////////////////////////////////////////////////////// +// +// usrp_basic_tx +// +//////////////////////////////////////////////////////////////// + + +// +// DAC input rate 64 MHz interleaved for a total input rate of 128 MHz +// DAC input is latched on rising edge of CLKOUT2 +// NCO is disabled +// interpolate 2x +// coarse modulator disabled +// + +static unsigned char tx_init_regs[] = { + REG_TX_PWR_DN, 0, + REG_TX_A_OFFSET_LO, 0, + REG_TX_A_OFFSET_HI, 0, + REG_TX_B_OFFSET_LO, 0, + REG_TX_B_OFFSET_HI, 0, + REG_TX_A_GAIN, (TX_X_GAIN_COARSE_FULL | 0), + REG_TX_B_GAIN, (TX_X_GAIN_COARSE_FULL | 0), + REG_TX_PGA, 0xff, // maximum gain (0 dB) + REG_TX_MISC, 0, + REG_TX_IF, (TX_IF_USE_CLKOUT1 + | TX_IF_I_FIRST + | TX_IF_INV_TX_SYNC + | TX_IF_2S_COMP + | TX_IF_INTERLEAVED), + REG_TX_DIGITAL, (TX_DIGITAL_2_DATA_PATHS + | TX_DIGITAL_INTERPOLATE_4X), + REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO + | TX_MODULATOR_COARSE_MODULATION_NONE), + REG_TX_NCO_FTW_7_0, 0, + REG_TX_NCO_FTW_15_8, 0, + REG_TX_NCO_FTW_23_16, 0 +}; + +usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) + : usrp_basic (which_board, open_tx_interface, fpga_filename, firmware_filename), + d_devhandle (0), d_ephandle (0), + d_bytes_seen (0), d_first_write (true), + d_tx_enable (false) +{ + if (!usrp_9862_write_many_all (d_udh, tx_init_regs, sizeof (tx_init_regs))){ + fprintf (stderr, "usrp_basic_tx: failed to init AD9862 TX regs\n"); + throw std::runtime_error ("usrp_basic_tx/init_9862"); + } + + if (0){ + // FIXME power down 2nd codec tx path + usrp_9862_write (d_udh, 1, REG_TX_PWR_DN, + (TX_PWR_DN_TX_DIGITAL + | TX_PWR_DN_TX_ANALOG_BOTH)); + } + + // Reset the tx path and leave it disabled. + set_tx_enable (false); + usrp_set_fpga_tx_reset (d_udh, true); + usrp_set_fpga_tx_reset (d_udh, false); + + set_fpga_tx_sample_rate_divisor (4); // we're using interp x4 + + probe_tx_slots (false); + + // check fusb buffering parameters + + if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); + + if (fusb_nblocks < 0) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); + + if (fusb_block_size == 0) + fusb_block_size = FUSB_BLOCK_SIZE; + + if (fusb_nblocks == 0) + fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); + + d_devhandle = fusb_sysconfig::make_devhandle (d_udh); + d_ephandle = d_devhandle->make_ephandle (USRP_TX_ENDPOINT, false, + fusb_block_size, fusb_nblocks); + + _write_fpga_reg(FR_ATR_MASK_0, 0); // zero Tx side Auto Transmit/Receive regs + _write_fpga_reg(FR_ATR_TXVAL_0, 0); + _write_fpga_reg(FR_ATR_RXVAL_0, 0); + _write_fpga_reg(FR_ATR_MASK_2, 0); + _write_fpga_reg(FR_ATR_TXVAL_2, 0); + _write_fpga_reg(FR_ATR_RXVAL_2, 0); +} + + +static unsigned char tx_fini_regs[] = { + REG_TX_PWR_DN, (TX_PWR_DN_TX_DIGITAL + | TX_PWR_DN_TX_ANALOG_BOTH), + REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO + | TX_MODULATOR_COARSE_MODULATION_NONE) +}; + +usrp_basic_tx::~usrp_basic_tx () +{ + d_ephandle->stop (); + delete d_ephandle; + delete d_devhandle; + + if (!usrp_9862_write_many_all (d_udh, tx_fini_regs, sizeof (tx_fini_regs))){ + fprintf (stderr, "usrp_basic_tx: failed to fini AD9862 TX regs\n"); + } +} + +bool +usrp_basic_tx::start () +{ + if (!usrp_basic::start ()) + return false; + + if (!set_tx_enable (true)){ + fprintf (stderr, "usrp_basic_tx: set_tx_enable failed\n"); + usb_strerror (); + return false; + } + + if (!d_ephandle->start ()){ + fprintf (stderr, "usrp_basic_tx: failed to start end point streaming"); + usb_strerror (); + return false; + } + + return true; +} + +bool +usrp_basic_tx::stop () +{ + bool ok = usrp_basic::stop (); + + if (!set_tx_enable (false)){ + fprintf (stderr, "usrp_basic_tx: set_tx_enable(false) failed\n"); + usb_strerror (); + ok = false; + } + if (!d_ephandle->stop ()){ + fprintf (stderr, "usrp_basic_tx: failed to stop end point streaming"); + usb_strerror (); + ok = false; + } + return ok; +} + +usrp_basic_tx * +usrp_basic_tx::make (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) +{ + usrp_basic_tx *u = 0; + + try { + u = new usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_basic_tx::set_fpga_tx_sample_rate_divisor (unsigned int div) +{ + return _write_fpga_reg (FR_TX_SAMPLE_RATE_DIV, div - 1); +} + +/*! + * \brief Write data to the A/D's via the FPGA. + * + * \p len must be a multiple of 512 bytes. + * \returns number of bytes written or -1 on error. + * + * if \p underrun is non-NULL, it will be set to true iff + * a transmit underrun condition is detected. + */ +int +usrp_basic_tx::write (const void *buf, int len, bool *underrun) +{ + int r; + + if (underrun) + *underrun = false; + + if (len < 0 || (len % 512) != 0){ + fprintf (stderr, "usrp_basic_tx::write: invalid length = %d\n", len); + return -1; + } + + r = d_ephandle->write (buf, len); + if (r > 0) + d_bytes_seen += r; + + /* + * In many cases, the FPGA reports an tx underrun right after we + * enable the Tx path. If this is our first write, check for the + * underrun to clear the condition, then ignore the result. + */ + if (d_first_write && d_bytes_seen >= 4 * FUSB_BLOCK_SIZE){ + d_first_write = false; + bool bogus_underrun; + usrp_check_tx_underrun (d_udh, &bogus_underrun); + } + + if (underrun != 0 && d_bytes_seen >= d_bytes_per_poll){ + d_bytes_seen = 0; + if (!usrp_check_tx_underrun (d_udh, underrun)){ + fprintf (stderr, "usrp_basic_tx: usrp_check_tx_underrun failed\n"); + usb_strerror (); + } + } + + return r; +} + +void +usrp_basic_tx::wait_for_completion () +{ + d_ephandle->wait_for_completion (); +} + +bool +usrp_basic_tx::set_tx_enable (bool on) +{ + d_tx_enable = on; + // fprintf (stderr, "set_tx_enable %d\n", on); + return usrp_set_fpga_tx_enable (d_udh, on); +} + +// conditional disable, return prev state +bool +usrp_basic_tx::disable_tx () +{ + bool enabled = tx_enable (); + if (enabled) + set_tx_enable (false); + return enabled; +} + +// conditional set +void +usrp_basic_tx::restore_tx (bool on) +{ + if (on != tx_enable ()) + set_tx_enable (on); +} + +bool +usrp_basic_tx::set_pga (int which, double gain) +{ + if (which < 0 || which > 3) + return false; + + gain = std::max (pga_min (), gain); + gain = std::min (pga_max (), gain); + + int codec = which >> 1; // 0 and 1 are same, as are 2 and 3 + + int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); + + return _write_9862 (codec, REG_TX_PGA, int_gain); +} + +double +usrp_basic_tx::pga (int which) const +{ + if (which < 0 || which > 3) + return READ_FAILED; + + int codec = which >> 1; + unsigned char v; + bool ok = _read_9862 (codec, REG_TX_PGA, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * v) + pga_min(); +} + +void +usrp_basic_tx::probe_tx_slots (bool verbose) +{ + struct usrp_dboard_eeprom eeprom; + static int slot_id_map[2] = { SLOT_TX_A, SLOT_TX_B }; + static const char *slot_name[2] = { "TX d'board A", "TX d'board B" }; + + for (int i = 0; i < 2; i++){ + int slot_id = slot_id_map [i]; + const char *msg = 0; + usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); + + switch (s){ + case UDBE_OK: + d_dbid[i] = eeprom.id; + msg = usrp_dbid_to_string (eeprom.id).c_str (); + // FIXME, figure out interpretation of dc offset for TX d'boards + // offset = (eeprom.offset[1] << 16) | (eeprom.offset[0] & 0xffff); + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_NO_EEPROM: + d_dbid[i] = -1; + msg = ""; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_INVALID_EEPROM: + d_dbid[i] = -2; + msg = "Invalid EEPROM contents"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_BAD_SLOT: + default: + assert (0); + } + + if (verbose){ + fflush (stdout); + fprintf (stderr, "%s: %s\n", slot_name[i], msg); + } + } +} + +bool +usrp_basic_tx::_write_oe (int which_dboard, int value, int mask) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic_tx::write_io (int which_dboard, int value, int mask) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic_tx::read_io (int which_dboard, int *value) +{ + if (! (0 <= which_dboard && which_dboard <= 1)) + return false; + + int t; + int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) + bool ok = _read_fpga_reg (reg, &t); + if (!ok) + return false; + + *value = t & 0xffff; // FIXME, more magic + return true; +} + +int +usrp_basic_tx::read_io (int which_dboard) +{ + int value; + if (!read_io (which_dboard, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic_tx::write_aux_dac (int which_dboard, int which_dac, int value) +{ + return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), + which_dac, value); +} + +bool +usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc, int *value) +{ + return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), + which_adc, value); +} + +int +usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc) +{ + return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); +} + +int +usrp_basic_tx::block_size () const { return d_ephandle->block_size(); } + diff --git a/usrp/host/lib/legacy/usrp_basic.h b/usrp/host/lib/legacy/usrp_basic.h new file mode 100644 index 00000000..796a0f49 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_basic.h @@ -0,0 +1,776 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * ---------------------------------------------------------------------- + * Mid level interface to the Universal Software Radio Peripheral (Rev 1) + * + * These classes implement the basic functionality for talking to the + * USRP. They try to be as independent of the signal processing code + * in FPGA as possible. They implement access to the low level + * peripherals on the board, provide a common way for reading and + * writing registers in the FPGA, and provide the high speed interface + * to streaming data across the USB. + * + * It is expected that subclasses will be derived that provide + * access to the functionality to a particular FPGA configuration. + * ---------------------------------------------------------------------- + */ + +#ifndef INCLUDED_USRP_BASIC_H +#define INCLUDED_USRP_BASIC_H + +#include +#include + +struct usb_dev_handle; +class fusb_devhandle; +class fusb_ephandle; + +/*! + * \brief base class for usrp operations + */ +class usrp_basic +{ +private: + // NOT IMPLEMENTED + usrp_basic (const usrp_basic &rhs); // no copy constructor + usrp_basic &operator= (const usrp_basic &rhs); // no assignment operator + + +protected: + struct usb_dev_handle *d_udh; + int d_usb_data_rate; // bytes/sec + int d_bytes_per_poll; // how often to poll for overruns + bool d_verbose; + + static const int MAX_REGS = 128; + unsigned int d_fpga_shadows[MAX_REGS]; + + usrp_basic (int which_board, + struct usb_dev_handle *open_interface (struct usb_device *dev), + const std::string fpga_filename = "", + const std::string firmware_filename = ""); + + /*! + * \brief advise usrp_basic of usb data rate (bytes/sec) + * + * N.B., this doesn't tweak any hardware. Derived classes + * should call this to inform us of the data rate whenever it's + * first set or if it changes. + * + * \param usb_data_rate bytes/sec + */ + void set_usb_data_rate (int usb_data_rate); + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param slot Which Tx or Rx slot to write. + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [0,3] RX slots must use only 0 and 1. TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool write_aux_dac (int slot, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param slot 2-bit slot number. E.g., SLOT_TX_A + * \param which_adc [0,1] + * \param value return 12-bit value [0,4095] + * \returns true iff successful + */ + bool read_aux_adc (int slot, int which_adc, int *value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param slot 2-bit slot number. E.g., SLOT_TX_A + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int read_aux_adc (int slot, int which_adc); + +public: + virtual ~usrp_basic (); + + /*! + * \brief return frequency of master oscillator on USRP + */ + long fpga_master_clock_freq () const { return 64000000; } + + /*! + * \returns usb data rate in bytes/sec + */ + int usb_data_rate () const { return d_usb_data_rate; } + + void set_verbose (bool on) { d_verbose = on; } + + //! magic value used on alternate register read interfaces + static const int READ_FAILED = -99999; + + /*! + * \brief Write EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin writing + * \param buf the data to write + * \returns true iff sucessful + */ + bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); + + /*! + * \brief Read EEPROM on motherboard or any daughterboard. + * \param i2c_addr I2C bus address of EEPROM + * \param eeprom_offset byte offset in EEPROM to begin reading + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + */ + std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); + + /*! + * \brief Write to I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of of 64 bytes. + */ + bool write_i2c (int i2c_addr, const std::string buf); + + /*! + * \brief Read from I2C peripheral + * \param i2c_addr I2C bus address (7-bits) + * \param len number of bytes to read + * \returns the data read if successful, else a zero length string. + * Reads are limited to a maximum of 64 bytes. + */ + std::string read_i2c (int i2c_addr, int len); + + /*! + * \brief Set ADC offset correction + * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... + * \param offset 16-bit value to subtract from raw ADC input. + */ + bool set_adc_offset (int which, int offset); + + /*! + * \brief Set DAC offset correction + * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... + * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). + * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; + * If 1 offset applied to +ve differential pin. + */ + bool set_dac_offset (int which, int offset, int offset_pin); + + /*! + * \brief Control ADC input buffer + * \param which which ADC[0,3] + * \param bypass if non-zero, bypass input buffer and connect input + * directly to switched cap SHA input of RxPGA. + */ + bool set_adc_buffer_bypass (int which, bool bypass); + + + /*! + * \brief return the usrp's serial number. + * + * \returns non-zero length string iff successful. + */ + std::string serial_number(); + + // ---------------------------------------------------------------- + // Low level implementation routines. + // You probably shouldn't be using these... + // + + bool _set_led (int which, bool on); + + /*! + * \brief Write FPGA register. + * \param regno 7-bit register number + * \param value 32-bit value + * \returns true iff successful + */ + bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value + + /*! + * \brief Read FPGA register. + * \param regno 7-bit register number + * \param value 32-bit value + * \returns true iff successful + */ + bool _read_fpga_reg (int regno, int *value); //< 7-bit regno, 32-bit value + + /*! + * \brief Read FPGA register. + * \param regno 7-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_fpga_reg (int regno); + + + /*! + * \brief Write FPGA register with mask. + * \param regno 7-bit register number + * \param value 16-bit value + * \param mask 16-bit value + * \returns true if successful + * Only use this for registers who actually implement a mask in the verilog firmware, like FR_RX_MASTER_SLAVE + */ + bool _write_fpga_reg_masked (int regno, int value, int mask); + + /*! + * \brief Write AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \param value 8-bit value + * \returns true iff successful + */ + bool _write_9862 (int which_codec, int regno, unsigned char value); + + /*! + * \brief Read AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \param value 8-bit value + * \returns true iff successful + */ + bool _read_9862 (int which_codec, int regno, unsigned char *value) const; + + /*! + * \brief Read AD9862 register. + * \param which_codec 0 or 1 + * \param regno 6-bit register number + * \returns register value if successful, else READ_FAILED + */ + int _read_9862 (int which_codec, int regno) const; + + /*! + * \brief Write data to SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripherals to write. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param buf the data to write + * \returns true iff successful + * Writes are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they are + * written to the peripheral immediately prior to writing \p buf. + */ + bool _write_spi (int optional_header, int enables, int format, std::string buf); + + /* + * \brief Read data from SPI bus peripheral. + * + * \param optional_header 0,1 or 2 bytes to write before buf. + * \param enables bitmask of peripheral to read. See usrp_spi_defs.h + * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* + * \param len number of bytes to read. Must be in [0,64]. + * \returns the data read if sucessful, else a zero length string. + * + * Reads are limited to a maximum of 64 bytes. + * + * If \p format specifies that optional_header bytes are present, they + * are written to the peripheral first. Then \p len bytes are read from + * the peripheral and returned. + */ + std::string _read_spi (int optional_header, int enables, int format, int len); + + /*! + * \brief Start data transfers. + * Called in base class to derived class order. + */ + bool start (); + + /*! + * \brief Stop data transfers. + * Called in base class to derived class order. + */ + bool stop (); +}; + + /*! + * \brief class for accessing the receive side of the USRP + */ +class usrp_basic_rx : public usrp_basic +{ +private: + fusb_devhandle *d_devhandle; + fusb_ephandle *d_ephandle; + int d_bytes_seen; // how many bytes we've seen + bool d_first_read; + bool d_rx_enable; + +protected: + int d_dbid[2]; // Rx daughterboard ID's + + /*! + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + usrp_basic_rx (int which_board, + int fusb_block_size=0, + int fusb_nblocks=0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); // throws if trouble + + bool set_rx_enable (bool on); + bool rx_enable () const { return d_rx_enable; } + + bool disable_rx (); // conditional disable, return prev state + void restore_rx (bool on); // conditional set + + void probe_rx_slots (bool verbose); + int dboard_to_slot (int dboard) { return (dboard << 1) | 1; } + +public: + ~usrp_basic_rx (); + + /*! + * \brief invokes constructor, returns instance or 0 if trouble + * + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + static usrp_basic_rx *make (int which_board, + int fusb_block_size=0, + int fusb_nblocks=0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); + + // MANIPULATORS + + /*! + * \brief tell the fpga the rate rx samples are coming from the A/D's + * + * div = fpga_master_clock_freq () / sample_rate + * + * sample_rate is determined by a myriad of registers + * in the 9862. That's why you have to tell us, so + * we can tell the fpga. + */ + bool set_fpga_rx_sample_rate_divisor (unsigned int div); + + /*! + * \brief read data from the D/A's via the FPGA. + * \p len must be a multiple of 512 bytes. + * + * \returns the number of bytes read, or -1 on error. + * + * If overrun is non-NULL it will be set true iff an RX overrun is detected. + */ + int read (void *buf, int len, bool *overrun); + + // ACCESSORS + + //! sampling rate of A/D converter + virtual long converter_rate() const { return fpga_master_clock_freq(); } // 64M + long adc_rate() const { return converter_rate(); } + long adc_freq() const { return converter_rate(); } //!< deprecated method name + + /*! + * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. + * + * \param which_dboard [0,1] which Rx daughterboard + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } + + // ---------------------------------------------------------------- + // routines for controlling the Programmable Gain Amplifier + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which A/D [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain setting in dB. + * + * \param which which A/D [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + double pga_min () const { return 0.0; } + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + double pga_max () const { return 20.0; } + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const { return 20.0 / 20; } + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value output + */ + bool read_io (int which_dboard, int *value); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param which_dboard [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool write_aux_dac (int which_board, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \param value return 12-bit value [0,4095] + * \returns true iff successful + */ + bool read_aux_adc (int which_dboard, int which_adc, int *value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int read_aux_adc (int which_dboard, int which_adc); + + /*! + * \brief returns current fusb block size + */ + int block_size() const; + + /*! + * \brief Enable/disable automatic DC offset removal control loop in FPGA + * + * \param bits which control loops to enable + * \param mask which \p bits to pay attention to + * + * If the corresponding bit is set, enable the automatic DC + * offset correction control loop. + * + *
+   * The 4 low bits are significant:
+   *
+   *   ADC0 = (1 << 0)
+   *   ADC1 = (1 << 1)
+   *   ADC2 = (1 << 2)
+   *   ADC3 = (1 << 3)
+   * 
+ * + * By default the control loop is enabled on all ADC's. + */ + bool set_dc_offset_cl_enable(int bits, int mask); + + // called in base class to derived class order + bool start (); + bool stop (); +}; + + /*! + * \brief class for accessing the transmit side of the USRP + */ +class usrp_basic_tx : public usrp_basic +{ +private: + fusb_devhandle *d_devhandle; + fusb_ephandle *d_ephandle; + int d_bytes_seen; // how many bytes we've seen + bool d_first_write; + bool d_tx_enable; + + protected: + int d_dbid[2]; // Tx daughterboard ID's + + /*! + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + usrp_basic_tx (int which_board, + int fusb_block_size=0, + int fusb_nblocks=0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); // throws if trouble + + bool set_tx_enable (bool on); + bool tx_enable () const { return d_tx_enable; } + + bool disable_tx (); // conditional disable, return prev state + void restore_tx (bool on); // conditional set + + void probe_tx_slots (bool verbose); + int dboard_to_slot (int dboard) { return (dboard << 1) | 0; } + +public: + + ~usrp_basic_tx (); + + /*! + * \brief invokes constructor, returns instance or 0 if trouble + * + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + static usrp_basic_tx *make (int which_board, int fusb_block_size=0, int fusb_nblocks=0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); + + // MANIPULATORS + + /*! + * \brief tell the fpga the rate tx samples are going to the D/A's + * + * div = fpga_master_clock_freq () * 2 + * + * sample_rate is determined by a myriad of registers + * in the 9862. That's why you have to tell us, so + * we can tell the fpga. + */ + bool set_fpga_tx_sample_rate_divisor (unsigned int div); + + /*! + * \brief Write data to the A/D's via the FPGA. + * + * \p len must be a multiple of 512 bytes. + * \returns number of bytes written or -1 on error. + * + * if \p underrun is non-NULL, it will be set to true iff + * a transmit underrun condition is detected. + */ + int write (const void *buf, int len, bool *underrun); + + /* + * Block until all outstanding writes have completed. + * This is typically used to assist with benchmarking + */ + void wait_for_completion (); + + // ACCESSORS + + //! sampling rate of D/A converter + virtual long converter_rate() const { return fpga_master_clock_freq () * 2; } // 128M + long dac_rate() const { return converter_rate(); } + long dac_freq() const { return converter_rate(); } //!< deprecated method name + + /*! + * \brief Return daughterboard ID for given Tx daughterboard slot [0,1]. + * + * \return daughterboard id >= 0 if successful + * \return -1 if no daugherboard + * \return -2 if invalid EEPROM on daughterboard + */ + int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } + + // ---------------------------------------------------------------- + // routines for controlling the Programmable Gain Amplifier + /*! + * \brief Set Programmable Gain Amplifier (PGA) + * + * \param which which D/A [0,3] + * \param gain_in_db gain value (linear in dB) + * + * gain is rounded to closest setting supported by hardware. + * Note that DAC 0 and DAC 1 share a gain setting as do DAC 2 and DAC 3. + * Setting DAC 0 affects DAC 1 and vice versa. Same with DAC 2 and DAC 3. + * + * \returns true iff sucessful. + * + * \sa pga_min(), pga_max(), pga_db_per_step() + */ + bool set_pga (int which, double gain_in_db); + + /*! + * \brief Return programmable gain amplifier gain in dB. + * + * \param which which D/A [0,3] + */ + double pga (int which) const; + + /*! + * \brief Return minimum legal PGA gain in dB. + */ + double pga_min () const { return -20.0; } + + /*! + * \brief Return maximum legal PGA gain in dB. + */ + double pga_max () const { return 0.0; } + + /*! + * \brief Return hardware step size of PGA (linear in dB). + */ + double pga_db_per_step () const { return 20.0/255; } + + /*! + * \brief Write direction register (output enables) for pins that go to daughterboard. + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + * + * Each d'board has 16-bits of general purpose i/o. + * Setting the bit makes it an output from the FPGA to the d'board. + * + * This register is initialized based on a value stored in the + * d'board EEPROM. In general, you shouldn't be using this routine + * without a very good reason. Using this method incorrectly will + * kill your USRP motherboard and/or daughterboard. + */ + bool _write_oe (int which_dboard, int value, int mask); + + /*! + * \brief Write daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value value to write into register + * \param mask which bits of value to write into reg + */ + bool write_io (int which_dboard, int value, int mask); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \param value return value + */ + bool read_io (int which_dboard, int *value); + + /*! + * \brief Read daughterboard i/o pin value + * + * \param which_dboard [0,1] which d'board + * \returns register value if successful, else READ_FAILED + */ + int read_io (int which_dboard); + + /*! + * \brief Write auxiliary digital to analog converter. + * + * \param which_dboard [0,1] which d'board + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. + * \param which_dac [2,3] TX slots must use only 2 and 3. + * \param value [0,4095] + * \returns true iff successful + */ + bool write_aux_dac (int which_board, int which_dac, int value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \param value return 12-bit value [0,4095] + * \returns true iff successful + */ + bool read_aux_adc (int which_dboard, int which_adc, int *value); + + /*! + * \brief Read auxiliary analog to digital converter. + * + * \param which_dboard [0,1] which d'board + * \param which_adc [0,1] + * \returns value in the range [0,4095] if successful, else READ_FAILED. + */ + int read_aux_adc (int which_dboard, int which_adc); + + /*! + * \brief returns current fusb block size + */ + int block_size() const; + + // called in base class to derived class order + bool start (); + bool stop (); +}; + +#endif diff --git a/usrp/host/lib/legacy/usrp_bytesex.h b/usrp/host/lib/legacy/usrp_bytesex.h new file mode 100644 index 00000000..6c4e129c --- /dev/null +++ b/usrp/host/lib/legacy/usrp_bytesex.h @@ -0,0 +1,105 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDED_USRP_BYTESEX_H +#define INCLUDED_USRP_BYTESEX_H + +/*! + * \brief routines for convertering between host and usrp byte order + * + * Prior to including this file, the user must include "config.h" + * which will or won't define WORDS_BIGENDIAN based on the + * result of the AC_C_BIGENDIAN autoconf test. + */ + +#ifdef HAVE_BYTESWAP_H +#include +#else +static inline unsigned short int +bswap_16 (unsigned short int x) +{ + return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)); +} + +static inline unsigned int +bswap32 (unsigned int x) +{ + return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) \ + | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); +} +#endif + + +#ifdef WORDS_BIGENDIAN + +static inline unsigned int +host_to_usrp_u32 (unsigned int x) +{ + return bswap_32(x); +} + +static inline unsigned int +usrp_to_host_u32 (unsigned int x) +{ + return bswap_32(x); +} + +static inline short int +host_to_usrp_short (short int x) +{ + return bswap_16 (x); +} + +static inline short int +usrp_to_host_short (short int x) +{ + return bswap_16 (x); +} + +#else + +static inline unsigned int +host_to_usrp_u32 (unsigned int x) +{ + return x; +} + +static inline unsigned int +usrp_to_host_u32 (unsigned int x) +{ + return x; +} + +static inline short int +host_to_usrp_short (short int x) +{ + return x; +} + +static inline short int +usrp_to_host_short (unsigned short int x) +{ + return x; +} + +#endif + +#endif /* INCLUDED_USRP_BYTESEX_H */ diff --git a/usrp/host/lib/legacy/usrp_config.cc b/usrp/host/lib/legacy/usrp_config.cc new file mode 100644 index 00000000..17bfd48d --- /dev/null +++ b/usrp/host/lib/legacy/usrp_config.cc @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usrp_config.h" + +int +usrp_rx_config_stream_count (unsigned int usrp_rx_config) +{ + return 1; +} + +int +usrp_tx_config_stream_count (unsigned int usrp_tx_config) +{ + return 1; +} diff --git a/usrp/host/lib/legacy/usrp_config.h b/usrp/host/lib/legacy/usrp_config.h new file mode 100644 index 00000000..91b398b9 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_config.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _USRP_CONFIG_H_ +#define _USRP_CONFIG_H_ + +/* + * ---------------------------------------------------------------- + * USRP Rx configurations. + * + * For now this is a placeholder, but will eventually specify the + * mapping from A/D outputs to DDC inputs (I & Q). + * + * What's implemented today is a single DDC that has its I input + * connected to ADC0 and its Q input connected to ADC1 + * ---------------------------------------------------------------- + */ + +#define USRP_RX_CONFIG_DEFAULT 0 + +/*! + * given a usrp_rx_config word, return the number of I & Q streams that + * are interleaved on the USB. + */ + +int usrp_rx_config_stream_count (unsigned int usrp_rx_config); + +/* + * USRP Tx configurations. + * + * For now this is a placeholder, but will eventually specify the + * mapping from DUC outputs to D/A inputs. + * + * What's implemented today is a single DUC that has its I output + * connected to DAC0 and its Q output connected to DAC1 + */ + +#define USRP_TX_CONFIG_DEFAULT 0 + +/*! + * given a usrp_tx_config word, return the number of I & Q streams that + * are interleaved on the USB. + */ + +int usrp_tx_config_stream_count (unsigned int usrp_tx_config); + + +#endif /* _USRP_CONFIG_H_ */ diff --git a/usrp/host/lib/legacy/usrp_dbid.dat b/usrp/host/lib/legacy/usrp_dbid.dat new file mode 100644 index 00000000..a85d0532 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_dbid.dat @@ -0,0 +1,74 @@ +# +# Copyright 2005 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# This file is used to generate usrp_dbid.h, usrp_dbid.cc and usrp_dbid.py + +"Basic Tx" 0x0000 +"Basic Rx" 0x0001 +"DBS Rx" 0x0002 +"TV Rx" 0x0003 + +"Flex 400 Rx" 0x0004 +"Flex 900 Rx" 0x0005 +"Flex 1200 Rx" 0x0006 +"Flex 2400 Rx" 0x0007 + +"Flex 400 Tx" 0x0008 +"Flex 900 Tx" 0x0009 +"Flex 1200 Tx" 0x000a +"Flex 2400 Tx" 0x000b + +"TV Rx Rev 2" 0x000c +"DBS Rx Rev 2_1" 0x000d + +"LF Tx" 0x000e +"LF Rx" 0x000f + +"Flex 400 Rx MIMO A" 0x0014 +"Flex 900 Rx MIMO A" 0x0015 +"Flex 1200 Rx MIMO A" 0x0016 +"Flex 2400 Rx MIMO A" 0x0017 + +"Flex 400 Tx MIMO A" 0x0018 +"Flex 900 Tx MIMO A" 0x0019 +"Flex 1200 Tx MIMO A" 0x001a +"Flex 2400 Tx MIMO A" 0x001b + +"Flex 400 Rx MIMO B" 0x0024 +"Flex 900 Rx MIMO B" 0x0025 +"Flex 1200 Rx MIMO B" 0x0026 +"Flex 2400 Rx MIMO B" 0x0027 + +"Flex 400 Tx MIMO B" 0x0028 +"Flex 900 Tx MIMO B" 0x0029 +"Flex 1200 Tx MIMO B" 0x002a +"Flex 2400 Tx MIMO B" 0x002b + +"Flex 1800 Rx" 0x0030 +"Flex 1800 Tx" 0x0031 +"Flex 1800 Rx MIMO A" 0x0032 +"Flex 1800 Tx MIMO A" 0x0033 +"Flex 1800 Rx MIMO B" 0x0034 +"Flex 1800 Tx MIMO B" 0x0035 + +"TV Rx Rev 3" 0x0040 + +"Experimental Tx" 0xfffe +"Experimental Rx" 0xffff diff --git a/usrp/host/lib/legacy/usrp_local_sighandler.cc b/usrp/host/lib/legacy/usrp_local_sighandler.cc new file mode 100644 index 00000000..ab6c485f --- /dev/null +++ b/usrp/host/lib/legacy/usrp_local_sighandler.cc @@ -0,0 +1,190 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * This is actually the same as gr_local_signhandler, but with a different name. + * We don't have a common library to put this in, so... + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +usrp_local_sighandler::usrp_local_sighandler (int signum, + void (*new_handler)(int)) + : d_signum (signum) +{ +#ifdef HAVE_SIGACTION + 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 (d_signum, &new_action, &d_old_action) < 0){ + perror ("sigaction (install new)"); + throw std::runtime_error ("sigaction"); + } +#endif +} + +usrp_local_sighandler::~usrp_local_sighandler () +{ +#ifdef HAVE_SIGACTION + if (sigaction (d_signum, &d_old_action, 0) < 0){ + perror ("sigaction (restore old)"); + throw std::runtime_error ("sigaction"); + } +#endif +} + +void +usrp_local_sighandler::throw_signal(int signum) throw(usrp_signal) +{ + throw usrp_signal (signum); +} + +/* + * Semi-hideous way to may a signal number into a signal name + */ + +#define SIGNAME(x) case x: return #x + +std::string +usrp_signal::name () const +{ + char tmp[128]; + + switch (signal ()){ +#ifdef SIGHUP + SIGNAME (SIGHUP); +#endif +#ifdef SIGINT + SIGNAME (SIGINT); +#endif +#ifdef SIGQUIT + SIGNAME (SIGQUIT); +#endif +#ifdef SIGILL + SIGNAME (SIGILL); +#endif +#ifdef SIGTRAP + SIGNAME (SIGTRAP); +#endif +#ifdef SIGABRT + SIGNAME (SIGABRT); +#endif +#ifdef SIGBUS + SIGNAME (SIGBUS); +#endif +#ifdef SIGFPE + SIGNAME (SIGFPE); +#endif +#ifdef SIGKILL + SIGNAME (SIGKILL); +#endif +#ifdef SIGUSR1 + SIGNAME (SIGUSR1); +#endif +#ifdef SIGSEGV + SIGNAME (SIGSEGV); +#endif +#ifdef SIGUSR2 + SIGNAME (SIGUSR2); +#endif +#ifdef SIGPIPE + SIGNAME (SIGPIPE); +#endif +#ifdef SIGALRM + SIGNAME (SIGALRM); +#endif +#ifdef SIGTERM + SIGNAME (SIGTERM); +#endif +#ifdef SIGSTKFLT + SIGNAME (SIGSTKFLT); +#endif +#ifdef SIGCHLD + SIGNAME (SIGCHLD); +#endif +#ifdef SIGCONT + SIGNAME (SIGCONT); +#endif +#ifdef SIGSTOP + SIGNAME (SIGSTOP); +#endif +#ifdef SIGTSTP + SIGNAME (SIGTSTP); +#endif +#ifdef SIGTTIN + SIGNAME (SIGTTIN); +#endif +#ifdef SIGTTOU + SIGNAME (SIGTTOU); +#endif +#ifdef SIGURG + SIGNAME (SIGURG); +#endif +#ifdef SIGXCPU + SIGNAME (SIGXCPU); +#endif +#ifdef SIGXFSZ + SIGNAME (SIGXFSZ); +#endif +#ifdef SIGVTALRM + SIGNAME (SIGVTALRM); +#endif +#ifdef SIGPROF + SIGNAME (SIGPROF); +#endif +#ifdef SIGWINCH + SIGNAME (SIGWINCH); +#endif +#ifdef SIGIO + SIGNAME (SIGIO); +#endif +#ifdef SIGPWR + SIGNAME (SIGPWR); +#endif +#ifdef SIGSYS + SIGNAME (SIGSYS); +#endif + default: +#if defined (HAVE_SNPRINTF) +#if defined (SIGRTMIN) && defined (SIGRTMAX) + if (signal () >= SIGRTMIN && signal () <= SIGRTMAX){ + snprintf (tmp, sizeof (tmp), "SIGRTMIN + %d", signal ()); + return tmp; + } +#endif + snprintf (tmp, sizeof (tmp), "SIGNAL %d", signal ()); + return tmp; +#else + return "Unknown signal"; +#endif + } +} diff --git a/usrp/host/lib/legacy/usrp_local_sighandler.h b/usrp/host/lib/legacy/usrp_local_sighandler.h new file mode 100644 index 00000000..f1c00cd5 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_local_sighandler.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_USRP_LOCAL_SIGHANDLER_H +#define INCLUDED_USRP_LOCAL_SIGHANDLER_H + +#include +#include + +/*! + * \brief Representation of signal. + */ +class usrp_signal +{ + int d_signum; +public: + usrp_signal (int signum) : d_signum (signum) {} + int signal () const { return d_signum; } + std::string name () const; +}; + + +/*! + * \brief Get and set signal handler. + * + * Constructor installs new handler, destructor reinstalls + * original value. + */ +class usrp_local_sighandler { + int d_signum; +#ifdef HAVE_SIGACTION + struct sigaction d_old_action; +#endif +public: + usrp_local_sighandler (int signum, void (*new_handler)(int)); + ~usrp_local_sighandler (); + + /* throw usrp_signal (signum) */ + static void throw_signal (int signum) throw (usrp_signal); +}; + +#endif /* INCLUDED_USRP_LOCAL_SIGHANDLER_H */ diff --git a/usrp/host/lib/legacy/usrp_prims.cc b/usrp/host/lib/legacy/usrp_prims.cc new file mode 100644 index 00000000..f666a29f --- /dev/null +++ b/usrp/host/lib/legacy/usrp_prims.cc @@ -0,0 +1,1356 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "usrp_prims.h" +#include "usrp_commands.h" +#include "usrp_ids.h" +#include "usrp_i2c_addr.h" +#include "fpga_regs_common.h" +#include "fpga_regs_standard.h" +#include +#include +#include +#include +#include +#include +#include +#include // FIXME should check with autoconf (nanosleep) +#include +#include +#include + +extern "C" { +#include "md5.h" +}; + +#define VERBOSE 0 + +using namespace ad9862; + +static const int FIRMWARE_HASH_SLOT = 0; +static const int FPGA_HASH_SLOT = 1; + +static const int hash_slot_addr[2] = { + USRP_HASH_SLOT_0_ADDR, + USRP_HASH_SLOT_1_ADDR +}; + +static char *default_firmware_filename = "std.ihx"; +static char *default_fpga_filename = "std_2rxhb_2tx.rbf"; + +#include "std_paths.h" + +static char * +find_file (const char *filename, int hw_rev) +{ + char **sp = std_paths; + static char path[1000]; + char *s; + + s = getenv("USRP_PATH"); + if (s) { + snprintf (path, sizeof (path), "%s/rev%d/%s", s, hw_rev, filename); + if (access (path, R_OK) == 0) + return path; + } + + while (*sp){ + snprintf (path, sizeof (path), "%s/rev%d/%s", *sp, hw_rev, filename); + if (access (path, R_OK) == 0) + return path; + sp++; + } + return 0; +} + +static const char * +get_proto_filename(const std::string user_filename, const char *env_var, const char *def) +{ + if (user_filename.length() != 0) + return user_filename.c_str(); + + char *s = getenv(env_var); + if (s && *s) + return s; + + return def; +} + + +static void power_down_9862s (struct usb_dev_handle *udh); + +void +usrp_one_time_init () +{ + static bool first = true; + + if (first){ + first = false; + usb_init (); // usb library init + usb_find_busses (); + usb_find_devices (); + } +} + +void +usrp_rescan () +{ + usb_find_busses (); + usb_find_devices (); +} + + +// ---------------------------------------------------------------- +// Danger, big, fragile KLUDGE. The problem is that we want to be +// able to get from a usb_dev_handle back to a usb_device, and the +// right way to do this is buried in a non-installed include file. + +static struct usb_device * +dev_handle_to_dev (usb_dev_handle *udh) +{ + struct usb_dev_handle_kludge { + int fd; + struct usb_bus *bus; + struct usb_device *device; + }; + + return ((struct usb_dev_handle_kludge *) udh)->device; +} + +// ---------------------------------------------------------------- + +/* + * q must be a real USRP, not an FX2. Return its hardware rev number. + */ +int +usrp_hw_rev (struct usb_device *q) +{ + return q->descriptor.bcdDevice & 0x00FF; +} + +/* + * q must be a real USRP, not an FX2. Return true if it's configured. + */ +static bool +_usrp_configured_p (struct usb_device *q) +{ + return (q->descriptor.bcdDevice & 0xFF00) != 0; +} + +bool +usrp_usrp_p (struct usb_device *q) +{ + return (q->descriptor.idVendor == USB_VID_FSF + && q->descriptor.idProduct == USB_PID_FSF_USRP); +} + +bool +usrp_fx2_p (struct usb_device *q) +{ + return (q->descriptor.idVendor == USB_VID_CYPRESS + && q->descriptor.idProduct == USB_PID_CYPRESS_FX2); +} + +bool +usrp_usrp0_p (struct usb_device *q) +{ + return usrp_usrp_p (q) && usrp_hw_rev (q) == 0; +} + +bool +usrp_usrp1_p (struct usb_device *q) +{ + return usrp_usrp_p (q) && usrp_hw_rev (q) == 1; +} + +bool +usrp_usrp2_p (struct usb_device *q) +{ + return usrp_usrp_p (q) && usrp_hw_rev (q) == 2; +} + + +bool +usrp_unconfigured_usrp_p (struct usb_device *q) +{ + return usrp_usrp_p (q) && !_usrp_configured_p (q); +} + +bool +usrp_configured_usrp_p (struct usb_device *q) +{ + return usrp_usrp_p (q) && _usrp_configured_p (q); +} + +// ---------------------------------------------------------------- + +struct usb_device * +usrp_find_device (int nth, bool fx2_ok_p) +{ + struct usb_bus *p; + struct usb_device *q; + int n_found = 0; + + usrp_one_time_init (); + + p = usb_get_busses(); + while (p != NULL){ + q = p->devices; + while (q != NULL){ + if (usrp_usrp_p (q) || (fx2_ok_p && usrp_fx2_p (q))){ + if (n_found == nth) // return this one + return q; + n_found++; // keep looking + } + q = q->next; + } + p = p->next; + } + return 0; // not found +} + +static struct usb_dev_handle * +usrp_open_interface (struct usb_device *dev, int interface, int altinterface) +{ + struct usb_dev_handle *udh = usb_open (dev); + if (udh == 0) + return 0; + + if (dev != dev_handle_to_dev (udh)){ + fprintf (stderr, "%s:%d: internal error!\n", __FILE__, __LINE__); + abort (); + } + +#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + // There's no get get_configuration function, and with some of the newer kernels + // setting the configuration, even if to the same value, hoses any other processes + // that have it open. Hence we opt to not set it at all (We've only + // got a single configuration anyway). This may hose the win32 stuff... + + // Appears to be required for libusb-win32 and Cygwin -- dew 09/20/06 + if (usb_set_configuration (udh, 1) < 0){ + /* + * Ignore this error. + * + * Seems that something changed in drivers/usb/core/devio.c:proc_setconfig such that + * it returns -EBUSY if _any_ of the interfaces of a device are open. + * We've only got a single configuration, so setting it doesn't even seem + * like it should be required. + */ + } +#endif + + if (usb_claim_interface (udh, interface) < 0){ + fprintf (stderr, "%s:usb_claim_interface: failed interface %d\n", __FUNCTION__,interface); + fprintf (stderr, "%s\n", usb_strerror()); + usb_close (udh); + return 0; + } + + if (usb_set_altinterface (udh, altinterface) < 0){ + fprintf (stderr, "%s:usb_set_alt_interface: failed\n", __FUNCTION__); + fprintf (stderr, "%s\n", usb_strerror()); + usb_release_interface (udh, interface); + usb_close (udh); + return 0; + } + + return udh; +} + +struct usb_dev_handle * +usrp_open_cmd_interface (struct usb_device *dev) +{ + return usrp_open_interface (dev, USRP_CMD_INTERFACE, USRP_CMD_ALTINTERFACE); +} + +struct usb_dev_handle * +usrp_open_rx_interface (struct usb_device *dev) +{ + return usrp_open_interface (dev, USRP_RX_INTERFACE, USRP_RX_ALTINTERFACE); +} + +struct usb_dev_handle * +usrp_open_tx_interface (struct usb_device *dev) +{ + return usrp_open_interface (dev, USRP_TX_INTERFACE, USRP_TX_ALTINTERFACE); +} + +bool +usrp_close_interface (struct usb_dev_handle *udh) +{ + // we're assuming that closing an interface automatically releases it. + return usb_close (udh) == 0; +} + +// ---------------------------------------------------------------- +// write internal ram using Cypress vendor extension + +static bool +write_internal_ram (struct usb_dev_handle *udh, unsigned char *buf, + int start_addr, size_t len) +{ + int addr; + int n; + int a; + int quanta = MAX_EP0_PKTSIZE; + + for (addr = start_addr; addr < start_addr + (int) len; addr += quanta){ + n = len + start_addr - addr; + if (n > quanta) + n = quanta; + + a = usb_control_msg (udh, 0x40, 0xA0, + addr, 0, (char *)(buf + (addr - start_addr)), n, 1000); + + if (a < 0){ + fprintf(stderr,"write_internal_ram failed: %s\n", usb_strerror()); + return false; + } + } + return true; +} + +// ---------------------------------------------------------------- +// whack the CPUCS register using the upload RAM vendor extension + +static bool +reset_cpu (struct usb_dev_handle *udh, bool reset_p) +{ + unsigned char v; + + if (reset_p) + v = 1; // hold processor in reset + else + v = 0; // release reset + + return write_internal_ram (udh, &v, 0xE600, 1); +} + +// ---------------------------------------------------------------- +// Load intel format file into cypress FX2 (8051) + +static bool +_usrp_load_firmware (struct usb_dev_handle *udh, const char *filename, + unsigned char hash[USRP_HASH_SIZE]) +{ + FILE *f = fopen (filename, "ra"); + if (f == 0){ + perror (filename); + return false; + } + + if (!reset_cpu (udh, true)) // hold CPU in reset while loading firmware + goto fail; + + + char s[1024]; + int length; + int addr; + int type; + unsigned char data[256]; + unsigned char checksum, a; + unsigned int b; + int i; + + while (!feof(f)){ + fgets(s, sizeof (s), f); /* we should not use more than 263 bytes normally */ + if(s[0]!=':'){ + fprintf(stderr,"%s: invalid line: \"%s\"\n", filename, s); + goto fail; + } + sscanf(s+1, "%02x", &length); + sscanf(s+3, "%04x", &addr); + sscanf(s+7, "%02x", &type); + + if(type==0){ + + a=length+(addr &0xff)+(addr>>8)+type; + for(i=0;i 0){ + if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_XFER, buf, n) != n) + goto fail; + } + + if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_END, 0, 0) != 0) + goto fail; + + fclose (fp); + + if (!usrp_set_hash (udh, FPGA_HASH_SLOT, hash)) + fprintf (stderr, "usrp: failed to write fpga hash slot\n"); + + // On the rev1 USRP, the {tx,rx}_{enable,reset} bits are + // controlled over the serial bus, and hence aren't observed until + // we've got a good fpga bitstream loaded. + + usrp_set_fpga_reset (udh, 0); // fpga out of master reset + + // now these commands will work + + ok &= usrp_set_fpga_tx_enable (udh, 0); + ok &= usrp_set_fpga_rx_enable (udh, 0); + + ok &= usrp_set_fpga_tx_reset (udh, 1); // reset tx and rx paths + ok &= usrp_set_fpga_rx_reset (udh, 1); + ok &= usrp_set_fpga_tx_reset (udh, 0); // reset tx and rx paths + ok &= usrp_set_fpga_rx_reset (udh, 0); + + if (!ok) + fprintf (stderr, "usrp: failed to reset tx and/or rx path\n"); + + // Manually reset all regs except master control to zero. + // FIXME may want to remove this when we rework FPGA reset strategy. + // In the mean while, this gets us reproducible behavior. + for (int i = 0; i < FR_USER_0; i++){ + if (i == FR_MASTER_CTRL) + continue; + usrp_write_fpga_reg(udh, i, 0); + } + + power_down_9862s (udh); // on the rev1, power these down! + usrp_set_led (udh, 1, 0); // led 1 off + + return true; + + fail: + power_down_9862s (udh); // on the rev1, power these down! + fclose (fp); + return false; +} + +// ---------------------------------------------------------------- + +bool +usrp_set_led (struct usb_dev_handle *udh, int which, bool on) +{ + int r = write_cmd (udh, VRQ_SET_LED, on, which, 0, 0); + + return r == 0; +} + +bool +usrp_set_hash (struct usb_dev_handle *udh, int which, + const unsigned char hash[USRP_HASH_SIZE]) +{ + which &= 1; + + // we use the Cypress firmware down load command to jam it in. + int r = usb_control_msg (udh, 0x40, 0xa0, hash_slot_addr[which], 0, + (char *) hash, USRP_HASH_SIZE, 1000); + return r == USRP_HASH_SIZE; +} + +bool +usrp_get_hash (struct usb_dev_handle *udh, int which, + unsigned char hash[USRP_HASH_SIZE]) +{ + which &= 1; + + // we use the Cypress firmware upload command to fetch it. + int r = usb_control_msg (udh, 0xc0, 0xa0, hash_slot_addr[which], 0, + (char *) hash, USRP_HASH_SIZE, 1000); + return r == USRP_HASH_SIZE; +} + +static bool +usrp_set_switch (struct usb_dev_handle *udh, int cmd_byte, bool on) +{ + return write_cmd (udh, cmd_byte, on, 0, 0, 0) == 0; +} + + +static bool +usrp1_fpga_write (struct usb_dev_handle *udh, + int regno, int value) +{ + // on the rev1 usrp, we use the generic spi_write interface + + unsigned char buf[4]; + + buf[0] = (value >> 24) & 0xff; // MSB first + buf[1] = (value >> 16) & 0xff; + buf[2] = (value >> 8) & 0xff; + buf[3] = (value >> 0) & 0xff; + + return usrp_spi_write (udh, 0x00 | (regno & 0x7f), + SPI_ENABLE_FPGA, + SPI_FMT_MSB | SPI_FMT_HDR_1, + buf, sizeof (buf)); +} + +static bool +usrp1_fpga_read (struct usb_dev_handle *udh, + int regno, int *value) +{ + *value = 0; + unsigned char buf[4]; + + bool ok = usrp_spi_read (udh, 0x80 | (regno & 0x7f), + SPI_ENABLE_FPGA, + SPI_FMT_MSB | SPI_FMT_HDR_1, + buf, sizeof (buf)); + + if (ok) + *value = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + return ok; +} + + +bool +usrp_write_fpga_reg (struct usb_dev_handle *udh, int reg, int value) +{ + switch (usrp_hw_rev (dev_handle_to_dev (udh))){ + case 0: // not supported ;) + abort(); + + default: + return usrp1_fpga_write (udh, reg, value); + } +} + +bool +usrp_read_fpga_reg (struct usb_dev_handle *udh, int reg, int *value) +{ + switch (usrp_hw_rev (dev_handle_to_dev (udh))){ + case 0: // not supported ;) + abort(); + + default: + return usrp1_fpga_read (udh, reg, value); + } +} + +bool +usrp_set_fpga_reset (struct usb_dev_handle *udh, bool on) +{ + return usrp_set_switch (udh, VRQ_FPGA_SET_RESET, on); +} + +bool +usrp_set_fpga_tx_enable (struct usb_dev_handle *udh, bool on) +{ + return usrp_set_switch (udh, VRQ_FPGA_SET_TX_ENABLE, on); +} + +bool +usrp_set_fpga_rx_enable (struct usb_dev_handle *udh, bool on) +{ + return usrp_set_switch (udh, VRQ_FPGA_SET_RX_ENABLE, on); +} + +bool +usrp_set_fpga_tx_reset (struct usb_dev_handle *udh, bool on) +{ + return usrp_set_switch (udh, VRQ_FPGA_SET_TX_RESET, on); +} + +bool +usrp_set_fpga_rx_reset (struct usb_dev_handle *udh, bool on) +{ + return usrp_set_switch (udh, VRQ_FPGA_SET_RX_RESET, on); +} + + +// ---------------------------------------------------------------- +// conditional load stuff + +static bool +compute_hash (const char *filename, unsigned char hash[USRP_HASH_SIZE]) +{ + assert (USRP_HASH_SIZE == 16); + memset (hash, 0, USRP_HASH_SIZE); + + FILE *fp = fopen (filename, "rb"); + if (fp == 0){ + perror (filename); + return false; + } + int r = md5_stream (fp, hash); + fclose (fp); + + return r == 0; +} + +static usrp_load_status_t +usrp_conditionally_load_something (struct usb_dev_handle *udh, + const char *filename, + bool force, + int slot, + bool loader (struct usb_dev_handle *, + const char *, + unsigned char [USRP_HASH_SIZE])) +{ + unsigned char file_hash[USRP_HASH_SIZE]; + unsigned char usrp_hash[USRP_HASH_SIZE]; + + if (access (filename, R_OK) != 0){ + perror (filename); + return ULS_ERROR; + } + + if (!compute_hash (filename, file_hash)) + return ULS_ERROR; + + if (!force + && usrp_get_hash (udh, slot, usrp_hash) + && memcmp (file_hash, usrp_hash, USRP_HASH_SIZE) == 0) + return ULS_ALREADY_LOADED; + + bool r = loader (udh, filename, file_hash); + + if (!r) + return ULS_ERROR; + + return ULS_OK; +} + +usrp_load_status_t +usrp_load_firmware (struct usb_dev_handle *udh, + const char *filename, + bool force) +{ + return usrp_conditionally_load_something (udh, filename, force, + FIRMWARE_HASH_SLOT, + _usrp_load_firmware); +} + +usrp_load_status_t +usrp_load_fpga (struct usb_dev_handle *udh, + const char *filename, + bool force) +{ + return usrp_conditionally_load_something (udh, filename, force, + FPGA_HASH_SLOT, + _usrp_load_fpga); +} + +static usb_dev_handle * +open_nth_cmd_interface (int nth) +{ + struct usb_device *udev = usrp_find_device (nth); + if (udev == 0){ + fprintf (stderr, "usrp: failed to find usrp[%d]\n", nth); + return 0; + } + + struct usb_dev_handle *udh; + + udh = usrp_open_cmd_interface (udev); + if (udh == 0){ + // FIXME this could be because somebody else has it open. + // We should delay and retry... + fprintf (stderr, "open_nth_cmd_interface: open_cmd_interface failed\n"); + usb_strerror (); + return 0; + } + + return udh; + } + +static bool +our_nanosleep (const struct timespec *delay) +{ + struct timespec new_delay = *delay; + struct timespec remainder; + + while (1){ + int r = nanosleep (&new_delay, &remainder); + if (r == 0) + return true; + if (errno == EINTR) + new_delay = remainder; + else { + perror ("nanosleep"); + return false; + } + } +} + +static bool +mdelay (int millisecs) +{ + struct timespec ts; + ts.tv_sec = millisecs / 1000; + ts.tv_nsec = (millisecs - (1000 * ts.tv_sec)) * 1000000; + return our_nanosleep (&ts); +} + +usrp_load_status_t +usrp_load_firmware_nth (int nth, const char *filename, bool force){ + struct usb_dev_handle *udh = open_nth_cmd_interface (nth); + if (udh == 0) + return ULS_ERROR; + + usrp_load_status_t s = usrp_load_firmware (udh, filename, force); + usrp_close_interface (udh); + + switch (s){ + + case ULS_ALREADY_LOADED: // nothing changed... + return ULS_ALREADY_LOADED; + break; + + case ULS_OK: + // we loaded firmware successfully. + + // It's highly likely that the board will renumerate (simulate a + // disconnect/reconnect sequence), invalidating our current + // handle. + + // FIXME. Turn this into a loop that rescans until we refind ourselves + + struct timespec t; // delay for 1 second + t.tv_sec = 2; + t.tv_nsec = 0; + our_nanosleep (&t); + + usb_find_busses (); // rescan busses and devices + usb_find_devices (); + + return ULS_OK; + + default: + case ULS_ERROR: // some kind of problem + return ULS_ERROR; + } +} + +static void +load_status_msg (usrp_load_status_t s, const char *type, const char *filename) +{ + char *e = getenv("USRP_VERBOSE"); + bool verbose = e != 0; + + switch (s){ + case ULS_ERROR: + fprintf (stderr, "usrp: failed to load %s %s.\n", type, filename); + break; + + case ULS_ALREADY_LOADED: + if (verbose) + fprintf (stderr, "usrp: %s %s already loaded.\n", type, filename); + break; + + case ULS_OK: + if (verbose) + fprintf (stderr, "usrp: %s %s loaded successfully.\n", type, filename); + break; + } +} + +bool +usrp_load_standard_bits (int nth, bool force, + const std::string fpga_filename, + const std::string firmware_filename) +{ + usrp_load_status_t s; + const char *filename; + const char *proto_filename; + int hw_rev; + + // first, figure out what hardware rev we're dealing with + { + struct usb_device *udev = usrp_find_device (nth); + if (udev == 0){ + fprintf (stderr, "usrp: failed to find usrp[%d]\n", nth); + return false; + } + hw_rev = usrp_hw_rev (udev); + } + + // start by loading the firmware + + proto_filename = get_proto_filename(firmware_filename, "USRP_FIRMWARE", + default_firmware_filename); + filename = find_file(proto_filename, hw_rev); + if (filename == 0){ + fprintf (stderr, "Can't find firmware: %s\n", proto_filename); + return false; + } + + s = usrp_load_firmware_nth (nth, filename, force); + load_status_msg (s, "firmware", filename); + + if (s == ULS_ERROR) + return false; + + // if we actually loaded firmware, we must reload fpga ... + if (s == ULS_OK) + force = true; + + // now move on to the fpga configuration bitstream + + proto_filename = get_proto_filename(fpga_filename, "USRP_FPGA", + default_fpga_filename); + filename = find_file (proto_filename, hw_rev); + if (filename == 0){ + fprintf (stderr, "Can't find fpga bitstream: %s\n", proto_filename); + return false; + } + + struct usb_dev_handle *udh = open_nth_cmd_interface (nth); + if (udh == 0) + return false; + + s = usrp_load_fpga (udh, filename, force); + usrp_close_interface (udh); + load_status_msg (s, "fpga bitstream", filename); + + if (s == ULS_ERROR) + return false; + + return true; +} + +bool +_usrp_get_status (struct usb_dev_handle *udh, int which, bool *trouble) +{ + unsigned char status; + *trouble = true; + + if (write_cmd (udh, VRQ_GET_STATUS, 0, which, + &status, sizeof (status)) != sizeof (status)) + return false; + + *trouble = status; + return true; +} + +bool +usrp_check_rx_overrun (struct usb_dev_handle *udh, bool *overrun_p) +{ + return _usrp_get_status (udh, GS_RX_OVERRUN, overrun_p); +} + +bool +usrp_check_tx_underrun (struct usb_dev_handle *udh, bool *underrun_p) +{ + return _usrp_get_status (udh, GS_TX_UNDERRUN, underrun_p); +} + + +bool +usrp_i2c_write (struct usb_dev_handle *udh, int i2c_addr, + const void *buf, int len) +{ + if (len < 1 || len > MAX_EP0_PKTSIZE) + return false; + + return write_cmd (udh, VRQ_I2C_WRITE, i2c_addr, 0, + (unsigned char *) buf, len) == len; +} + + +bool +usrp_i2c_read (struct usb_dev_handle *udh, int i2c_addr, + void *buf, int len) +{ + if (len < 1 || len > MAX_EP0_PKTSIZE) + return false; + + return write_cmd (udh, VRQ_I2C_READ, i2c_addr, 0, + (unsigned char *) buf, len) == len; +} + +bool +usrp_spi_write (struct usb_dev_handle *udh, + int optional_header, int enables, int format, + const void *buf, int len) +{ + if (len < 0 || len > MAX_EP0_PKTSIZE) + return false; + + return write_cmd (udh, VRQ_SPI_WRITE, + optional_header, + ((enables & 0xff) << 8) | (format & 0xff), + (unsigned char *) buf, len) == len; +} + + +bool +usrp_spi_read (struct usb_dev_handle *udh, + int optional_header, int enables, int format, + void *buf, int len) +{ + if (len < 0 || len > MAX_EP0_PKTSIZE) + return false; + + return write_cmd (udh, VRQ_SPI_READ, + optional_header, + ((enables & 0xff) << 8) | (format & 0xff), + (unsigned char *) buf, len) == len; +} + +bool +usrp_9862_write (struct usb_dev_handle *udh, int which_codec, + int regno, int value) +{ + if (0) + fprintf (stderr, "usrp_9862_write which = %d, reg = %2d, val = %3d (0x%02x)\n", + which_codec, regno, value, value); + + unsigned char buf[1]; + + buf[0] = value; + + return usrp_spi_write (udh, 0x00 | (regno & 0x3f), + which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B, + SPI_FMT_MSB | SPI_FMT_HDR_1, + buf, 1); +} + +bool +usrp_9862_read (struct usb_dev_handle *udh, int which_codec, + int regno, unsigned char *value) +{ + return usrp_spi_read (udh, 0x80 | (regno & 0x3f), + which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B, + SPI_FMT_MSB | SPI_FMT_HDR_1, + value, 1); +} + +bool +usrp_9862_write_many (struct usb_dev_handle *udh, + int which_codec, + const unsigned char *buf, + int len) +{ + if (len & 0x1) + return false; // must be even + + bool result = true; + + while (len > 0){ + result &= usrp_9862_write (udh, which_codec, buf[0], buf[1]); + len -= 2; + buf += 2; + } + + return result; +} + + +bool +usrp_9862_write_many_all (struct usb_dev_handle *udh, + const unsigned char *buf, int len) +{ + // FIXME handle 2/2 and 4/4 versions + + bool result; + result = usrp_9862_write_many (udh, 0, buf, len); + result &= usrp_9862_write_many (udh, 1, buf, len); + return result; +} + +static void +power_down_9862s (struct usb_dev_handle *udh) +{ + static const unsigned char regs[] = { + REG_RX_PWR_DN, 0x01, // everything + REG_TX_PWR_DN, 0x0f, // pwr dn digital and analog_both + REG_TX_MODULATOR, 0x00 // coarse & fine modulators disabled + }; + + switch (usrp_hw_rev (dev_handle_to_dev (udh))){ + case 0: + break; + + default: + usrp_9862_write_many_all (udh, regs, sizeof (regs)); + break; + } +} + + + +static const int EEPROM_PAGESIZE = 16; + +bool +usrp_eeprom_write (struct usb_dev_handle *udh, int i2c_addr, + int eeprom_offset, const void *buf, int len) +{ + unsigned char cmd[2]; + const unsigned char *p = (unsigned char *) buf; + + // The simplest thing that could possibly work: + // all writes are single byte writes. + // + // We could speed this up using the page write feature, + // but we write so infrequently, why bother... + + while (len-- > 0){ + cmd[0] = eeprom_offset++; + cmd[1] = *p++; + bool r = usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd)); + mdelay (10); // delay 10ms worst case write time + if (!r) + return false; + } + + return true; +} + +bool +usrp_eeprom_read (struct usb_dev_handle *udh, int i2c_addr, + int eeprom_offset, void *buf, int len) +{ + unsigned char *p = (unsigned char *) buf; + + // We setup a random read by first doing a "zero byte write". + // Writes carry an address. Reads use an implicit address. + + unsigned char cmd[1]; + cmd[0] = eeprom_offset; + if (!usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd))) + return false; + + while (len > 0){ + int n = std::min (len, MAX_EP0_PKTSIZE); + if (!usrp_i2c_read (udh, i2c_addr, p, n)) + return false; + len -= n; + p += n; + } + return true; +} + +// ---------------------------------------------------------------- + +static bool +slot_to_codec (int slot, int *which_codec) +{ + *which_codec = 0; + + switch (slot){ + case SLOT_TX_A: + case SLOT_RX_A: + *which_codec = 0; + break; + + case SLOT_TX_B: + case SLOT_RX_B: + *which_codec = 1; + break; + + default: + fprintf (stderr, "usrp_prims:slot_to_codec: invalid slot = %d\n", slot); + return false; + } + return true; +} + +static bool +tx_slot_p (int slot) +{ + switch (slot){ + case SLOT_TX_A: + case SLOT_TX_B: + return true; + + default: + return false; + } +} + +bool +usrp_write_aux_dac (struct usb_dev_handle *udh, int slot, + int which_dac, int value) +{ + int which_codec; + + if (!slot_to_codec (slot, &which_codec)) + return false; + + if (!(0 <= which_dac && which_dac < 4)){ + fprintf (stderr, "usrp_write_aux_dac: invalid dac = %d\n", which_dac); + return false; + } + + value &= 0x0fff; // mask to 12-bits + + if (which_dac == 3){ + // dac 3 is really 12-bits. Use value as is. + bool r = true; + r &= usrp_9862_write (udh, which_codec, 43, (value >> 4)); // most sig + r &= usrp_9862_write (udh, which_codec, 42, (value & 0xf) << 4); // least sig + return r; + } + else { + // dac 0, 1, and 2 are really 8 bits. + value = value >> 4; // shift value appropriately + return usrp_9862_write (udh, which_codec, 36 + which_dac, value); + } +} + + +bool +usrp_read_aux_adc (struct usb_dev_handle *udh, int slot, + int which_adc, int *value) +{ + *value = 0; + int which_codec; + + if (!slot_to_codec (slot, &which_codec)) + return false; + + if (!(0 <= which_codec && which_codec < 2)){ + fprintf (stderr, "usrp_read_aux_adc: invalid adc = %d\n", which_adc); + return false; + } + + unsigned char aux_adc_control = + AUX_ADC_CTRL_REFSEL_A // on chip reference + | AUX_ADC_CTRL_REFSEL_B; // on chip reference + + int rd_reg = 26; // base address of two regs to read for result + + // program the ADC mux bits + if (tx_slot_p (slot)) + aux_adc_control |= AUX_ADC_CTRL_SELECT_A2 | AUX_ADC_CTRL_SELECT_B2; + else { + rd_reg += 2; + aux_adc_control |= AUX_ADC_CTRL_SELECT_A1 | AUX_ADC_CTRL_SELECT_B1; + } + + // I'm not sure if we can set the mux and issue a start conversion + // in the same cycle, so let's do them one at a time. + + usrp_9862_write (udh, which_codec, 34, aux_adc_control); + + if (which_adc == 0) + aux_adc_control |= AUX_ADC_CTRL_START_A; + else { + rd_reg += 4; + aux_adc_control |= AUX_ADC_CTRL_START_B; + } + + // start the conversion + usrp_9862_write (udh, which_codec, 34, aux_adc_control); + + // read the 10-bit result back + unsigned char v_lo = 0; + unsigned char v_hi = 0; + bool r = usrp_9862_read (udh, which_codec, rd_reg, &v_lo); + r &= usrp_9862_read (udh, which_codec, rd_reg + 1, &v_hi); + + if (r) + *value = ((v_hi << 2) | ((v_lo >> 6) & 0x3)) << 2; // format as 12-bit + + return r; +} + +// ---------------------------------------------------------------- + +static int slot_to_i2c_addr (int slot) +{ + switch (slot){ + case SLOT_TX_A: return I2C_ADDR_TX_A; + case SLOT_RX_A: return I2C_ADDR_RX_A; + case SLOT_TX_B: return I2C_ADDR_TX_B; + case SLOT_RX_B: return I2C_ADDR_RX_B; + default: return -1; + } +} + +static void +set_chksum (unsigned char *buf) +{ + int sum = 0; + unsigned int i; + for (i = 0; i < DB_EEPROM_CLEN - 1; i++) + sum += buf[i]; + buf[i] = -sum; +} + +static usrp_dbeeprom_status_t +read_dboard_eeprom (struct usb_dev_handle *udh, + int slot_id, unsigned char *buf) +{ + int i2c_addr = slot_to_i2c_addr (slot_id); + if (i2c_addr == -1) + return UDBE_BAD_SLOT; + + if (!usrp_eeprom_read (udh, i2c_addr, 0, buf, DB_EEPROM_CLEN)) + return UDBE_NO_EEPROM; + + if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE) + return UDBE_INVALID_EEPROM; + + int sum = 0; + for (unsigned int i = 0; i < DB_EEPROM_CLEN; i++) + sum += buf[i]; + + if ((sum & 0xff) != 0) + return UDBE_INVALID_EEPROM; + + return UDBE_OK; +} + +usrp_dbeeprom_status_t +usrp_read_dboard_eeprom (struct usb_dev_handle *udh, + int slot_id, usrp_dboard_eeprom *eeprom) +{ + unsigned char buf[DB_EEPROM_CLEN]; + + memset (eeprom, 0, sizeof (*eeprom)); + + usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf); + if (s != UDBE_OK) + return s; + + eeprom->id = (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB]; + eeprom->oe = (buf[DB_EEPROM_OE_MSB] << 8) | buf[DB_EEPROM_OE_LSB]; + eeprom->offset[0] = (buf[DB_EEPROM_OFFSET_0_MSB] << 8) | buf[DB_EEPROM_OFFSET_0_LSB]; + eeprom->offset[1] = (buf[DB_EEPROM_OFFSET_1_MSB] << 8) | buf[DB_EEPROM_OFFSET_1_LSB]; + + return UDBE_OK; +} + +bool +usrp_write_dboard_offsets (struct usb_dev_handle *udh, int slot_id, + short offset0, short offset1) +{ + unsigned char buf[DB_EEPROM_CLEN]; + + usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf); + if (s != UDBE_OK) + return false; + + buf[DB_EEPROM_OFFSET_0_LSB] = (offset0 >> 0) & 0xff; + buf[DB_EEPROM_OFFSET_0_MSB] = (offset0 >> 8) & 0xff; + buf[DB_EEPROM_OFFSET_1_LSB] = (offset1 >> 0) & 0xff; + buf[DB_EEPROM_OFFSET_1_MSB] = (offset1 >> 8) & 0xff; + set_chksum (buf); + + return usrp_eeprom_write (udh, slot_to_i2c_addr (slot_id), + 0, buf, sizeof (buf)); +} + +std::string +usrp_serial_number(struct usb_dev_handle *udh) +{ + unsigned char iserial = usb_device(udh)->descriptor.iSerialNumber; + if (iserial == 0) + return ""; + + char buf[1024]; + if (usb_get_string_simple(udh, iserial, buf, sizeof(buf)) < 0) + return ""; + + return buf; +} diff --git a/usrp/host/lib/legacy/usrp_prims.h b/usrp/host/lib/legacy/usrp_prims.h new file mode 100644 index 00000000..97cfb4a4 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_prims.h @@ -0,0 +1,294 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004,2006 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * Low level primitives for directly messing with USRP hardware. + * + * If you're trying to use the USRP, you'll probably want to take a look + * at the usrp_rx and usrp_tx classes. They hide a bunch of low level details + * and provide high performance streaming i/o. + * + * This interface is built on top of libusb, which allegedly works under + * Linux, *BSD and Mac OS/X. http://libusb.sourceforge.net + */ + +#ifndef _USRP_PRIMS_H_ +#define _USRP_PRIMS_H_ + +#include +#include + +static const int USRP_HASH_SIZE = 16; + +enum usrp_load_status_t { ULS_ERROR = 0, ULS_OK, ULS_ALREADY_LOADED }; + +struct usb_dev_handle; +struct usb_device; + +/*! + * \brief initialize libusb; probe busses and devices. + * Safe to call more than once. + */ +void usrp_one_time_init (); + +/* + * force a rescan of the buses and devices + */ +void usrp_rescan (); + +/*! + * \brief locate Nth (zero based) USRP device in system. + * Return pointer or 0 if not found. + * + * The following kinds of devices are considered USRPs: + * + * unconfigured USRP (no firwmare loaded) + * configured USRP (firmware loaded) + * unconfigured Cypress FX2 (only if fx2_ok_p is true) + */ +struct usb_device *usrp_find_device (int nth, bool fx2_ok_p = false); + +bool usrp_usrp_p (struct usb_device *q); //< is this a USRP +bool usrp_usrp0_p (struct usb_device *q); //< is this a USRP Rev 0 +bool usrp_usrp1_p (struct usb_device *q); //< is this a USRP Rev 1 +bool usrp_usrp2_p (struct usb_device *q); //< is this a USRP Rev 2 +int usrp_hw_rev (struct usb_device *q); //< return h/w rev code + +bool usrp_fx2_p (struct usb_device *q); //< is this an unconfigured Cypress FX2 + +bool usrp_unconfigured_usrp_p (struct usb_device *q); //< some kind of unconfigured USRP +bool usrp_configured_usrp_p (struct usb_device *q); //< some kind of configured USRP + +/*! + * \brief given a usb_device return an instance of the appropriate usb_dev_handle + * + * These routines claim the specified interface and select the + * correct alternate interface. (USB nomenclature is totally screwed!) + * + * If interface can't be opened, or is already claimed by some other + * process, 0 is returned. + */ +struct usb_dev_handle *usrp_open_cmd_interface (struct usb_device *dev); +struct usb_dev_handle *usrp_open_rx_interface (struct usb_device *dev); +struct usb_dev_handle *usrp_open_tx_interface (struct usb_device *dev); + +/*! + * \brief close interface. + */ +bool usrp_close_interface (struct usb_dev_handle *udh); + +/*! + * \brief load intel hex format file into USRP/Cypress FX2 (8051). + * + * The filename extension is typically *.ihx + * + * Note that loading firmware may cause the device to renumerate. I.e., + * change its configuration, invalidating the current device handle. + */ + +usrp_load_status_t +usrp_load_firmware (struct usb_dev_handle *udh, const char *filename, bool force); + +/*! + * \brief load intel hex format file into USRP FX2 (8051). + * + * The filename extension is typically *.ihx + * + * Note that loading firmware may cause the device to renumerate. I.e., + * change its configuration, invalidating the current device handle. + * If the result is ULS_OK, usrp_load_firmware_nth delays 1 second + * then rescans the busses and devices. + */ +usrp_load_status_t +usrp_load_firmware_nth (int nth, const char *filename, bool force); + +/*! + * \brief load fpga configuration bitstream + */ +usrp_load_status_t +usrp_load_fpga (struct usb_dev_handle *udh, const char *filename, bool force); + +/*! + * \brief load the regular firmware and fpga bitstream in the Nth USRP. + * + * This is the normal starting point... + */ +bool usrp_load_standard_bits (int nth, bool force, + const std::string fpga_filename = "", + const std::string firmware_filename = ""); + +/*! + * \brief copy the given \p hash into the USRP hash slot \p which. + * The usrp implements two hash slots, 0 and 1. + */ +bool usrp_set_hash (struct usb_dev_handle *udh, int which, + const unsigned char hash[USRP_HASH_SIZE]); + +/*! + * \brief retrieve the \p hash from the USRP hash slot \p which. + * The usrp implements two hash slots, 0 and 1. + */ +bool usrp_get_hash (struct usb_dev_handle *udh, int which, + unsigned char hash[USRP_HASH_SIZE]); + +bool usrp_write_fpga_reg (struct usb_dev_handle *udh, int reg, int value); +bool usrp_read_fpga_reg (struct usb_dev_handle *udh, int reg, int *value); +bool usrp_set_fpga_reset (struct usb_dev_handle *udh, bool on); +bool usrp_set_fpga_tx_enable (struct usb_dev_handle *udh, bool on); +bool usrp_set_fpga_rx_enable (struct usb_dev_handle *udh, bool on); +bool usrp_set_fpga_tx_reset (struct usb_dev_handle *udh, bool on); +bool usrp_set_fpga_rx_reset (struct usb_dev_handle *udh, bool on); +bool usrp_set_led (struct usb_dev_handle *udh, int which, bool on); + +bool usrp_check_rx_overrun (struct usb_dev_handle *udh, bool *overrun_p); +bool usrp_check_tx_underrun (struct usb_dev_handle *udh, bool *underrun_p); + +// i2c_read and i2c_write are limited to a maximum len of 64 bytes. + +bool usrp_i2c_write (struct usb_dev_handle *udh, int i2c_addr, + const void *buf, int len); + +bool usrp_i2c_read (struct usb_dev_handle *udh, int i2c_addr, + void *buf, int len); + +// spi_read and spi_write are limited to a maximum of 64 bytes +// See usrp_spi_defs.h for more info + +bool usrp_spi_write (struct usb_dev_handle *udh, + int optional_header, int enables, int format, + const void *buf, int len); + +bool usrp_spi_read (struct usb_dev_handle *udh, + int optional_header, int enables, int format, + void *buf, int len); + + +bool usrp_9862_write (struct usb_dev_handle *udh, + int which_codec, // [0, 1] + int regno, // [0, 63] + int value); // [0, 255] + +bool usrp_9862_read (struct usb_dev_handle *udh, + int which_codec, // [0, 1] + int regno, // [0, 63] + unsigned char *value); // [0, 255] + +/*! + * \brief Write multiple 9862 regs at once. + * + * \p buf contains alternating register_number, register_value pairs. + * \p len must be even and is the length of buf in bytes. + */ +bool usrp_9862_write_many (struct usb_dev_handle *udh, int which_codec, + const unsigned char *buf, int len); + + +/*! + * \brief write specified regs to all 9862's in the system + */ +bool usrp_9862_write_many_all (struct usb_dev_handle *udh, + const unsigned char *buf, int len); + + +// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool usrp_eeprom_write (struct usb_dev_handle *udh, int i2c_addr, + int eeprom_offset, const void *buf, int len); + + +// Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool usrp_eeprom_read (struct usb_dev_handle *udh, int i2c_addr, + int eeprom_offset, void *buf, int len); + + +// Slot specific i/o routines + +/*! + * \brief write to the specified aux dac. + * + * \p slot: which Tx or Rx slot to write. + * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's + * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's + * + * \p which_dac: [0,3] RX slots must use only 0 and 1. + * TX slots must use only 2 and 3. + * + * AUX DAC 3 is really the 9862 sigma delta output. + * + * \p value to write to aux dac. All dacs take straight + * binary values. Although dacs 0, 1 and 2 are 8-bit and dac 3 is 12-bit, + * the interface is in terms of 12-bit values [0,4095] + */ +bool usrp_write_aux_dac (struct usb_dev_handle *uhd, int slot, + int which_dac, int value); + +/*! + * \brief Read the specified aux adc + * + * \p slot: which Tx or Rx slot to read aux dac + * \p which_adc: [0,1] which of the two adcs to read + * \p *value: return value, 12-bit straight binary. + */ +bool usrp_read_aux_adc (struct usb_dev_handle *udh, int slot, + int which_adc, int *value); + + +/*! + * \brief usrp daughterboard id to name mapping + */ +const std::string usrp_dbid_to_string (int dbid); + + +enum usrp_dbeeprom_status_t { UDBE_OK, UDBE_BAD_SLOT, UDBE_NO_EEPROM, UDBE_INVALID_EEPROM }; + +struct usrp_dboard_eeprom { + unsigned short id; // d'board identifier code + unsigned short oe; // fpga output enables: + // If bit set, i/o pin is an output from FPGA. + short offset[2]; // ADC/DAC offset correction +}; + +/*! + * \brief Read and return parsed daughterboard eeprom + */ +usrp_dbeeprom_status_t +usrp_read_dboard_eeprom (struct usb_dev_handle *udh, + int slot_id, usrp_dboard_eeprom *eeprom); + +/*! + * \brief write ADC/DAC offset calibration constants to d'board eeprom + */ +bool usrp_write_dboard_offsets (struct usb_dev_handle *udh, int slot_id, + short offset0, short offset1); + +/*! + * \brief return a usrp's serial number. + * + * Note that this only works on a configured usrp. + * \returns non-zero length string iff successful. + */ +std::string usrp_serial_number(struct usb_dev_handle *udh); + +#endif /* _USRP_PRIMS_H_ */ diff --git a/usrp/host/lib/legacy/usrp_slots.h b/usrp/host/lib/legacy/usrp_slots.h new file mode 100644 index 00000000..c4971d16 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_slots.h @@ -0,0 +1,33 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_USRP_SLOTS_H +#define INCLUDED_USRP_SLOTS_H + +// daughterboard slot numbers used in some calls + +static const int SLOT_TX_A = 0; +static const int SLOT_RX_A = 1; +static const int SLOT_TX_B = 2; +static const int SLOT_RX_B = 3; + +#endif /* INCLUDED_USRP_SLOTS_H */ diff --git a/usrp/host/lib/legacy/usrp_standard.cc b/usrp/host/lib/legacy/usrp_standard.cc new file mode 100644 index 00000000..6b4ec939 --- /dev/null +++ b/usrp/host/lib/legacy/usrp_standard.cc @@ -0,0 +1,831 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "usrp_prims.h" +#include "fpga_regs_common.h" +#include "fpga_regs_standard.h" +#include +#include +#include +#include + + +static const int OLD_CAPS_VAL = 0xaa55ff77; +static const int DEFAULT_CAPS_VAL = ((2 << bmFR_RB_CAPS_NDUC_SHIFT) + | (2 << bmFR_RB_CAPS_NDDC_SHIFT) + | bmFR_RB_CAPS_RX_HAS_HALFBAND); + +// #define USE_FPGA_TX_CORDIC + + +using namespace ad9862; + +#define NELEM(x) (sizeof (x) / sizeof (x[0])) + + +static unsigned int +compute_freq_control_word_fpga (double master_freq, double target_freq, + double *actual_freq, bool verbose) +{ + static const int NBITS = 14; + + int v = (int) rint (target_freq / master_freq * pow (2.0, 32.0)); + + if (0) + v = (v >> (32 - NBITS)) << (32 - NBITS); // keep only top NBITS + + *actual_freq = v * master_freq / pow (2.0, 32.0); + + if (verbose) + fprintf (stderr, + "compute_freq_control_word_fpga: target = %g actual = %g delta = %g\n", + target_freq, *actual_freq, *actual_freq - target_freq); + + return (unsigned int) v; +} + +// The 9862 uses an unsigned 24-bit frequency tuning word and +// a separate register to control the sign. + +static unsigned int +compute_freq_control_word_9862 (double master_freq, double target_freq, + double *actual_freq, bool verbose) +{ + double sign = 1.0; + + if (target_freq < 0) + sign = -1.0; + + int v = (int) rint (fabs (target_freq) / master_freq * pow (2.0, 24.0)); + *actual_freq = v * master_freq / pow (2.0, 24.0) * sign; + + if (verbose) + fprintf (stderr, + "compute_freq_control_word_9862: target = %g actual = %g delta = %g v = %8d\n", + target_freq, *actual_freq, *actual_freq - target_freq, v); + + return (unsigned int) v; +} + +// ---------------------------------------------------------------- + +usrp_standard_common::usrp_standard_common(usrp_basic *parent) +{ + // read new FPGA capability register + if (!parent->_read_fpga_reg(FR_RB_CAPS, &d_fpga_caps)){ + fprintf (stderr, "usrp_standard_common: failed to read FPGA cap register.\n"); + throw std::runtime_error ("usrp_standard_common::ctor"); + } + // If we don't have the cap register, set the value to what it would + // have had if we did have one ;) + if (d_fpga_caps == OLD_CAPS_VAL) + d_fpga_caps = DEFAULT_CAPS_VAL; + + if (0){ + fprintf(stdout, "has_rx_halfband = %d\n", has_rx_halfband()); + fprintf(stdout, "nddcs = %d\n", nddcs()); + fprintf(stdout, "has_tx_halfband = %d\n", has_tx_halfband()); + fprintf(stdout, "nducs = %d\n", nducs()); + } +} + +bool +usrp_standard_common::has_rx_halfband() const +{ + return (d_fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND) ? true : false; +} + +int +usrp_standard_common::nddcs() const +{ + return (d_fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT; +} + +bool +usrp_standard_common::has_tx_halfband() const +{ + return (d_fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND) ? true : false; +} + +int +usrp_standard_common::nducs() const +{ + return (d_fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT; +} + +// ---------------------------------------------------------------- + +static int +real_rx_mux_value (int mux, int nchan) +{ + if (mux != -1) + return mux; + + return 0x32103210; +} + +usrp_standard_rx::usrp_standard_rx (int which_board, + unsigned int decim_rate, + int nchan, int mux, int mode, + int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) + : usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename), + usrp_standard_common(this), + d_nchan (1), d_sw_mux (0x0), d_hw_mux (0x0) +{ + if (!set_format(make_format())){ + fprintf (stderr, "usrp_standard_rx: set_format failed\n"); + throw std::runtime_error ("usrp_standard_rx::ctor"); + } + if (!set_nchannels (nchan)){ + fprintf (stderr, "usrp_standard_rx: set_nchannels failed\n"); + throw std::runtime_error ("usrp_standard_rx::ctor"); + } + if (!set_decim_rate (decim_rate)){ + fprintf (stderr, "usrp_standard_rx: set_decim_rate failed\n"); + throw std::runtime_error ("usrp_standard_rx::ctor"); + } + if (!set_mux (real_rx_mux_value (mux, nchan))){ + fprintf (stderr, "usrp_standard_rx: set_mux failed\n"); + throw std::runtime_error ("usrp_standard_rx::ctor"); + } + if (!set_fpga_mode (mode)){ + fprintf (stderr, "usrp_standard_rx: set_fpga_mode failed\n"); + throw std::runtime_error ("usrp_standard_rx::ctor"); + } + + for (int i = 0; i < MAX_CHAN; i++){ + set_rx_freq(i, 0); + set_ddc_phase(i, 0); + } +} + +usrp_standard_rx::~usrp_standard_rx () +{ + // fprintf(stderr, "\nusrp_standard_rx: dtor\n"); +} + +bool +usrp_standard_rx::start () +{ + if (!usrp_basic_rx::start ()) + return false; + + // add our code here + + return true; +} + +bool +usrp_standard_rx::stop () +{ + bool ok = usrp_basic_rx::stop (); + + // add our code here + + return ok; +} + +usrp_standard_rx * +usrp_standard_rx::make (int which_board, + unsigned int decim_rate, + int nchan, int mux, int mode, + int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) +{ + usrp_standard_rx *u = 0; + + try { + u = new usrp_standard_rx (which_board, decim_rate, + nchan, mux, mode, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_standard_rx::set_decim_rate(unsigned int rate) +{ + if ((rate & 0x1) || rate < 4 || rate > 256){ + fprintf (stderr, "usrp_standard_rx::set_decim_rate: rate must be EVEN and in [4, 256]\n"); + return false; + } + + d_decim_rate = rate; + set_usb_data_rate ((adc_rate () / rate * nchannels ()) + * (2 * sizeof (short))); + + bool s = disable_rx (); + int v = has_rx_halfband() ? d_decim_rate/2 - 1 : d_decim_rate - 1; + bool ok = _write_fpga_reg (FR_DECIM_RATE, v); + restore_rx (s); + return ok; +} + +bool usrp_standard_rx::set_nchannels (int nchan) +{ + if (!(nchan == 1 || nchan == 2 || nchan == 4)) + return false; + + if (nchan > nddcs()) + return false; + + d_nchan = nchan; + + return write_hw_mux_reg (); +} + + +// map software mux value to hw mux value +// +// Software mux value: +// +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-------+-------+-------+-------+-------+-------+-------+-------+ +// | Q3 | I3 | Q2 | I2 | Q1 | I1 | Q0 | I0 | +// +-------+-------+-------+-------+-------+-------+-------+-------+ +// +// Each 4-bit I field is either 0,1,2,3 +// Each 4-bit Q field is either 0,1,2,3 or 0xf (input is const zero) +// All Q's must be 0xf or none of them may be 0xf +// +// +// Hardware mux value: +// +// 3 2 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-----------------------+-------+-------+-------+-------+-+-----+ +// | must be zero | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH | +// +-----------------------+-------+-------+-------+-------+-+-----+ + + +static bool +map_sw_mux_to_hw_mux (int sw_mux, int *hw_mux_ptr) +{ + // confirm that all I's are either 0,1,2,3 + + for (int i = 0; i < 8; i += 2){ + int t = (sw_mux >> (4 * i)) & 0xf; + if (!(0 <= t && t <= 3)) + return false; + } + + // confirm that all Q's are either 0,1,2,3 or 0xf + + for (int i = 1; i < 8; i += 2){ + int t = (sw_mux >> (4 * i)) & 0xf; + if (!(t == 0xf || (0 <= t && t <= 3))) + return false; + } + + // confirm that all Q inputs are 0xf (const zero input), + // or none of them are 0xf + + int q_and = 1; + int q_or = 0; + + for (int i = 0; i < 4; i++){ + int qx_is_0xf = ((sw_mux >> (8 * i + 4)) & 0xf) == 0xf; + q_and &= qx_is_0xf; + q_or |= qx_is_0xf; + } + + if (q_and || !q_or){ // OK + int hw_mux_value = 0; + + for (int i = 0; i < 8; i++){ + int t = (sw_mux >> (4 * i)) & 0x3; + hw_mux_value |= t << (2 * i + 4); + } + + if (q_and) + hw_mux_value |= 0x8; // all Q's zero + + *hw_mux_ptr = hw_mux_value; + return true; + } + else + return false; +} + +bool +usrp_standard_rx::set_mux (int mux) +{ + if (!map_sw_mux_to_hw_mux (mux, &d_hw_mux)) + return false; + + // fprintf (stderr, "sw_mux = 0x%08x hw_mux = 0x%08x\n", mux, d_hw_mux); + + d_sw_mux = mux; + return write_hw_mux_reg (); +} + +bool +usrp_standard_rx::write_hw_mux_reg () +{ + bool s = disable_rx (); + bool ok = _write_fpga_reg (FR_RX_MUX, d_hw_mux | d_nchan); + restore_rx (s); + return ok; +} + + +bool +usrp_standard_rx::set_rx_freq (int channel, double freq) +{ + if (channel < 0 || channel > MAX_CHAN) + return false; + + unsigned int v = + compute_freq_control_word_fpga (adc_freq(), + freq, &d_rx_freq[channel], + d_verbose); + + return _write_fpga_reg (FR_RX_FREQ_0 + channel, v); +} + +unsigned int +usrp_standard_rx::decim_rate () const { return d_decim_rate; } + +int +usrp_standard_rx::nchannels () const { return d_nchan; } + +int +usrp_standard_rx::mux () const { return d_sw_mux; } + +double +usrp_standard_rx::rx_freq (int channel) const +{ + if (channel < 0 || channel >= MAX_CHAN) + return 0; + + return d_rx_freq[channel]; +} + +bool +usrp_standard_rx::set_fpga_mode (int mode) +{ + return _write_fpga_reg (FR_MODE, mode); +} + +bool +usrp_standard_rx::set_ddc_phase(int channel, int phase) +{ + if (channel < 0 || channel >= MAX_CHAN) + return false; + + return _write_fpga_reg(FR_RX_PHASE_0 + channel, phase); +} + + +// To avoid quiet failures, check for things that our code cares about. + +static bool +rx_format_is_valid(unsigned int format) +{ + int width = usrp_standard_rx::format_width(format); + int want_q = usrp_standard_rx::format_want_q(format); + + if (!(width == 8 || width == 16)) // FIXME add other widths when valid + return false; + + if (!want_q) // FIXME remove check when the rest of the code can handle I only + return false; + + return true; +} + +bool +usrp_standard_rx::set_format(unsigned int format) +{ + if (!rx_format_is_valid(format)) + return false; + + return _write_fpga_reg(FR_RX_FORMAT, format); +} + +unsigned int +usrp_standard_rx::format() const +{ + return d_fpga_shadows[FR_RX_FORMAT]; +} + +// ---------------------------------------------------------------- + +unsigned int +usrp_standard_rx::make_format(int width, int shift, bool want_q, bool bypass_halfband) +{ + unsigned int format = + (((width << bmFR_RX_FORMAT_WIDTH_SHIFT) & bmFR_RX_FORMAT_WIDTH_MASK) + | (shift << bmFR_RX_FORMAT_SHIFT_SHIFT) & bmFR_RX_FORMAT_SHIFT_MASK); + + if (want_q) + format |= bmFR_RX_FORMAT_WANT_Q; + if (bypass_halfband) + format |= bmFR_RX_FORMAT_BYPASS_HB; + + return format; +} + +int +usrp_standard_rx::format_width(unsigned int format) +{ + return (format & bmFR_RX_FORMAT_WIDTH_MASK) >> bmFR_RX_FORMAT_WIDTH_SHIFT; +} + +int +usrp_standard_rx::format_shift(unsigned int format) +{ + return (format & bmFR_RX_FORMAT_SHIFT_MASK) >> bmFR_RX_FORMAT_SHIFT_SHIFT; +} + +bool +usrp_standard_rx::format_want_q(unsigned int format) +{ + return (format & bmFR_RX_FORMAT_WANT_Q) != 0; +} + +bool +usrp_standard_rx::format_bypass_halfband(unsigned int format) +{ + return (format & bmFR_RX_FORMAT_BYPASS_HB) != 0; +} + +////////////////////////////////////////////////////////////////// + + +// tx data is timed to CLKOUT1 (64 MHz) +// interpolate 4x +// fine modulator enabled + + +static unsigned char tx_regs_use_nco[] = { + REG_TX_IF, (TX_IF_USE_CLKOUT1 + | TX_IF_I_FIRST + | TX_IF_2S_COMP + | TX_IF_INTERLEAVED), + REG_TX_DIGITAL, (TX_DIGITAL_2_DATA_PATHS + | TX_DIGITAL_INTERPOLATE_4X) +}; + + +static int +real_tx_mux_value (int mux, int nchan) +{ + if (mux != -1) + return mux; + + switch (nchan){ + case 1: + return 0x0098; + case 2: + return 0xba98; + default: + assert (0); + } +} + +usrp_standard_tx::usrp_standard_tx (int which_board, + unsigned int interp_rate, + int nchan, int mux, + int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) + : usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks, fpga_filename, firmware_filename), + usrp_standard_common(this), + d_sw_mux (0x8), d_hw_mux (0x81) +{ + if (!usrp_9862_write_many_all (d_udh, tx_regs_use_nco, sizeof (tx_regs_use_nco))){ + fprintf (stderr, "usrp_standard_tx: failed to init AD9862 TX regs\n"); + throw std::runtime_error ("usrp_standard_tx::ctor"); + } + if (!set_nchannels (nchan)){ + fprintf (stderr, "usrp_standard_tx: set_nchannels failed\n"); + throw std::runtime_error ("usrp_standard_tx::ctor"); + } + if (!set_interp_rate (interp_rate)){ + fprintf (stderr, "usrp_standard_tx: set_interp_rate failed\n"); + throw std::runtime_error ("usrp_standard_tx::ctor"); + } + if (!set_mux (real_tx_mux_value (mux, nchan))){ + fprintf (stderr, "usrp_standard_tx: set_mux failed\n"); + throw std::runtime_error ("usrp_standard_tx::ctor"); + } + + for (int i = 0; i < MAX_CHAN; i++){ + d_tx_modulator_shadow[i] = (TX_MODULATOR_DISABLE_NCO + | TX_MODULATOR_COARSE_MODULATION_NONE); + d_coarse_mod[i] = CM_OFF; + set_tx_freq (i, 0); + } +} + +usrp_standard_tx::~usrp_standard_tx () +{ + // fprintf(stderr, "\nusrp_standard_tx: dtor\n"); +} + +bool +usrp_standard_tx::start () +{ + if (!usrp_basic_tx::start ()) + return false; + + // add our code here + + return true; +} + +bool +usrp_standard_tx::stop () +{ + bool ok = usrp_basic_tx::stop (); + + // add our code here + + return ok; +} + +usrp_standard_tx * +usrp_standard_tx::make (int which_board, + unsigned int interp_rate, + int nchan, int mux, + int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) +{ + usrp_standard_tx *u = 0; + + try { + u = new usrp_standard_tx (which_board, interp_rate, nchan, mux, + fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_standard_tx::set_interp_rate (unsigned int rate) +{ + // fprintf (stderr, "usrp_standard_tx::set_interp_rate\n"); + + if ((rate & 0x3) || rate < 4 || rate > 512){ + fprintf (stderr, "usrp_standard_tx::set_interp_rate: rate must be in [4, 512] and a multiple of 4.\n"); + return false; + } + + d_interp_rate = rate; + set_usb_data_rate ((dac_rate () / rate * nchannels ()) + * (2 * sizeof (short))); + + // We're using the interp by 4 feature of the 9862 so that we can + // use its fine modulator. Thus, we reduce the FPGA's interpolation rate + // by a factor of 4. + + bool s = disable_tx (); + bool ok = _write_fpga_reg (FR_INTERP_RATE, d_interp_rate/4 - 1); + restore_tx (s); + return ok; +} + +bool +usrp_standard_tx::set_nchannels (int nchan) +{ + if (!(nchan == 1 || nchan == 2)) + return false; + + if (nchan > nducs()) + return false; + + d_nchan = nchan; + return write_hw_mux_reg (); +} + +bool +usrp_standard_tx::set_mux (int mux) +{ + d_sw_mux = mux; + d_hw_mux = mux << 4; + return write_hw_mux_reg (); +} + +bool +usrp_standard_tx::write_hw_mux_reg () +{ + bool s = disable_tx (); + bool ok = _write_fpga_reg (FR_TX_MUX, d_hw_mux | d_nchan); + restore_tx (s); + return ok; +} + +#ifdef USE_FPGA_TX_CORDIC + +bool +usrp_standard_tx::set_tx_freq (int channel, double freq) +{ + if (channel < 0 || channel >= MAX_CHAN) + return false; + + // This assumes we're running the 4x on-chip interpolator. + + unsigned int v = + compute_freq_control_word_fpga (dac_freq () / 4, + freq, &d_tx_freq[channel], + d_verbose); + + return _write_fpga_reg (FR_TX_FREQ_0 + channel, v); +} + + +#else + +bool +usrp_standard_tx::set_tx_freq (int channel, double freq) +{ + if (channel < 0 || channel >= MAX_CHAN) + return false; + + // split freq into fine and coarse components + + coarse_mod_t cm; + double coarse; + + assert (dac_freq () == 128000000); + + if (freq < -44e6) // too low + return false; + else if (freq < -24e6){ // [-44, -24) + cm = CM_NEG_FDAC_OVER_4; + coarse = -dac_freq () / 4; + } + else if (freq < -8e6){ // [-24, -8) + cm = CM_NEG_FDAC_OVER_8; + coarse = -dac_freq () / 8; + } + else if (freq < 8e6){ // [-8, 8) + cm = CM_OFF; + coarse = 0; + } + else if (freq < 24e6){ // [8, 24) + cm = CM_POS_FDAC_OVER_8; + coarse = dac_freq () / 8; + } + else if (freq <= 44e6){ // [24, 44] + cm = CM_POS_FDAC_OVER_4; + coarse = dac_freq () / 4; + } + else // too high + return false; + + + set_coarse_modulator (channel, cm); // set bits in d_tx_modulator_shadow + + double fine = freq - coarse; + + + // Compute fine tuning word... + // This assumes we're running the 4x on-chip interpolator. + // (This is required to use the fine modulator.) + + unsigned int v = + compute_freq_control_word_9862 (dac_freq () / 4, + fine, &d_tx_freq[channel], d_verbose); + + d_tx_freq[channel] += coarse; // adjust actual + + unsigned char high, mid, low; + + high = (v >> 16) & 0xff; + mid = (v >> 8) & 0xff; + low = (v >> 0) & 0xff; + + bool ok = true; + + // write the fine tuning word + ok &= _write_9862 (channel, REG_TX_NCO_FTW_23_16, high); + ok &= _write_9862 (channel, REG_TX_NCO_FTW_15_8, mid); + ok &= _write_9862 (channel, REG_TX_NCO_FTW_7_0, low); + + + d_tx_modulator_shadow[channel] |= TX_MODULATOR_ENABLE_NCO; + + if (fine < 0) + d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_FINE_TUNE; + else + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_NEG_FINE_TUNE; + + ok &=_write_9862 (channel, REG_TX_MODULATOR, d_tx_modulator_shadow[channel]); + + return ok; +} +#endif + +bool +usrp_standard_tx::set_coarse_modulator (int channel, coarse_mod_t cm) +{ + if (channel < 0 || channel >= MAX_CHAN) + return false; + + switch (cm){ + case CM_NEG_FDAC_OVER_4: + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE; + break; + + case CM_NEG_FDAC_OVER_8: + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE; + break; + + case CM_OFF: + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; + break; + + case CM_POS_FDAC_OVER_8: + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8; + break; + + case CM_POS_FDAC_OVER_4: + d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; + d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4; + break; + + default: + return false; + } + + d_coarse_mod[channel] = cm; + return true; +} + +unsigned int +usrp_standard_tx::interp_rate () const { return d_interp_rate; } + +int +usrp_standard_tx::nchannels () const { return d_nchan; } + +int +usrp_standard_tx::mux () const { return d_sw_mux; } + +double +usrp_standard_tx::tx_freq (int channel) const +{ + if (channel < 0 || channel >= MAX_CHAN) + return 0; + + return d_tx_freq[channel]; +} + +usrp_standard_tx::coarse_mod_t +usrp_standard_tx::coarse_modulator (int channel) const +{ + if (channel < 0 || channel >= MAX_CHAN) + return CM_OFF; + + return d_coarse_mod[channel]; +} diff --git a/usrp/host/lib/legacy/usrp_standard.h b/usrp/host/lib/legacy/usrp_standard.h new file mode 100644 index 00000000..6fee4b9c --- /dev/null +++ b/usrp/host/lib/legacy/usrp_standard.h @@ -0,0 +1,366 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_USRP_STANDARD_H +#define INCLUDED_USRP_STANDARD_H + +#include + +class usrp_standard_common +{ + int d_fpga_caps; // capability register val + +protected: + usrp_standard_common(usrp_basic *parent); + +public: + /*! + *\brief does the FPGA implement the final Rx half-band filter? + * If it doesn't, the maximum decimation factor with proper gain + * is 1/2 of what it would otherwise be. + */ + bool has_rx_halfband() const; + + /*! + * \brief number of digital downconverters implemented in the FPGA + * This will be 0, 1, 2 or 4. + */ + int nddcs() const; + + /*! + *\brief does the FPGA implement the initial Tx half-band filter? + */ + bool has_tx_halfband() const; + + /*! + * \brief number of digital upconverters implemented in the FPGA + * This will be 0, 1, or 2. + */ + int nducs() const; +}; + +/*! + * \brief standard usrp RX class. + * + * Assumes digital down converter in FPGA + */ +class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common +{ + private: + static const int MAX_CHAN = 4; + unsigned int d_decim_rate; + int d_nchan; + int d_sw_mux; + int d_hw_mux; + double d_rx_freq[MAX_CHAN]; + + protected: + usrp_standard_rx (int which_board, + unsigned int decim_rate, + int nchan = 1, + int mux = -1, + int mode = 0, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); // throws if trouble + + bool write_hw_mux_reg (); + + public: + + enum { + FPGA_MODE_NORMAL = 0x00, + FPGA_MODE_LOOPBACK = 0x01, + FPGA_MODE_COUNTING = 0x02, + FPGA_MODE_COUNTING_32BIT = 0x04 + }; + + ~usrp_standard_rx (); + + /*! + * \brief invokes constructor, returns instance or 0 if trouble + * + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + static usrp_standard_rx *make (int which_board, + unsigned int decim_rate, + int nchan = 1, + int mux = -1, + int mode = 0, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); + /*! + * \brief Set decimator rate. \p rate MUST BE EVEN and in [8, 256]. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () * nchannels () + */ + bool set_decim_rate (unsigned int rate); + + /*! + * \brief Set number of active channels. \p nchannels must be 1, 2 or 4. + * + * The final complex sample rate across the USB is + * adc_freq () / decim_rate () * nchannels () + */ + bool set_nchannels (int nchannels); + + /*! + * \brief Set input mux configuration. + * + * This determines which ADC (or constant zero) is connected to + * each DDC input. There are 4 DDCs. Each has two inputs. + * + *
+   * Mux value:
+   *
+   *    3                   2                   1                       
+   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   * +-------+-------+-------+-------+-------+-------+-------+-------+
+   * |   Q3  |   I3  |   Q2  |   I2  |   Q1  |   I1  |   Q0  |   I0  |
+   * +-------+-------+-------+-------+-------+-------+-------+-------+
+   *
+   * Each 4-bit I field is either 0,1,2,3
+   * Each 4-bit Q field is either 0,1,2,3 or 0xf (input is const zero)
+   * All Q's must be 0xf or none of them may be 0xf
+   * 
+ */ + bool set_mux (int mux); + + /*! + * \brief set the frequency of the digital down converter. + * + * \p channel must be in the range [0,3]. \p freq is the center + * frequency in Hz. \p freq may be either negative or postive. + * The frequency specified is quantized. Use rx_freq to retrieve + * the actual value used. + */ + bool set_rx_freq (int channel, double freq); + + /*! + * \brief set fpga mode + */ + bool set_fpga_mode (int mode); + + /*! + * \brief Set the digital down converter phase register. + * + * \param channel which ddc channel [0, 3] + * \param phase 32-bit integer phase value. + */ + bool set_ddc_phase(int channel, int phase); + + /*! + * \brief Specify Rx data format. + * + * \param format format specifier + * + * Rx data format control register + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------+-+-+---------+-------+ + * | Reserved (Must be zero) |B|Q| WIDTH | SHIFT | + * +-----------------------------------------+-+-+---------+-------+ + * + * SHIFT specifies arithmetic right shift [0, 15] + * WIDTH specifies bit-width of I & Q samples across the USB [1, 16] (not all valid) + * Q if set deliver both I & Q, else just I + * B if set bypass half-band filter. + * + * Right now the acceptable values are: + * + * B Q WIDTH SHIFT + * 0 1 16 0 + * 0 1 8 8 + * + * More valid combos to come. + * + * Default value is 0x00000300 16-bits, 0 shift, deliver both I & Q. + */ + bool set_format(unsigned int format); + + static unsigned int make_format(int width=16, int shift=0, + bool want_q=true, bool bypass_halfband=false); + static int format_width(unsigned int format); + static int format_shift(unsigned int format); + static bool format_want_q(unsigned int format); + static bool format_bypass_halfband(unsigned int format); + + // ACCESSORS + unsigned int decim_rate () const; + double rx_freq (int channel) const; + int nchannels () const; + int mux () const; + unsigned int format () const; + + // called in base class to derived class order + bool start (); + bool stop (); +}; + +// ---------------------------------------------------------------- + +/*! + * \brief standard usrp TX class. + * + * Uses digital upconverter (coarse & fine modulators) in AD9862... + */ +class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common +{ + public: + enum coarse_mod_t { + CM_NEG_FDAC_OVER_4, // -32 MHz + CM_NEG_FDAC_OVER_8, // -16 MHz + CM_OFF, + CM_POS_FDAC_OVER_8, // +16 MHz + CM_POS_FDAC_OVER_4 // +32 MHz + }; + + protected: + static const int MAX_CHAN = 2; + unsigned int d_interp_rate; + int d_nchan; + int d_sw_mux; + int d_hw_mux; + double d_tx_freq[MAX_CHAN]; + coarse_mod_t d_coarse_mod[MAX_CHAN]; + unsigned char d_tx_modulator_shadow[MAX_CHAN]; + + virtual bool set_coarse_modulator (int channel, coarse_mod_t cm); + usrp_standard_tx::coarse_mod_t coarse_modulator (int channel) const; + + protected: + usrp_standard_tx (int which_board, + unsigned int interp_rate, + int nchan = 1, + int mux = -1, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); // throws if trouble + + bool write_hw_mux_reg (); + + public: + ~usrp_standard_tx (); + + /*! + * \brief invokes constructor, returns instance or 0 if trouble + * + * \param which_board Which USRP board on usb (not particularly useful; use 0) + * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. + * Use zero for a reasonable default. + * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. + */ + static usrp_standard_tx *make (int which_board, + unsigned int interp_rate, + int nchan = 1, + int mux = -1, + int fusb_block_size = 0, + int fusb_nblocks = 0, + const std::string fpga_filename = "", + const std::string firmware_filename = "" + ); + + /*! + * \brief Set interpolator rate. \p rate must be in [4, 512] and a multiple of 4. + * + * The final complex sample rate across the USB is + * dac_freq () / interp_rate () * nchannels () + */ + virtual bool set_interp_rate (unsigned int rate); + + /*! + * \brief Set number of active channels. \p nchannels must be 1 or 2. + * + * The final complex sample rate across the USB is + * dac_freq () / decim_rate () * nchannels () + */ + bool set_nchannels (int nchannels); + + /*! + * \brief Set output mux configuration. + * + *
+   *     3                   2                   1                       
+   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   *  +-------------------------------+-------+-------+-------+-------+
+   *  |                               | DAC3  | DAC2  | DAC1  |  DAC0 |
+   *  +-------------------------------+-------+-------+-------+-------+
+   * 
+   *  There are two interpolators with complex inputs and outputs.
+   *  There are four DACs.
+   * 
+   *  Each 4-bit DACx field specifies the source for the DAC and
+   *  whether or not that DAC is enabled.  Each subfield is coded
+   *  like this: 
+   * 
+   *     3 2 1 0
+   *    +-+-----+
+   *    |E|  N  |
+   *    +-+-----+
+   * 
+   *  Where E is set if the DAC is enabled, and N specifies which
+   *  interpolator output is connected to this DAC.
+   * 
+   *   N   which interp output
+   *  ---  -------------------
+   *   0   chan 0 I
+   *   1   chan 0 Q
+   *   2   chan 1 I
+   *   3   chan 1 Q
+   * 
+ */ + bool set_mux (int mux); + + /*! + * \brief set the frequency of the digital up converter. + * + * \p channel must be in the range [0,1]. \p freq is the center + * frequency in Hz. It must be in the range [-44M, 44M]. + * The frequency specified is quantized. Use tx_freq to retrieve + * the actual value used. + */ + virtual bool set_tx_freq (int channel, double freq); // chan: [0,1] + + // ACCESSORS + unsigned int interp_rate () const; + double tx_freq (int channel) const; + int nchannels () const; + int mux () const; + + // called in base class to derived class order + bool start (); + bool stop (); +}; + +#endif /* INCLUDED_USRP_STANDARD_H */ diff --git a/usrp/host/lib/md5.c b/usrp/host/lib/md5.c deleted file mode 100644 index abee067f..00000000 --- a/usrp/host/lib/md5.c +++ /dev/null @@ -1,452 +0,0 @@ -/* md5.c - Functions to compute MD5 message digest of files or memory blocks - according to the definition of MD5 in RFC 1321 from April 1992. - Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc. - NOTE: The canonical source of this file is maintained with the GNU C - Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. - - 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 2, 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Boston, MA 02110-1301, USA. */ - -/* Written by Ulrich Drepper , 1995. */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "md5.h" - -#include - -#include -#include - -// #include "unlocked-io.h" - -#ifdef _LIBC -# include -# if __BYTE_ORDER == __BIG_ENDIAN -# define WORDS_BIGENDIAN 1 -# endif -/* We need to keep the namespace clean so define the MD5 function - protected using leading __ . */ -# define md5_init_ctx __md5_init_ctx -# define md5_process_block __md5_process_block -# define md5_process_bytes __md5_process_bytes -# define md5_finish_ctx __md5_finish_ctx -# define md5_read_ctx __md5_read_ctx -# define md5_stream __md5_stream -# define md5_buffer __md5_buffer -#endif - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#else -# define SWAP(n) (n) -#endif - -#define BLOCKSIZE 4096 -/* Ensure that BLOCKSIZE is a multiple of 64. */ -#if BLOCKSIZE % 64 != 0 -/* FIXME-someday (soon?): use #error instead of this kludge. */ -"invalid BLOCKSIZE" -#endif - -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - - -/* Initialize structure containing state of computation. - (RFC 1321, 3.3: Step 3) */ -void -md5_init_ctx (struct md5_ctx *ctx) -{ - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Put result from CTX in first 16 bytes following RESBUF. The result - must be in little endian byte order. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -void * -md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) -{ - ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); - ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); - ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); - ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -void * -md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) -{ - /* Take yet unprocessed bytes into account. */ - md5_uint32 bytes = ctx->buflen; - size_t pad; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; - - pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; - memcpy (&ctx->buffer[bytes], fillbuf, pad); - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); - *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | - (ctx->total[0] >> 29)); - - /* Process last bytes. */ - md5_process_block (ctx->buffer, bytes + pad + 8, ctx); - - return md5_read_ctx (ctx, resbuf); -} - -/* Compute MD5 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 16 bytes - beginning at RESBLOCK. */ -int -md5_stream (FILE *stream, void *resblock) -{ - struct md5_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - md5_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - while (1) - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - - if (sum == BLOCKSIZE) - break; - - if (n == 0) - { - /* Check for the error flag IFF N == 0, so that we don't - exit the loop after a partial read due to e.g., EAGAIN - or EWOULDBLOCK. */ - if (ferror (stream)) - return 1; - goto process_partial_block; - } - - /* We've read at least one byte, so ignore errors. But always - check for EOF, since feof may be true even though N > 0. - Otherwise, we could end up calling fread after EOF. */ - if (feof (stream)) - goto process_partial_block; - } - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - md5_process_block (buffer, BLOCKSIZE, &ctx); - } - - process_partial_block:; - - /* Process any remaining bytes. */ - if (sum > 0) - md5_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - md5_finish_ctx (&ctx, resblock); - return 0; -} - -/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void * -md5_buffer (const char *buffer, size_t len, void *resblock) -{ - struct md5_ctx ctx; - - /* Initialize the computation context. */ - md5_init_ctx (&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - md5_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return md5_finish_ctx (&ctx, resblock); -} - - -void -md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx) -{ - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy (&ctx->buffer[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 64) - { - md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 64) - { -#if !_STRING_ARCH_unaligned -/* To check alignment gcc has an appropriate operator. Other - compilers don't. */ -# if __GNUC__ >= 2 -# define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) -# else -# define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0) -# endif - if (UNALIGNED_P (buffer)) - while (len > 64) - { - md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else -#endif - { - md5_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&ctx->buffer[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - md5_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[64], left_over); - } - ctx->buflen = left_over; - } -} - - -/* These are the four functions used in the four steps of the MD5 algorithm - and defined in the RFC 1321. The first function is a little bit optimized - (as found in Colin Plumbs public domain implementation). */ -/* #define FF(b, c, d) ((b & c) | (~b & d)) */ -#define FF(b, c, d) (d ^ (b & (c ^ d))) -#define FG(b, c, d) FF (d, b, c) -#define FH(b, c, d) (b ^ c ^ d) -#define FI(b, c, d) (c ^ (b | ~d)) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. */ - -void -md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx) -{ - md5_uint32 correct_words[16]; - const md5_uint32 *words = buffer; - size_t nwords = len / sizeof (md5_uint32); - const md5_uint32 *endp = words + nwords; - md5_uint32 A = ctx->A; - md5_uint32 B = ctx->B; - md5_uint32 C = ctx->C; - md5_uint32 D = ctx->D; - - /* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < len) - ++ctx->total[1]; - - /* Process all bytes in the buffer with 64 bytes in each round of - the loop. */ - while (words < endp) - { - md5_uint32 *cwp = correct_words; - md5_uint32 A_save = A; - md5_uint32 B_save = B; - md5_uint32 C_save = C; - md5_uint32 D_save = D; - - /* First round: using the given function, the context and a constant - the next context is computed. Because the algorithms processing - unit is a 32-bit word and it is determined to work on words in - little endian byte order we perhaps have to change the byte order - before the computation. To reduce the work for the next steps - we store the swapped words in the array CORRECT_WORDS. */ - -#define OP(a, b, c, d, s, T) \ - do \ - { \ - a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ - ++words; \ - a = rol (a, s); \ - a += b; \ - } \ - while (0) - - /* Before we start, one word to the strange constants. - They are defined in RFC 1321 as - - T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or - perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}' - */ - - /* Round 1. */ - OP (A, B, C, D, 7, 0xd76aa478); - OP (D, A, B, C, 12, 0xe8c7b756); - OP (C, D, A, B, 17, 0x242070db); - OP (B, C, D, A, 22, 0xc1bdceee); - OP (A, B, C, D, 7, 0xf57c0faf); - OP (D, A, B, C, 12, 0x4787c62a); - OP (C, D, A, B, 17, 0xa8304613); - OP (B, C, D, A, 22, 0xfd469501); - OP (A, B, C, D, 7, 0x698098d8); - OP (D, A, B, C, 12, 0x8b44f7af); - OP (C, D, A, B, 17, 0xffff5bb1); - OP (B, C, D, A, 22, 0x895cd7be); - OP (A, B, C, D, 7, 0x6b901122); - OP (D, A, B, C, 12, 0xfd987193); - OP (C, D, A, B, 17, 0xa679438e); - OP (B, C, D, A, 22, 0x49b40821); - - /* For the second to fourth round we have the possibly swapped words - in CORRECT_WORDS. Redefine the macro to take an additional first - argument specifying the function to use. */ -#undef OP -#define OP(f, a, b, c, d, k, s, T) \ - do \ - { \ - a += f (b, c, d) + correct_words[k] + T; \ - a = rol (a, s); \ - a += b; \ - } \ - while (0) - - /* Round 2. */ - OP (FG, A, B, C, D, 1, 5, 0xf61e2562); - OP (FG, D, A, B, C, 6, 9, 0xc040b340); - OP (FG, C, D, A, B, 11, 14, 0x265e5a51); - OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); - OP (FG, A, B, C, D, 5, 5, 0xd62f105d); - OP (FG, D, A, B, C, 10, 9, 0x02441453); - OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); - OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); - OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); - OP (FG, D, A, B, C, 14, 9, 0xc33707d6); - OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); - OP (FG, B, C, D, A, 8, 20, 0x455a14ed); - OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); - OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); - OP (FG, C, D, A, B, 7, 14, 0x676f02d9); - OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); - - /* Round 3. */ - OP (FH, A, B, C, D, 5, 4, 0xfffa3942); - OP (FH, D, A, B, C, 8, 11, 0x8771f681); - OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); - OP (FH, B, C, D, A, 14, 23, 0xfde5380c); - OP (FH, A, B, C, D, 1, 4, 0xa4beea44); - OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); - OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); - OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); - OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); - OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); - OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); - OP (FH, B, C, D, A, 6, 23, 0x04881d05); - OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); - OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); - OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); - OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); - - /* Round 4. */ - OP (FI, A, B, C, D, 0, 6, 0xf4292244); - OP (FI, D, A, B, C, 7, 10, 0x432aff97); - OP (FI, C, D, A, B, 14, 15, 0xab9423a7); - OP (FI, B, C, D, A, 5, 21, 0xfc93a039); - OP (FI, A, B, C, D, 12, 6, 0x655b59c3); - OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); - OP (FI, C, D, A, B, 10, 15, 0xffeff47d); - OP (FI, B, C, D, A, 1, 21, 0x85845dd1); - OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); - OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); - OP (FI, C, D, A, B, 6, 15, 0xa3014314); - OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); - OP (FI, A, B, C, D, 4, 6, 0xf7537e82); - OP (FI, D, A, B, C, 11, 10, 0xbd3af235); - OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); - OP (FI, B, C, D, A, 9, 21, 0xeb86d391); - - /* Add the starting values of the context. */ - A += A_save; - B += B_save; - C += C_save; - D += D_save; - } - - /* Put checksum in context given as argument. */ - ctx->A = A; - ctx->B = B; - ctx->C = C; - ctx->D = D; -} diff --git a/usrp/host/lib/md5.h b/usrp/host/lib/md5.h deleted file mode 100644 index 709cefb9..00000000 --- a/usrp/host/lib/md5.h +++ /dev/null @@ -1,129 +0,0 @@ -/* md5.h - Declaration of functions and data types used for MD5 sum - computing library functions. - Copyright (C) 1995, 1996, 1999, 2000, 2003 Free Software Foundation, Inc. - NOTE: The canonical source of this file is maintained with the GNU C - Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. - - 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 2, 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Boston, MA 02110-1301, USA. */ - -#ifndef _MD5_H -#define _MD5_H 1 - -#include -#include - -/* The following contortions are an attempt to use the C preprocessor - to determine an unsigned integral type that is 32 bits wide. An - alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but - doing that would require that the configure script compile and *run* - the resulting executable. Locally running cross-compiled executables - is usually not possible. */ - -#ifdef _LIBC -# include -typedef uint32_t md5_uint32; -typedef uintptr_t md5_uintptr; -#else -# define UINT_MAX_32_BITS 4294967295U - -# if UINT_MAX == UINT_MAX_32_BITS - typedef unsigned int md5_uint32; -# else -# if USHRT_MAX == UINT_MAX_32_BITS - typedef unsigned short md5_uint32; -# else -# if ULONG_MAX == UINT_MAX_32_BITS - typedef unsigned long md5_uint32; -# else - /* The following line is intended to evoke an error. - Using #error is not portable enough. */ - "Cannot determine unsigned 32-bit data type." -# endif -# endif -# endif -/* We have to make a guess about the integer type equivalent in size - to pointers which should always be correct. */ -typedef unsigned long int md5_uintptr; -#endif - -/* Structure to save state of computation between the single steps. */ -struct md5_ctx -{ - md5_uint32 A; - md5_uint32 B; - md5_uint32 C; - md5_uint32 D; - - md5_uint32 total[2]; - md5_uint32 buflen; - char buffer[128]; -}; - -/* - * The following three functions are build up the low level used in - * the functions `md5_stream' and `md5_buffer'. - */ - -/* Initialize structure containing state of computation. - (RFC 1321, 3.3: Step 3) */ -extern void md5_init_ctx (struct md5_ctx *ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void md5_process_block (const void *buffer, size_t len, - struct md5_ctx *ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void md5_process_bytes (const void *buffer, size_t len, - struct md5_ctx *ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 16 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF be correctly - aligned for a 32 bits value. */ -extern void *md5_finish_ctx (struct md5_ctx *ctx, void *resbuf); - - -/* Put result from CTX in first 16 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void *md5_read_ctx (const struct md5_ctx *ctx, void *resbuf); - - -/* Compute MD5 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 16 bytes - beginning at RESBLOCK. */ -extern int md5_stream (FILE *stream, void *resblock); - -/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void *md5_buffer (const char *buffer, size_t len, void *resblock); - -#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) - -#endif diff --git a/usrp/host/lib/mld_threads.h b/usrp/host/lib/mld_threads.h deleted file mode 100644 index 08b3555a..00000000 --- a/usrp/host/lib/mld_threads.h +++ /dev/null @@ -1,257 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio. - * - * Primary Author: Michael Dickens, NCIP Lab, University of Notre Dame - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _INCLUDED_MLD_THREADS_H_ -#define _INCLUDED_MLD_THREADS_H_ - -/* classes which allow for either pthreads or omni_threads */ - -#ifdef _USE_OMNI_THREADS_ -#include -#else -#include -#endif - -#include - -#define __INLINE__ inline - -class mld_condition_t; - -class mld_mutex_t { -#ifdef _USE_OMNI_THREADS_ - typedef omni_mutex l_mutex, *l_mutex_ptr; -#else - typedef pthread_mutex_t l_mutex, *l_mutex_ptr; -#endif - - friend class mld_condition_t; - -private: - l_mutex_ptr d_mutex; - -protected: - inline l_mutex_ptr mutex () { return (d_mutex); }; - -public: - __INLINE__ mld_mutex_t () { -#ifdef _USE_OMNI_THREADS_ - d_mutex = new omni_mutex (); -#else - d_mutex = (l_mutex_ptr) new l_mutex; - int l_ret = pthread_mutex_init (d_mutex, NULL); - if (l_ret != 0) { - fprintf (stderr, "Error %d creating mutex.\n", l_ret); - throw std::runtime_error ("mld_mutex_t::mld_mutex_t()\n"); - } -#endif - }; - - __INLINE__ ~mld_mutex_t () { - unlock (); -#ifndef _USE_OMNI_THREADS_ - int l_ret = pthread_mutex_destroy (d_mutex); - if (l_ret != 0) { - fprintf (stderr, "mld_mutex_t::~mld_mutex_t(): " - "Error %d destroying mutex.\n", l_ret); - } -#endif - delete d_mutex; - d_mutex = NULL; - }; - - __INLINE__ void lock () { -#ifdef _USE_OMNI_THREADS_ - d_mutex->lock (); -#else - int l_ret = pthread_mutex_lock (d_mutex); - if (l_ret != 0) { - fprintf (stderr, "mld_mutex_t::lock(): " - "Error %d locking mutex.\n", l_ret); - } -#endif - }; - - __INLINE__ void unlock () { -#ifdef _USE_OMNI_THREADS_ - d_mutex->unlock (); -#else - int l_ret = pthread_mutex_unlock (d_mutex); - if (l_ret != 0) { - fprintf (stderr, "mld_mutex_t::unlock(): " - "Error %d locking mutex.\n", l_ret); - } -#endif - }; - - __INLINE__ bool trylock () { -#ifdef _USE_OMNI_THREADS_ - int l_ret = d_mutex->trylock (); -#else - int l_ret = pthread_mutex_unlock (d_mutex); -#endif - return (l_ret == 0 ? true : false); - }; - - inline void acquire () { lock(); }; - inline void release () { unlock(); }; - inline void wait () { lock(); }; - inline void post () { unlock(); }; -}; - -typedef mld_mutex_t mld_mutex, *mld_mutex_ptr; - -class mld_condition_t { -#ifdef _USE_OMNI_THREADS_ - typedef omni_condition l_condition, *l_condition_ptr; -#else - typedef pthread_cond_t l_condition, *l_condition_ptr; -#endif - -private: - l_condition_ptr d_condition; - mld_mutex_ptr d_mutex; - bool d_waiting; - -public: - __INLINE__ mld_condition_t () { - d_waiting = false; - d_mutex = new mld_mutex (); -#ifdef _USE_OMNI_THREADS_ - d_condition = new omni_condition (d_mutex->mutex ()); -#else - d_condition = (l_condition_ptr) new l_condition; - int l_ret = pthread_cond_init (d_condition, NULL); - if (l_ret != 0) { - fprintf (stderr, "Error %d creating condition.\n", l_ret); - throw std::runtime_error ("mld_condition_t::mld_condition_t()\n"); - } -#endif - }; - - __INLINE__ ~mld_condition_t () { - signal (); -#ifndef _USE_OMNI_THREADS_ - int l_ret = pthread_cond_destroy (d_condition); - if (l_ret != 0) { - fprintf (stderr, "mld_condition_t::mld_condition_t(): " - "Error %d destroying condition.\n", l_ret); - } -#endif - delete d_condition; - d_condition = NULL; - delete d_mutex; - d_mutex = NULL; - }; - - __INLINE__ void signal () { - if (d_waiting == true) { -#ifdef _USE_OMNI_THREADS_ - d_condition->signal (); -#else - int l_ret = pthread_cond_signal (d_condition); - if (l_ret != 0) { - fprintf (stderr, "mld_condition_t::signal(): " - "Error %d.\n", l_ret); - } -#endif - d_waiting = false; - } - }; - - __INLINE__ void wait () { - if (d_waiting == false) { - d_waiting = true; -#ifdef _USE_OMNI_THREADS_ - d_condition->wait (); -#else - int l_ret = pthread_cond_wait (d_condition, d_mutex->mutex ()); - if (l_ret != 0) { - fprintf (stderr, "mld_condition_t::wait(): " - "Error %d.\n", l_ret); - } -#endif - } - }; -}; - -typedef mld_condition_t mld_condition, *mld_condition_ptr; - -class mld_thread_t { -#ifdef _USE_OMNI_THREADS_ - typedef omni_thread l_thread, *l_thread_ptr; -#else - typedef pthread_t l_thread, *l_thread_ptr; -#endif - -private: -#ifndef _USE_OMNI_THREADS_ - l_thread d_thread; - void (*d_start_routine)(void*); - void *d_arg; -#else - l_thread_ptr d_thread; -#endif - -#ifndef _USE_OMNI_THREADS_ - static void* local_start_routine (void *arg) { - mld_thread_t* This = (mld_thread_t*) arg; - (*(This->d_start_routine))(This->d_arg); - return (NULL); - }; -#endif - -public: - __INLINE__ mld_thread_t (void (*start_routine)(void *), void *arg) { -#ifdef _USE_OMNI_THREADS_ - d_thread = new omni_thread (start_routine, arg); - d_thread->start (); -#else - d_start_routine = start_routine; - d_arg = arg; - int l_ret = pthread_create (&d_thread, NULL, local_start_routine, this); - if (l_ret != 0) { - fprintf (stderr, "Error %d creating thread.\n", l_ret); - throw std::runtime_error ("mld_thread_t::mld_thread_t()\n"); - } -#endif - }; - - __INLINE__ ~mld_thread_t () { -#ifdef _USE_OMNI_THREADS_ -// delete d_thread; - d_thread = NULL; -#else - int l_ret = pthread_detach (d_thread); - if (l_ret != 0) { - fprintf (stderr, "Error %d detaching thread.\n", l_ret); - throw std::runtime_error ("mld_thread_t::~mld_thread_t()\n"); - } -#endif - }; -}; - -typedef mld_thread_t mld_thread, *mld_thread_ptr; - -#endif /* _INCLUDED_MLD_THREADS_H_ */ diff --git a/usrp/host/lib/rate_to_regval.h b/usrp/host/lib/rate_to_regval.h deleted file mode 100644 index 1ffdc0f6..00000000 --- a/usrp/host/lib/rate_to_regval.h +++ /dev/null @@ -1,97 +0,0 @@ - { 1, 0x00 }, - { 2, 0x01 }, - { 3, 0x02 }, - { 4, 0x11 }, - { 5, 0x04 }, - { 6, 0x05 }, - { 7, 0x06 }, - { 8, 0x13 }, - { 9, 0x08 }, - { 10, 0x09 }, - { 11, 0x0a }, - { 12, 0x15 }, - { 13, 0x0c }, - { 14, 0x0d }, - { 15, 0x0e }, - { 16, 0x33 }, - { 18, 0x18 }, - { 20, 0x19 }, - { 21, 0x26 }, - { 22, 0x1a }, - { 24, 0x35 }, - { 25, 0x44 }, - { 26, 0x1c }, - { 27, 0x28 }, - { 28, 0x1d }, - { 30, 0x1e }, - { 32, 0x37 }, - { 33, 0x2a }, - { 35, 0x46 }, - { 36, 0x55 }, - { 39, 0x2c }, - { 40, 0x39 }, - { 42, 0x56 }, - { 44, 0x3a }, - { 45, 0x2e }, - { 48, 0x57 }, - { 49, 0x66 }, - { 50, 0x49 }, - { 52, 0x3c }, - { 54, 0x58 }, - { 55, 0x4a }, - { 56, 0x3d }, - { 60, 0x59 }, - { 63, 0x68 }, - { 64, 0x77 }, - { 65, 0x4c }, - { 66, 0x5a }, - { 70, 0x69 }, - { 72, 0x5b }, - { 75, 0x4e }, - { 77, 0x6a }, - { 78, 0x5c }, - { 80, 0x79 }, - { 81, 0x88 }, - { 84, 0x5d }, - { 88, 0x7a }, - { 90, 0x5e }, - { 91, 0x6c }, - { 96, 0x7b }, - { 98, 0x6d }, - { 99, 0x8a }, - { 100, 0x99 }, - { 104, 0x7c }, - { 105, 0x6e }, - { 108, 0x8b }, - { 110, 0x9a }, - { 112, 0x7d }, - { 117, 0x8c }, - { 120, 0x9b }, - { 121, 0xaa }, - { 126, 0x8d }, - { 128, 0x7f }, - { 130, 0x9c }, - { 132, 0xab }, - { 135, 0x8e }, - { 140, 0x9d }, - { 143, 0xac }, - { 144, 0xbb }, - { 150, 0x9e }, - { 154, 0xad }, - { 156, 0xbc }, - { 160, 0x9f }, - { 165, 0xae }, - { 168, 0xbd }, - { 169, 0xcc }, - { 176, 0xaf }, - { 180, 0xbe }, - { 182, 0xcd }, - { 192, 0xbf }, - { 195, 0xce }, - { 196, 0xdd }, - { 208, 0xcf }, - { 210, 0xde }, - { 224, 0xdf }, - { 225, 0xee }, - { 240, 0xef }, - { 256, 0xff } diff --git a/usrp/host/lib/std_paths.h.in b/usrp/host/lib/std_paths.h.in deleted file mode 100644 index 22a8ecbd..00000000 --- a/usrp/host/lib/std_paths.h.in +++ /dev/null @@ -1,27 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2005 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -static char *std_paths[] = { - "@prefix@/share/usrp", - "/usr/local/share/usrp", - 0 -}; diff --git a/usrp/host/lib/usrp_basic.cc b/usrp/host/lib/usrp_basic.cc deleted file mode 100644 index cc9df04e..00000000 --- a/usrp/host/lib/usrp_basic.cc +++ /dev/null @@ -1,1240 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "usrp_basic.h" -#include "usrp_prims.h" -#include "usrp_interfaces.h" -#include "fpga_regs_common.h" -#include "fusb.h" -#include -#include -#include -#include -#include - -using namespace ad9862; - -#define NELEM(x) (sizeof (x) / sizeof (x[0])) - -// These set the buffer size used for each end point using the fast -// usb interface. The kernel ends up locking down this much memory. - -static const int FUSB_BUFFER_SIZE = fusb_sysconfig::default_buffer_size(); -static const int FUSB_BLOCK_SIZE = fusb_sysconfig::max_block_size(); -static const int FUSB_NBLOCKS = FUSB_BUFFER_SIZE / FUSB_BLOCK_SIZE; - - -static const double POLLING_INTERVAL = 0.1; // seconds - -//////////////////////////////////////////////////////////////// - -static struct usb_dev_handle * -open_rx_interface (struct usb_device *dev) -{ - struct usb_dev_handle *udh = usrp_open_rx_interface (dev); - if (udh == 0){ - fprintf (stderr, "usrp_basic_rx: can't open rx interface\n"); - usb_strerror (); - } - return udh; -} - -static struct usb_dev_handle * -open_tx_interface (struct usb_device *dev) -{ - struct usb_dev_handle *udh = usrp_open_tx_interface (dev); - if (udh == 0){ - fprintf (stderr, "usrp_basic_tx: can't open tx interface\n"); - usb_strerror (); - } - return udh; -} - - -////////////////////////////////////////////////////////////////// -// -// usrp_basic -// -//////////////////////////////////////////////////////////////// - - -// Given: -// CLKIN = 64 MHz -// CLKSEL pin = high -// -// These settings give us: -// CLKOUT1 = CLKIN = 64 MHz -// CLKOUT2 = CLKIN = 64 MHz -// ADC is clocked at 64 MHz -// DAC is clocked at 128 MHz - -static unsigned char common_regs[] = { - REG_GENERAL, 0, - REG_DLL, (DLL_DISABLE_INTERNAL_XTAL_OSC - | DLL_MULT_2X - | DLL_FAST), - REG_CLKOUT, CLKOUT2_EQ_DLL_OVER_2, - REG_AUX_ADC_CLK, AUX_ADC_CLK_CLK_OVER_4 -}; - - -usrp_basic::usrp_basic (int which_board, - struct usb_dev_handle * - open_interface (struct usb_device *dev), - const std::string fpga_filename, - const std::string firmware_filename) - : d_udh (0), - d_usb_data_rate (16000000), // SWAG, see below - d_bytes_per_poll ((int) (POLLING_INTERVAL * d_usb_data_rate)), - d_verbose (false) -{ - /* - * SWAG: Scientific Wild Ass Guess. - * - * d_usb_data_rate is used only to determine how often to poll for over- and under-runs. - * We defualt it to 1/2 of our best case. Classes derived from usrp_basic (e.g., - * usrp_standard_tx and usrp_standard_rx) call set_usb_data_rate() to tell us the - * actual rate. This doesn't change our throughput, that's determined by the signal - * processing code in the FPGA (which we know nothing about), and the system limits - * determined by libusb, fusb_*, and the underlying drivers. - */ - memset (d_fpga_shadows, 0, sizeof (d_fpga_shadows)); - - usrp_one_time_init (); - - if (!usrp_load_standard_bits (which_board, false, fpga_filename, firmware_filename)) - throw std::runtime_error ("usrp_basic/usrp_load_standard_bits"); - - struct usb_device *dev = usrp_find_device (which_board); - if (dev == 0){ - fprintf (stderr, "usrp_basic: can't find usrp[%d]\n", which_board); - throw std::runtime_error ("usrp_basic/usrp_find_device"); - } - - if (!(usrp_usrp_p(dev) && usrp_hw_rev(dev) >= 1)){ - fprintf (stderr, "usrp_basic: sorry, this code only works with USRP revs >= 1\n"); - throw std::runtime_error ("usrp_basic/bad_rev"); - } - - if ((d_udh = open_interface (dev)) == 0) - throw std::runtime_error ("usrp_basic/open_interface"); - - // initialize registers that are common to rx and tx - - if (!usrp_9862_write_many_all (d_udh, common_regs, sizeof (common_regs))){ - fprintf (stderr, "usrp_basic: failed to init common AD9862 regs\n"); - throw std::runtime_error ("usrp_basic/init_9862"); - } - - _write_fpga_reg (FR_MODE, 0); // ensure we're in normal mode - _write_fpga_reg (FR_DEBUG_EN, 0); // disable debug outputs -} - -usrp_basic::~usrp_basic () -{ - if (d_udh) - usb_close (d_udh); -} - -bool -usrp_basic::start () -{ - return true; // nop -} - -bool -usrp_basic::stop () -{ - return true; // nop -} - -void -usrp_basic::set_usb_data_rate (int usb_data_rate) -{ - d_usb_data_rate = usb_data_rate; - d_bytes_per_poll = (int) (usb_data_rate * POLLING_INTERVAL); -} - -bool -usrp_basic::write_aux_dac (int slot, int which_dac, int value) -{ - return usrp_write_aux_dac (d_udh, slot, which_dac, value); -} - -bool -usrp_basic::read_aux_adc (int slot, int which_adc, int *value) -{ - return usrp_read_aux_adc (d_udh, slot, which_adc, value); -} - -int -usrp_basic::read_aux_adc (int slot, int which_adc) -{ - int value; - if (!read_aux_adc (slot, which_adc, &value)) - return READ_FAILED; - - return value; -} - -bool -usrp_basic::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) -{ - return usrp_eeprom_write (d_udh, i2c_addr, eeprom_offset, buf.data (), buf.size ()); -} - -std::string -usrp_basic::read_eeprom (int i2c_addr, int eeprom_offset, int len) -{ - if (len <= 0) - return ""; - - char buf[len]; - - if (!usrp_eeprom_read (d_udh, i2c_addr, eeprom_offset, buf, len)) - return ""; - - return std::string (buf, len); -} - -bool -usrp_basic::write_i2c (int i2c_addr, const std::string buf) -{ - return usrp_i2c_write (d_udh, i2c_addr, buf.data (), buf.size ()); -} - -std::string -usrp_basic::read_i2c (int i2c_addr, int len) -{ - if (len <= 0) - return ""; - - char buf[len]; - - if (!usrp_i2c_read (d_udh, i2c_addr, buf, len)) - return ""; - - return std::string (buf, len); -} - -std::string -usrp_basic::serial_number() -{ - return usrp_serial_number(d_udh); -} - -// ---------------------------------------------------------------- - -bool -usrp_basic::set_adc_offset (int which, int offset) -{ - if (which < 0 || which > 3) - return false; - - return _write_fpga_reg (FR_ADC_OFFSET_0 + which, offset); -} - -bool -usrp_basic::set_dac_offset (int which, int offset, int offset_pin) -{ - if (which < 0 || which > 3) - return false; - - int which_codec = which >> 1; - int tx_a = (which & 0x1) == 0; - int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1); - int hi = (offset >> 2); - bool ok; - - if (tx_a){ - ok = _write_9862 (which_codec, REG_TX_A_OFFSET_LO, lo); - ok &= _write_9862 (which_codec, REG_TX_A_OFFSET_HI, hi); - } - else { - ok = _write_9862 (which_codec, REG_TX_B_OFFSET_LO, lo); - ok &= _write_9862 (which_codec, REG_TX_B_OFFSET_HI, hi); - } - return ok; -} - -bool -usrp_basic::set_adc_buffer_bypass (int which, bool bypass) -{ - if (which < 0 || which > 3) - return false; - - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; - - unsigned char cur_rx; - unsigned char cur_pwr_dn; - - // If the input buffer is bypassed, we need to power it down too. - - bool ok = _read_9862 (codec, reg, &cur_rx); - ok &= _read_9862 (codec, REG_RX_PWR_DN, &cur_pwr_dn); - if (!ok) - return false; - - if (bypass){ - cur_rx |= RX_X_BYPASS_INPUT_BUFFER; - cur_pwr_dn |= ((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B; - } - else { - cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER; - cur_pwr_dn &= ~(((which & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); - } - - ok &= _write_9862 (codec, reg, cur_rx); - ok &= _write_9862 (codec, REG_RX_PWR_DN, cur_pwr_dn); - return ok; -} - -// ---------------------------------------------------------------- - -bool -usrp_basic::_write_fpga_reg (int regno, int value) -{ - if (d_verbose){ - fprintf (stdout, "_write_fpga_reg(%3d, 0x%08x)\n", regno, value); - fflush (stdout); - } - - if (regno >= 0 && regno < MAX_REGS) - d_fpga_shadows[regno] = value; - - return usrp_write_fpga_reg (d_udh, regno, value); -} - -bool -usrp_basic::_write_fpga_reg_masked (int regno, int value, int mask) -{ - //Only use this for registers who actually use a mask in the verilog firmware, like FR_RX_MASTER_SLAVE - //value is a 16 bits value and mask is a 16 bits mask - if (d_verbose){ - fprintf (stdout, "_write_fpga_reg_masked(%3d, 0x%04x,0x%04x)\n", regno, value, mask); - fflush (stdout); - } - - if (regno >= 0 && regno < MAX_REGS) - d_fpga_shadows[regno] = value; - - return usrp_write_fpga_reg (d_udh, regno, (value & 0xffff) | ((mask & 0xffff)<<16)); -} - - -bool -usrp_basic::_read_fpga_reg (int regno, int *value) -{ - return usrp_read_fpga_reg (d_udh, regno, value); -} - -int -usrp_basic::_read_fpga_reg (int regno) -{ - int value; - if (!_read_fpga_reg (regno, &value)) - return READ_FAILED; - return value; -} - -bool -usrp_basic::_write_9862 (int which_codec, int regno, unsigned char value) -{ - if (0 && d_verbose){ - // FIXME really want to enable logging in usrp_prims:usrp_9862_write - fprintf(stdout, "_write_9862(codec = %d, regno = %2d, val = 0x%02x)\n", which_codec, regno, value); - fflush(stdout); - } - - return usrp_9862_write (d_udh, which_codec, regno, value); -} - - -bool -usrp_basic::_read_9862 (int which_codec, int regno, unsigned char *value) const -{ - return usrp_9862_read (d_udh, which_codec, regno, value); -} - -int -usrp_basic::_read_9862 (int which_codec, int regno) const -{ - unsigned char value; - if (!_read_9862 (which_codec, regno, &value)) - return READ_FAILED; - return value; -} - -bool -usrp_basic::_write_spi (int optional_header, int enables, int format, std::string buf) -{ - return usrp_spi_write (d_udh, optional_header, enables, format, - buf.data(), buf.size()); -} - -std::string -usrp_basic::_read_spi (int optional_header, int enables, int format, int len) -{ - if (len <= 0) - return ""; - - char buf[len]; - - if (!usrp_spi_read (d_udh, optional_header, enables, format, buf, len)) - return ""; - - return std::string (buf, len); -} - - -bool -usrp_basic::_set_led (int which, bool on) -{ - return usrp_set_led (d_udh, which, on); -} - -//////////////////////////////////////////////////////////////// -// -// usrp_basic_rx -// -//////////////////////////////////////////////////////////////// - -static unsigned char rx_init_regs[] = { - REG_RX_PWR_DN, 0, - REG_RX_A, 0, // minimum gain = 0x00 (max gain = 0x14) - REG_RX_B, 0, // minimum gain = 0x00 (max gain = 0x14) - REG_RX_MISC, (RX_MISC_HS_DUTY_CYCLE | RX_MISC_CLK_DUTY), - REG_RX_IF, (RX_IF_USE_CLKOUT1 - | RX_IF_2S_COMP), - REG_RX_DIGITAL, (RX_DIGITAL_2_CHAN) -}; - - -usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename - ) - : usrp_basic (which_board, open_rx_interface, fpga_filename, firmware_filename), - d_devhandle (0), d_ephandle (0), - d_bytes_seen (0), d_first_read (true), - d_rx_enable (false) -{ - // initialize rx specific registers - - if (!usrp_9862_write_many_all (d_udh, rx_init_regs, sizeof (rx_init_regs))){ - fprintf (stderr, "usrp_basic_rx: failed to init AD9862 RX regs\n"); - throw std::runtime_error ("usrp_basic_rx/init_9862"); - } - - if (0){ - // FIXME power down 2nd codec rx path - usrp_9862_write (d_udh, 1, REG_RX_PWR_DN, 0x1); // power down everything - } - - // Reset the rx path and leave it disabled. - set_rx_enable (false); - usrp_set_fpga_rx_reset (d_udh, true); - usrp_set_fpga_rx_reset (d_udh, false); - - set_fpga_rx_sample_rate_divisor (2); // usually correct - - set_dc_offset_cl_enable(0xf, 0xf); // enable DC offset removal control loops - - probe_rx_slots (false); - - // check fusb buffering parameters - - if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) - throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); - - if (fusb_nblocks < 0) - throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); - - if (fusb_block_size == 0) - fusb_block_size = FUSB_BLOCK_SIZE; - - if (fusb_nblocks == 0) - fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); - - d_devhandle = fusb_sysconfig::make_devhandle (d_udh); - d_ephandle = d_devhandle->make_ephandle (USRP_RX_ENDPOINT, true, - fusb_block_size, fusb_nblocks); - - _write_fpga_reg(FR_ATR_MASK_1, 0); // zero Rx side Auto Transmit/Receive regs - _write_fpga_reg(FR_ATR_TXVAL_1, 0); - _write_fpga_reg(FR_ATR_RXVAL_1, 0); - _write_fpga_reg(FR_ATR_MASK_3, 0); - _write_fpga_reg(FR_ATR_TXVAL_3, 0); - _write_fpga_reg(FR_ATR_RXVAL_3, 0); -} - -static unsigned char rx_fini_regs[] = { - REG_RX_PWR_DN, 0x1 // power down everything -}; - -usrp_basic_rx::~usrp_basic_rx () -{ - if (!set_rx_enable (false)){ - fprintf (stderr, "usrp_basic_rx: set_fpga_rx_enable failed\n"); - usb_strerror (); - } - - d_ephandle->stop (); - delete d_ephandle; - delete d_devhandle; - - if (!usrp_9862_write_many_all (d_udh, rx_fini_regs, sizeof (rx_fini_regs))){ - fprintf (stderr, "usrp_basic_rx: failed to fini AD9862 RX regs\n"); - } -} - - -bool -usrp_basic_rx::start () -{ - if (!usrp_basic::start ()) // invoke parent's method - return false; - - // fire off reads before asserting rx_enable - - if (!d_ephandle->start ()){ - fprintf (stderr, "usrp_basic_rx: failed to start end point streaming"); - usb_strerror (); - return false; - } - - if (!set_rx_enable (true)){ - fprintf (stderr, "usrp_basic_rx: set_rx_enable failed\n"); - usb_strerror (); - return false; - } - - return true; -} - -bool -usrp_basic_rx::stop () -{ - bool ok = usrp_basic::stop(); - - if (!d_ephandle->stop()){ - fprintf (stderr, "usrp_basic_rx: failed to stop end point streaming"); - usb_strerror (); - ok = false; - } - if (!set_rx_enable(false)){ - fprintf (stderr, "usrp_basic_rx: set_rx_enable(false) failed\n"); - usb_strerror (); - ok = false; - } - return false; -} - -usrp_basic_rx * -usrp_basic_rx::make (int which_board, int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename) -{ - usrp_basic_rx *u = 0; - - try { - u = new usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); - return u; - } - catch (...){ - delete u; - return 0; - } - - return u; -} - -bool -usrp_basic_rx::set_fpga_rx_sample_rate_divisor (unsigned int div) -{ - return _write_fpga_reg (FR_RX_SAMPLE_RATE_DIV, div - 1); -} - - -/* - * \brief read data from the D/A's via the FPGA. - * \p len must be a multiple of 512 bytes. - * - * \returns the number of bytes read, or -1 on error. - * - * If overrun is non-NULL it will be set true iff an RX overrun is detected. - */ -int -usrp_basic_rx::read (void *buf, int len, bool *overrun) -{ - int r; - - if (overrun) - *overrun = false; - - if (len < 0 || (len % 512) != 0){ - fprintf (stderr, "usrp_basic_rx::read: invalid length = %d\n", len); - return -1; - } - - r = d_ephandle->read (buf, len); - if (r > 0) - d_bytes_seen += r; - - /* - * In many cases, the FPGA reports an rx overrun right after we - * enable the Rx path. If this is our first read, check for the - * overrun to clear the condition, then ignore the result. - */ - if (0 && d_first_read){ // FIXME - d_first_read = false; - bool bogus_overrun; - usrp_check_rx_overrun (d_udh, &bogus_overrun); - } - - if (overrun != 0 && d_bytes_seen >= d_bytes_per_poll){ - d_bytes_seen = 0; - if (!usrp_check_rx_overrun (d_udh, overrun)){ - fprintf (stderr, "usrp_basic_rx: usrp_check_rx_overrun failed\n"); - usb_strerror (); - } - } - - return r; -} - -bool -usrp_basic_rx::set_rx_enable (bool on) -{ - d_rx_enable = on; - return usrp_set_fpga_rx_enable (d_udh, on); -} - -// conditional disable, return prev state -bool -usrp_basic_rx::disable_rx () -{ - bool enabled = rx_enable (); - if (enabled) - set_rx_enable (false); - return enabled; -} - -// conditional set -void -usrp_basic_rx::restore_rx (bool on) -{ - if (on != rx_enable ()) - set_rx_enable (on); -} - -bool -usrp_basic_rx::set_pga (int which, double gain) -{ - if (which < 0 || which > 3) - return false; - - gain = std::max (pga_min (), gain); - gain = std::min (pga_max (), gain); - - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; - - // read current value to get input buffer bypass flag. - unsigned char cur_rx; - if (!_read_9862 (codec, reg, &cur_rx)) - return false; - - int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); - - cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f); - return _write_9862 (codec, reg, cur_rx); -} - -double -usrp_basic_rx::pga (int which) const -{ - if (which < 0 || which > 3) - return READ_FAILED; - - int codec = which >> 1; - int reg = (which & 1) == 0 ? REG_RX_A : REG_RX_B; - unsigned char v; - bool ok = _read_9862 (codec, reg, &v); - if (!ok) - return READ_FAILED; - - return (pga_db_per_step() * (v & 0x1f)) + pga_min(); -} - -static int -slot_id_to_oe_reg (int slot_id) -{ - static int reg[4] = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 }; - assert (0 <= slot_id && slot_id < 4); - return reg[slot_id]; -} - -static int -slot_id_to_io_reg (int slot_id) -{ - static int reg[4] = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 }; - assert (0 <= slot_id && slot_id < 4); - return reg[slot_id]; -} - -void -usrp_basic_rx::probe_rx_slots (bool verbose) -{ - struct usrp_dboard_eeprom eeprom; - static int slot_id_map[2] = { SLOT_RX_A, SLOT_RX_B }; - static const char *slot_name[2] = { "RX d'board A", "RX d'board B" }; - - for (int i = 0; i < 2; i++){ - int slot_id = slot_id_map [i]; - const char *msg = 0; - usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); - - switch (s){ - case UDBE_OK: - d_dbid[i] = eeprom.id; - msg = usrp_dbid_to_string (eeprom.id).c_str (); - set_adc_offset (2*i+0, eeprom.offset[0]); - set_adc_offset (2*i+1, eeprom.offset[1]); - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_NO_EEPROM: - d_dbid[i] = -1; - msg = ""; - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_INVALID_EEPROM: - d_dbid[i] = -2; - msg = "Invalid EEPROM contents"; - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_BAD_SLOT: - default: - assert (0); - } - - if (verbose){ - fflush (stdout); - fprintf (stderr, "%s: %s\n", slot_name[i], msg); - } - } -} - -bool -usrp_basic_rx::_write_oe (int which_dboard, int value, int mask) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); -} - -bool -usrp_basic_rx::write_io (int which_dboard, int value, int mask) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); -} - -bool -usrp_basic_rx::read_io (int which_dboard, int *value) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - int t; - int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) - bool ok = _read_fpga_reg (reg, &t); - if (!ok) - return false; - - *value = (t >> 16) & 0xffff; // FIXME, more magic - return true; -} - -int -usrp_basic_rx::read_io (int which_dboard) -{ - int value; - if (!read_io (which_dboard, &value)) - return READ_FAILED; - return value; -} - -bool -usrp_basic_rx::write_aux_dac (int which_dboard, int which_dac, int value) -{ - return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), - which_dac, value); -} - -bool -usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc, int *value) -{ - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), - which_adc, value); -} - -int -usrp_basic_rx::read_aux_adc (int which_dboard, int which_adc) -{ - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); -} - -int -usrp_basic_rx::block_size () const { return d_ephandle->block_size(); } - -bool -usrp_basic_rx::set_dc_offset_cl_enable(int bits, int mask) -{ - return _write_fpga_reg(FR_DC_OFFSET_CL_EN, - (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); -} - -//////////////////////////////////////////////////////////////// -// -// usrp_basic_tx -// -//////////////////////////////////////////////////////////////// - - -// -// DAC input rate 64 MHz interleaved for a total input rate of 128 MHz -// DAC input is latched on rising edge of CLKOUT2 -// NCO is disabled -// interpolate 2x -// coarse modulator disabled -// - -static unsigned char tx_init_regs[] = { - REG_TX_PWR_DN, 0, - REG_TX_A_OFFSET_LO, 0, - REG_TX_A_OFFSET_HI, 0, - REG_TX_B_OFFSET_LO, 0, - REG_TX_B_OFFSET_HI, 0, - REG_TX_A_GAIN, (TX_X_GAIN_COARSE_FULL | 0), - REG_TX_B_GAIN, (TX_X_GAIN_COARSE_FULL | 0), - REG_TX_PGA, 0xff, // maximum gain (0 dB) - REG_TX_MISC, 0, - REG_TX_IF, (TX_IF_USE_CLKOUT1 - | TX_IF_I_FIRST - | TX_IF_INV_TX_SYNC - | TX_IF_2S_COMP - | TX_IF_INTERLEAVED), - REG_TX_DIGITAL, (TX_DIGITAL_2_DATA_PATHS - | TX_DIGITAL_INTERPOLATE_4X), - REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO - | TX_MODULATOR_COARSE_MODULATION_NONE), - REG_TX_NCO_FTW_7_0, 0, - REG_TX_NCO_FTW_15_8, 0, - REG_TX_NCO_FTW_23_16, 0 -}; - -usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename) - : usrp_basic (which_board, open_tx_interface, fpga_filename, firmware_filename), - d_devhandle (0), d_ephandle (0), - d_bytes_seen (0), d_first_write (true), - d_tx_enable (false) -{ - if (!usrp_9862_write_many_all (d_udh, tx_init_regs, sizeof (tx_init_regs))){ - fprintf (stderr, "usrp_basic_tx: failed to init AD9862 TX regs\n"); - throw std::runtime_error ("usrp_basic_tx/init_9862"); - } - - if (0){ - // FIXME power down 2nd codec tx path - usrp_9862_write (d_udh, 1, REG_TX_PWR_DN, - (TX_PWR_DN_TX_DIGITAL - | TX_PWR_DN_TX_ANALOG_BOTH)); - } - - // Reset the tx path and leave it disabled. - set_tx_enable (false); - usrp_set_fpga_tx_reset (d_udh, true); - usrp_set_fpga_tx_reset (d_udh, false); - - set_fpga_tx_sample_rate_divisor (4); // we're using interp x4 - - probe_tx_slots (false); - - // check fusb buffering parameters - - if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) - throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); - - if (fusb_nblocks < 0) - throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); - - if (fusb_block_size == 0) - fusb_block_size = FUSB_BLOCK_SIZE; - - if (fusb_nblocks == 0) - fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); - - d_devhandle = fusb_sysconfig::make_devhandle (d_udh); - d_ephandle = d_devhandle->make_ephandle (USRP_TX_ENDPOINT, false, - fusb_block_size, fusb_nblocks); - - _write_fpga_reg(FR_ATR_MASK_0, 0); // zero Tx side Auto Transmit/Receive regs - _write_fpga_reg(FR_ATR_TXVAL_0, 0); - _write_fpga_reg(FR_ATR_RXVAL_0, 0); - _write_fpga_reg(FR_ATR_MASK_2, 0); - _write_fpga_reg(FR_ATR_TXVAL_2, 0); - _write_fpga_reg(FR_ATR_RXVAL_2, 0); - _write_fpga_reg(FR_ATR_TX_DELAY, 0); - _write_fpga_reg(FR_ATR_RX_DELAY, 0); -} - - -static unsigned char tx_fini_regs[] = { - REG_TX_PWR_DN, (TX_PWR_DN_TX_DIGITAL - | TX_PWR_DN_TX_ANALOG_BOTH), - REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO - | TX_MODULATOR_COARSE_MODULATION_NONE) -}; - -usrp_basic_tx::~usrp_basic_tx () -{ - d_ephandle->stop (); - delete d_ephandle; - delete d_devhandle; - - if (!usrp_9862_write_many_all (d_udh, tx_fini_regs, sizeof (tx_fini_regs))){ - fprintf (stderr, "usrp_basic_tx: failed to fini AD9862 TX regs\n"); - } -} - -bool -usrp_basic_tx::start () -{ - if (!usrp_basic::start ()) - return false; - - if (!set_tx_enable (true)){ - fprintf (stderr, "usrp_basic_tx: set_tx_enable failed\n"); - usb_strerror (); - return false; - } - - if (!d_ephandle->start ()){ - fprintf (stderr, "usrp_basic_tx: failed to start end point streaming"); - usb_strerror (); - return false; - } - - return true; -} - -bool -usrp_basic_tx::stop () -{ - bool ok = usrp_basic::stop (); - - if (!set_tx_enable (false)){ - fprintf (stderr, "usrp_basic_tx: set_tx_enable(false) failed\n"); - usb_strerror (); - ok = false; - } - if (!d_ephandle->stop ()){ - fprintf (stderr, "usrp_basic_tx: failed to stop end point streaming"); - usb_strerror (); - ok = false; - } - return ok; -} - -usrp_basic_tx * -usrp_basic_tx::make (int which_board, int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename) -{ - usrp_basic_tx *u = 0; - - try { - u = new usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); - return u; - } - catch (...){ - delete u; - return 0; - } - - return u; -} - -bool -usrp_basic_tx::set_fpga_tx_sample_rate_divisor (unsigned int div) -{ - return _write_fpga_reg (FR_TX_SAMPLE_RATE_DIV, div - 1); -} - -/*! - * \brief Write data to the A/D's via the FPGA. - * - * \p len must be a multiple of 512 bytes. - * \returns number of bytes written or -1 on error. - * - * if \p underrun is non-NULL, it will be set to true iff - * a transmit underrun condition is detected. - */ -int -usrp_basic_tx::write (const void *buf, int len, bool *underrun) -{ - int r; - - if (underrun) - *underrun = false; - - if (len < 0 || (len % 512) != 0){ - fprintf (stderr, "usrp_basic_tx::write: invalid length = %d\n", len); - return -1; - } - - r = d_ephandle->write (buf, len); - if (r > 0) - d_bytes_seen += r; - - /* - * In many cases, the FPGA reports an tx underrun right after we - * enable the Tx path. If this is our first write, check for the - * underrun to clear the condition, then ignore the result. - */ - if (d_first_write && d_bytes_seen >= 4 * FUSB_BLOCK_SIZE){ - d_first_write = false; - bool bogus_underrun; - usrp_check_tx_underrun (d_udh, &bogus_underrun); - } - - if (underrun != 0 && d_bytes_seen >= d_bytes_per_poll){ - d_bytes_seen = 0; - if (!usrp_check_tx_underrun (d_udh, underrun)){ - fprintf (stderr, "usrp_basic_tx: usrp_check_tx_underrun failed\n"); - usb_strerror (); - } - } - - return r; -} - -void -usrp_basic_tx::wait_for_completion () -{ - d_ephandle->wait_for_completion (); -} - -bool -usrp_basic_tx::set_tx_enable (bool on) -{ - d_tx_enable = on; - // fprintf (stderr, "set_tx_enable %d\n", on); - return usrp_set_fpga_tx_enable (d_udh, on); -} - -// conditional disable, return prev state -bool -usrp_basic_tx::disable_tx () -{ - bool enabled = tx_enable (); - if (enabled) - set_tx_enable (false); - return enabled; -} - -// conditional set -void -usrp_basic_tx::restore_tx (bool on) -{ - if (on != tx_enable ()) - set_tx_enable (on); -} - -bool -usrp_basic_tx::set_pga (int which, double gain) -{ - if (which < 0 || which > 3) - return false; - - gain = std::max (pga_min (), gain); - gain = std::min (pga_max (), gain); - - int codec = which >> 1; // 0 and 1 are same, as are 2 and 3 - - int int_gain = (int) rint ((gain - pga_min ()) / pga_db_per_step()); - - return _write_9862 (codec, REG_TX_PGA, int_gain); -} - -double -usrp_basic_tx::pga (int which) const -{ - if (which < 0 || which > 3) - return READ_FAILED; - - int codec = which >> 1; - unsigned char v; - bool ok = _read_9862 (codec, REG_TX_PGA, &v); - if (!ok) - return READ_FAILED; - - return (pga_db_per_step() * v) + pga_min(); -} - -void -usrp_basic_tx::probe_tx_slots (bool verbose) -{ - struct usrp_dboard_eeprom eeprom; - static int slot_id_map[2] = { SLOT_TX_A, SLOT_TX_B }; - static const char *slot_name[2] = { "TX d'board A", "TX d'board B" }; - - for (int i = 0; i < 2; i++){ - int slot_id = slot_id_map [i]; - const char *msg = 0; - usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); - - switch (s){ - case UDBE_OK: - d_dbid[i] = eeprom.id; - msg = usrp_dbid_to_string (eeprom.id).c_str (); - // FIXME, figure out interpretation of dc offset for TX d'boards - // offset = (eeprom.offset[1] << 16) | (eeprom.offset[0] & 0xffff); - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_NO_EEPROM: - d_dbid[i] = -1; - msg = ""; - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_INVALID_EEPROM: - d_dbid[i] = -2; - msg = "Invalid EEPROM contents"; - _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); - _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); - break; - - case UDBE_BAD_SLOT: - default: - assert (0); - } - - if (verbose){ - fflush (stdout); - fprintf (stderr, "%s: %s\n", slot_name[i], msg); - } - } -} - -bool -usrp_basic_tx::_write_oe (int which_dboard, int value, int mask) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - return _write_fpga_reg (slot_id_to_oe_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); -} - -bool -usrp_basic_tx::write_io (int which_dboard, int value, int mask) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - return _write_fpga_reg (slot_id_to_io_reg (dboard_to_slot (which_dboard)), - (mask << 16) | (value & 0xffff)); -} - -bool -usrp_basic_tx::read_io (int which_dboard, int *value) -{ - if (! (0 <= which_dboard && which_dboard <= 1)) - return false; - - int t; - int reg = which_dboard + 1; // FIXME, *very* magic number (fix in serial_io.v) - bool ok = _read_fpga_reg (reg, &t); - if (!ok) - return false; - - *value = t & 0xffff; // FIXME, more magic - return true; -} - -int -usrp_basic_tx::read_io (int which_dboard) -{ - int value; - if (!read_io (which_dboard, &value)) - return READ_FAILED; - return value; -} - -bool -usrp_basic_tx::write_aux_dac (int which_dboard, int which_dac, int value) -{ - return usrp_basic::write_aux_dac (dboard_to_slot (which_dboard), - which_dac, value); -} - -bool -usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc, int *value) -{ - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), - which_adc, value); -} - -int -usrp_basic_tx::read_aux_adc (int which_dboard, int which_adc) -{ - return usrp_basic::read_aux_adc (dboard_to_slot (which_dboard), which_adc); -} - -int -usrp_basic_tx::block_size () const { return d_ephandle->block_size(); } - diff --git a/usrp/host/lib/usrp_basic.h b/usrp/host/lib/usrp_basic.h deleted file mode 100644 index 796a0f49..00000000 --- a/usrp/host/lib/usrp_basic.h +++ /dev/null @@ -1,776 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -/* - * ---------------------------------------------------------------------- - * Mid level interface to the Universal Software Radio Peripheral (Rev 1) - * - * These classes implement the basic functionality for talking to the - * USRP. They try to be as independent of the signal processing code - * in FPGA as possible. They implement access to the low level - * peripherals on the board, provide a common way for reading and - * writing registers in the FPGA, and provide the high speed interface - * to streaming data across the USB. - * - * It is expected that subclasses will be derived that provide - * access to the functionality to a particular FPGA configuration. - * ---------------------------------------------------------------------- - */ - -#ifndef INCLUDED_USRP_BASIC_H -#define INCLUDED_USRP_BASIC_H - -#include -#include - -struct usb_dev_handle; -class fusb_devhandle; -class fusb_ephandle; - -/*! - * \brief base class for usrp operations - */ -class usrp_basic -{ -private: - // NOT IMPLEMENTED - usrp_basic (const usrp_basic &rhs); // no copy constructor - usrp_basic &operator= (const usrp_basic &rhs); // no assignment operator - - -protected: - struct usb_dev_handle *d_udh; - int d_usb_data_rate; // bytes/sec - int d_bytes_per_poll; // how often to poll for overruns - bool d_verbose; - - static const int MAX_REGS = 128; - unsigned int d_fpga_shadows[MAX_REGS]; - - usrp_basic (int which_board, - struct usb_dev_handle *open_interface (struct usb_device *dev), - const std::string fpga_filename = "", - const std::string firmware_filename = ""); - - /*! - * \brief advise usrp_basic of usb data rate (bytes/sec) - * - * N.B., this doesn't tweak any hardware. Derived classes - * should call this to inform us of the data rate whenever it's - * first set or if it changes. - * - * \param usb_data_rate bytes/sec - */ - void set_usb_data_rate (int usb_data_rate); - - /*! - * \brief Write auxiliary digital to analog converter. - * - * \param slot Which Tx or Rx slot to write. - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. - * \param which_dac [0,3] RX slots must use only 0 and 1. TX slots must use only 2 and 3. - * \param value [0,4095] - * \returns true iff successful - */ - bool write_aux_dac (int slot, int which_dac, int value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param slot 2-bit slot number. E.g., SLOT_TX_A - * \param which_adc [0,1] - * \param value return 12-bit value [0,4095] - * \returns true iff successful - */ - bool read_aux_adc (int slot, int which_adc, int *value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param slot 2-bit slot number. E.g., SLOT_TX_A - * \param which_adc [0,1] - * \returns value in the range [0,4095] if successful, else READ_FAILED. - */ - int read_aux_adc (int slot, int which_adc); - -public: - virtual ~usrp_basic (); - - /*! - * \brief return frequency of master oscillator on USRP - */ - long fpga_master_clock_freq () const { return 64000000; } - - /*! - * \returns usb data rate in bytes/sec - */ - int usb_data_rate () const { return d_usb_data_rate; } - - void set_verbose (bool on) { d_verbose = on; } - - //! magic value used on alternate register read interfaces - static const int READ_FAILED = -99999; - - /*! - * \brief Write EEPROM on motherboard or any daughterboard. - * \param i2c_addr I2C bus address of EEPROM - * \param eeprom_offset byte offset in EEPROM to begin writing - * \param buf the data to write - * \returns true iff sucessful - */ - bool write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf); - - /*! - * \brief Read EEPROM on motherboard or any daughterboard. - * \param i2c_addr I2C bus address of EEPROM - * \param eeprom_offset byte offset in EEPROM to begin reading - * \param len number of bytes to read - * \returns the data read if successful, else a zero length string. - */ - std::string read_eeprom (int i2c_addr, int eeprom_offset, int len); - - /*! - * \brief Write to I2C peripheral - * \param i2c_addr I2C bus address (7-bits) - * \param buf the data to write - * \returns true iff successful - * Writes are limited to a maximum of of 64 bytes. - */ - bool write_i2c (int i2c_addr, const std::string buf); - - /*! - * \brief Read from I2C peripheral - * \param i2c_addr I2C bus address (7-bits) - * \param len number of bytes to read - * \returns the data read if successful, else a zero length string. - * Reads are limited to a maximum of 64 bytes. - */ - std::string read_i2c (int i2c_addr, int len); - - /*! - * \brief Set ADC offset correction - * \param which which ADC[0,3]: 0 = RX_A I, 1 = RX_A Q... - * \param offset 16-bit value to subtract from raw ADC input. - */ - bool set_adc_offset (int which, int offset); - - /*! - * \brief Set DAC offset correction - * \param which which DAC[0,3]: 0 = TX_A I, 1 = TX_A Q... - * \param offset 10-bit offset value (ambiguous format: See AD9862 datasheet). - * \param offset_pin 1-bit value. If 0 offset applied to -ve differential pin; - * If 1 offset applied to +ve differential pin. - */ - bool set_dac_offset (int which, int offset, int offset_pin); - - /*! - * \brief Control ADC input buffer - * \param which which ADC[0,3] - * \param bypass if non-zero, bypass input buffer and connect input - * directly to switched cap SHA input of RxPGA. - */ - bool set_adc_buffer_bypass (int which, bool bypass); - - - /*! - * \brief return the usrp's serial number. - * - * \returns non-zero length string iff successful. - */ - std::string serial_number(); - - // ---------------------------------------------------------------- - // Low level implementation routines. - // You probably shouldn't be using these... - // - - bool _set_led (int which, bool on); - - /*! - * \brief Write FPGA register. - * \param regno 7-bit register number - * \param value 32-bit value - * \returns true iff successful - */ - bool _write_fpga_reg (int regno, int value); //< 7-bit regno, 32-bit value - - /*! - * \brief Read FPGA register. - * \param regno 7-bit register number - * \param value 32-bit value - * \returns true iff successful - */ - bool _read_fpga_reg (int regno, int *value); //< 7-bit regno, 32-bit value - - /*! - * \brief Read FPGA register. - * \param regno 7-bit register number - * \returns register value if successful, else READ_FAILED - */ - int _read_fpga_reg (int regno); - - - /*! - * \brief Write FPGA register with mask. - * \param regno 7-bit register number - * \param value 16-bit value - * \param mask 16-bit value - * \returns true if successful - * Only use this for registers who actually implement a mask in the verilog firmware, like FR_RX_MASTER_SLAVE - */ - bool _write_fpga_reg_masked (int regno, int value, int mask); - - /*! - * \brief Write AD9862 register. - * \param which_codec 0 or 1 - * \param regno 6-bit register number - * \param value 8-bit value - * \returns true iff successful - */ - bool _write_9862 (int which_codec, int regno, unsigned char value); - - /*! - * \brief Read AD9862 register. - * \param which_codec 0 or 1 - * \param regno 6-bit register number - * \param value 8-bit value - * \returns true iff successful - */ - bool _read_9862 (int which_codec, int regno, unsigned char *value) const; - - /*! - * \brief Read AD9862 register. - * \param which_codec 0 or 1 - * \param regno 6-bit register number - * \returns register value if successful, else READ_FAILED - */ - int _read_9862 (int which_codec, int regno) const; - - /*! - * \brief Write data to SPI bus peripheral. - * - * \param optional_header 0,1 or 2 bytes to write before buf. - * \param enables bitmask of peripherals to write. See usrp_spi_defs.h - * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* - * \param buf the data to write - * \returns true iff successful - * Writes are limited to a maximum of 64 bytes. - * - * If \p format specifies that optional_header bytes are present, they are - * written to the peripheral immediately prior to writing \p buf. - */ - bool _write_spi (int optional_header, int enables, int format, std::string buf); - - /* - * \brief Read data from SPI bus peripheral. - * - * \param optional_header 0,1 or 2 bytes to write before buf. - * \param enables bitmask of peripheral to read. See usrp_spi_defs.h - * \param format transaction format. See usrp_spi_defs.h SPI_FMT_* - * \param len number of bytes to read. Must be in [0,64]. - * \returns the data read if sucessful, else a zero length string. - * - * Reads are limited to a maximum of 64 bytes. - * - * If \p format specifies that optional_header bytes are present, they - * are written to the peripheral first. Then \p len bytes are read from - * the peripheral and returned. - */ - std::string _read_spi (int optional_header, int enables, int format, int len); - - /*! - * \brief Start data transfers. - * Called in base class to derived class order. - */ - bool start (); - - /*! - * \brief Stop data transfers. - * Called in base class to derived class order. - */ - bool stop (); -}; - - /*! - * \brief class for accessing the receive side of the USRP - */ -class usrp_basic_rx : public usrp_basic -{ -private: - fusb_devhandle *d_devhandle; - fusb_ephandle *d_ephandle; - int d_bytes_seen; // how many bytes we've seen - bool d_first_read; - bool d_rx_enable; - -protected: - int d_dbid[2]; // Rx daughterboard ID's - - /*! - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - usrp_basic_rx (int which_board, - int fusb_block_size=0, - int fusb_nblocks=0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); // throws if trouble - - bool set_rx_enable (bool on); - bool rx_enable () const { return d_rx_enable; } - - bool disable_rx (); // conditional disable, return prev state - void restore_rx (bool on); // conditional set - - void probe_rx_slots (bool verbose); - int dboard_to_slot (int dboard) { return (dboard << 1) | 1; } - -public: - ~usrp_basic_rx (); - - /*! - * \brief invokes constructor, returns instance or 0 if trouble - * - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - static usrp_basic_rx *make (int which_board, - int fusb_block_size=0, - int fusb_nblocks=0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); - - // MANIPULATORS - - /*! - * \brief tell the fpga the rate rx samples are coming from the A/D's - * - * div = fpga_master_clock_freq () / sample_rate - * - * sample_rate is determined by a myriad of registers - * in the 9862. That's why you have to tell us, so - * we can tell the fpga. - */ - bool set_fpga_rx_sample_rate_divisor (unsigned int div); - - /*! - * \brief read data from the D/A's via the FPGA. - * \p len must be a multiple of 512 bytes. - * - * \returns the number of bytes read, or -1 on error. - * - * If overrun is non-NULL it will be set true iff an RX overrun is detected. - */ - int read (void *buf, int len, bool *overrun); - - // ACCESSORS - - //! sampling rate of A/D converter - virtual long converter_rate() const { return fpga_master_clock_freq(); } // 64M - long adc_rate() const { return converter_rate(); } - long adc_freq() const { return converter_rate(); } //!< deprecated method name - - /*! - * \brief Return daughterboard ID for given Rx daughterboard slot [0,1]. - * - * \param which_dboard [0,1] which Rx daughterboard - * - * \return daughterboard id >= 0 if successful - * \return -1 if no daugherboard - * \return -2 if invalid EEPROM on daughterboard - */ - int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } - - // ---------------------------------------------------------------- - // routines for controlling the Programmable Gain Amplifier - /*! - * \brief Set Programmable Gain Amplifier (PGA) - * - * \param which which A/D [0,3] - * \param gain_in_db gain value (linear in dB) - * - * gain is rounded to closest setting supported by hardware. - * - * \returns true iff sucessful. - * - * \sa pga_min(), pga_max(), pga_db_per_step() - */ - bool set_pga (int which, double gain_in_db); - - /*! - * \brief Return programmable gain amplifier gain setting in dB. - * - * \param which which A/D [0,3] - */ - double pga (int which) const; - - /*! - * \brief Return minimum legal PGA gain in dB. - */ - double pga_min () const { return 0.0; } - - /*! - * \brief Return maximum legal PGA gain in dB. - */ - double pga_max () const { return 20.0; } - - /*! - * \brief Return hardware step size of PGA (linear in dB). - */ - double pga_db_per_step () const { return 20.0 / 20; } - - /*! - * \brief Write direction register (output enables) for pins that go to daughterboard. - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - * - * Each d'board has 16-bits of general purpose i/o. - * Setting the bit makes it an output from the FPGA to the d'board. - * - * This register is initialized based on a value stored in the - * d'board EEPROM. In general, you shouldn't be using this routine - * without a very good reason. Using this method incorrectly will - * kill your USRP motherboard and/or daughterboard. - */ - bool _write_oe (int which_dboard, int value, int mask); - - /*! - * \brief Write daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - */ - bool write_io (int which_dboard, int value, int mask); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value output - */ - bool read_io (int which_dboard, int *value); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \returns register value if successful, else READ_FAILED - */ - int read_io (int which_dboard); - - /*! - * \brief Write auxiliary digital to analog converter. - * - * \param which_dboard [0,1] which d'board - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. - * \param which_dac [2,3] TX slots must use only 2 and 3. - * \param value [0,4095] - * \returns true iff successful - */ - bool write_aux_dac (int which_board, int which_dac, int value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \param value return 12-bit value [0,4095] - * \returns true iff successful - */ - bool read_aux_adc (int which_dboard, int which_adc, int *value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \returns value in the range [0,4095] if successful, else READ_FAILED. - */ - int read_aux_adc (int which_dboard, int which_adc); - - /*! - * \brief returns current fusb block size - */ - int block_size() const; - - /*! - * \brief Enable/disable automatic DC offset removal control loop in FPGA - * - * \param bits which control loops to enable - * \param mask which \p bits to pay attention to - * - * If the corresponding bit is set, enable the automatic DC - * offset correction control loop. - * - *
-   * The 4 low bits are significant:
-   *
-   *   ADC0 = (1 << 0)
-   *   ADC1 = (1 << 1)
-   *   ADC2 = (1 << 2)
-   *   ADC3 = (1 << 3)
-   * 
- * - * By default the control loop is enabled on all ADC's. - */ - bool set_dc_offset_cl_enable(int bits, int mask); - - // called in base class to derived class order - bool start (); - bool stop (); -}; - - /*! - * \brief class for accessing the transmit side of the USRP - */ -class usrp_basic_tx : public usrp_basic -{ -private: - fusb_devhandle *d_devhandle; - fusb_ephandle *d_ephandle; - int d_bytes_seen; // how many bytes we've seen - bool d_first_write; - bool d_tx_enable; - - protected: - int d_dbid[2]; // Tx daughterboard ID's - - /*! - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - usrp_basic_tx (int which_board, - int fusb_block_size=0, - int fusb_nblocks=0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); // throws if trouble - - bool set_tx_enable (bool on); - bool tx_enable () const { return d_tx_enable; } - - bool disable_tx (); // conditional disable, return prev state - void restore_tx (bool on); // conditional set - - void probe_tx_slots (bool verbose); - int dboard_to_slot (int dboard) { return (dboard << 1) | 0; } - -public: - - ~usrp_basic_tx (); - - /*! - * \brief invokes constructor, returns instance or 0 if trouble - * - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - static usrp_basic_tx *make (int which_board, int fusb_block_size=0, int fusb_nblocks=0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); - - // MANIPULATORS - - /*! - * \brief tell the fpga the rate tx samples are going to the D/A's - * - * div = fpga_master_clock_freq () * 2 - * - * sample_rate is determined by a myriad of registers - * in the 9862. That's why you have to tell us, so - * we can tell the fpga. - */ - bool set_fpga_tx_sample_rate_divisor (unsigned int div); - - /*! - * \brief Write data to the A/D's via the FPGA. - * - * \p len must be a multiple of 512 bytes. - * \returns number of bytes written or -1 on error. - * - * if \p underrun is non-NULL, it will be set to true iff - * a transmit underrun condition is detected. - */ - int write (const void *buf, int len, bool *underrun); - - /* - * Block until all outstanding writes have completed. - * This is typically used to assist with benchmarking - */ - void wait_for_completion (); - - // ACCESSORS - - //! sampling rate of D/A converter - virtual long converter_rate() const { return fpga_master_clock_freq () * 2; } // 128M - long dac_rate() const { return converter_rate(); } - long dac_freq() const { return converter_rate(); } //!< deprecated method name - - /*! - * \brief Return daughterboard ID for given Tx daughterboard slot [0,1]. - * - * \return daughterboard id >= 0 if successful - * \return -1 if no daugherboard - * \return -2 if invalid EEPROM on daughterboard - */ - int daughterboard_id (int which_dboard) const { return d_dbid[which_dboard & 0x1]; } - - // ---------------------------------------------------------------- - // routines for controlling the Programmable Gain Amplifier - /*! - * \brief Set Programmable Gain Amplifier (PGA) - * - * \param which which D/A [0,3] - * \param gain_in_db gain value (linear in dB) - * - * gain is rounded to closest setting supported by hardware. - * Note that DAC 0 and DAC 1 share a gain setting as do DAC 2 and DAC 3. - * Setting DAC 0 affects DAC 1 and vice versa. Same with DAC 2 and DAC 3. - * - * \returns true iff sucessful. - * - * \sa pga_min(), pga_max(), pga_db_per_step() - */ - bool set_pga (int which, double gain_in_db); - - /*! - * \brief Return programmable gain amplifier gain in dB. - * - * \param which which D/A [0,3] - */ - double pga (int which) const; - - /*! - * \brief Return minimum legal PGA gain in dB. - */ - double pga_min () const { return -20.0; } - - /*! - * \brief Return maximum legal PGA gain in dB. - */ - double pga_max () const { return 0.0; } - - /*! - * \brief Return hardware step size of PGA (linear in dB). - */ - double pga_db_per_step () const { return 20.0/255; } - - /*! - * \brief Write direction register (output enables) for pins that go to daughterboard. - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - * - * Each d'board has 16-bits of general purpose i/o. - * Setting the bit makes it an output from the FPGA to the d'board. - * - * This register is initialized based on a value stored in the - * d'board EEPROM. In general, you shouldn't be using this routine - * without a very good reason. Using this method incorrectly will - * kill your USRP motherboard and/or daughterboard. - */ - bool _write_oe (int which_dboard, int value, int mask); - - /*! - * \brief Write daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value value to write into register - * \param mask which bits of value to write into reg - */ - bool write_io (int which_dboard, int value, int mask); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \param value return value - */ - bool read_io (int which_dboard, int *value); - - /*! - * \brief Read daughterboard i/o pin value - * - * \param which_dboard [0,1] which d'board - * \returns register value if successful, else READ_FAILED - */ - int read_io (int which_dboard); - - /*! - * \brief Write auxiliary digital to analog converter. - * - * \param which_dboard [0,1] which d'board - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's. - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's. - * \param which_dac [2,3] TX slots must use only 2 and 3. - * \param value [0,4095] - * \returns true iff successful - */ - bool write_aux_dac (int which_board, int which_dac, int value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \param value return 12-bit value [0,4095] - * \returns true iff successful - */ - bool read_aux_adc (int which_dboard, int which_adc, int *value); - - /*! - * \brief Read auxiliary analog to digital converter. - * - * \param which_dboard [0,1] which d'board - * \param which_adc [0,1] - * \returns value in the range [0,4095] if successful, else READ_FAILED. - */ - int read_aux_adc (int which_dboard, int which_adc); - - /*! - * \brief returns current fusb block size - */ - int block_size() const; - - // called in base class to derived class order - bool start (); - bool stop (); -}; - -#endif diff --git a/usrp/host/lib/usrp_bytesex.h b/usrp/host/lib/usrp_bytesex.h deleted file mode 100644 index 391d29cc..00000000 --- a/usrp/host/lib/usrp_bytesex.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ -#ifndef INCLUDED_USRP_BYTESEX_H -#define INCLUDED_USRP_BYTESEX_H - -/*! - * \brief routines for convertering between host and usrp byte order - * - * Prior to including this file, the user must include "config.h" - * which will or won't define WORDS_BIGENDIAN based on the - * result of the AC_C_BIGENDIAN autoconf test. - */ - -#ifdef HAVE_BYTESWAP_H -#include -#else -static inline unsigned short int -bswap_16 (unsigned short int x) -{ - return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)); -} -#endif - - -#ifdef WORDS_BIGENDIAN - -static inline short int -host_to_usrp_short (short int x) -{ - return bswap_16 (x); -} - -static inline short int -usrp_to_host_short (short int x) -{ - return bswap_16 (x); -} - -#else - -static inline short int -host_to_usrp_short (short int x) -{ - return x; -} - -static inline short int -usrp_to_host_short (unsigned short int x) -{ - return x; -} - -#endif - -#endif /* INCLUDED_USRP_BYTESEX_H */ diff --git a/usrp/host/lib/usrp_config.cc b/usrp/host/lib/usrp_config.cc deleted file mode 100644 index 17bfd48d..00000000 --- a/usrp/host/lib/usrp_config.cc +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include "usrp_config.h" - -int -usrp_rx_config_stream_count (unsigned int usrp_rx_config) -{ - return 1; -} - -int -usrp_tx_config_stream_count (unsigned int usrp_tx_config) -{ - return 1; -} diff --git a/usrp/host/lib/usrp_config.h b/usrp/host/lib/usrp_config.h deleted file mode 100644 index 91b398b9..00000000 --- a/usrp/host/lib/usrp_config.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef _USRP_CONFIG_H_ -#define _USRP_CONFIG_H_ - -/* - * ---------------------------------------------------------------- - * USRP Rx configurations. - * - * For now this is a placeholder, but will eventually specify the - * mapping from A/D outputs to DDC inputs (I & Q). - * - * What's implemented today is a single DDC that has its I input - * connected to ADC0 and its Q input connected to ADC1 - * ---------------------------------------------------------------- - */ - -#define USRP_RX_CONFIG_DEFAULT 0 - -/*! - * given a usrp_rx_config word, return the number of I & Q streams that - * are interleaved on the USB. - */ - -int usrp_rx_config_stream_count (unsigned int usrp_rx_config); - -/* - * USRP Tx configurations. - * - * For now this is a placeholder, but will eventually specify the - * mapping from DUC outputs to D/A inputs. - * - * What's implemented today is a single DUC that has its I output - * connected to DAC0 and its Q output connected to DAC1 - */ - -#define USRP_TX_CONFIG_DEFAULT 0 - -/*! - * given a usrp_tx_config word, return the number of I & Q streams that - * are interleaved on the USB. - */ - -int usrp_tx_config_stream_count (unsigned int usrp_tx_config); - - -#endif /* _USRP_CONFIG_H_ */ diff --git a/usrp/host/lib/usrp_dbid.dat b/usrp/host/lib/usrp_dbid.dat deleted file mode 100644 index a85d0532..00000000 --- a/usrp/host/lib/usrp_dbid.dat +++ /dev/null @@ -1,74 +0,0 @@ -# -# Copyright 2005 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# - -# This file is used to generate usrp_dbid.h, usrp_dbid.cc and usrp_dbid.py - -"Basic Tx" 0x0000 -"Basic Rx" 0x0001 -"DBS Rx" 0x0002 -"TV Rx" 0x0003 - -"Flex 400 Rx" 0x0004 -"Flex 900 Rx" 0x0005 -"Flex 1200 Rx" 0x0006 -"Flex 2400 Rx" 0x0007 - -"Flex 400 Tx" 0x0008 -"Flex 900 Tx" 0x0009 -"Flex 1200 Tx" 0x000a -"Flex 2400 Tx" 0x000b - -"TV Rx Rev 2" 0x000c -"DBS Rx Rev 2_1" 0x000d - -"LF Tx" 0x000e -"LF Rx" 0x000f - -"Flex 400 Rx MIMO A" 0x0014 -"Flex 900 Rx MIMO A" 0x0015 -"Flex 1200 Rx MIMO A" 0x0016 -"Flex 2400 Rx MIMO A" 0x0017 - -"Flex 400 Tx MIMO A" 0x0018 -"Flex 900 Tx MIMO A" 0x0019 -"Flex 1200 Tx MIMO A" 0x001a -"Flex 2400 Tx MIMO A" 0x001b - -"Flex 400 Rx MIMO B" 0x0024 -"Flex 900 Rx MIMO B" 0x0025 -"Flex 1200 Rx MIMO B" 0x0026 -"Flex 2400 Rx MIMO B" 0x0027 - -"Flex 400 Tx MIMO B" 0x0028 -"Flex 900 Tx MIMO B" 0x0029 -"Flex 1200 Tx MIMO B" 0x002a -"Flex 2400 Tx MIMO B" 0x002b - -"Flex 1800 Rx" 0x0030 -"Flex 1800 Tx" 0x0031 -"Flex 1800 Rx MIMO A" 0x0032 -"Flex 1800 Tx MIMO A" 0x0033 -"Flex 1800 Rx MIMO B" 0x0034 -"Flex 1800 Tx MIMO B" 0x0035 - -"TV Rx Rev 3" 0x0040 - -"Experimental Tx" 0xfffe -"Experimental Rx" 0xffff diff --git a/usrp/host/lib/usrp_local_sighandler.cc b/usrp/host/lib/usrp_local_sighandler.cc deleted file mode 100644 index ab6c485f..00000000 --- a/usrp/host/lib/usrp_local_sighandler.cc +++ /dev/null @@ -1,190 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -/* - * This is actually the same as gr_local_signhandler, but with a different name. - * We don't have a common library to put this in, so... - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -usrp_local_sighandler::usrp_local_sighandler (int signum, - void (*new_handler)(int)) - : d_signum (signum) -{ -#ifdef HAVE_SIGACTION - 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 (d_signum, &new_action, &d_old_action) < 0){ - perror ("sigaction (install new)"); - throw std::runtime_error ("sigaction"); - } -#endif -} - -usrp_local_sighandler::~usrp_local_sighandler () -{ -#ifdef HAVE_SIGACTION - if (sigaction (d_signum, &d_old_action, 0) < 0){ - perror ("sigaction (restore old)"); - throw std::runtime_error ("sigaction"); - } -#endif -} - -void -usrp_local_sighandler::throw_signal(int signum) throw(usrp_signal) -{ - throw usrp_signal (signum); -} - -/* - * Semi-hideous way to may a signal number into a signal name - */ - -#define SIGNAME(x) case x: return #x - -std::string -usrp_signal::name () const -{ - char tmp[128]; - - switch (signal ()){ -#ifdef SIGHUP - SIGNAME (SIGHUP); -#endif -#ifdef SIGINT - SIGNAME (SIGINT); -#endif -#ifdef SIGQUIT - SIGNAME (SIGQUIT); -#endif -#ifdef SIGILL - SIGNAME (SIGILL); -#endif -#ifdef SIGTRAP - SIGNAME (SIGTRAP); -#endif -#ifdef SIGABRT - SIGNAME (SIGABRT); -#endif -#ifdef SIGBUS - SIGNAME (SIGBUS); -#endif -#ifdef SIGFPE - SIGNAME (SIGFPE); -#endif -#ifdef SIGKILL - SIGNAME (SIGKILL); -#endif -#ifdef SIGUSR1 - SIGNAME (SIGUSR1); -#endif -#ifdef SIGSEGV - SIGNAME (SIGSEGV); -#endif -#ifdef SIGUSR2 - SIGNAME (SIGUSR2); -#endif -#ifdef SIGPIPE - SIGNAME (SIGPIPE); -#endif -#ifdef SIGALRM - SIGNAME (SIGALRM); -#endif -#ifdef SIGTERM - SIGNAME (SIGTERM); -#endif -#ifdef SIGSTKFLT - SIGNAME (SIGSTKFLT); -#endif -#ifdef SIGCHLD - SIGNAME (SIGCHLD); -#endif -#ifdef SIGCONT - SIGNAME (SIGCONT); -#endif -#ifdef SIGSTOP - SIGNAME (SIGSTOP); -#endif -#ifdef SIGTSTP - SIGNAME (SIGTSTP); -#endif -#ifdef SIGTTIN - SIGNAME (SIGTTIN); -#endif -#ifdef SIGTTOU - SIGNAME (SIGTTOU); -#endif -#ifdef SIGURG - SIGNAME (SIGURG); -#endif -#ifdef SIGXCPU - SIGNAME (SIGXCPU); -#endif -#ifdef SIGXFSZ - SIGNAME (SIGXFSZ); -#endif -#ifdef SIGVTALRM - SIGNAME (SIGVTALRM); -#endif -#ifdef SIGPROF - SIGNAME (SIGPROF); -#endif -#ifdef SIGWINCH - SIGNAME (SIGWINCH); -#endif -#ifdef SIGIO - SIGNAME (SIGIO); -#endif -#ifdef SIGPWR - SIGNAME (SIGPWR); -#endif -#ifdef SIGSYS - SIGNAME (SIGSYS); -#endif - default: -#if defined (HAVE_SNPRINTF) -#if defined (SIGRTMIN) && defined (SIGRTMAX) - if (signal () >= SIGRTMIN && signal () <= SIGRTMAX){ - snprintf (tmp, sizeof (tmp), "SIGRTMIN + %d", signal ()); - return tmp; - } -#endif - snprintf (tmp, sizeof (tmp), "SIGNAL %d", signal ()); - return tmp; -#else - return "Unknown signal"; -#endif - } -} diff --git a/usrp/host/lib/usrp_local_sighandler.h b/usrp/host/lib/usrp_local_sighandler.h deleted file mode 100644 index f1c00cd5..00000000 --- a/usrp/host/lib/usrp_local_sighandler.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_USRP_LOCAL_SIGHANDLER_H -#define INCLUDED_USRP_LOCAL_SIGHANDLER_H - -#include -#include - -/*! - * \brief Representation of signal. - */ -class usrp_signal -{ - int d_signum; -public: - usrp_signal (int signum) : d_signum (signum) {} - int signal () const { return d_signum; } - std::string name () const; -}; - - -/*! - * \brief Get and set signal handler. - * - * Constructor installs new handler, destructor reinstalls - * original value. - */ -class usrp_local_sighandler { - int d_signum; -#ifdef HAVE_SIGACTION - struct sigaction d_old_action; -#endif -public: - usrp_local_sighandler (int signum, void (*new_handler)(int)); - ~usrp_local_sighandler (); - - /* throw usrp_signal (signum) */ - static void throw_signal (int signum) throw (usrp_signal); -}; - -#endif /* INCLUDED_USRP_LOCAL_SIGHANDLER_H */ diff --git a/usrp/host/lib/usrp_prims.cc b/usrp/host/lib/usrp_prims.cc deleted file mode 100644 index f666a29f..00000000 --- a/usrp/host/lib/usrp_prims.cc +++ /dev/null @@ -1,1356 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004,2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "usrp_prims.h" -#include "usrp_commands.h" -#include "usrp_ids.h" -#include "usrp_i2c_addr.h" -#include "fpga_regs_common.h" -#include "fpga_regs_standard.h" -#include -#include -#include -#include -#include -#include -#include -#include // FIXME should check with autoconf (nanosleep) -#include -#include -#include - -extern "C" { -#include "md5.h" -}; - -#define VERBOSE 0 - -using namespace ad9862; - -static const int FIRMWARE_HASH_SLOT = 0; -static const int FPGA_HASH_SLOT = 1; - -static const int hash_slot_addr[2] = { - USRP_HASH_SLOT_0_ADDR, - USRP_HASH_SLOT_1_ADDR -}; - -static char *default_firmware_filename = "std.ihx"; -static char *default_fpga_filename = "std_2rxhb_2tx.rbf"; - -#include "std_paths.h" - -static char * -find_file (const char *filename, int hw_rev) -{ - char **sp = std_paths; - static char path[1000]; - char *s; - - s = getenv("USRP_PATH"); - if (s) { - snprintf (path, sizeof (path), "%s/rev%d/%s", s, hw_rev, filename); - if (access (path, R_OK) == 0) - return path; - } - - while (*sp){ - snprintf (path, sizeof (path), "%s/rev%d/%s", *sp, hw_rev, filename); - if (access (path, R_OK) == 0) - return path; - sp++; - } - return 0; -} - -static const char * -get_proto_filename(const std::string user_filename, const char *env_var, const char *def) -{ - if (user_filename.length() != 0) - return user_filename.c_str(); - - char *s = getenv(env_var); - if (s && *s) - return s; - - return def; -} - - -static void power_down_9862s (struct usb_dev_handle *udh); - -void -usrp_one_time_init () -{ - static bool first = true; - - if (first){ - first = false; - usb_init (); // usb library init - usb_find_busses (); - usb_find_devices (); - } -} - -void -usrp_rescan () -{ - usb_find_busses (); - usb_find_devices (); -} - - -// ---------------------------------------------------------------- -// Danger, big, fragile KLUDGE. The problem is that we want to be -// able to get from a usb_dev_handle back to a usb_device, and the -// right way to do this is buried in a non-installed include file. - -static struct usb_device * -dev_handle_to_dev (usb_dev_handle *udh) -{ - struct usb_dev_handle_kludge { - int fd; - struct usb_bus *bus; - struct usb_device *device; - }; - - return ((struct usb_dev_handle_kludge *) udh)->device; -} - -// ---------------------------------------------------------------- - -/* - * q must be a real USRP, not an FX2. Return its hardware rev number. - */ -int -usrp_hw_rev (struct usb_device *q) -{ - return q->descriptor.bcdDevice & 0x00FF; -} - -/* - * q must be a real USRP, not an FX2. Return true if it's configured. - */ -static bool -_usrp_configured_p (struct usb_device *q) -{ - return (q->descriptor.bcdDevice & 0xFF00) != 0; -} - -bool -usrp_usrp_p (struct usb_device *q) -{ - return (q->descriptor.idVendor == USB_VID_FSF - && q->descriptor.idProduct == USB_PID_FSF_USRP); -} - -bool -usrp_fx2_p (struct usb_device *q) -{ - return (q->descriptor.idVendor == USB_VID_CYPRESS - && q->descriptor.idProduct == USB_PID_CYPRESS_FX2); -} - -bool -usrp_usrp0_p (struct usb_device *q) -{ - return usrp_usrp_p (q) && usrp_hw_rev (q) == 0; -} - -bool -usrp_usrp1_p (struct usb_device *q) -{ - return usrp_usrp_p (q) && usrp_hw_rev (q) == 1; -} - -bool -usrp_usrp2_p (struct usb_device *q) -{ - return usrp_usrp_p (q) && usrp_hw_rev (q) == 2; -} - - -bool -usrp_unconfigured_usrp_p (struct usb_device *q) -{ - return usrp_usrp_p (q) && !_usrp_configured_p (q); -} - -bool -usrp_configured_usrp_p (struct usb_device *q) -{ - return usrp_usrp_p (q) && _usrp_configured_p (q); -} - -// ---------------------------------------------------------------- - -struct usb_device * -usrp_find_device (int nth, bool fx2_ok_p) -{ - struct usb_bus *p; - struct usb_device *q; - int n_found = 0; - - usrp_one_time_init (); - - p = usb_get_busses(); - while (p != NULL){ - q = p->devices; - while (q != NULL){ - if (usrp_usrp_p (q) || (fx2_ok_p && usrp_fx2_p (q))){ - if (n_found == nth) // return this one - return q; - n_found++; // keep looking - } - q = q->next; - } - p = p->next; - } - return 0; // not found -} - -static struct usb_dev_handle * -usrp_open_interface (struct usb_device *dev, int interface, int altinterface) -{ - struct usb_dev_handle *udh = usb_open (dev); - if (udh == 0) - return 0; - - if (dev != dev_handle_to_dev (udh)){ - fprintf (stderr, "%s:%d: internal error!\n", __FILE__, __LINE__); - abort (); - } - -#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) - // There's no get get_configuration function, and with some of the newer kernels - // setting the configuration, even if to the same value, hoses any other processes - // that have it open. Hence we opt to not set it at all (We've only - // got a single configuration anyway). This may hose the win32 stuff... - - // Appears to be required for libusb-win32 and Cygwin -- dew 09/20/06 - if (usb_set_configuration (udh, 1) < 0){ - /* - * Ignore this error. - * - * Seems that something changed in drivers/usb/core/devio.c:proc_setconfig such that - * it returns -EBUSY if _any_ of the interfaces of a device are open. - * We've only got a single configuration, so setting it doesn't even seem - * like it should be required. - */ - } -#endif - - if (usb_claim_interface (udh, interface) < 0){ - fprintf (stderr, "%s:usb_claim_interface: failed interface %d\n", __FUNCTION__,interface); - fprintf (stderr, "%s\n", usb_strerror()); - usb_close (udh); - return 0; - } - - if (usb_set_altinterface (udh, altinterface) < 0){ - fprintf (stderr, "%s:usb_set_alt_interface: failed\n", __FUNCTION__); - fprintf (stderr, "%s\n", usb_strerror()); - usb_release_interface (udh, interface); - usb_close (udh); - return 0; - } - - return udh; -} - -struct usb_dev_handle * -usrp_open_cmd_interface (struct usb_device *dev) -{ - return usrp_open_interface (dev, USRP_CMD_INTERFACE, USRP_CMD_ALTINTERFACE); -} - -struct usb_dev_handle * -usrp_open_rx_interface (struct usb_device *dev) -{ - return usrp_open_interface (dev, USRP_RX_INTERFACE, USRP_RX_ALTINTERFACE); -} - -struct usb_dev_handle * -usrp_open_tx_interface (struct usb_device *dev) -{ - return usrp_open_interface (dev, USRP_TX_INTERFACE, USRP_TX_ALTINTERFACE); -} - -bool -usrp_close_interface (struct usb_dev_handle *udh) -{ - // we're assuming that closing an interface automatically releases it. - return usb_close (udh) == 0; -} - -// ---------------------------------------------------------------- -// write internal ram using Cypress vendor extension - -static bool -write_internal_ram (struct usb_dev_handle *udh, unsigned char *buf, - int start_addr, size_t len) -{ - int addr; - int n; - int a; - int quanta = MAX_EP0_PKTSIZE; - - for (addr = start_addr; addr < start_addr + (int) len; addr += quanta){ - n = len + start_addr - addr; - if (n > quanta) - n = quanta; - - a = usb_control_msg (udh, 0x40, 0xA0, - addr, 0, (char *)(buf + (addr - start_addr)), n, 1000); - - if (a < 0){ - fprintf(stderr,"write_internal_ram failed: %s\n", usb_strerror()); - return false; - } - } - return true; -} - -// ---------------------------------------------------------------- -// whack the CPUCS register using the upload RAM vendor extension - -static bool -reset_cpu (struct usb_dev_handle *udh, bool reset_p) -{ - unsigned char v; - - if (reset_p) - v = 1; // hold processor in reset - else - v = 0; // release reset - - return write_internal_ram (udh, &v, 0xE600, 1); -} - -// ---------------------------------------------------------------- -// Load intel format file into cypress FX2 (8051) - -static bool -_usrp_load_firmware (struct usb_dev_handle *udh, const char *filename, - unsigned char hash[USRP_HASH_SIZE]) -{ - FILE *f = fopen (filename, "ra"); - if (f == 0){ - perror (filename); - return false; - } - - if (!reset_cpu (udh, true)) // hold CPU in reset while loading firmware - goto fail; - - - char s[1024]; - int length; - int addr; - int type; - unsigned char data[256]; - unsigned char checksum, a; - unsigned int b; - int i; - - while (!feof(f)){ - fgets(s, sizeof (s), f); /* we should not use more than 263 bytes normally */ - if(s[0]!=':'){ - fprintf(stderr,"%s: invalid line: \"%s\"\n", filename, s); - goto fail; - } - sscanf(s+1, "%02x", &length); - sscanf(s+3, "%04x", &addr); - sscanf(s+7, "%02x", &type); - - if(type==0){ - - a=length+(addr &0xff)+(addr>>8)+type; - for(i=0;i 0){ - if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_XFER, buf, n) != n) - goto fail; - } - - if (write_cmd (udh, VRQ_FPGA_LOAD, 0, FL_END, 0, 0) != 0) - goto fail; - - fclose (fp); - - if (!usrp_set_hash (udh, FPGA_HASH_SLOT, hash)) - fprintf (stderr, "usrp: failed to write fpga hash slot\n"); - - // On the rev1 USRP, the {tx,rx}_{enable,reset} bits are - // controlled over the serial bus, and hence aren't observed until - // we've got a good fpga bitstream loaded. - - usrp_set_fpga_reset (udh, 0); // fpga out of master reset - - // now these commands will work - - ok &= usrp_set_fpga_tx_enable (udh, 0); - ok &= usrp_set_fpga_rx_enable (udh, 0); - - ok &= usrp_set_fpga_tx_reset (udh, 1); // reset tx and rx paths - ok &= usrp_set_fpga_rx_reset (udh, 1); - ok &= usrp_set_fpga_tx_reset (udh, 0); // reset tx and rx paths - ok &= usrp_set_fpga_rx_reset (udh, 0); - - if (!ok) - fprintf (stderr, "usrp: failed to reset tx and/or rx path\n"); - - // Manually reset all regs except master control to zero. - // FIXME may want to remove this when we rework FPGA reset strategy. - // In the mean while, this gets us reproducible behavior. - for (int i = 0; i < FR_USER_0; i++){ - if (i == FR_MASTER_CTRL) - continue; - usrp_write_fpga_reg(udh, i, 0); - } - - power_down_9862s (udh); // on the rev1, power these down! - usrp_set_led (udh, 1, 0); // led 1 off - - return true; - - fail: - power_down_9862s (udh); // on the rev1, power these down! - fclose (fp); - return false; -} - -// ---------------------------------------------------------------- - -bool -usrp_set_led (struct usb_dev_handle *udh, int which, bool on) -{ - int r = write_cmd (udh, VRQ_SET_LED, on, which, 0, 0); - - return r == 0; -} - -bool -usrp_set_hash (struct usb_dev_handle *udh, int which, - const unsigned char hash[USRP_HASH_SIZE]) -{ - which &= 1; - - // we use the Cypress firmware down load command to jam it in. - int r = usb_control_msg (udh, 0x40, 0xa0, hash_slot_addr[which], 0, - (char *) hash, USRP_HASH_SIZE, 1000); - return r == USRP_HASH_SIZE; -} - -bool -usrp_get_hash (struct usb_dev_handle *udh, int which, - unsigned char hash[USRP_HASH_SIZE]) -{ - which &= 1; - - // we use the Cypress firmware upload command to fetch it. - int r = usb_control_msg (udh, 0xc0, 0xa0, hash_slot_addr[which], 0, - (char *) hash, USRP_HASH_SIZE, 1000); - return r == USRP_HASH_SIZE; -} - -static bool -usrp_set_switch (struct usb_dev_handle *udh, int cmd_byte, bool on) -{ - return write_cmd (udh, cmd_byte, on, 0, 0, 0) == 0; -} - - -static bool -usrp1_fpga_write (struct usb_dev_handle *udh, - int regno, int value) -{ - // on the rev1 usrp, we use the generic spi_write interface - - unsigned char buf[4]; - - buf[0] = (value >> 24) & 0xff; // MSB first - buf[1] = (value >> 16) & 0xff; - buf[2] = (value >> 8) & 0xff; - buf[3] = (value >> 0) & 0xff; - - return usrp_spi_write (udh, 0x00 | (regno & 0x7f), - SPI_ENABLE_FPGA, - SPI_FMT_MSB | SPI_FMT_HDR_1, - buf, sizeof (buf)); -} - -static bool -usrp1_fpga_read (struct usb_dev_handle *udh, - int regno, int *value) -{ - *value = 0; - unsigned char buf[4]; - - bool ok = usrp_spi_read (udh, 0x80 | (regno & 0x7f), - SPI_ENABLE_FPGA, - SPI_FMT_MSB | SPI_FMT_HDR_1, - buf, sizeof (buf)); - - if (ok) - *value = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; - - return ok; -} - - -bool -usrp_write_fpga_reg (struct usb_dev_handle *udh, int reg, int value) -{ - switch (usrp_hw_rev (dev_handle_to_dev (udh))){ - case 0: // not supported ;) - abort(); - - default: - return usrp1_fpga_write (udh, reg, value); - } -} - -bool -usrp_read_fpga_reg (struct usb_dev_handle *udh, int reg, int *value) -{ - switch (usrp_hw_rev (dev_handle_to_dev (udh))){ - case 0: // not supported ;) - abort(); - - default: - return usrp1_fpga_read (udh, reg, value); - } -} - -bool -usrp_set_fpga_reset (struct usb_dev_handle *udh, bool on) -{ - return usrp_set_switch (udh, VRQ_FPGA_SET_RESET, on); -} - -bool -usrp_set_fpga_tx_enable (struct usb_dev_handle *udh, bool on) -{ - return usrp_set_switch (udh, VRQ_FPGA_SET_TX_ENABLE, on); -} - -bool -usrp_set_fpga_rx_enable (struct usb_dev_handle *udh, bool on) -{ - return usrp_set_switch (udh, VRQ_FPGA_SET_RX_ENABLE, on); -} - -bool -usrp_set_fpga_tx_reset (struct usb_dev_handle *udh, bool on) -{ - return usrp_set_switch (udh, VRQ_FPGA_SET_TX_RESET, on); -} - -bool -usrp_set_fpga_rx_reset (struct usb_dev_handle *udh, bool on) -{ - return usrp_set_switch (udh, VRQ_FPGA_SET_RX_RESET, on); -} - - -// ---------------------------------------------------------------- -// conditional load stuff - -static bool -compute_hash (const char *filename, unsigned char hash[USRP_HASH_SIZE]) -{ - assert (USRP_HASH_SIZE == 16); - memset (hash, 0, USRP_HASH_SIZE); - - FILE *fp = fopen (filename, "rb"); - if (fp == 0){ - perror (filename); - return false; - } - int r = md5_stream (fp, hash); - fclose (fp); - - return r == 0; -} - -static usrp_load_status_t -usrp_conditionally_load_something (struct usb_dev_handle *udh, - const char *filename, - bool force, - int slot, - bool loader (struct usb_dev_handle *, - const char *, - unsigned char [USRP_HASH_SIZE])) -{ - unsigned char file_hash[USRP_HASH_SIZE]; - unsigned char usrp_hash[USRP_HASH_SIZE]; - - if (access (filename, R_OK) != 0){ - perror (filename); - return ULS_ERROR; - } - - if (!compute_hash (filename, file_hash)) - return ULS_ERROR; - - if (!force - && usrp_get_hash (udh, slot, usrp_hash) - && memcmp (file_hash, usrp_hash, USRP_HASH_SIZE) == 0) - return ULS_ALREADY_LOADED; - - bool r = loader (udh, filename, file_hash); - - if (!r) - return ULS_ERROR; - - return ULS_OK; -} - -usrp_load_status_t -usrp_load_firmware (struct usb_dev_handle *udh, - const char *filename, - bool force) -{ - return usrp_conditionally_load_something (udh, filename, force, - FIRMWARE_HASH_SLOT, - _usrp_load_firmware); -} - -usrp_load_status_t -usrp_load_fpga (struct usb_dev_handle *udh, - const char *filename, - bool force) -{ - return usrp_conditionally_load_something (udh, filename, force, - FPGA_HASH_SLOT, - _usrp_load_fpga); -} - -static usb_dev_handle * -open_nth_cmd_interface (int nth) -{ - struct usb_device *udev = usrp_find_device (nth); - if (udev == 0){ - fprintf (stderr, "usrp: failed to find usrp[%d]\n", nth); - return 0; - } - - struct usb_dev_handle *udh; - - udh = usrp_open_cmd_interface (udev); - if (udh == 0){ - // FIXME this could be because somebody else has it open. - // We should delay and retry... - fprintf (stderr, "open_nth_cmd_interface: open_cmd_interface failed\n"); - usb_strerror (); - return 0; - } - - return udh; - } - -static bool -our_nanosleep (const struct timespec *delay) -{ - struct timespec new_delay = *delay; - struct timespec remainder; - - while (1){ - int r = nanosleep (&new_delay, &remainder); - if (r == 0) - return true; - if (errno == EINTR) - new_delay = remainder; - else { - perror ("nanosleep"); - return false; - } - } -} - -static bool -mdelay (int millisecs) -{ - struct timespec ts; - ts.tv_sec = millisecs / 1000; - ts.tv_nsec = (millisecs - (1000 * ts.tv_sec)) * 1000000; - return our_nanosleep (&ts); -} - -usrp_load_status_t -usrp_load_firmware_nth (int nth, const char *filename, bool force){ - struct usb_dev_handle *udh = open_nth_cmd_interface (nth); - if (udh == 0) - return ULS_ERROR; - - usrp_load_status_t s = usrp_load_firmware (udh, filename, force); - usrp_close_interface (udh); - - switch (s){ - - case ULS_ALREADY_LOADED: // nothing changed... - return ULS_ALREADY_LOADED; - break; - - case ULS_OK: - // we loaded firmware successfully. - - // It's highly likely that the board will renumerate (simulate a - // disconnect/reconnect sequence), invalidating our current - // handle. - - // FIXME. Turn this into a loop that rescans until we refind ourselves - - struct timespec t; // delay for 1 second - t.tv_sec = 2; - t.tv_nsec = 0; - our_nanosleep (&t); - - usb_find_busses (); // rescan busses and devices - usb_find_devices (); - - return ULS_OK; - - default: - case ULS_ERROR: // some kind of problem - return ULS_ERROR; - } -} - -static void -load_status_msg (usrp_load_status_t s, const char *type, const char *filename) -{ - char *e = getenv("USRP_VERBOSE"); - bool verbose = e != 0; - - switch (s){ - case ULS_ERROR: - fprintf (stderr, "usrp: failed to load %s %s.\n", type, filename); - break; - - case ULS_ALREADY_LOADED: - if (verbose) - fprintf (stderr, "usrp: %s %s already loaded.\n", type, filename); - break; - - case ULS_OK: - if (verbose) - fprintf (stderr, "usrp: %s %s loaded successfully.\n", type, filename); - break; - } -} - -bool -usrp_load_standard_bits (int nth, bool force, - const std::string fpga_filename, - const std::string firmware_filename) -{ - usrp_load_status_t s; - const char *filename; - const char *proto_filename; - int hw_rev; - - // first, figure out what hardware rev we're dealing with - { - struct usb_device *udev = usrp_find_device (nth); - if (udev == 0){ - fprintf (stderr, "usrp: failed to find usrp[%d]\n", nth); - return false; - } - hw_rev = usrp_hw_rev (udev); - } - - // start by loading the firmware - - proto_filename = get_proto_filename(firmware_filename, "USRP_FIRMWARE", - default_firmware_filename); - filename = find_file(proto_filename, hw_rev); - if (filename == 0){ - fprintf (stderr, "Can't find firmware: %s\n", proto_filename); - return false; - } - - s = usrp_load_firmware_nth (nth, filename, force); - load_status_msg (s, "firmware", filename); - - if (s == ULS_ERROR) - return false; - - // if we actually loaded firmware, we must reload fpga ... - if (s == ULS_OK) - force = true; - - // now move on to the fpga configuration bitstream - - proto_filename = get_proto_filename(fpga_filename, "USRP_FPGA", - default_fpga_filename); - filename = find_file (proto_filename, hw_rev); - if (filename == 0){ - fprintf (stderr, "Can't find fpga bitstream: %s\n", proto_filename); - return false; - } - - struct usb_dev_handle *udh = open_nth_cmd_interface (nth); - if (udh == 0) - return false; - - s = usrp_load_fpga (udh, filename, force); - usrp_close_interface (udh); - load_status_msg (s, "fpga bitstream", filename); - - if (s == ULS_ERROR) - return false; - - return true; -} - -bool -_usrp_get_status (struct usb_dev_handle *udh, int which, bool *trouble) -{ - unsigned char status; - *trouble = true; - - if (write_cmd (udh, VRQ_GET_STATUS, 0, which, - &status, sizeof (status)) != sizeof (status)) - return false; - - *trouble = status; - return true; -} - -bool -usrp_check_rx_overrun (struct usb_dev_handle *udh, bool *overrun_p) -{ - return _usrp_get_status (udh, GS_RX_OVERRUN, overrun_p); -} - -bool -usrp_check_tx_underrun (struct usb_dev_handle *udh, bool *underrun_p) -{ - return _usrp_get_status (udh, GS_TX_UNDERRUN, underrun_p); -} - - -bool -usrp_i2c_write (struct usb_dev_handle *udh, int i2c_addr, - const void *buf, int len) -{ - if (len < 1 || len > MAX_EP0_PKTSIZE) - return false; - - return write_cmd (udh, VRQ_I2C_WRITE, i2c_addr, 0, - (unsigned char *) buf, len) == len; -} - - -bool -usrp_i2c_read (struct usb_dev_handle *udh, int i2c_addr, - void *buf, int len) -{ - if (len < 1 || len > MAX_EP0_PKTSIZE) - return false; - - return write_cmd (udh, VRQ_I2C_READ, i2c_addr, 0, - (unsigned char *) buf, len) == len; -} - -bool -usrp_spi_write (struct usb_dev_handle *udh, - int optional_header, int enables, int format, - const void *buf, int len) -{ - if (len < 0 || len > MAX_EP0_PKTSIZE) - return false; - - return write_cmd (udh, VRQ_SPI_WRITE, - optional_header, - ((enables & 0xff) << 8) | (format & 0xff), - (unsigned char *) buf, len) == len; -} - - -bool -usrp_spi_read (struct usb_dev_handle *udh, - int optional_header, int enables, int format, - void *buf, int len) -{ - if (len < 0 || len > MAX_EP0_PKTSIZE) - return false; - - return write_cmd (udh, VRQ_SPI_READ, - optional_header, - ((enables & 0xff) << 8) | (format & 0xff), - (unsigned char *) buf, len) == len; -} - -bool -usrp_9862_write (struct usb_dev_handle *udh, int which_codec, - int regno, int value) -{ - if (0) - fprintf (stderr, "usrp_9862_write which = %d, reg = %2d, val = %3d (0x%02x)\n", - which_codec, regno, value, value); - - unsigned char buf[1]; - - buf[0] = value; - - return usrp_spi_write (udh, 0x00 | (regno & 0x3f), - which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B, - SPI_FMT_MSB | SPI_FMT_HDR_1, - buf, 1); -} - -bool -usrp_9862_read (struct usb_dev_handle *udh, int which_codec, - int regno, unsigned char *value) -{ - return usrp_spi_read (udh, 0x80 | (regno & 0x3f), - which_codec == 0 ? SPI_ENABLE_CODEC_A : SPI_ENABLE_CODEC_B, - SPI_FMT_MSB | SPI_FMT_HDR_1, - value, 1); -} - -bool -usrp_9862_write_many (struct usb_dev_handle *udh, - int which_codec, - const unsigned char *buf, - int len) -{ - if (len & 0x1) - return false; // must be even - - bool result = true; - - while (len > 0){ - result &= usrp_9862_write (udh, which_codec, buf[0], buf[1]); - len -= 2; - buf += 2; - } - - return result; -} - - -bool -usrp_9862_write_many_all (struct usb_dev_handle *udh, - const unsigned char *buf, int len) -{ - // FIXME handle 2/2 and 4/4 versions - - bool result; - result = usrp_9862_write_many (udh, 0, buf, len); - result &= usrp_9862_write_many (udh, 1, buf, len); - return result; -} - -static void -power_down_9862s (struct usb_dev_handle *udh) -{ - static const unsigned char regs[] = { - REG_RX_PWR_DN, 0x01, // everything - REG_TX_PWR_DN, 0x0f, // pwr dn digital and analog_both - REG_TX_MODULATOR, 0x00 // coarse & fine modulators disabled - }; - - switch (usrp_hw_rev (dev_handle_to_dev (udh))){ - case 0: - break; - - default: - usrp_9862_write_many_all (udh, regs, sizeof (regs)); - break; - } -} - - - -static const int EEPROM_PAGESIZE = 16; - -bool -usrp_eeprom_write (struct usb_dev_handle *udh, int i2c_addr, - int eeprom_offset, const void *buf, int len) -{ - unsigned char cmd[2]; - const unsigned char *p = (unsigned char *) buf; - - // The simplest thing that could possibly work: - // all writes are single byte writes. - // - // We could speed this up using the page write feature, - // but we write so infrequently, why bother... - - while (len-- > 0){ - cmd[0] = eeprom_offset++; - cmd[1] = *p++; - bool r = usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd)); - mdelay (10); // delay 10ms worst case write time - if (!r) - return false; - } - - return true; -} - -bool -usrp_eeprom_read (struct usb_dev_handle *udh, int i2c_addr, - int eeprom_offset, void *buf, int len) -{ - unsigned char *p = (unsigned char *) buf; - - // We setup a random read by first doing a "zero byte write". - // Writes carry an address. Reads use an implicit address. - - unsigned char cmd[1]; - cmd[0] = eeprom_offset; - if (!usrp_i2c_write (udh, i2c_addr, cmd, sizeof (cmd))) - return false; - - while (len > 0){ - int n = std::min (len, MAX_EP0_PKTSIZE); - if (!usrp_i2c_read (udh, i2c_addr, p, n)) - return false; - len -= n; - p += n; - } - return true; -} - -// ---------------------------------------------------------------- - -static bool -slot_to_codec (int slot, int *which_codec) -{ - *which_codec = 0; - - switch (slot){ - case SLOT_TX_A: - case SLOT_RX_A: - *which_codec = 0; - break; - - case SLOT_TX_B: - case SLOT_RX_B: - *which_codec = 1; - break; - - default: - fprintf (stderr, "usrp_prims:slot_to_codec: invalid slot = %d\n", slot); - return false; - } - return true; -} - -static bool -tx_slot_p (int slot) -{ - switch (slot){ - case SLOT_TX_A: - case SLOT_TX_B: - return true; - - default: - return false; - } -} - -bool -usrp_write_aux_dac (struct usb_dev_handle *udh, int slot, - int which_dac, int value) -{ - int which_codec; - - if (!slot_to_codec (slot, &which_codec)) - return false; - - if (!(0 <= which_dac && which_dac < 4)){ - fprintf (stderr, "usrp_write_aux_dac: invalid dac = %d\n", which_dac); - return false; - } - - value &= 0x0fff; // mask to 12-bits - - if (which_dac == 3){ - // dac 3 is really 12-bits. Use value as is. - bool r = true; - r &= usrp_9862_write (udh, which_codec, 43, (value >> 4)); // most sig - r &= usrp_9862_write (udh, which_codec, 42, (value & 0xf) << 4); // least sig - return r; - } - else { - // dac 0, 1, and 2 are really 8 bits. - value = value >> 4; // shift value appropriately - return usrp_9862_write (udh, which_codec, 36 + which_dac, value); - } -} - - -bool -usrp_read_aux_adc (struct usb_dev_handle *udh, int slot, - int which_adc, int *value) -{ - *value = 0; - int which_codec; - - if (!slot_to_codec (slot, &which_codec)) - return false; - - if (!(0 <= which_codec && which_codec < 2)){ - fprintf (stderr, "usrp_read_aux_adc: invalid adc = %d\n", which_adc); - return false; - } - - unsigned char aux_adc_control = - AUX_ADC_CTRL_REFSEL_A // on chip reference - | AUX_ADC_CTRL_REFSEL_B; // on chip reference - - int rd_reg = 26; // base address of two regs to read for result - - // program the ADC mux bits - if (tx_slot_p (slot)) - aux_adc_control |= AUX_ADC_CTRL_SELECT_A2 | AUX_ADC_CTRL_SELECT_B2; - else { - rd_reg += 2; - aux_adc_control |= AUX_ADC_CTRL_SELECT_A1 | AUX_ADC_CTRL_SELECT_B1; - } - - // I'm not sure if we can set the mux and issue a start conversion - // in the same cycle, so let's do them one at a time. - - usrp_9862_write (udh, which_codec, 34, aux_adc_control); - - if (which_adc == 0) - aux_adc_control |= AUX_ADC_CTRL_START_A; - else { - rd_reg += 4; - aux_adc_control |= AUX_ADC_CTRL_START_B; - } - - // start the conversion - usrp_9862_write (udh, which_codec, 34, aux_adc_control); - - // read the 10-bit result back - unsigned char v_lo = 0; - unsigned char v_hi = 0; - bool r = usrp_9862_read (udh, which_codec, rd_reg, &v_lo); - r &= usrp_9862_read (udh, which_codec, rd_reg + 1, &v_hi); - - if (r) - *value = ((v_hi << 2) | ((v_lo >> 6) & 0x3)) << 2; // format as 12-bit - - return r; -} - -// ---------------------------------------------------------------- - -static int slot_to_i2c_addr (int slot) -{ - switch (slot){ - case SLOT_TX_A: return I2C_ADDR_TX_A; - case SLOT_RX_A: return I2C_ADDR_RX_A; - case SLOT_TX_B: return I2C_ADDR_TX_B; - case SLOT_RX_B: return I2C_ADDR_RX_B; - default: return -1; - } -} - -static void -set_chksum (unsigned char *buf) -{ - int sum = 0; - unsigned int i; - for (i = 0; i < DB_EEPROM_CLEN - 1; i++) - sum += buf[i]; - buf[i] = -sum; -} - -static usrp_dbeeprom_status_t -read_dboard_eeprom (struct usb_dev_handle *udh, - int slot_id, unsigned char *buf) -{ - int i2c_addr = slot_to_i2c_addr (slot_id); - if (i2c_addr == -1) - return UDBE_BAD_SLOT; - - if (!usrp_eeprom_read (udh, i2c_addr, 0, buf, DB_EEPROM_CLEN)) - return UDBE_NO_EEPROM; - - if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE) - return UDBE_INVALID_EEPROM; - - int sum = 0; - for (unsigned int i = 0; i < DB_EEPROM_CLEN; i++) - sum += buf[i]; - - if ((sum & 0xff) != 0) - return UDBE_INVALID_EEPROM; - - return UDBE_OK; -} - -usrp_dbeeprom_status_t -usrp_read_dboard_eeprom (struct usb_dev_handle *udh, - int slot_id, usrp_dboard_eeprom *eeprom) -{ - unsigned char buf[DB_EEPROM_CLEN]; - - memset (eeprom, 0, sizeof (*eeprom)); - - usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf); - if (s != UDBE_OK) - return s; - - eeprom->id = (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB]; - eeprom->oe = (buf[DB_EEPROM_OE_MSB] << 8) | buf[DB_EEPROM_OE_LSB]; - eeprom->offset[0] = (buf[DB_EEPROM_OFFSET_0_MSB] << 8) | buf[DB_EEPROM_OFFSET_0_LSB]; - eeprom->offset[1] = (buf[DB_EEPROM_OFFSET_1_MSB] << 8) | buf[DB_EEPROM_OFFSET_1_LSB]; - - return UDBE_OK; -} - -bool -usrp_write_dboard_offsets (struct usb_dev_handle *udh, int slot_id, - short offset0, short offset1) -{ - unsigned char buf[DB_EEPROM_CLEN]; - - usrp_dbeeprom_status_t s = read_dboard_eeprom (udh, slot_id, buf); - if (s != UDBE_OK) - return false; - - buf[DB_EEPROM_OFFSET_0_LSB] = (offset0 >> 0) & 0xff; - buf[DB_EEPROM_OFFSET_0_MSB] = (offset0 >> 8) & 0xff; - buf[DB_EEPROM_OFFSET_1_LSB] = (offset1 >> 0) & 0xff; - buf[DB_EEPROM_OFFSET_1_MSB] = (offset1 >> 8) & 0xff; - set_chksum (buf); - - return usrp_eeprom_write (udh, slot_to_i2c_addr (slot_id), - 0, buf, sizeof (buf)); -} - -std::string -usrp_serial_number(struct usb_dev_handle *udh) -{ - unsigned char iserial = usb_device(udh)->descriptor.iSerialNumber; - if (iserial == 0) - return ""; - - char buf[1024]; - if (usb_get_string_simple(udh, iserial, buf, sizeof(buf)) < 0) - return ""; - - return buf; -} diff --git a/usrp/host/lib/usrp_prims.h b/usrp/host/lib/usrp_prims.h deleted file mode 100644 index 97cfb4a4..00000000 --- a/usrp/host/lib/usrp_prims.h +++ /dev/null @@ -1,294 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2003,2004,2006 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -/* - * Low level primitives for directly messing with USRP hardware. - * - * If you're trying to use the USRP, you'll probably want to take a look - * at the usrp_rx and usrp_tx classes. They hide a bunch of low level details - * and provide high performance streaming i/o. - * - * This interface is built on top of libusb, which allegedly works under - * Linux, *BSD and Mac OS/X. http://libusb.sourceforge.net - */ - -#ifndef _USRP_PRIMS_H_ -#define _USRP_PRIMS_H_ - -#include -#include - -static const int USRP_HASH_SIZE = 16; - -enum usrp_load_status_t { ULS_ERROR = 0, ULS_OK, ULS_ALREADY_LOADED }; - -struct usb_dev_handle; -struct usb_device; - -/*! - * \brief initialize libusb; probe busses and devices. - * Safe to call more than once. - */ -void usrp_one_time_init (); - -/* - * force a rescan of the buses and devices - */ -void usrp_rescan (); - -/*! - * \brief locate Nth (zero based) USRP device in system. - * Return pointer or 0 if not found. - * - * The following kinds of devices are considered USRPs: - * - * unconfigured USRP (no firwmare loaded) - * configured USRP (firmware loaded) - * unconfigured Cypress FX2 (only if fx2_ok_p is true) - */ -struct usb_device *usrp_find_device (int nth, bool fx2_ok_p = false); - -bool usrp_usrp_p (struct usb_device *q); //< is this a USRP -bool usrp_usrp0_p (struct usb_device *q); //< is this a USRP Rev 0 -bool usrp_usrp1_p (struct usb_device *q); //< is this a USRP Rev 1 -bool usrp_usrp2_p (struct usb_device *q); //< is this a USRP Rev 2 -int usrp_hw_rev (struct usb_device *q); //< return h/w rev code - -bool usrp_fx2_p (struct usb_device *q); //< is this an unconfigured Cypress FX2 - -bool usrp_unconfigured_usrp_p (struct usb_device *q); //< some kind of unconfigured USRP -bool usrp_configured_usrp_p (struct usb_device *q); //< some kind of configured USRP - -/*! - * \brief given a usb_device return an instance of the appropriate usb_dev_handle - * - * These routines claim the specified interface and select the - * correct alternate interface. (USB nomenclature is totally screwed!) - * - * If interface can't be opened, or is already claimed by some other - * process, 0 is returned. - */ -struct usb_dev_handle *usrp_open_cmd_interface (struct usb_device *dev); -struct usb_dev_handle *usrp_open_rx_interface (struct usb_device *dev); -struct usb_dev_handle *usrp_open_tx_interface (struct usb_device *dev); - -/*! - * \brief close interface. - */ -bool usrp_close_interface (struct usb_dev_handle *udh); - -/*! - * \brief load intel hex format file into USRP/Cypress FX2 (8051). - * - * The filename extension is typically *.ihx - * - * Note that loading firmware may cause the device to renumerate. I.e., - * change its configuration, invalidating the current device handle. - */ - -usrp_load_status_t -usrp_load_firmware (struct usb_dev_handle *udh, const char *filename, bool force); - -/*! - * \brief load intel hex format file into USRP FX2 (8051). - * - * The filename extension is typically *.ihx - * - * Note that loading firmware may cause the device to renumerate. I.e., - * change its configuration, invalidating the current device handle. - * If the result is ULS_OK, usrp_load_firmware_nth delays 1 second - * then rescans the busses and devices. - */ -usrp_load_status_t -usrp_load_firmware_nth (int nth, const char *filename, bool force); - -/*! - * \brief load fpga configuration bitstream - */ -usrp_load_status_t -usrp_load_fpga (struct usb_dev_handle *udh, const char *filename, bool force); - -/*! - * \brief load the regular firmware and fpga bitstream in the Nth USRP. - * - * This is the normal starting point... - */ -bool usrp_load_standard_bits (int nth, bool force, - const std::string fpga_filename = "", - const std::string firmware_filename = ""); - -/*! - * \brief copy the given \p hash into the USRP hash slot \p which. - * The usrp implements two hash slots, 0 and 1. - */ -bool usrp_set_hash (struct usb_dev_handle *udh, int which, - const unsigned char hash[USRP_HASH_SIZE]); - -/*! - * \brief retrieve the \p hash from the USRP hash slot \p which. - * The usrp implements two hash slots, 0 and 1. - */ -bool usrp_get_hash (struct usb_dev_handle *udh, int which, - unsigned char hash[USRP_HASH_SIZE]); - -bool usrp_write_fpga_reg (struct usb_dev_handle *udh, int reg, int value); -bool usrp_read_fpga_reg (struct usb_dev_handle *udh, int reg, int *value); -bool usrp_set_fpga_reset (struct usb_dev_handle *udh, bool on); -bool usrp_set_fpga_tx_enable (struct usb_dev_handle *udh, bool on); -bool usrp_set_fpga_rx_enable (struct usb_dev_handle *udh, bool on); -bool usrp_set_fpga_tx_reset (struct usb_dev_handle *udh, bool on); -bool usrp_set_fpga_rx_reset (struct usb_dev_handle *udh, bool on); -bool usrp_set_led (struct usb_dev_handle *udh, int which, bool on); - -bool usrp_check_rx_overrun (struct usb_dev_handle *udh, bool *overrun_p); -bool usrp_check_tx_underrun (struct usb_dev_handle *udh, bool *underrun_p); - -// i2c_read and i2c_write are limited to a maximum len of 64 bytes. - -bool usrp_i2c_write (struct usb_dev_handle *udh, int i2c_addr, - const void *buf, int len); - -bool usrp_i2c_read (struct usb_dev_handle *udh, int i2c_addr, - void *buf, int len); - -// spi_read and spi_write are limited to a maximum of 64 bytes -// See usrp_spi_defs.h for more info - -bool usrp_spi_write (struct usb_dev_handle *udh, - int optional_header, int enables, int format, - const void *buf, int len); - -bool usrp_spi_read (struct usb_dev_handle *udh, - int optional_header, int enables, int format, - void *buf, int len); - - -bool usrp_9862_write (struct usb_dev_handle *udh, - int which_codec, // [0, 1] - int regno, // [0, 63] - int value); // [0, 255] - -bool usrp_9862_read (struct usb_dev_handle *udh, - int which_codec, // [0, 1] - int regno, // [0, 63] - unsigned char *value); // [0, 255] - -/*! - * \brief Write multiple 9862 regs at once. - * - * \p buf contains alternating register_number, register_value pairs. - * \p len must be even and is the length of buf in bytes. - */ -bool usrp_9862_write_many (struct usb_dev_handle *udh, int which_codec, - const unsigned char *buf, int len); - - -/*! - * \brief write specified regs to all 9862's in the system - */ -bool usrp_9862_write_many_all (struct usb_dev_handle *udh, - const unsigned char *buf, int len); - - -// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. -// Which EEPROM is determined by i2c_addr. See i2c_addr.h - -bool usrp_eeprom_write (struct usb_dev_handle *udh, int i2c_addr, - int eeprom_offset, const void *buf, int len); - - -// Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. -// Which EEPROM is determined by i2c_addr. See i2c_addr.h - -bool usrp_eeprom_read (struct usb_dev_handle *udh, int i2c_addr, - int eeprom_offset, void *buf, int len); - - -// Slot specific i/o routines - -/*! - * \brief write to the specified aux dac. - * - * \p slot: which Tx or Rx slot to write. - * N.B., SLOT_TX_A and SLOT_RX_A share the same AUX DAC's - * SLOT_TX_B and SLOT_RX_B share the same AUX DAC's - * - * \p which_dac: [0,3] RX slots must use only 0 and 1. - * TX slots must use only 2 and 3. - * - * AUX DAC 3 is really the 9862 sigma delta output. - * - * \p value to write to aux dac. All dacs take straight - * binary values. Although dacs 0, 1 and 2 are 8-bit and dac 3 is 12-bit, - * the interface is in terms of 12-bit values [0,4095] - */ -bool usrp_write_aux_dac (struct usb_dev_handle *uhd, int slot, - int which_dac, int value); - -/*! - * \brief Read the specified aux adc - * - * \p slot: which Tx or Rx slot to read aux dac - * \p which_adc: [0,1] which of the two adcs to read - * \p *value: return value, 12-bit straight binary. - */ -bool usrp_read_aux_adc (struct usb_dev_handle *udh, int slot, - int which_adc, int *value); - - -/*! - * \brief usrp daughterboard id to name mapping - */ -const std::string usrp_dbid_to_string (int dbid); - - -enum usrp_dbeeprom_status_t { UDBE_OK, UDBE_BAD_SLOT, UDBE_NO_EEPROM, UDBE_INVALID_EEPROM }; - -struct usrp_dboard_eeprom { - unsigned short id; // d'board identifier code - unsigned short oe; // fpga output enables: - // If bit set, i/o pin is an output from FPGA. - short offset[2]; // ADC/DAC offset correction -}; - -/*! - * \brief Read and return parsed daughterboard eeprom - */ -usrp_dbeeprom_status_t -usrp_read_dboard_eeprom (struct usb_dev_handle *udh, - int slot_id, usrp_dboard_eeprom *eeprom); - -/*! - * \brief write ADC/DAC offset calibration constants to d'board eeprom - */ -bool usrp_write_dboard_offsets (struct usb_dev_handle *udh, int slot_id, - short offset0, short offset1); - -/*! - * \brief return a usrp's serial number. - * - * Note that this only works on a configured usrp. - * \returns non-zero length string iff successful. - */ -std::string usrp_serial_number(struct usb_dev_handle *udh); - -#endif /* _USRP_PRIMS_H_ */ diff --git a/usrp/host/lib/usrp_slots.h b/usrp/host/lib/usrp_slots.h deleted file mode 100644 index c4971d16..00000000 --- a/usrp/host/lib/usrp_slots.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_USRP_SLOTS_H -#define INCLUDED_USRP_SLOTS_H - -// daughterboard slot numbers used in some calls - -static const int SLOT_TX_A = 0; -static const int SLOT_RX_A = 1; -static const int SLOT_TX_B = 2; -static const int SLOT_RX_B = 3; - -#endif /* INCLUDED_USRP_SLOTS_H */ diff --git a/usrp/host/lib/usrp_standard.cc b/usrp/host/lib/usrp_standard.cc deleted file mode 100644 index 6b4ec939..00000000 --- a/usrp/host/lib/usrp_standard.cc +++ /dev/null @@ -1,831 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include - -#include "usrp_prims.h" -#include "fpga_regs_common.h" -#include "fpga_regs_standard.h" -#include -#include -#include -#include - - -static const int OLD_CAPS_VAL = 0xaa55ff77; -static const int DEFAULT_CAPS_VAL = ((2 << bmFR_RB_CAPS_NDUC_SHIFT) - | (2 << bmFR_RB_CAPS_NDDC_SHIFT) - | bmFR_RB_CAPS_RX_HAS_HALFBAND); - -// #define USE_FPGA_TX_CORDIC - - -using namespace ad9862; - -#define NELEM(x) (sizeof (x) / sizeof (x[0])) - - -static unsigned int -compute_freq_control_word_fpga (double master_freq, double target_freq, - double *actual_freq, bool verbose) -{ - static const int NBITS = 14; - - int v = (int) rint (target_freq / master_freq * pow (2.0, 32.0)); - - if (0) - v = (v >> (32 - NBITS)) << (32 - NBITS); // keep only top NBITS - - *actual_freq = v * master_freq / pow (2.0, 32.0); - - if (verbose) - fprintf (stderr, - "compute_freq_control_word_fpga: target = %g actual = %g delta = %g\n", - target_freq, *actual_freq, *actual_freq - target_freq); - - return (unsigned int) v; -} - -// The 9862 uses an unsigned 24-bit frequency tuning word and -// a separate register to control the sign. - -static unsigned int -compute_freq_control_word_9862 (double master_freq, double target_freq, - double *actual_freq, bool verbose) -{ - double sign = 1.0; - - if (target_freq < 0) - sign = -1.0; - - int v = (int) rint (fabs (target_freq) / master_freq * pow (2.0, 24.0)); - *actual_freq = v * master_freq / pow (2.0, 24.0) * sign; - - if (verbose) - fprintf (stderr, - "compute_freq_control_word_9862: target = %g actual = %g delta = %g v = %8d\n", - target_freq, *actual_freq, *actual_freq - target_freq, v); - - return (unsigned int) v; -} - -// ---------------------------------------------------------------- - -usrp_standard_common::usrp_standard_common(usrp_basic *parent) -{ - // read new FPGA capability register - if (!parent->_read_fpga_reg(FR_RB_CAPS, &d_fpga_caps)){ - fprintf (stderr, "usrp_standard_common: failed to read FPGA cap register.\n"); - throw std::runtime_error ("usrp_standard_common::ctor"); - } - // If we don't have the cap register, set the value to what it would - // have had if we did have one ;) - if (d_fpga_caps == OLD_CAPS_VAL) - d_fpga_caps = DEFAULT_CAPS_VAL; - - if (0){ - fprintf(stdout, "has_rx_halfband = %d\n", has_rx_halfband()); - fprintf(stdout, "nddcs = %d\n", nddcs()); - fprintf(stdout, "has_tx_halfband = %d\n", has_tx_halfband()); - fprintf(stdout, "nducs = %d\n", nducs()); - } -} - -bool -usrp_standard_common::has_rx_halfband() const -{ - return (d_fpga_caps & bmFR_RB_CAPS_RX_HAS_HALFBAND) ? true : false; -} - -int -usrp_standard_common::nddcs() const -{ - return (d_fpga_caps & bmFR_RB_CAPS_NDDC_MASK) >> bmFR_RB_CAPS_NDDC_SHIFT; -} - -bool -usrp_standard_common::has_tx_halfband() const -{ - return (d_fpga_caps & bmFR_RB_CAPS_TX_HAS_HALFBAND) ? true : false; -} - -int -usrp_standard_common::nducs() const -{ - return (d_fpga_caps & bmFR_RB_CAPS_NDUC_MASK) >> bmFR_RB_CAPS_NDUC_SHIFT; -} - -// ---------------------------------------------------------------- - -static int -real_rx_mux_value (int mux, int nchan) -{ - if (mux != -1) - return mux; - - return 0x32103210; -} - -usrp_standard_rx::usrp_standard_rx (int which_board, - unsigned int decim_rate, - int nchan, int mux, int mode, - int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename - ) - : usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename), - usrp_standard_common(this), - d_nchan (1), d_sw_mux (0x0), d_hw_mux (0x0) -{ - if (!set_format(make_format())){ - fprintf (stderr, "usrp_standard_rx: set_format failed\n"); - throw std::runtime_error ("usrp_standard_rx::ctor"); - } - if (!set_nchannels (nchan)){ - fprintf (stderr, "usrp_standard_rx: set_nchannels failed\n"); - throw std::runtime_error ("usrp_standard_rx::ctor"); - } - if (!set_decim_rate (decim_rate)){ - fprintf (stderr, "usrp_standard_rx: set_decim_rate failed\n"); - throw std::runtime_error ("usrp_standard_rx::ctor"); - } - if (!set_mux (real_rx_mux_value (mux, nchan))){ - fprintf (stderr, "usrp_standard_rx: set_mux failed\n"); - throw std::runtime_error ("usrp_standard_rx::ctor"); - } - if (!set_fpga_mode (mode)){ - fprintf (stderr, "usrp_standard_rx: set_fpga_mode failed\n"); - throw std::runtime_error ("usrp_standard_rx::ctor"); - } - - for (int i = 0; i < MAX_CHAN; i++){ - set_rx_freq(i, 0); - set_ddc_phase(i, 0); - } -} - -usrp_standard_rx::~usrp_standard_rx () -{ - // fprintf(stderr, "\nusrp_standard_rx: dtor\n"); -} - -bool -usrp_standard_rx::start () -{ - if (!usrp_basic_rx::start ()) - return false; - - // add our code here - - return true; -} - -bool -usrp_standard_rx::stop () -{ - bool ok = usrp_basic_rx::stop (); - - // add our code here - - return ok; -} - -usrp_standard_rx * -usrp_standard_rx::make (int which_board, - unsigned int decim_rate, - int nchan, int mux, int mode, - int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename - ) -{ - usrp_standard_rx *u = 0; - - try { - u = new usrp_standard_rx (which_board, decim_rate, - nchan, mux, mode, - fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); - return u; - } - catch (...){ - delete u; - return 0; - } - - return u; -} - -bool -usrp_standard_rx::set_decim_rate(unsigned int rate) -{ - if ((rate & 0x1) || rate < 4 || rate > 256){ - fprintf (stderr, "usrp_standard_rx::set_decim_rate: rate must be EVEN and in [4, 256]\n"); - return false; - } - - d_decim_rate = rate; - set_usb_data_rate ((adc_rate () / rate * nchannels ()) - * (2 * sizeof (short))); - - bool s = disable_rx (); - int v = has_rx_halfband() ? d_decim_rate/2 - 1 : d_decim_rate - 1; - bool ok = _write_fpga_reg (FR_DECIM_RATE, v); - restore_rx (s); - return ok; -} - -bool usrp_standard_rx::set_nchannels (int nchan) -{ - if (!(nchan == 1 || nchan == 2 || nchan == 4)) - return false; - - if (nchan > nddcs()) - return false; - - d_nchan = nchan; - - return write_hw_mux_reg (); -} - - -// map software mux value to hw mux value -// -// Software mux value: -// -// 3 2 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 -// +-------+-------+-------+-------+-------+-------+-------+-------+ -// | Q3 | I3 | Q2 | I2 | Q1 | I1 | Q0 | I0 | -// +-------+-------+-------+-------+-------+-------+-------+-------+ -// -// Each 4-bit I field is either 0,1,2,3 -// Each 4-bit Q field is either 0,1,2,3 or 0xf (input is const zero) -// All Q's must be 0xf or none of them may be 0xf -// -// -// Hardware mux value: -// -// 3 2 1 -// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 -// +-----------------------+-------+-------+-------+-------+-+-----+ -// | must be zero | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH | -// +-----------------------+-------+-------+-------+-------+-+-----+ - - -static bool -map_sw_mux_to_hw_mux (int sw_mux, int *hw_mux_ptr) -{ - // confirm that all I's are either 0,1,2,3 - - for (int i = 0; i < 8; i += 2){ - int t = (sw_mux >> (4 * i)) & 0xf; - if (!(0 <= t && t <= 3)) - return false; - } - - // confirm that all Q's are either 0,1,2,3 or 0xf - - for (int i = 1; i < 8; i += 2){ - int t = (sw_mux >> (4 * i)) & 0xf; - if (!(t == 0xf || (0 <= t && t <= 3))) - return false; - } - - // confirm that all Q inputs are 0xf (const zero input), - // or none of them are 0xf - - int q_and = 1; - int q_or = 0; - - for (int i = 0; i < 4; i++){ - int qx_is_0xf = ((sw_mux >> (8 * i + 4)) & 0xf) == 0xf; - q_and &= qx_is_0xf; - q_or |= qx_is_0xf; - } - - if (q_and || !q_or){ // OK - int hw_mux_value = 0; - - for (int i = 0; i < 8; i++){ - int t = (sw_mux >> (4 * i)) & 0x3; - hw_mux_value |= t << (2 * i + 4); - } - - if (q_and) - hw_mux_value |= 0x8; // all Q's zero - - *hw_mux_ptr = hw_mux_value; - return true; - } - else - return false; -} - -bool -usrp_standard_rx::set_mux (int mux) -{ - if (!map_sw_mux_to_hw_mux (mux, &d_hw_mux)) - return false; - - // fprintf (stderr, "sw_mux = 0x%08x hw_mux = 0x%08x\n", mux, d_hw_mux); - - d_sw_mux = mux; - return write_hw_mux_reg (); -} - -bool -usrp_standard_rx::write_hw_mux_reg () -{ - bool s = disable_rx (); - bool ok = _write_fpga_reg (FR_RX_MUX, d_hw_mux | d_nchan); - restore_rx (s); - return ok; -} - - -bool -usrp_standard_rx::set_rx_freq (int channel, double freq) -{ - if (channel < 0 || channel > MAX_CHAN) - return false; - - unsigned int v = - compute_freq_control_word_fpga (adc_freq(), - freq, &d_rx_freq[channel], - d_verbose); - - return _write_fpga_reg (FR_RX_FREQ_0 + channel, v); -} - -unsigned int -usrp_standard_rx::decim_rate () const { return d_decim_rate; } - -int -usrp_standard_rx::nchannels () const { return d_nchan; } - -int -usrp_standard_rx::mux () const { return d_sw_mux; } - -double -usrp_standard_rx::rx_freq (int channel) const -{ - if (channel < 0 || channel >= MAX_CHAN) - return 0; - - return d_rx_freq[channel]; -} - -bool -usrp_standard_rx::set_fpga_mode (int mode) -{ - return _write_fpga_reg (FR_MODE, mode); -} - -bool -usrp_standard_rx::set_ddc_phase(int channel, int phase) -{ - if (channel < 0 || channel >= MAX_CHAN) - return false; - - return _write_fpga_reg(FR_RX_PHASE_0 + channel, phase); -} - - -// To avoid quiet failures, check for things that our code cares about. - -static bool -rx_format_is_valid(unsigned int format) -{ - int width = usrp_standard_rx::format_width(format); - int want_q = usrp_standard_rx::format_want_q(format); - - if (!(width == 8 || width == 16)) // FIXME add other widths when valid - return false; - - if (!want_q) // FIXME remove check when the rest of the code can handle I only - return false; - - return true; -} - -bool -usrp_standard_rx::set_format(unsigned int format) -{ - if (!rx_format_is_valid(format)) - return false; - - return _write_fpga_reg(FR_RX_FORMAT, format); -} - -unsigned int -usrp_standard_rx::format() const -{ - return d_fpga_shadows[FR_RX_FORMAT]; -} - -// ---------------------------------------------------------------- - -unsigned int -usrp_standard_rx::make_format(int width, int shift, bool want_q, bool bypass_halfband) -{ - unsigned int format = - (((width << bmFR_RX_FORMAT_WIDTH_SHIFT) & bmFR_RX_FORMAT_WIDTH_MASK) - | (shift << bmFR_RX_FORMAT_SHIFT_SHIFT) & bmFR_RX_FORMAT_SHIFT_MASK); - - if (want_q) - format |= bmFR_RX_FORMAT_WANT_Q; - if (bypass_halfband) - format |= bmFR_RX_FORMAT_BYPASS_HB; - - return format; -} - -int -usrp_standard_rx::format_width(unsigned int format) -{ - return (format & bmFR_RX_FORMAT_WIDTH_MASK) >> bmFR_RX_FORMAT_WIDTH_SHIFT; -} - -int -usrp_standard_rx::format_shift(unsigned int format) -{ - return (format & bmFR_RX_FORMAT_SHIFT_MASK) >> bmFR_RX_FORMAT_SHIFT_SHIFT; -} - -bool -usrp_standard_rx::format_want_q(unsigned int format) -{ - return (format & bmFR_RX_FORMAT_WANT_Q) != 0; -} - -bool -usrp_standard_rx::format_bypass_halfband(unsigned int format) -{ - return (format & bmFR_RX_FORMAT_BYPASS_HB) != 0; -} - -////////////////////////////////////////////////////////////////// - - -// tx data is timed to CLKOUT1 (64 MHz) -// interpolate 4x -// fine modulator enabled - - -static unsigned char tx_regs_use_nco[] = { - REG_TX_IF, (TX_IF_USE_CLKOUT1 - | TX_IF_I_FIRST - | TX_IF_2S_COMP - | TX_IF_INTERLEAVED), - REG_TX_DIGITAL, (TX_DIGITAL_2_DATA_PATHS - | TX_DIGITAL_INTERPOLATE_4X) -}; - - -static int -real_tx_mux_value (int mux, int nchan) -{ - if (mux != -1) - return mux; - - switch (nchan){ - case 1: - return 0x0098; - case 2: - return 0xba98; - default: - assert (0); - } -} - -usrp_standard_tx::usrp_standard_tx (int which_board, - unsigned int interp_rate, - int nchan, int mux, - int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename - ) - : usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks, fpga_filename, firmware_filename), - usrp_standard_common(this), - d_sw_mux (0x8), d_hw_mux (0x81) -{ - if (!usrp_9862_write_many_all (d_udh, tx_regs_use_nco, sizeof (tx_regs_use_nco))){ - fprintf (stderr, "usrp_standard_tx: failed to init AD9862 TX regs\n"); - throw std::runtime_error ("usrp_standard_tx::ctor"); - } - if (!set_nchannels (nchan)){ - fprintf (stderr, "usrp_standard_tx: set_nchannels failed\n"); - throw std::runtime_error ("usrp_standard_tx::ctor"); - } - if (!set_interp_rate (interp_rate)){ - fprintf (stderr, "usrp_standard_tx: set_interp_rate failed\n"); - throw std::runtime_error ("usrp_standard_tx::ctor"); - } - if (!set_mux (real_tx_mux_value (mux, nchan))){ - fprintf (stderr, "usrp_standard_tx: set_mux failed\n"); - throw std::runtime_error ("usrp_standard_tx::ctor"); - } - - for (int i = 0; i < MAX_CHAN; i++){ - d_tx_modulator_shadow[i] = (TX_MODULATOR_DISABLE_NCO - | TX_MODULATOR_COARSE_MODULATION_NONE); - d_coarse_mod[i] = CM_OFF; - set_tx_freq (i, 0); - } -} - -usrp_standard_tx::~usrp_standard_tx () -{ - // fprintf(stderr, "\nusrp_standard_tx: dtor\n"); -} - -bool -usrp_standard_tx::start () -{ - if (!usrp_basic_tx::start ()) - return false; - - // add our code here - - return true; -} - -bool -usrp_standard_tx::stop () -{ - bool ok = usrp_basic_tx::stop (); - - // add our code here - - return ok; -} - -usrp_standard_tx * -usrp_standard_tx::make (int which_board, - unsigned int interp_rate, - int nchan, int mux, - int fusb_block_size, int fusb_nblocks, - const std::string fpga_filename, - const std::string firmware_filename - ) -{ - usrp_standard_tx *u = 0; - - try { - u = new usrp_standard_tx (which_board, interp_rate, nchan, mux, - fusb_block_size, fusb_nblocks, - fpga_filename, firmware_filename); - return u; - } - catch (...){ - delete u; - return 0; - } - - return u; -} - -bool -usrp_standard_tx::set_interp_rate (unsigned int rate) -{ - // fprintf (stderr, "usrp_standard_tx::set_interp_rate\n"); - - if ((rate & 0x3) || rate < 4 || rate > 512){ - fprintf (stderr, "usrp_standard_tx::set_interp_rate: rate must be in [4, 512] and a multiple of 4.\n"); - return false; - } - - d_interp_rate = rate; - set_usb_data_rate ((dac_rate () / rate * nchannels ()) - * (2 * sizeof (short))); - - // We're using the interp by 4 feature of the 9862 so that we can - // use its fine modulator. Thus, we reduce the FPGA's interpolation rate - // by a factor of 4. - - bool s = disable_tx (); - bool ok = _write_fpga_reg (FR_INTERP_RATE, d_interp_rate/4 - 1); - restore_tx (s); - return ok; -} - -bool -usrp_standard_tx::set_nchannels (int nchan) -{ - if (!(nchan == 1 || nchan == 2)) - return false; - - if (nchan > nducs()) - return false; - - d_nchan = nchan; - return write_hw_mux_reg (); -} - -bool -usrp_standard_tx::set_mux (int mux) -{ - d_sw_mux = mux; - d_hw_mux = mux << 4; - return write_hw_mux_reg (); -} - -bool -usrp_standard_tx::write_hw_mux_reg () -{ - bool s = disable_tx (); - bool ok = _write_fpga_reg (FR_TX_MUX, d_hw_mux | d_nchan); - restore_tx (s); - return ok; -} - -#ifdef USE_FPGA_TX_CORDIC - -bool -usrp_standard_tx::set_tx_freq (int channel, double freq) -{ - if (channel < 0 || channel >= MAX_CHAN) - return false; - - // This assumes we're running the 4x on-chip interpolator. - - unsigned int v = - compute_freq_control_word_fpga (dac_freq () / 4, - freq, &d_tx_freq[channel], - d_verbose); - - return _write_fpga_reg (FR_TX_FREQ_0 + channel, v); -} - - -#else - -bool -usrp_standard_tx::set_tx_freq (int channel, double freq) -{ - if (channel < 0 || channel >= MAX_CHAN) - return false; - - // split freq into fine and coarse components - - coarse_mod_t cm; - double coarse; - - assert (dac_freq () == 128000000); - - if (freq < -44e6) // too low - return false; - else if (freq < -24e6){ // [-44, -24) - cm = CM_NEG_FDAC_OVER_4; - coarse = -dac_freq () / 4; - } - else if (freq < -8e6){ // [-24, -8) - cm = CM_NEG_FDAC_OVER_8; - coarse = -dac_freq () / 8; - } - else if (freq < 8e6){ // [-8, 8) - cm = CM_OFF; - coarse = 0; - } - else if (freq < 24e6){ // [8, 24) - cm = CM_POS_FDAC_OVER_8; - coarse = dac_freq () / 8; - } - else if (freq <= 44e6){ // [24, 44] - cm = CM_POS_FDAC_OVER_4; - coarse = dac_freq () / 4; - } - else // too high - return false; - - - set_coarse_modulator (channel, cm); // set bits in d_tx_modulator_shadow - - double fine = freq - coarse; - - - // Compute fine tuning word... - // This assumes we're running the 4x on-chip interpolator. - // (This is required to use the fine modulator.) - - unsigned int v = - compute_freq_control_word_9862 (dac_freq () / 4, - fine, &d_tx_freq[channel], d_verbose); - - d_tx_freq[channel] += coarse; // adjust actual - - unsigned char high, mid, low; - - high = (v >> 16) & 0xff; - mid = (v >> 8) & 0xff; - low = (v >> 0) & 0xff; - - bool ok = true; - - // write the fine tuning word - ok &= _write_9862 (channel, REG_TX_NCO_FTW_23_16, high); - ok &= _write_9862 (channel, REG_TX_NCO_FTW_15_8, mid); - ok &= _write_9862 (channel, REG_TX_NCO_FTW_7_0, low); - - - d_tx_modulator_shadow[channel] |= TX_MODULATOR_ENABLE_NCO; - - if (fine < 0) - d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_FINE_TUNE; - else - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_NEG_FINE_TUNE; - - ok &=_write_9862 (channel, REG_TX_MODULATOR, d_tx_modulator_shadow[channel]); - - return ok; -} -#endif - -bool -usrp_standard_tx::set_coarse_modulator (int channel, coarse_mod_t cm) -{ - if (channel < 0 || channel >= MAX_CHAN) - return false; - - switch (cm){ - case CM_NEG_FDAC_OVER_4: - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE; - break; - - case CM_NEG_FDAC_OVER_8: - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_NEG_COARSE_TUNE; - break; - - case CM_OFF: - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; - break; - - case CM_POS_FDAC_OVER_8: - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_8; - break; - - case CM_POS_FDAC_OVER_4: - d_tx_modulator_shadow[channel] &= ~TX_MODULATOR_CM_MASK; - d_tx_modulator_shadow[channel] |= TX_MODULATOR_COARSE_MODULATION_F_OVER_4; - break; - - default: - return false; - } - - d_coarse_mod[channel] = cm; - return true; -} - -unsigned int -usrp_standard_tx::interp_rate () const { return d_interp_rate; } - -int -usrp_standard_tx::nchannels () const { return d_nchan; } - -int -usrp_standard_tx::mux () const { return d_sw_mux; } - -double -usrp_standard_tx::tx_freq (int channel) const -{ - if (channel < 0 || channel >= MAX_CHAN) - return 0; - - return d_tx_freq[channel]; -} - -usrp_standard_tx::coarse_mod_t -usrp_standard_tx::coarse_modulator (int channel) const -{ - if (channel < 0 || channel >= MAX_CHAN) - return CM_OFF; - - return d_coarse_mod[channel]; -} diff --git a/usrp/host/lib/usrp_standard.h b/usrp/host/lib/usrp_standard.h deleted file mode 100644 index 6fee4b9c..00000000 --- a/usrp/host/lib/usrp_standard.h +++ /dev/null @@ -1,366 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2004 Free Software Foundation, Inc. - * - * This file is part of GNU Radio - * - * GNU Radio is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GNU Radio is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Radio; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#ifndef INCLUDED_USRP_STANDARD_H -#define INCLUDED_USRP_STANDARD_H - -#include - -class usrp_standard_common -{ - int d_fpga_caps; // capability register val - -protected: - usrp_standard_common(usrp_basic *parent); - -public: - /*! - *\brief does the FPGA implement the final Rx half-band filter? - * If it doesn't, the maximum decimation factor with proper gain - * is 1/2 of what it would otherwise be. - */ - bool has_rx_halfband() const; - - /*! - * \brief number of digital downconverters implemented in the FPGA - * This will be 0, 1, 2 or 4. - */ - int nddcs() const; - - /*! - *\brief does the FPGA implement the initial Tx half-band filter? - */ - bool has_tx_halfband() const; - - /*! - * \brief number of digital upconverters implemented in the FPGA - * This will be 0, 1, or 2. - */ - int nducs() const; -}; - -/*! - * \brief standard usrp RX class. - * - * Assumes digital down converter in FPGA - */ -class usrp_standard_rx : public usrp_basic_rx, usrp_standard_common -{ - private: - static const int MAX_CHAN = 4; - unsigned int d_decim_rate; - int d_nchan; - int d_sw_mux; - int d_hw_mux; - double d_rx_freq[MAX_CHAN]; - - protected: - usrp_standard_rx (int which_board, - unsigned int decim_rate, - int nchan = 1, - int mux = -1, - int mode = 0, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); // throws if trouble - - bool write_hw_mux_reg (); - - public: - - enum { - FPGA_MODE_NORMAL = 0x00, - FPGA_MODE_LOOPBACK = 0x01, - FPGA_MODE_COUNTING = 0x02, - FPGA_MODE_COUNTING_32BIT = 0x04 - }; - - ~usrp_standard_rx (); - - /*! - * \brief invokes constructor, returns instance or 0 if trouble - * - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - static usrp_standard_rx *make (int which_board, - unsigned int decim_rate, - int nchan = 1, - int mux = -1, - int mode = 0, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); - /*! - * \brief Set decimator rate. \p rate MUST BE EVEN and in [8, 256]. - * - * The final complex sample rate across the USB is - * adc_freq () / decim_rate () * nchannels () - */ - bool set_decim_rate (unsigned int rate); - - /*! - * \brief Set number of active channels. \p nchannels must be 1, 2 or 4. - * - * The final complex sample rate across the USB is - * adc_freq () / decim_rate () * nchannels () - */ - bool set_nchannels (int nchannels); - - /*! - * \brief Set input mux configuration. - * - * This determines which ADC (or constant zero) is connected to - * each DDC input. There are 4 DDCs. Each has two inputs. - * - *
-   * Mux value:
-   *
-   *    3                   2                   1                       
-   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-   * +-------+-------+-------+-------+-------+-------+-------+-------+
-   * |   Q3  |   I3  |   Q2  |   I2  |   Q1  |   I1  |   Q0  |   I0  |
-   * +-------+-------+-------+-------+-------+-------+-------+-------+
-   *
-   * Each 4-bit I field is either 0,1,2,3
-   * Each 4-bit Q field is either 0,1,2,3 or 0xf (input is const zero)
-   * All Q's must be 0xf or none of them may be 0xf
-   * 
- */ - bool set_mux (int mux); - - /*! - * \brief set the frequency of the digital down converter. - * - * \p channel must be in the range [0,3]. \p freq is the center - * frequency in Hz. \p freq may be either negative or postive. - * The frequency specified is quantized. Use rx_freq to retrieve - * the actual value used. - */ - bool set_rx_freq (int channel, double freq); - - /*! - * \brief set fpga mode - */ - bool set_fpga_mode (int mode); - - /*! - * \brief Set the digital down converter phase register. - * - * \param channel which ddc channel [0, 3] - * \param phase 32-bit integer phase value. - */ - bool set_ddc_phase(int channel, int phase); - - /*! - * \brief Specify Rx data format. - * - * \param format format specifier - * - * Rx data format control register - * - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-----------------------------------------+-+-+---------+-------+ - * | Reserved (Must be zero) |B|Q| WIDTH | SHIFT | - * +-----------------------------------------+-+-+---------+-------+ - * - * SHIFT specifies arithmetic right shift [0, 15] - * WIDTH specifies bit-width of I & Q samples across the USB [1, 16] (not all valid) - * Q if set deliver both I & Q, else just I - * B if set bypass half-band filter. - * - * Right now the acceptable values are: - * - * B Q WIDTH SHIFT - * 0 1 16 0 - * 0 1 8 8 - * - * More valid combos to come. - * - * Default value is 0x00000300 16-bits, 0 shift, deliver both I & Q. - */ - bool set_format(unsigned int format); - - static unsigned int make_format(int width=16, int shift=0, - bool want_q=true, bool bypass_halfband=false); - static int format_width(unsigned int format); - static int format_shift(unsigned int format); - static bool format_want_q(unsigned int format); - static bool format_bypass_halfband(unsigned int format); - - // ACCESSORS - unsigned int decim_rate () const; - double rx_freq (int channel) const; - int nchannels () const; - int mux () const; - unsigned int format () const; - - // called in base class to derived class order - bool start (); - bool stop (); -}; - -// ---------------------------------------------------------------- - -/*! - * \brief standard usrp TX class. - * - * Uses digital upconverter (coarse & fine modulators) in AD9862... - */ -class usrp_standard_tx : public usrp_basic_tx, usrp_standard_common -{ - public: - enum coarse_mod_t { - CM_NEG_FDAC_OVER_4, // -32 MHz - CM_NEG_FDAC_OVER_8, // -16 MHz - CM_OFF, - CM_POS_FDAC_OVER_8, // +16 MHz - CM_POS_FDAC_OVER_4 // +32 MHz - }; - - protected: - static const int MAX_CHAN = 2; - unsigned int d_interp_rate; - int d_nchan; - int d_sw_mux; - int d_hw_mux; - double d_tx_freq[MAX_CHAN]; - coarse_mod_t d_coarse_mod[MAX_CHAN]; - unsigned char d_tx_modulator_shadow[MAX_CHAN]; - - virtual bool set_coarse_modulator (int channel, coarse_mod_t cm); - usrp_standard_tx::coarse_mod_t coarse_modulator (int channel) const; - - protected: - usrp_standard_tx (int which_board, - unsigned int interp_rate, - int nchan = 1, - int mux = -1, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); // throws if trouble - - bool write_hw_mux_reg (); - - public: - ~usrp_standard_tx (); - - /*! - * \brief invokes constructor, returns instance or 0 if trouble - * - * \param which_board Which USRP board on usb (not particularly useful; use 0) - * \param fusb_block_size fast usb xfer block size. Must be a multiple of 512. - * Use zero for a reasonable default. - * \param fusb_nblocks number of fast usb URBs to allocate. Use zero for a reasonable default. - */ - static usrp_standard_tx *make (int which_board, - unsigned int interp_rate, - int nchan = 1, - int mux = -1, - int fusb_block_size = 0, - int fusb_nblocks = 0, - const std::string fpga_filename = "", - const std::string firmware_filename = "" - ); - - /*! - * \brief Set interpolator rate. \p rate must be in [4, 512] and a multiple of 4. - * - * The final complex sample rate across the USB is - * dac_freq () / interp_rate () * nchannels () - */ - virtual bool set_interp_rate (unsigned int rate); - - /*! - * \brief Set number of active channels. \p nchannels must be 1 or 2. - * - * The final complex sample rate across the USB is - * dac_freq () / decim_rate () * nchannels () - */ - bool set_nchannels (int nchannels); - - /*! - * \brief Set output mux configuration. - * - *
-   *     3                   2                   1                       
-   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
-   *  +-------------------------------+-------+-------+-------+-------+
-   *  |                               | DAC3  | DAC2  | DAC1  |  DAC0 |
-   *  +-------------------------------+-------+-------+-------+-------+
-   * 
-   *  There are two interpolators with complex inputs and outputs.
-   *  There are four DACs.
-   * 
-   *  Each 4-bit DACx field specifies the source for the DAC and
-   *  whether or not that DAC is enabled.  Each subfield is coded
-   *  like this: 
-   * 
-   *     3 2 1 0
-   *    +-+-----+
-   *    |E|  N  |
-   *    +-+-----+
-   * 
-   *  Where E is set if the DAC is enabled, and N specifies which
-   *  interpolator output is connected to this DAC.
-   * 
-   *   N   which interp output
-   *  ---  -------------------
-   *   0   chan 0 I
-   *   1   chan 0 Q
-   *   2   chan 1 I
-   *   3   chan 1 Q
-   * 
- */ - bool set_mux (int mux); - - /*! - * \brief set the frequency of the digital up converter. - * - * \p channel must be in the range [0,1]. \p freq is the center - * frequency in Hz. It must be in the range [-44M, 44M]. - * The frequency specified is quantized. Use tx_freq to retrieve - * the actual value used. - */ - virtual bool set_tx_freq (int channel, double freq); // chan: [0,1] - - // ACCESSORS - unsigned int interp_rate () const; - double tx_freq (int channel) const; - int nchannels () const; - int mux () const; - - // called in base class to derived class order - bool start (); - bool stop (); -}; - -#endif /* INCLUDED_USRP_STANDARD_H */ diff --git a/usrp/host/swig/Makefile.am b/usrp/host/swig/Makefile.am index 8868d1ec..35a81ea2 100644 --- a/usrp/host/swig/Makefile.am +++ b/usrp/host/swig/Makefile.am @@ -64,7 +64,7 @@ _usrp_prims_la_SOURCES = \ noinst_HEADERS = -_usrp_prims_la_LIBADD = $(top_builddir)/usrp/host/lib/libusrp.la -lstdc++ $(PYTHON_LDFLAGS) +_usrp_prims_la_LIBADD = $(USRP_LA) -lstdc++ $(PYTHON_LDFLAGS) _usrp_prims_la_LDFLAGS = $(NO_UNDEFINED) -module -avoid-version