diff options
Diffstat (limited to 'vrt/lib')
-rw-r--r-- | vrt/lib/.gitignore | 4 | ||||
-rw-r--r-- | vrt/lib/Makefile.am | 50 | ||||
-rw-r--r-- | vrt/lib/copiers.cc | 71 | ||||
-rw-r--r-- | vrt/lib/data_handler.cc | 32 | ||||
-rw-r--r-- | vrt/lib/data_handler.h | 53 | ||||
-rw-r--r-- | vrt/lib/expanded_header.cc | 135 | ||||
-rw-r--r-- | vrt/lib/expanded_header_parse_switch_body.h | 320 | ||||
-rw-r--r-- | vrt/lib/expanded_header_unparse_switch_body.h | 272 | ||||
-rwxr-xr-x | vrt/lib/gen_parse_switch_body.py | 85 | ||||
-rwxr-xr-x | vrt/lib/gen_unparse_switch_body.py | 79 | ||||
-rw-r--r-- | vrt/lib/rx.cc | 124 | ||||
-rw-r--r-- | vrt/lib/rx_packet_handler.cc | 41 | ||||
-rw-r--r-- | vrt/lib/socket_rx_buffer.cc | 278 | ||||
-rw-r--r-- | vrt/lib/socket_rx_buffer.h | 122 |
14 files changed, 1666 insertions, 0 deletions
diff --git a/vrt/lib/.gitignore b/vrt/lib/.gitignore new file mode 100644 index 000000000..02b052397 --- /dev/null +++ b/vrt/lib/.gitignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +.deps +.libs diff --git a/vrt/lib/Makefile.am b/vrt/lib/Makefile.am new file mode 100644 index 000000000..303384c6e --- /dev/null +++ b/vrt/lib/Makefile.am @@ -0,0 +1,50 @@ +# +# Copyright 2007,2008,2009 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = \ + $(VRT_INCLUDES) \ + $(BOOST_CPPFLAGS) \ + $(CPPUNIT_INCLUDES) \ + $(GRUEL_INCLUDES) + +bin_PROGRAMS = + +lib_LTLIBRARIES = \ + libvrt.la + +libvrt_la_SOURCES = \ + copiers.cc \ + data_handler.cc \ + expanded_header.cc \ + rx.cc \ + rx_packet_handler.cc \ + socket_rx_buffer.cc + +libvrt_la_LIBADD = + +# Private headers not needed for above the API development +noinst_HEADERS = \ + data_handler.h \ + expanded_header_parse_switch_body.h \ + expanded_header_unparse_switch_body.h \ + socket_rx_buffer.h + +EXTRA_DIST = \ + gen_parse_switch_body.py \ + gen_unparse_switch_body.py diff --git a/vrt/lib/copiers.cc b/vrt/lib/copiers.cc new file mode 100644 index 000000000..ddf82abcf --- /dev/null +++ b/vrt/lib/copiers.cc @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/copiers.h> +#include <arpa/inet.h> +#include <assert.h> +#include <string.h> + +namespace vrt { + + void + copy_net_16sc_to_host_16sc(size_t nitems, + const uint32_t *items, + std::complex<int16_t> *host_items) + { +#ifdef WORDS_BIGENDIAN + + assert(sizeof(items[0]) == sizeof(host_items[0])); + memcpy(host_items, items, nitems * sizeof(items[0])); + +#else + + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohl(items[i]); + //printf("%9d\n", items[i]); + host_items[i] = std::complex<int16_t>((t >> 16), t & 0xffff); + } + +#endif + } + + void + copy_net_16sc_to_host_32fc(size_t nitems, + const uint32_t *items, + std::complex<float> *host_items) + { + // FIXME SIMD welcome here + + for (size_t i = 0; i < nitems; i++){ + uint32_t t = ntohl(items[i]); + int16_t re = (t >> 16) & 0xffff; + int16_t im = (t & 0xffff); + host_items[i] = std::complex<float>(re * 1.0/32768, im * 1.0/32768); + } + } + +}; + diff --git a/vrt/lib/data_handler.cc b/vrt/lib/data_handler.cc new file mode 100644 index 000000000..7d1f73a9a --- /dev/null +++ b/vrt/lib/data_handler.cc @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "data_handler.h" + +namespace vrt { + + data_handler::~data_handler() + { + // default nop destructor + } + +} + diff --git a/vrt/lib/data_handler.h b/vrt/lib/data_handler.h new file mode 100644 index 000000000..c041e48be --- /dev/null +++ b/vrt/lib/data_handler.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_VRT_DATA_HANDLER_H +#define INCLUDED_VRT_DATA_HANDLER_H + +#include <stdint.h> +#include <stddef.h> + +namespace vrt { + + /*! + * \brief Abstract function object called to handle received data blocks. + */ + class data_handler + { + public: + + enum result_bits { + DONE = 0x0002, //< do not call this object again + }; + + typedef int result; //< bitmask of result_bits + + /*! + * \param base points to the beginning of the data + * \param len is the length in bytes of the data + * \returns bitmask composed of DONE + */ + virtual result operator()(const void *base, size_t len) = 0; + virtual ~data_handler(); + }; + +} // namespace vrt + +#endif /* INCLUDED_VRT_DATA_HANDLER_H */ diff --git a/vrt/lib/expanded_header.cc b/vrt/lib/expanded_header.cc new file mode 100644 index 000000000..8b22fb925 --- /dev/null +++ b/vrt/lib/expanded_header.cc @@ -0,0 +1,135 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/expanded_header.h> +#include <gruel/inet.h> +//#include <stdio.h> + +namespace vrt { + + // lookup tables indexed by packet type + unsigned char expanded_header::s_if_data[16] = { + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_ext_data[16] = { + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_data[16] = { + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_context[16] = { + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + unsigned char expanded_header::s_stream_id[16] = { + 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + + // dispatch codeword bits + static const int HAS_STREAM_ID = 1 << 0; + static const int HAS_CLASS_ID = 1 << 1; + static const int HAS_INTEGER_SECS = 1 << 2; + static const int HAS_FRACTIONAL_SECS = 1 << 3; + static const int HAS_TRAILER = 1 << 4; + + static int + compute_codeword(const expanded_header &h) + { + int cw = 0; + if (h.stream_id_p()) cw |= HAS_STREAM_ID; + if (h.class_id_p()) cw |= HAS_CLASS_ID; + if (h.integer_secs_p()) cw |= HAS_INTEGER_SECS; + if (h.fractional_secs_p()) cw |= HAS_FRACTIONAL_SECS; + if (h.trailer_p()) cw |= HAS_TRAILER; + return cw; + } + + void expanded_header::unparse(const expanded_header *h, // in + size_t n32_bit_words_payload, // in + uint32_t *header, // out + size_t *n32_bit_words_header, // out + uint32_t *trailer, // out + size_t *n32_bit_words_trailer){// out + int cw = compute_codeword(*h); + //fills in the header (except word0), header length, trailer, trailer length + switch (cw & 0x1f){ +#include "expanded_header_unparse_switch_body.h" + } + //fill in the header word 0 with the calculated length + size_t n32_bit_words_packet = *n32_bit_words_header + n32_bit_words_payload + *n32_bit_words_trailer; + header[0] = htonl((h->header & ~VRTH_PKT_SIZE_MASK) | (n32_bit_words_packet & VRTH_PKT_SIZE_MASK)); + } + + bool + expanded_header::parse(const uint32_t *packet, // in + size_t n32_bit_words_packet, // in + expanded_header *h, // out + const uint32_t **payload, // out + size_t *n32_bit_words_payload) // out + { + size_t n32_bit_words_header = 0; + size_t n32_bit_words_trailer = 0; + size_t len = n32_bit_words_packet; + const uint32_t *p = packet; + + *payload = 0; + *n32_bit_words_payload = 0; + + // printf("parse: n32_bit_words_packet = %zd\n", n32_bit_words_packet); + + if (len < 1){ // must have at least the header word + h->header = 0; + return false; + } + + h->header = ntohl(p[0]); + + if (h->pkt_size() > len) + return false; // VRT header says packet is bigger than what we've got + + len = h->pkt_size(); // valid length of packet + + int cw = compute_codeword(*h); + switch (cw & 0x1f){ +#include "expanded_header_parse_switch_body.h" + } + + if (n32_bit_words_header + n32_bit_words_trailer > len) + return false; // negative payload len + + *payload = p + n32_bit_words_header; + *n32_bit_words_payload = len - (n32_bit_words_header + n32_bit_words_trailer); + + // printf("parse: hdr = 0x%08x, cw = 0x%02x, n32_bit_words_header = %d, n32_bit_words_trailer = %d\n", + // h->header, cw, n32_bit_words_header, n32_bit_words_trailer); + + return true; + } + + +}; // vrt diff --git a/vrt/lib/expanded_header_parse_switch_body.h b/vrt/lib/expanded_header_parse_switch_body.h new file mode 100644 index 000000000..6bfaf3799 --- /dev/null +++ b/vrt/lib/expanded_header_parse_switch_body.h @@ -0,0 +1,320 @@ + case 0: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 1; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 1: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 2; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 2: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 3; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 3: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 4; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 4: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = 0; + n32_bit_words_header = 2; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 5: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = 0; + n32_bit_words_header = 3; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 6: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = 0; + n32_bit_words_header = 4; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 7: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = 0; + n32_bit_words_header = 5; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 8: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + n32_bit_words_header = 3; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 9: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + n32_bit_words_header = 4; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 10: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + n32_bit_words_header = 5; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 11: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + n32_bit_words_header = 6; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 12: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + n32_bit_words_header = 4; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 13: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + n32_bit_words_header = 5; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 14: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + n32_bit_words_header = 6; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 15: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = ((uint64_t)(ntohl(p[5])) << 32) | ntohl(p[6]); + n32_bit_words_header = 7; + h->trailer = 0; + n32_bit_words_trailer = 0; + break; + + case 16: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 1; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 17: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 2; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 18: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 3; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 19: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = 0; + n32_bit_words_header = 4; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 20: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = 0; + n32_bit_words_header = 2; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 21: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = 0; + n32_bit_words_header = 3; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 22: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = 0; + n32_bit_words_header = 4; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 23: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = 0; + n32_bit_words_header = 5; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 24: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + n32_bit_words_header = 3; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 25: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + n32_bit_words_header = 4; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 26: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + n32_bit_words_header = 5; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 27: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = 0; + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + n32_bit_words_header = 6; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 28: + h->stream_id = 0; + h->class_id = 0; + h->integer_secs = ntohl(p[1]); + h->fractional_secs = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + n32_bit_words_header = 4; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 29: + h->stream_id = ntohl(p[1]); + h->class_id = 0; + h->integer_secs = ntohl(p[2]); + h->fractional_secs = ((uint64_t)(ntohl(p[3])) << 32) | ntohl(p[4]); + n32_bit_words_header = 5; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 30: + h->stream_id = 0; + h->class_id = ((uint64_t)(ntohl(p[1])) << 32) | ntohl(p[2]); + h->integer_secs = ntohl(p[3]); + h->fractional_secs = ((uint64_t)(ntohl(p[4])) << 32) | ntohl(p[5]); + n32_bit_words_header = 6; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + + case 31: + h->stream_id = ntohl(p[1]); + h->class_id = ((uint64_t)(ntohl(p[2])) << 32) | ntohl(p[3]); + h->integer_secs = ntohl(p[4]); + h->fractional_secs = ((uint64_t)(ntohl(p[5])) << 32) | ntohl(p[6]); + n32_bit_words_header = 7; + h->trailer = ntohl(p[len-1]); + n32_bit_words_trailer = 1; + break; + diff --git a/vrt/lib/expanded_header_unparse_switch_body.h b/vrt/lib/expanded_header_unparse_switch_body.h new file mode 100644 index 000000000..ca6e14989 --- /dev/null +++ b/vrt/lib/expanded_header_unparse_switch_body.h @@ -0,0 +1,272 @@ + case 0: + *n32_bit_words_header = 1; + *n32_bit_words_trailer = 0; + break; + + case 1: + header[1] = htonl(h->stream_id); + *n32_bit_words_header = 2; + *n32_bit_words_trailer = 0; + break; + + case 2: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + *n32_bit_words_header = 3; + *n32_bit_words_trailer = 0; + break; + + case 3: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + *n32_bit_words_trailer = 0; + break; + + case 4: + header[1] = htonl(h->integer_secs); + *n32_bit_words_header = 2; + *n32_bit_words_trailer = 0; + break; + + case 5: + header[1] = htonl(h->stream_id); + header[2] = htonl(h->integer_secs); + *n32_bit_words_header = 3; + *n32_bit_words_trailer = 0; + break; + + case 6: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl(h->integer_secs); + *n32_bit_words_header = 4; + *n32_bit_words_trailer = 0; + break; + + case 7: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl(h->integer_secs); + *n32_bit_words_header = 5; + *n32_bit_words_trailer = 0; + break; + + case 8: + header[1] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 3; + *n32_bit_words_trailer = 0; + break; + + case 9: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + *n32_bit_words_trailer = 0; + break; + + case 10: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 5; + *n32_bit_words_trailer = 0; + break; + + case 11: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[5] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 6; + *n32_bit_words_trailer = 0; + break; + + case 12: + header[1] = htonl(h->integer_secs); + header[2] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + *n32_bit_words_trailer = 0; + break; + + case 13: + header[1] = htonl(h->stream_id); + header[2] = htonl(h->integer_secs); + header[3] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 5; + *n32_bit_words_trailer = 0; + break; + + case 14: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl(h->integer_secs); + header[4] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[5] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 6; + *n32_bit_words_trailer = 0; + break; + + case 15: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl(h->integer_secs); + header[5] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[6] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 7; + *n32_bit_words_trailer = 0; + break; + + case 16: + *n32_bit_words_header = 1; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 17: + header[1] = htonl(h->stream_id); + *n32_bit_words_header = 2; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 18: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + *n32_bit_words_header = 3; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 19: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 20: + header[1] = htonl(h->integer_secs); + *n32_bit_words_header = 2; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 21: + header[1] = htonl(h->stream_id); + header[2] = htonl(h->integer_secs); + *n32_bit_words_header = 3; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 22: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl(h->integer_secs); + *n32_bit_words_header = 4; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 23: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl(h->integer_secs); + *n32_bit_words_header = 5; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 24: + header[1] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 3; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 25: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 26: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 5; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 27: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[5] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 6; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 28: + header[1] = htonl(h->integer_secs); + header[2] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 4; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 29: + header[1] = htonl(h->stream_id); + header[2] = htonl(h->integer_secs); + header[3] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[4] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 5; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 30: + header[1] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[2] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[3] = htonl(h->integer_secs); + header[4] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[5] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 6; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + + case 31: + header[1] = htonl(h->stream_id); + header[2] = htonl((uint32_t)((h->class_id >> 32) & 0xffffffff)); + header[3] = htonl((uint32_t)((h->class_id >> 0) & 0xffffffff)); + header[4] = htonl(h->integer_secs); + header[5] = htonl((uint32_t)((h->fractional_secs >> 32) & 0xffffffff)); + header[6] = htonl((uint32_t)((h->fractional_secs >> 0) & 0xffffffff)); + *n32_bit_words_header = 7; + trailer[0] = htonl(h->trailer); + *n32_bit_words_trailer = 1; + break; + diff --git a/vrt/lib/gen_parse_switch_body.py b/vrt/lib/gen_parse_switch_body.py new file mode 100755 index 000000000..d13e490b7 --- /dev/null +++ b/vrt/lib/gen_parse_switch_body.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# Copyright 2009 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import sys + +# dispatch codeword bits +HAS_STREAM_ID = 1 << 0; +HAS_CLASS_ID = 1 << 1; +HAS_INTEGER_SECS = 1 << 2; +HAS_FRACTIONAL_SECS = 1 << 3; +HAS_TRAILER = 1 << 4; + +def do_case(f, cw): + + def do32(name, mask, index): + f.write(" ") + if cw & mask: + f.write("h->%s = ntohl(p[%d]);\n" % (name, index)) + return 1 + else: + f.write("h->%s = 0;\n" % (name,)) + return 0 + + def do64(name, mask, index): + f.write(" ") + if cw & mask: + f.write("h->%s = ((uint64_t)(ntohl(p[%d])) << 32) | ntohl(p[%d]);\n" % (name, index, index+1)) + return 2 + else: + f.write("h->%s = 0;\n" % (name,)) + return 0 + + def dolength(index): + f.write(" n32_bit_words_header = %d;\n"%index) + + def dotrailer(name, mask): + if cw & mask: + f.write(" h->%s = ntohl(p[len-1]);\n" % (name,)) + f.write(" n32_bit_words_trailer = 1;\n") + return 1 + else: + f.write(" h->%s = 0;\n" % (name,)) + f.write(" n32_bit_words_trailer = 0;\n") + return 0 + + f.write(" case %d:\n" % (cw,)) + + index = 1 + index += do32("stream_id", HAS_STREAM_ID, index) + index += do64("class_id", HAS_CLASS_ID, index) + index += do32("integer_secs", HAS_INTEGER_SECS, index) + index += do64("fractional_secs", HAS_FRACTIONAL_SECS, index) + dolength(index) + dotrailer("trailer", HAS_TRAILER) + + f.write(" break;\n\n") + + +def main(): + f = sys.stdout + + for cw in range(32): + do_case(f, cw) + + +if __name__ == '__main__': + main() diff --git a/vrt/lib/gen_unparse_switch_body.py b/vrt/lib/gen_unparse_switch_body.py new file mode 100755 index 000000000..6c7cd01b3 --- /dev/null +++ b/vrt/lib/gen_unparse_switch_body.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 2009 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +import sys + +# dispatch codeword bits +HAS_STREAM_ID = 1 << 0; +HAS_CLASS_ID = 1 << 1; +HAS_INTEGER_SECS = 1 << 2; +HAS_FRACTIONAL_SECS = 1 << 3; +HAS_TRAILER = 1 << 4; + +def do_case(f, cw): + + def do32(name, mask, index): + if cw & mask: + f.write(" header[%d] = htonl(h->%s);\n" % (index, name)) + return 1 + return 0 + + def do64(name, mask, index): + if cw & mask: + f.write(" header[%d] = htonl((uint32_t)((h->%s >> 32) & 0xffffffff));\n" % (index, name)) + f.write(" header[%d] = htonl((uint32_t)((h->%s >> 0) & 0xffffffff));\n" % (index+1, name)) + return 2 + return 0 + + def dolength(index): + f.write(" *n32_bit_words_header = %d;\n"%index) + + def dotrailer(name, mask): + if cw & mask: + f.write(" trailer[%d] = htonl(h->%s);\n" % (0, name)) + f.write(" *n32_bit_words_trailer = 1;\n") + return 1 + else: + f.write(" *n32_bit_words_trailer = 0;\n") + return 0 + + f.write(" case %d:\n" % (cw,)) + + index = 1 + index += do32("stream_id", HAS_STREAM_ID, index) + index += do64("class_id", HAS_CLASS_ID, index) + index += do32("integer_secs", HAS_INTEGER_SECS, index) + index += do64("fractional_secs", HAS_FRACTIONAL_SECS, index) + dolength(index) + dotrailer("trailer", HAS_TRAILER) + + f.write(" break;\n\n") + + +def main(): + f = sys.stdout + + for cw in range(32): + do_case(f, cw) + + +if __name__ == '__main__': + main() diff --git a/vrt/lib/rx.cc b/vrt/lib/rx.cc new file mode 100644 index 000000000..2d741e908 --- /dev/null +++ b/vrt/lib/rx.cc @@ -0,0 +1,124 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/rx.h> +#include <vrt/expanded_header.h> +#include "socket_rx_buffer.h" +#include "data_handler.h" +#include <unistd.h> +#include <stdio.h> +#include <stdexcept> + +static void +print_words(FILE *fp, size_t offset, const uint32_t *buf, size_t n) +{ + size_t i; + for (i = 0; i < n; i++){ + if (i % 4 == 0){ + fprintf(fp, "%04zx:", i); + } + + putc(' ', fp); + fprintf(fp, "%08x", buf[i]); + if (i % 4 == 3) + putc('\n', fp); + } + + putc('\n', fp); +} + + + +namespace vrt { + + rx::sptr + rx::make(int socket_fd, size_t rx_bufsize) + { + return sptr(new rx(socket_fd, rx_bufsize)); + } + + rx::rx(int socket_fd, size_t rx_bufsize) + : d_socket_fd(socket_fd), + d_srb(new socket_rx_buffer(socket_fd, rx_bufsize)) + { + } + + rx::~rx() + { + delete d_srb; + ::close(d_socket_fd); + } + + + class vrt_data_handler : public data_handler + { + rx_packet_handler *d_handler; + + public: + vrt_data_handler(rx_packet_handler *handler) + : d_handler(handler){} + + ~vrt_data_handler(); + + result operator()(const void *base, size_t len); + }; + + vrt_data_handler::~vrt_data_handler(){} + + // N.B., There may be more than 1 VRT packet in a frame (usually IF-Context packets) + data_handler::result + vrt_data_handler::operator()(const void *base, size_t len) + { + const uint32_t *word_base = (const uint32_t *) base; + size_t word_len = len/(sizeof(uint32_t)); + + bool want_more = true; + while (word_len > 0 && want_more){ + const uint32_t *payload; + size_t n32_bit_words; + expanded_header hdr; + if (!expanded_header::parse(word_base, word_len, + &hdr, &payload, &n32_bit_words)){ + if (1){ + fprintf(stderr, "vrt_data_handler: malformed VRT packet!\n"); + print_words(stderr, 0, word_base, word_len); + } + return 0; + } + want_more = (*d_handler)(payload, n32_bit_words, &hdr); + word_base += hdr.pkt_size(); + word_len -= hdr.pkt_size(); + } + return !want_more ? data_handler::DONE : 0; + } + + + bool + rx::rx_packets(rx_packet_handler *handler, bool dont_wait) + { + vrt_data_handler h(handler); + socket_rx_buffer::result r = d_srb->rx_frames(&h, dont_wait ? 0 : -1); + return r == socket_rx_buffer::EB_OK || r == socket_rx_buffer::EB_WOULD_BLOCK; + } + +}; // vrt diff --git a/vrt/lib/rx_packet_handler.cc b/vrt/lib/rx_packet_handler.cc new file mode 100644 index 000000000..11f90278d --- /dev/null +++ b/vrt/lib/rx_packet_handler.cc @@ -0,0 +1,41 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <vrt/rx_packet_handler.h> + +namespace vrt { + + rx_packet_handler::~rx_packet_handler(){} + + // default operator is a NOP + bool + rx_packet_handler::operator()(const uint32_t *payload, + size_t n32_bit_words, + const expanded_header *hdr) + { + return true; + } + + +}; // vrt diff --git a/vrt/lib/socket_rx_buffer.cc b/vrt/lib/socket_rx_buffer.cc new file mode 100644 index 000000000..6ed211b9a --- /dev/null +++ b/vrt/lib/socket_rx_buffer.cc @@ -0,0 +1,278 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "socket_rx_buffer.h" +#include "data_handler.h" +#include <linux/if_packet.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <iostream> +#include <cmath> +#include <errno.h> +#include <stdexcept> +#include <string.h> +#include <fcntl.h> +#include <cstdio> + + +#define SOCKET_RX_BUFFER_DEBUG 1 // define to 0 or 1 +#if SOCKET_RX_BUFFER_DEBUG +#define DEBUG_LOG(x) ::write(2, (x), 1) +#else +#define DEBUG_LOG(X) +#endif + +#define DEFAULT_MEM_SIZE 62.5e6 // ~0.5s @ 125 MB/s +#define MAX_MEM_SIZE 1000e6 // ~10.00s @ 100 MB/s. +#define MAX_SLAB_SIZE 131072 // 128 KB (FIXME fish out of /proc/slabinfo) + + +namespace vrt { + + const unsigned int socket_rx_buffer::MAX_PKTLEN = 8192; + const unsigned int socket_rx_buffer::MIN_PKTLEN = 64; + + socket_rx_buffer::socket_rx_buffer(int socket_fd, size_t rx_bufsize) + : d_fd(socket_fd), d_using_tpring(false), d_buflen(0), d_buf(0), d_frame_nr(0), + d_frame_size(0), d_head(0), d_ring(0) + { + if (rx_bufsize == 0) + d_buflen = (size_t)DEFAULT_MEM_SIZE; + else + d_buflen = std::min((size_t)MAX_MEM_SIZE, rx_bufsize); + + if (!open()){ + throw std::runtime_error("socket_rx_buffer::open failed"); + } + } + + socket_rx_buffer::~socket_rx_buffer() + { + close(); + } + + bool + socket_rx_buffer::open() + { + if (try_packet_ring()){ + d_using_tpring = true; + // fprintf(stderr, "socket_rx_buffer: using memory mapped interface\n"); + } + else { + d_using_tpring = false; + // fprintf(stderr, "socket_rx_buffer: NOT using memory mapped interface\n"); + + // Increase socket buffer if possible + + int rcvbuf_size = d_buflen; +#if defined(SO_RCVBUFFORCE) + if (setsockopt(d_fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf_size, sizeof(rcvbuf_size)) != 0){ + perror("setsockopt(SO_RCVBUFFORCE)"); + fprintf(stderr, "Are you running as root? If not, please do.\n"); + } + else { + fprintf(stderr, "SO_RCVBUFFORCE = %zd\n", d_buflen); + } +#endif + } + + return true; + } + + bool + socket_rx_buffer::try_packet_ring() + { + struct tpacket_req req; + size_t page_size = getpagesize(); + + // Calculate minimum power-of-two aligned size for frames + req.tp_frame_size = + (unsigned int)rint(pow(2, ceil(log2(TPACKET_ALIGN(TPACKET_HDRLEN)+TPACKET_ALIGN(MAX_PKTLEN))))); + d_frame_size = req.tp_frame_size; + + // Calculate minimum contiguous pages needed to enclose a frame + int npages = (page_size > req.tp_frame_size) ? 1 : ((req.tp_frame_size+page_size-1)/page_size); + req.tp_block_size = page_size << (int)ceil(log2(npages)); + + // Calculate number of blocks + req.tp_block_nr = (int)(d_buflen/req.tp_block_size); + + + // Recalculate buffer length + d_buflen = req.tp_block_nr*req.tp_block_size; + + // Finally, calculate total number of frames. Since frames, blocks, + // and pages are all power-of-two aligned, frames are contiguous + req.tp_frame_nr = d_buflen/req.tp_frame_size; + d_frame_nr = req.tp_frame_nr; + +#if 0 + if (SOCKET_RX_BUFFER_DEBUG) + std::cerr << "socket_rx_buffer:" + << " frame_size=" << req.tp_frame_size + << " block_size=" << req.tp_block_size + << " block_nr=" << req.tp_block_nr + << " frame_nr=" << req.tp_frame_nr + << " buflen=" << d_buflen + << std::endl; +#endif + + // Try to get kernel shared memory buffer + if (setsockopt(d_fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req)) != 0){ + // perror("socket_rx_buffer: setsockopt"); + return false; + } + + void *p = mmap(0, d_buflen, PROT_READ|PROT_WRITE, MAP_SHARED, d_fd, 0); + if (p == MAP_FAILED){ + perror("socket_rx_buffer: mmap"); + return false; + } + d_buf = (uint8_t *) p; + + // Initialize our pointers into the packet ring + d_ring.resize(req.tp_frame_nr); + for (unsigned int i=0; i < req.tp_frame_nr; i++) + d_ring[i] = (uint8_t *)(d_buf+i*req.tp_frame_size); + + return true; + } + + bool + socket_rx_buffer::close() + { + return true; + } + + inline bool + socket_rx_buffer::frame_available() + { + return (((tpacket_hdr *)d_ring[d_head])->tp_status != TP_STATUS_KERNEL); + } + + socket_rx_buffer::result + socket_rx_buffer::rx_frames(data_handler *f, int timeout_in_ms) + { + if (!d_using_tpring){ + + // ---------------------------------------------------------------- + // Use recv instead of kernel Rx packet ring + // ---------------------------------------------------------------- + + unsigned char buf[MAX_PKTLEN]; + bool dont_wait = timeout_in_ms == 0; // FIXME treating timeout as 0 or inf + int flags = dont_wait ? MSG_DONTWAIT : 0; + + ssize_t rr = recv(d_fd, buf, sizeof(buf), flags); + if (rr == -1){ // error? + if (errno == EAGAIN){ // non-blocking, nothing there + return EB_WOULD_BLOCK; + } + perror("rx_frames: recv"); + return EB_ERROR; + } + + // Got first packet. Call handler + + data_handler::result r = (*f)(buf, rr); + if (r & data_handler::DONE) + return EB_OK; + + // Now do as many as we can without blocking + + while (1){ + rr = recv(d_fd, buf, sizeof(buf), MSG_DONTWAIT); + if (rr == -1){ // error? + if (errno == EAGAIN) // non-blocking, nothing there + return EB_OK; // return OK; we've processed >= 1 packets + perror("rx_frames: recv"); + return EB_ERROR; + } + + r = (*f)(buf, rr); + if (r & data_handler::DONE) + break; + } + return EB_OK; + } + + // ---------------------------------------------------------------- + // Use kernel Rx packet ring + // ---------------------------------------------------------------- + + DEBUG_LOG("\n"); + + while (!frame_available()) { + if (timeout_in_ms == 0) { + DEBUG_LOG("w"); + return EB_WOULD_BLOCK; + } + + struct pollfd pfd; + pfd.fd = d_fd; + pfd.revents = 0; + pfd.events = POLLIN; + + // DEBUG_LOG("P"); + + int pres = poll(&pfd, 1, timeout_in_ms); + if (pres == -1) { + perror("poll"); + return EB_ERROR; + } + + if (pres == 0) { + DEBUG_LOG("t"); + return EB_TIMED_OUT; + } + } + + // Iterate through available packets + while (frame_available()) { + // Get start of ethernet frame and length + tpacket_hdr *hdr = (tpacket_hdr *)d_ring[d_head]; + void *base = (uint8_t *)hdr+hdr->tp_mac; + size_t len = hdr->tp_len; + + if (1) + fprintf(stderr, "socket_rx_buffer: base = %p tp_mac = %3d tp_net = %3d\n", + base, hdr->tp_mac, hdr->tp_net); + + // Invoke data handler + data_handler::result r = (*f)(base, len); + hdr->tp_status = TP_STATUS_KERNEL; // mark it free + + inc_head(); + + if (r & data_handler::DONE) + break; + } + + DEBUG_LOG("|"); + return EB_OK; + } + +} // namespace vrt diff --git a/vrt/lib/socket_rx_buffer.h b/vrt/lib/socket_rx_buffer.h new file mode 100644 index 000000000..053c30c12 --- /dev/null +++ b/vrt/lib/socket_rx_buffer.h @@ -0,0 +1,122 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef INCLUDED_VRT_SOCKET_RX_BUFFER_H +#define INCLUDED_VRT_SOCKET_RX_BUFFER_H + +#include <boost/utility.hpp> +#include <vector> +#include <memory> +#include <stdint.h> + +namespace vrt { + + class data_handler; + + /*! + * \brief high-performance interface to receive datagrams + * + * On many systems it should be possible to implement this on top of libpcap + * + * \internal + */ + class socket_rx_buffer : boost::noncopyable + { + + int d_fd; // socket file descriptor + bool d_using_tpring; // using kernel mapped packet ring + size_t d_buflen; // length of our buffer + uint8_t *d_buf; // packet ring + unsigned int d_frame_nr; // max frames on ring + size_t d_frame_size; // frame storage size + unsigned int d_head; // pointer to next frame + + std::vector<uint8_t *> d_ring; // pointers into buffer + + bool frame_available(); + + void inc_head() + { + if (d_head + 1 >= d_frame_nr) + d_head = 0; + else + d_head = d_head + 1; + } + + bool open(); + bool close(); + bool try_packet_ring(); + + public: + + enum result { + EB_OK, //< everything's fine + EB_ERROR, //< A non-recoverable error occurred + EB_WOULD_BLOCK, //< A timeout of 0 was specified and nothing was ready + EB_TIMED_OUT, //< The timeout expired before anything was ready + }; + + static const unsigned int MAX_PKTLEN; + static const unsigned int MIN_PKTLEN; + + /*! + * \param socket_fd file descriptor that corresponds to a socket + * \param rx_bufsize is a hint as to the number of bytes of memory + * to allocate for received ethernet frames (0 -> reasonable default) + */ + socket_rx_buffer(int socket_fd, size_t rx_bufsize = 0); + ~socket_rx_buffer(); + + /*! + * \brief Call \p f for each frame in the receive buffer. + * \param f is the frame data handler + * \param timeout (in ms) controls behavior when there are no frames to read + * + * If \p timeout is 0, rx_frames will not wait for frames if none are + * available, and f will not be invoked. If \p timeout is -1 (the + * default), rx_frames will block indefinitely until frames are + * available. If \p timeout is positive, it indicates the number of + * milliseconds to wait for a frame to become available. Once the + * timeout has expired, rx_frames will return, f never having been + * invoked. + * + * \p f will be called on each frame that is available. + * \p f returns a bit mask with one of the following set or cleared: + * + * data_handler::DONE - return from rx_frames now even though more frames + * might be available; otherwise continue if more + * frames are ready. + * + * \returns EB_OK if at least one frame was received + * \returns EB_WOULD_BLOCK if \p timeout is 0 and the call would have blocked + * \returns EB_TIMED_OUT if timeout occurred + * \returns EB_ERROR if there was an unrecoverable error. + */ + result rx_frames(data_handler *f, int timeout=-1); + + /* + * \brief Returns maximum possible number of frames in buffer + */ + unsigned int max_frames() const { return d_using_tpring ? d_frame_nr : 0; } + }; + +}; // namespace vrt + +#endif /* INCLUDED_VRT_SOCKET_RX_BUFFER_H */ |