diff options
Diffstat (limited to 'usrp/host/lib/inband')
-rw-r--r-- | usrp/host/lib/inband/Makefile.am | 92 | ||||
-rwxr-xr-x | usrp/host/lib/inband/dump_packets.py | 65 | ||||
-rwxr-xr-x | usrp/host/lib/inband/gen_test_packets.py | 87 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband.cc | 35 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband.h | 35 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband_packet_prims.cc | 161 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband_packet_prims.h | 41 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband_usrp_server.cc | 455 | ||||
-rw-r--r-- | usrp/host/lib/inband/qa_inband_usrp_server.h | 42 | ||||
-rw-r--r-- | usrp/host/lib/inband/test_inband.cc | 36 | ||||
-rw-r--r-- | usrp/host/lib/inband/usb_packet.py | 115 | ||||
-rw-r--r-- | usrp/host/lib/inband/usrp_inband_usb_packet.h | 149 | ||||
-rw-r--r-- | usrp/host/lib/inband/usrp_server.cc | 394 | ||||
-rw-r--r-- | usrp/host/lib/inband/usrp_server.h | 82 | ||||
-rw-r--r-- | usrp/host/lib/inband/usrp_server.mbh | 256 |
15 files changed, 2045 insertions, 0 deletions
diff --git a/usrp/host/lib/inband/Makefile.am b/usrp/host/lib/inband/Makefile.am new file mode 100644 index 000000000..9f6a7ab62 --- /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 000000000..ea27ca515 --- /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 000000000..1e22722d2 --- /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 000000000..00a821f39 --- /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 <qa_inband.h> +#include <qa_inband_packet_prims.h> +#include <qa_inband_usrp_server.h> + +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 000000000..92386f955 --- /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 <cppunit/TestSuite.h> + +//! 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 000000000..9d9a0c70e --- /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 <config.h> +#endif + +#include <qa_inband_packet_prims.h> +#include <cppunit/TestAssert.h> +#include <stdio.h> +#include <usrp_inband_usb_packet.h> // 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 000000000..5e60c60e7 --- /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 <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +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 000000000..2885a4d88 --- /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 <config.h> +#endif + +#include <qa_inband_usrp_server.h> +#include <cppunit/TestAssert.h> +#include <stdio.h> +#include <usrp_server.h> +#include <mb_mblock.h> +#include <mb_runtime.h> +#include <mb_protocol_class.h> +#include <mb_class_registry.h> +#include <vector> +#include <iostream> + +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<long> d_tx_chans; + std::vector<long> 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 000000000..eb0f7a3e0 --- /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 <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> + +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 000000000..49619bef2 --- /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 <cppunit/TextTestRunner.h> +#include <qa_inband.h> + +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 000000000..5ca19b790 --- /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 000000000..471bfc666 --- /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 <usrp_bytesex.h> +#include <mb_mblock.h> + +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<<FL_END_OF_BURST_SHIFT; + d_word0 = host_to_usrp_u32(word0); + } + + void set_header(int flags, int chan, int tag, int payload_len){ + uint32_t word0 = ((flags & FL_ALL_FLAGS) + | ((chan & CHAN_MASK) << CHAN_SHIFT) + | ((tag & TAG_MASK) << TAG_SHIFT) + | ((payload_len & PAYLOAD_LEN_MASK) << PAYLOAD_LEN_SHIFT)); + d_word0 = host_to_usrp_u32(word0); + } + + uint32_t timestamp() const { + return usrp_to_host_u32(d_timestamp); + } + + int rssi() const { + uint32_t word0 = usrp_to_host_u32(d_word0); + return (word0 >> 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 000000000..5041a9213 --- /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 <config.h> +#endif +#include <usrp_server.h> +#include <iostream> +#include <usrp_inband_usb_packet.h> +#include <mb_class_registry.h> +#include <vector> + +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<long>(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 000000000..87c2768e5 --- /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 <mb_mblock.h> +#include <vector> + +/*! + * \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<mb_port_sptr> 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 000000000..97bb0e664 --- /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) + ) + ) |