From bf77d1f6e38e1cb561520408ecff8f633f5cefc7 Mon Sep 17 00:00:00 2001
From: Josh Blum <josh@joshknows.com>
Date: Fri, 9 Jul 2010 17:35:15 -0700
Subject: uhd: added error codes to rx metadata, switched examples to use

vrt packet handler fills in error codes and inspects non-data packet
---
 host/examples/benchmark_rx_rate.cpp       |  5 ++++-
 host/examples/rx_timed_samples.cpp        |  9 ++++++---
 host/include/uhd/device.hpp               |  2 +-
 host/include/uhd/types/metadata.hpp       | 29 ++++++++++++++++++++++++++---
 host/lib/transport/gen_vrt_if_packet.py   |  4 ++--
 host/lib/transport/vrt_packet_handler.hpp | 31 +++++++++++++++++++------------
 host/lib/types.cpp                        |  3 ++-
 7 files changed, 60 insertions(+), 23 deletions(-)

diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp
index 7b512e56e..ba4dd34d0 100644
--- a/host/examples/benchmark_rx_rate.cpp
+++ b/host/examples/benchmark_rx_rate.cpp
@@ -53,10 +53,13 @@ static inline void test_device(
             uhd::io_type_t::COMPLEX_FLOAT32,
             uhd::device::RECV_MODE_ONE_PACKET
         );
-        if (num_rx_samps == 0){
+        if (num_rx_samps == 0 and md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT){
             std::cerr << "Unexpected timeout on recv, exit test..." << std::endl;
             return;
         }
+        if (num_rx_samps == 0){
+            std::cerr << "Unexpected error on recv, continuing..." << std::endl;
+        }
         if (not md.has_time_spec){
             std::cerr << "Metadata missing time spec, exit test..." << std::endl;
             return;
diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp
index fcf662c4e..95f805007 100644
--- a/host/examples/rx_timed_samples.cpp
+++ b/host/examples/rx_timed_samples.cpp
@@ -93,13 +93,16 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
             uhd::device::RECV_MODE_ONE_PACKET
         );
         if (num_rx_samps == 0 and num_acc_samps > 0){
-            std::cout << "Got timeout before all samples received, possible packet loss, exiting loop..." << std::endl;
+            std::cout << boost::format(
+                "Got error code 0x%x before all samples received, possible packet loss, exiting loop..."
+            ) % md.error_code << std::endl;
             break;
         }
         if (num_rx_samps == 0) continue; //wait for packets with contents
 
-        if(verbose) std::cout << boost::format("Got packet: %u samples, %u full secs, %f frac secs")
-            % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl;
+        if(verbose) std::cout << boost::format(
+            "Got packet: %u samples, %u full secs, %f frac secs"
+        ) % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl;
 
         num_acc_samps += num_rx_samps;
     }
diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp
index a0c29f2e6..a19d22880 100644
--- a/host/include/uhd/device.hpp
+++ b/host/include/uhd/device.hpp
@@ -174,7 +174,7 @@ public:
      * \param metadata data to fill describing the buffer
      * \param io_type the type of data to fill into the buffer
      * \param recv_mode tells recv how to load the buffer
-     * \return the number of samples received
+     * \return the number of samples received or 0 on error
      */
     virtual size_t recv(
         const std::vector<void *> &buffs,
diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp
index 6712e2594..039196250 100644
--- a/host/include/uhd/types/metadata.hpp
+++ b/host/include/uhd/types/metadata.hpp
@@ -57,10 +57,33 @@ namespace uhd{
         bool end_of_burst;
 
         /*!
-         * Error conditions (TODO):
-         * Previous packets dropped?
-         * Timed-out on receive?
+         * Error conditions:
+         * - none: no error associated with this metadata
+         * - timeout: no packet received, underlying code timed-out
+         * - late command: a stream command was issued in the past
+         * - broken chain: expected another stream command
+         * - overrun: an internal receive buffer has overrun
+         * - bad packet: the buffer was unrecognizable as a vrt packet
+         *
+         * Note: When an overrun occurs in continuous streaming mode,
+         * the device will continue to send samples to the host.
+         * For other streaming modes, streaming will discontinue
+         * until the user issues a new stream command.
+         *
+         * Note: The metadata fields have meaning for the following error codes:
+         * - none
+         * - late command
+         * - broken chain
+         * - overrun
          */
+        enum error_code_t {
+            ERROR_CODE_NONE         = 0x0,
+            ERROR_CODE_TIMEOUT      = 0x1,
+            ERROR_CODE_LATE_COMMAND = 0x2,
+            ERROR_CODE_BROKEN_CHAIN = 0x4,
+            ERROR_CODE_OVERRUN      = 0x8,
+            ERROR_CODE_BAD_PACKET   = 0xf
+        } error_code;
 
         /*!
          * The default constructor:
diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py
index 07ce391ee..7438f5ff4 100755
--- a/host/lib/transport/gen_vrt_if_packet.py
+++ b/host/lib/transport/gen_vrt_if_packet.py
@@ -150,8 +150,8 @@ void vrt::if_hdr_unpack_$(suffix)(
     //extract fields from the header
     if_packet_info.packet_type = if_packet_info_t::packet_type_t(vrt_hdr_word >> 29);
     if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf;
-    if_packet_info.sob = bool(vrt_hdr_word & $hex(0x1 << 25));
-    if_packet_info.eob = bool(vrt_hdr_word & $hex(0x1 << 24));
+    //if_packet_info.sob = bool(vrt_hdr_word & $hex(0x1 << 25)); //not implemented
+    //if_packet_info.eob = bool(vrt_hdr_word & $hex(0x1 << 24)); //not implemented
 
     boost::uint8_t pred = 0;
     if(vrt_hdr_word & $hex(0x1 << 28)) pred |= $hex($sid_p);
diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp
index 11d749a81..c0d6bbe12 100644
--- a/host/lib/transport/vrt_packet_handler.hpp
+++ b/host/lib/transport/vrt_packet_handler.hpp
@@ -42,7 +42,7 @@ namespace vrt_packet_handler{
     typedef boost::function<bool(managed_recv_buffs_t &)> get_recv_buffs_t;
     typedef boost::function<void(size_t /*which channel*/)> handle_overrun_t;
 
-    void handle_overrun_nop(size_t){}
+    static inline void handle_overrun_nop(size_t){}
 
     struct recv_state{
         //width of the receiver in channels
@@ -89,8 +89,7 @@ namespace vrt_packet_handler{
             //extract packet words and check thats its enough to move on
             size_t num_packet_words32 = state.managed_buffs[i]->size()/sizeof(boost::uint32_t);
             if (num_packet_words32 <= vrt_header_offset_words32){
-                state.size_of_copy_buffs = 0;
-                return; //must exit here after setting the buffer
+                throw std::runtime_error("recv buffer smaller than vrt packet offset");
             }
 
             //unpack the vrt header into the info struct
@@ -105,15 +104,16 @@ namespace vrt_packet_handler{
             }
             state.next_packet_seq[i] = (if_packet_info.packet_count+1)%16;
 
-            //make sure that its a data packet (TODO handle non-data packets)
+            //handle the non-data packet case and parse its contents
             if (if_packet_info.packet_type != uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA){
-                std::cout << "vrt packet handler _recv1_helper got non data packet" << std::endl;
+
                 //extract the context word (we dont know the endianness so mirror the bytes)
                 boost::uint32_t word0 = vrt_data[0] | uhd::byteswap(vrt_data[0]);
-                if (word0 & 8) handle_overrun(i);
-                // 4: broken chain, 2: late command...
-                state.size_of_copy_buffs = 0;
-                return; //must exit here after setting the buffer
+                if (word0 & uhd::rx_metadata_t::ERROR_CODE_OVERRUN) handle_overrun(i);
+                metadata.error_code = uhd::rx_metadata_t::error_code_t(word0 & 0xf);
+
+                //break to exit loop and store metadata below
+                state.size_of_copy_buffs = 0; break;
             }
 
             //setup the buffer to point to the data
@@ -131,9 +131,10 @@ namespace vrt_packet_handler{
         metadata.time_spec = uhd::time_spec_t(
             time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), tick_rate
         );
-        metadata.start_of_burst = if_packet_info.sob;
+        static const int tlr_sob_flags = (1 << 21) | (1 << 9); //enable and indicator bits
+        metadata.start_of_burst = if_packet_info.has_tlr and (int(if_packet_info.tlr & tlr_sob_flags) == tlr_sob_flags);
         static const int tlr_eob_flags = (1 << 20) | (1 << 8); //enable and indicator bits
-        metadata.end_of_burst = if_packet_info.has_tlr and (int(if_packet_info.tlr & tlr_eob_flags) == tlr_eob_flags);
+        metadata.end_of_burst   = if_packet_info.has_tlr and (int(if_packet_info.tlr & tlr_eob_flags) == tlr_eob_flags);
     }
 
     /*******************************************************************
@@ -156,11 +157,15 @@ namespace vrt_packet_handler{
         size_t vrt_header_offset_words32
     ){
         metadata.has_time_spec = false; //false unless set in the helper
+        metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_NONE;
 
         //perform a receive if no rx data is waiting to be copied
         if (state.size_of_copy_buffs == 0){
             state.fragment_offset_in_samps = 0;
-            if (not get_recv_buffs(state.managed_buffs)) return 0;
+            if (not get_recv_buffs(state.managed_buffs)){
+                metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_TIMEOUT;
+                return 0;
+            }
             try{
                 _recv1_helper(
                     state, metadata, tick_rate,
@@ -168,7 +173,9 @@ namespace vrt_packet_handler{
                     vrt_header_offset_words32
                 );
             }catch(const std::exception &e){
+                state.size_of_copy_buffs = 0; //reset copy buffs size
                 std::cerr << "Error (recv): " << e.what() << std::endl;
+                metadata.error_code = uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET;
                 return 0;
             }
         }
diff --git a/host/lib/types.cpp b/host/lib/types.cpp
index 1e7917ad7..e0ce61058 100644
--- a/host/lib/types.cpp
+++ b/host/lib/types.cpp
@@ -101,7 +101,8 @@ rx_metadata_t::rx_metadata_t(void):
     more_fragments(false),
     fragment_offset(0),
     start_of_burst(false),
-    end_of_burst(false)
+    end_of_burst(false),
+    error_code(ERROR_CODE_NONE)
 {
     /* NOP */
 }
-- 
cgit v1.2.3