trx_interface: implement incoming data handler

This change implements a receive path for UL bursts, namely
the OsmoTRX style header conversation to GSMTAP.
diff --git a/lib/trx_interface/trx_impl.cc b/lib/trx_interface/trx_impl.cc
index abcd45e..48ef51a 100644
--- a/lib/trx_interface/trx_impl.cc
+++ b/lib/trx_interface/trx_impl.cc
@@ -30,6 +30,9 @@
 #include "udp_socket.h"
 #include "trx_impl.h"
 
+#define BURST_SIZE     148
+#define DATA_IF_MTU    160
+
 namespace gr {
   namespace grgsm {
 
@@ -53,6 +56,9 @@
         gr::io_signature::make(0, 0, 0))
     {
         message_port_register_in(pmt::mp("bursts"));
+        message_port_register_out(pmt::mp("bursts"));
+
+        // Bind a port handler
         set_msg_handler(pmt::mp("bursts"),
           boost::bind(&trx_impl::handle_dl_burst, this, _1));
 
@@ -62,9 +68,15 @@
         std::string data_src_port = boost::lexical_cast<std::string> (base_port + 2);
         std::string data_dst_port = boost::lexical_cast<std::string> (base_port + 102);
 
-        // Init DATA interface
-        d_data_sock = new udp_socket(remote_addr, data_src_port, data_dst_port);
-        d_clck_sock = new udp_socket(remote_addr, clck_src_port, clck_dst_port);
+        // Init DATA & CLCK interfaces
+        d_data_sock = new udp_socket(remote_addr,
+          data_src_port, data_dst_port, DATA_IF_MTU);
+        d_clck_sock = new udp_socket(remote_addr,
+          clck_src_port, clck_dst_port, DATA_IF_MTU);
+
+        // Bind DATA interface handler
+        d_data_sock->udp_rx_handler = boost::bind(
+          &trx_impl::handle_ul_burst, this, _1, _2);
     }
 
     /*
@@ -156,5 +168,56 @@
       d_data_sock->udp_send(buf, 158);
     }
 
+    void
+    trx_impl::handle_ul_burst(uint8_t *payload, size_t len)
+    {
+      // Check length according to the protocol
+      if (len != 154)
+        return;
+
+      /* Make sure TS index is correct */
+      if (payload[0] >= 8)
+        return;
+
+      /* Unpack and check frame number */
+      uint32_t fn = (payload[1] << 24)
+        | (payload[2] << 16)
+        | (payload[3] << 8)
+        | payload[4];
+
+      if (fn >= 2715648)
+        return;
+
+      // Prepare a buffer for GSMTAP header and burst
+      uint8_t buf[sizeof(gsmtap_hdr) + BURST_SIZE];
+
+      // Set up pointer to GSMTAP header structure
+      struct gsmtap_hdr *header = (struct gsmtap_hdr *) buf;
+      memset(header, 0x00, sizeof(struct gsmtap_hdr));
+
+      // Fill in basic info
+      header->version = GSMTAP_VERSION;
+      header->hdr_len = sizeof(gsmtap_hdr) / 4;
+      header->type = GSMTAP_TYPE_UM_BURST;
+
+      // Set timeslot index and frame number
+      header->timeslot = payload[0];
+      header->frame_number = htobe32(fn);
+
+      // HACK: use GSMTAP_BURST_NORMAL for now
+      // FIXME: We will need to distinguish between RACN and NORMAL
+      header->sub_type = GSMTAP_BURST_NORMAL;
+
+      // Copy burst bits (0 & 1) for source message
+      memcpy(buf + sizeof(gsmtap_hdr), payload + 6, BURST_SIZE);
+
+      // Create a pmt blob
+      pmt::pmt_t blob = pmt::make_blob(buf, sizeof(gsmtap_hdr) + BURST_SIZE);
+      pmt::pmt_t msg = pmt::cons(pmt::PMT_NIL, blob);
+
+      /* Send a message to the output */
+      message_port_pub(pmt::mp("bursts"), msg);
+    }
+
   } /* namespace grgsm */
 } /* namespace gr */