/* packet-vrt.c
 * Routines for VRT (VITA 49) packet disassembly
 * Copyright 2012 Ettus Research LLC - Nick Foster <nick@ettus.com>: original dissector
 * Copyright 2013 Alexander Chemeris <alexander.chemeris@gmail.com>: dissector improvement
 * Copyright 2013 Dario Lombardo (lomato@gmail.com): Official Wireshark port
 *
 * Original dissector repository: https://github.com/bistromath/vrt-dissector
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */


#include "config.h"
#include <epan/packet.h>
#include <epan/prefs.h>

void proto_register_vrt(void);
void proto_reg_handoff_vrt(void);

#define VITA_49_PORT    4991

static gboolean vrt_use_ettus_uhd_header_format = FALSE;

static int proto_vrt = -1;

/* fields */
static int hf_vrt_header = -1; /* 32-bit header */
static int hf_vrt_type = -1; /* 4-bit pkt type */
static int hf_vrt_cidflag = -1; /* 1-bit class ID flag */
static int hf_vrt_tflag = -1; /* 1-bit trailer flag */
static int hf_vrt_tsmflag = -1; /* 1-bit timestamp mode */
static int hf_vrt_tsi = -1; /* 2-bit timestamp type */
static int hf_vrt_tsf = -1; /* 2-bit fractional timestamp type */
static int hf_vrt_seq = -1; /* 4-bit sequence number */
static int hf_vrt_len = -1; /* 16-bit length */
static int hf_vrt_sid = -1; /* 32-bit stream ID (opt.) */
static int hf_vrt_cid = -1; /* 64-bit class ID (opt.) */
static int hf_vrt_cid_oui = -1; /* 24-bit class ID OUI */
static int hf_vrt_cid_icc = -1; /* 16-bit class ID ICC */
static int hf_vrt_cid_pcc = -1; /* 16-bit class ID PCC */
static int hf_vrt_ts_int = -1; /* 32-bit integer timestamp (opt.) */
static int hf_vrt_ts_frac_picosecond = -1; /* 64-bit fractional timestamp (opt.) */
static int hf_vrt_ts_frac_sample = -1; /* 64-bit fractional timestamp (opt.) */
static int hf_vrt_data = -1; /* data */
static int hf_vrt_trailer = -1; /* 32-bit trailer (opt.) */
static int hf_vrt_trailer_enables = -1; /* trailer indicator enables */
static int hf_vrt_trailer_ind = -1; /* trailer indicators */
static int hf_vrt_trailer_e = -1; /* ass con pac cnt enable */
static int hf_vrt_trailer_acpc = -1; /* associated context packet count */
static int hf_vrt_trailer_en_caltime = -1; /* calibrated time indicator */
static int hf_vrt_trailer_en_valid = -1; /* valid data ind */
static int hf_vrt_trailer_en_reflock = -1; /* reference locked ind */
static int hf_vrt_trailer_en_agc = -1; /* AGC/MGC enabled ind */
static int hf_vrt_trailer_en_sig = -1; /* signal detected ind */
static int hf_vrt_trailer_en_inv = -1; /* spectral inversion ind */
static int hf_vrt_trailer_en_overrng = -1; /* overrange indicator */
static int hf_vrt_trailer_en_sampleloss = -1; /* sample loss indicator */
static int hf_vrt_trailer_en_user0 = -1; /* User indicator 0 */
static int hf_vrt_trailer_en_user1 = -1; /* User indicator 1 */
static int hf_vrt_trailer_en_user2 = -1; /* User indicator 2 */
static int hf_vrt_trailer_en_user3 = -1; /* User indicator 3 */
static int hf_vrt_trailer_ind_caltime = -1; /* calibrated time indicator */
static int hf_vrt_trailer_ind_valid = -1; /* valid data ind */
static int hf_vrt_trailer_ind_reflock = -1; /* reference locked ind */
static int hf_vrt_trailer_ind_agc = -1; /* AGC/MGC enabled ind */
static int hf_vrt_trailer_ind_sig = -1; /* signal detected ind */
static int hf_vrt_trailer_ind_inv = -1; /* spectral inversion ind */
static int hf_vrt_trailer_ind_overrng = -1; /* overrange indicator */
static int hf_vrt_trailer_ind_sampleloss = -1; /* sample loss indicator */
static int hf_vrt_trailer_ind_user0 = -1; /* User indicator 0 */
static int hf_vrt_trailer_ind_user1 = -1; /* User indicator 1 */
static int hf_vrt_trailer_ind_user2 = -1; /* User indicator 2 */
static int hf_vrt_trailer_ind_user3 = -1; /* User indicator 3 */

/* subtree state variables */
static gint ett_vrt = -1;
static gint ett_header = -1;
static gint ett_trailer = -1;
static gint ett_indicators = -1;
static gint ett_ind_enables = -1;
static gint ett_cid = -1;

static const value_string packet_types[] = {
    {0x00, "IF data packet without stream ID"},
    {0x01, "IF data packet with stream ID"},
    {0x02, "Extension data packet without stream ID"},
    {0x03, "Extension data packet with stream ID"},
    {0x04, "IF context packet"},
    {0x05, "Extension context packet"},
    {0, NULL}
};

static const value_string tsi_types[] = {
    {0x00, "No integer-seconds timestamp field included"},
    {0x01, "Coordinated Universal Time (UTC)"},
    {0x02, "GPS time"},
    {0x03, "Other"},
    {0, NULL}
};

static const value_string tsf_types[] = {
    {0x00, "No fractional-seconds timestamp field included"},
    {0x01, "Sample count timestamp"},
    {0x02, "Real time (picoseconds) timestamp"},
    {0x03, "Free running count timestamp"},
    {0, NULL}
};

static const value_string tsm_types[] = {
    {0x00, "Precise timestamp resolution"},
    {0x01, "General timestamp resolution"},
    {0, NULL}
};

static int * const enable_hfs[] = {
    &hf_vrt_trailer_en_user3,
    &hf_vrt_trailer_en_user2,
    &hf_vrt_trailer_en_user1,
    &hf_vrt_trailer_en_user0,
    &hf_vrt_trailer_en_sampleloss,
    &hf_vrt_trailer_en_overrng,
    &hf_vrt_trailer_en_inv,
    &hf_vrt_trailer_en_sig,
    &hf_vrt_trailer_en_agc,
    &hf_vrt_trailer_en_reflock,
    &hf_vrt_trailer_en_valid,
    &hf_vrt_trailer_en_caltime
};

static int * const ind_hfs[] = {
    &hf_vrt_trailer_ind_user3,
    &hf_vrt_trailer_ind_user2,
    &hf_vrt_trailer_ind_user1,
    &hf_vrt_trailer_ind_user0,
    &hf_vrt_trailer_ind_sampleloss,
    &hf_vrt_trailer_ind_overrng,
    &hf_vrt_trailer_ind_inv,
    &hf_vrt_trailer_ind_sig,
    &hf_vrt_trailer_ind_agc,
    &hf_vrt_trailer_ind_reflock,
    &hf_vrt_trailer_ind_valid,
    &hf_vrt_trailer_ind_caltime
};

static void dissect_header(tvbuff_t *tvb, proto_tree *tree, int type, int offset);
static void dissect_trailer(tvbuff_t *tvb, proto_tree *tree, int offset);
static void dissect_cid(tvbuff_t *tvb, proto_tree *tree, int offset);

static int dissect_vrt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    int     offset = 0;
    guint8  type;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "VITA 49");
    col_clear(pinfo->cinfo,COL_INFO);

    /* HACK to support UHD's weird header offset on data packets. */
    if (vrt_use_ettus_uhd_header_format && tvb_get_guint8(tvb, 0) == 0)
        offset += 4;

    /* get packet type */
    type = tvb_get_guint8(tvb, offset) >> 4;
    col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type, packet_types, "Reserved packet type (0x%02x)"));

    if (tree) { /* we're being asked for details */
        guint8  sidflag;
        guint8  cidflag;
        guint8  tflag;
        guint8  tsitype;
        guint8  tsftype;
        guint16 len;
        guint16 nsamps;

        proto_tree *vrt_tree;
        proto_item *ti;

        /* get SID, CID, T flags and TSI, TSF types */
        sidflag = (((type & 0x01) != 0) || (type == 4)) ? 1 : 0;
        cidflag = (tvb_get_guint8(tvb, offset) >> 3) & 0x01;
        /* tflag is in data packets but not context packets */
        tflag =   (tvb_get_guint8(tvb, offset) >> 2) & 0x01;
        if (type == 4)
            tflag = 0; /* this should be unnecessary but we do it just in case */
        /* tsmflag is in context packets but not data packets
           tsmflag = (tvb_get_guint8(tvb, offset) >> 0) & 0x01; */
        tsitype = (tvb_get_guint8(tvb, offset+1) >> 6) & 0x03;
        tsftype = (tvb_get_guint8(tvb, offset+1) >> 4) & 0x03;
        len     = tvb_get_ntohs(tvb, offset+2);

        nsamps  = len - 1;  /* (Before adjusting word count for optional fields) */

        ti = proto_tree_add_item(tree, proto_vrt, tvb, offset, -1, ENC_NA);
        vrt_tree = proto_item_add_subtree(ti, ett_vrt);

        dissect_header(tvb, vrt_tree, type, offset);
        offset += 4;

        /* header's done! if SID (last bit of type), put the stream ID here */
        if (sidflag) {
            proto_tree_add_item(vrt_tree, hf_vrt_sid, tvb, offset, 4, ENC_BIG_ENDIAN);
            nsamps -= 1;
            offset += 4;

        }

        /* if there's a class ID (cidflag), put the class ID here */
        if (cidflag) {
            dissect_cid(tvb, vrt_tree, offset);
            nsamps -= 2;
            offset += 8;
        }

        /* if TSI and/or TSF, populate those here */
        if (tsitype != 0) {
            proto_tree_add_item(vrt_tree, hf_vrt_ts_int, tvb, offset, 4, ENC_BIG_ENDIAN);
            nsamps -= 1;
            offset += 4;
        }
        if (tsftype != 0) {
            if (tsftype == 1 || tsftype == 3) {
                proto_tree_add_item(vrt_tree, hf_vrt_ts_frac_sample, tvb, offset, 8, ENC_BIG_ENDIAN);
            } else if (tsftype == 2) {
                proto_tree_add_item(vrt_tree, hf_vrt_ts_frac_picosecond, tvb, offset, 8, ENC_BIG_ENDIAN);
            }
            nsamps -= 2;
            offset += 8;
        }

        if (tflag) {
            nsamps -= 1;
        }

        /* now we've got either a context packet or a data packet
           TODO: parse context packet fully instead of just spewing data */

        /* we're into the data */
        if (nsamps != 0) {
            proto_tree_add_item(vrt_tree, hf_vrt_data, tvb, offset, nsamps*4, ENC_NA);
        }

        offset += nsamps*4;

        if (tflag) {
            dissect_trailer(tvb, vrt_tree, offset);
        }
    }
    return tvb_captured_length(tvb);
}

static void dissect_header(tvbuff_t *tvb, proto_tree *tree, int type, int offset)
{
    proto_item *hdr_item;
    proto_tree *hdr_tree;

    hdr_item = proto_tree_add_item(tree, hf_vrt_header, tvb, offset, 4, ENC_BIG_ENDIAN);

    hdr_tree = proto_item_add_subtree(hdr_item, ett_header);
    proto_tree_add_item(hdr_tree, hf_vrt_type, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(hdr_tree, hf_vrt_cidflag, tvb, offset, 1, ENC_BIG_ENDIAN);
    if (type == 4) {
        proto_tree_add_item(hdr_tree, hf_vrt_tsmflag, tvb, offset, 1, ENC_BIG_ENDIAN);
    } else {
        proto_tree_add_item(hdr_tree, hf_vrt_tflag, tvb, offset, 1, ENC_BIG_ENDIAN);
    }
    offset += 1;
    proto_tree_add_item(hdr_tree, hf_vrt_tsi, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(hdr_tree, hf_vrt_tsf, tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(hdr_tree, hf_vrt_seq, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;
    proto_tree_add_item(hdr_tree, hf_vrt_len, tvb, offset, 2, ENC_BIG_ENDIAN);
}

static void dissect_trailer(tvbuff_t *tvb, proto_tree *tree, int offset)
{
    proto_item *enable_item, *ind_item, *trailer_item;
    proto_tree *enable_tree;
    proto_tree *ind_tree;
    proto_tree *trailer_tree;
    guint16     en_bits;
    gint16      i;

    trailer_item = proto_tree_add_item(tree, hf_vrt_trailer, tvb, offset, 4, ENC_BIG_ENDIAN);
    trailer_tree = proto_item_add_subtree(trailer_item, ett_trailer);

    /* grab the indicator enables and the indicators;
       only display enables, indicators which are enabled */
    enable_item = proto_tree_add_item(trailer_tree, hf_vrt_trailer_enables, tvb, offset, 2, ENC_BIG_ENDIAN);
    ind_item = proto_tree_add_item(trailer_tree, hf_vrt_trailer_ind, tvb, offset + 1, 2, ENC_BIG_ENDIAN);
    /* grab enable bits */
    en_bits = (tvb_get_ntohs(tvb, offset) & 0xFFF0) >> 4;

    /* if there's any enables, start trees for enable bits and for indicators
       only enables and indicators which are enabled get printed. */
    if (en_bits) {
        enable_tree = proto_item_add_subtree(enable_item, ett_ind_enables);
        ind_tree = proto_item_add_subtree(ind_item, ett_indicators);
        for (i = 11; i >= 0; i--) {
            if (en_bits & (1<<i)) {
                /* XXX: Display needs to be improved ... */
                proto_tree_add_item(enable_tree, *enable_hfs[i], tvb, offset,   2, ENC_BIG_ENDIAN);
                proto_tree_add_item(ind_tree, *ind_hfs[i],       tvb, offset+1, 2, ENC_BIG_ENDIAN);
            }
        }
    }
    offset += 3;
    proto_tree_add_item(trailer_tree, hf_vrt_trailer_e,    tvb, offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(trailer_tree, hf_vrt_trailer_acpc, tvb, offset, 1, ENC_BIG_ENDIAN);
}

static void dissect_cid(tvbuff_t *tvb, proto_tree *tree, int offset)
{
    proto_item *cid_item;
    proto_tree *cid_tree;

    cid_item = proto_tree_add_item(tree, hf_vrt_cid, tvb, offset, 8, ENC_BIG_ENDIAN);
    cid_tree = proto_item_add_subtree(cid_item, ett_cid);

    offset += 1;
    proto_tree_add_item(cid_tree, hf_vrt_cid_oui, tvb, offset, 3, ENC_BIG_ENDIAN);
    offset += 3;
    proto_tree_add_item(cid_tree, hf_vrt_cid_icc, tvb, offset, 2, ENC_BIG_ENDIAN);
    offset += 2;
    proto_tree_add_item(cid_tree, hf_vrt_cid_pcc, tvb, offset, 2, ENC_BIG_ENDIAN);
}

void
proto_register_vrt(void)
{
    module_t *vrt_module;

    static hf_register_info hf[] = {
        { &hf_vrt_header,
            { "VRT header", "vrt.hdr",
            FT_UINT32, BASE_HEX,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_type,
            { "Packet type", "vrt.type",
            FT_UINT8, BASE_DEC,
            VALS(packet_types), 0xF0,
            NULL, HFILL }
        },
        { &hf_vrt_cidflag,
            { "Class ID included", "vrt.cidflag",
            FT_BOOLEAN, 8,
            NULL, 0x08,
            NULL, HFILL }
        },
        { &hf_vrt_tflag,
            { "Trailer included", "vrt.tflag",
            FT_BOOLEAN, 8,
            NULL, 0x04,
            NULL, HFILL }
        },
        { &hf_vrt_tsmflag,
            { "Timestamp mode", "vrt.tsmflag",
            FT_UINT8, BASE_DEC,
            VALS(tsm_types), 0x01,
            NULL, HFILL }
        },
        { &hf_vrt_tsi,
            { "Integer timestamp type", "vrt.tsi",
            FT_UINT8, BASE_DEC,
            VALS(tsi_types), 0xC0,
            NULL, HFILL }
        },
        { &hf_vrt_tsf,
            { "Fractional timestamp type", "vrt.tsf",
            FT_UINT8, BASE_DEC,
            VALS(tsf_types), 0x30,
            NULL, HFILL }
        },
        { &hf_vrt_seq,
            { "Sequence number", "vrt.seq",
            FT_UINT8, BASE_DEC,
            NULL, 0x0F,
            NULL, HFILL }
        },
        { &hf_vrt_len,
            { "Length", "vrt.len",
            FT_UINT16, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_ts_int,
            { "Integer timestamp", "vrt.ts_int",
            FT_UINT32, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_ts_frac_sample,
            { "Fractional timestamp (samples)", "vrt.ts_frac_sample",
            FT_UINT64, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_ts_frac_picosecond,
            { "Fractional timestamp (picoseconds)", "vrt.ts_frac_picosecond",
            FT_UINT64, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_sid,
            { "Stream ID", "vrt.sid",
            FT_UINT32, BASE_HEX,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_cid,
            { "Class ID", "vrt.cid",
            FT_UINT64, BASE_HEX,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_data,
            { "Data", "vrt.data",
            FT_BYTES, BASE_NONE,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_trailer,
            { "Trailer", "vrt.trailer",
            FT_UINT32, BASE_HEX,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_enables,
            { "Indicator enable bits", "vrt.enables",
            FT_UINT16, BASE_HEX,
            NULL, 0xFFF0,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind,
            { "Indicator bits", "vrt.indicators",
            FT_UINT16, BASE_HEX,
            NULL, 0x0FFF,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_e,
            { "Associated context packet count enabled", "vrt.e",
            FT_BOOLEAN, 8,
            NULL, 0x80,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_acpc,
            { "Associated context packet count", "vrt.acpc",
            FT_UINT8, BASE_DEC,
            NULL, 0x7F,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_caltime,
            { "Calibrated time indicator", "vrt.caltime",
            FT_BOOLEAN, 16,
            NULL, 0x0800,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_valid,
            { "Valid signal indicator", "vrt.valid",
            FT_BOOLEAN, 16,
            NULL, 0x0400,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_reflock,
            { "Reference lock indicator", "vrt.reflock",
            FT_BOOLEAN, 16,
            NULL, 0x0200,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_agc,
            { "AGC/MGC indicator", "vrt.agc",
            FT_BOOLEAN, 16,
            NULL, 0x0100,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_sig,
            { "Signal detected indicator", "vrt.sig",
            FT_BOOLEAN, 16,
            NULL, 0x0080,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_inv,
            { "Spectral inversion indicator", "vrt.inv",
            FT_BOOLEAN, 16,
            NULL, 0x0040,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_overrng,
            { "Overrange indicator", "vrt.overrng",
            FT_BOOLEAN, 16,
            NULL, 0x0020,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_sampleloss,
            { "Lost sample indicator", "vrt.sampleloss",
            FT_BOOLEAN, 16,
            NULL, 0x0010,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_user0,
            { "User indicator 0", "vrt.user0",
            FT_BOOLEAN, 16,
            NULL, 0x00008,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_user1,
            { "User indicator 1", "vrt.user1",
            FT_BOOLEAN, 16,
            NULL, 0x0004,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_user2,
            { "User indicator 2", "vrt.user2",
            FT_BOOLEAN, 16,
            NULL, 0x00002,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_ind_user3,
            { "User indicator 3", "vrt.user3",
            FT_BOOLEAN, 16,
            NULL, 0x0001,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_caltime,
            { "Calibrated time indicator enable", "vrt.caltime_en",
            FT_BOOLEAN, 16,
            NULL, 0x8000,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_valid,
            { "Valid signal indicator enable", "vrt.valid_en",
            FT_BOOLEAN, 16,
            NULL, 0x4000,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_reflock,
            { "Reference lock indicator enable", "vrt.reflock_en",
            FT_BOOLEAN, 16,
            NULL, 0x2000,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_agc,
            { "AGC/MGC indicator enable", "vrt.agc_en",
            FT_BOOLEAN, 16,
            NULL, 0x1000,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_sig,
            { "Signal detected indicator enable", "vrt.sig_en",
            FT_BOOLEAN, 16,
            NULL, 0x0800,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_inv,
            { "Spectral inversion indicator enable", "vrt.inv_en",
            FT_BOOLEAN, 16,
            NULL, 0x0400,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_overrng,
            { "Overrange indicator enable", "vrt.overrng_en",
            FT_BOOLEAN, 16,
            NULL, 0x0200,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_sampleloss,
            { "Lost sample indicator enable", "vrt.sampleloss_en",
            FT_BOOLEAN, 16,
            NULL, 0x0100,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_user0,
            { "User indicator 0 enable", "vrt.user0_en",
            FT_BOOLEAN, 16,
            NULL, 0x0080,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_user1,
            { "User indicator 1 enable", "vrt.user1_en",
            FT_BOOLEAN, 16,
            NULL, 0x0040,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_user2,
            { "User indicator 2 enable", "vrt.user2_en",
            FT_BOOLEAN, 16,
            NULL, 0x0020,
            NULL, HFILL }
        },
        { &hf_vrt_trailer_en_user3,
            { "User indicator 3 enable", "vrt.user3_en",
            FT_BOOLEAN, 16,
            NULL, 0x0010,
            NULL, HFILL }
        },
        { &hf_vrt_cid_oui,
            { "Class ID Organizationally Unique ID", "vrt.oui",
            FT_UINT24, BASE_HEX,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_cid_icc,
            { "Class ID Information Class Code", "vrt.icc",
            FT_UINT16, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        },
        { &hf_vrt_cid_pcc,
            { "Class ID Packet Class Code", "vrt.pcc",
            FT_UINT16, BASE_DEC,
            NULL, 0x00,
            NULL, HFILL }
        }
    };

    static gint *ett[] = {
        &ett_vrt,
        &ett_header,
        &ett_trailer,
        &ett_indicators,
        &ett_ind_enables,
        &ett_cid
     };

    proto_vrt = proto_register_protocol ("VITA 49 radio transport protocol", "VITA 49", "vrt");

    proto_register_field_array(proto_vrt, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));

    vrt_module = prefs_register_protocol(proto_vrt, NULL);
    prefs_register_bool_preference(vrt_module, "ettus_uhd_header_format",
        "Use Ettus UHD header format",
        "Activate workaround for weird Ettus UHD header offset on data packets",
        &vrt_use_ettus_uhd_header_format);
}

void
proto_reg_handoff_vrt(void)
{
    dissector_handle_t vrt_handle;

    vrt_handle = create_dissector_handle(dissect_vrt, proto_vrt);
    dissector_add_uint_with_preference("udp.port", VITA_49_PORT, vrt_handle);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
