/* packet-ncsi.c
 * Routines for NCSI dissection
 * Copyright 2017-2019, Jeremy Kerr <jk@ozlabs.org>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/*
 * Network Controller Sideband Interface (NCSI) protocol support.
 * Specs at http://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.0.1.pdf
 */

#include <config.h>

#include <epan/packet.h>
#include <epan/etypes.h>

void proto_reg_handoff_ncsi(void);
void proto_register_ncsi(void);

static int proto_ncsi = -1;

/* Common header fields */
static int hf_ncsi_mc_id = -1;
static int hf_ncsi_revision = -1;
static int hf_ncsi_iid = -1;
static int hf_ncsi_type = -1;
static int hf_ncsi_type_code = -1;
static int hf_ncsi_type_resp = -1;
static int hf_ncsi_chan = -1;
static int hf_ncsi_plen = -1;

/* Response generics */
static int hf_ncsi_resp = -1;
static int hf_ncsi_reason = -1;

/* Select package */
static int hf_ncsi_sp_hwarb = -1;

/* Disable channel */
static int hf_ncsi_dc_ald = -1;

/* AEN enable */
static int hf_ncsi_aene_mc = -1;

/* Set MAC Address */
static int hf_ncsi_sm_mac = -1;
static int hf_ncsi_sm_macno = -1;
static int hf_ncsi_sm_at = -1;
static int hf_ncsi_sm_e = -1;

/* Broadcast filter */
static int hf_ncsi_bf = -1;
static int hf_ncsi_bf_arp = -1;
static int hf_ncsi_bf_dhcpc = -1;
static int hf_ncsi_bf_dhcps = -1;
static int hf_ncsi_bf_netbios = -1;

/* AEN payload fields */
static int hf_ncsi_aen_type = -1;
static int hf_ncsi_aen_lsc_stat = -1;
static int hf_ncsi_aen_lsc_oemstat = -1;
static int hf_ncsi_aen_lsc_hcstat = -1;

/* generic link status */
static int hf_ncsi_lstat = -1;
static int hf_ncsi_lstat_flag = -1;
static int hf_ncsi_lstat_speed_duplex = -1;
static int hf_ncsi_lstat_autoneg = -1;
static int hf_ncsi_lstat_autoneg_complete = -1;
static int hf_ncsi_lstat_parallel_detection = -1;
static int hf_ncsi_lstat_1000TFD = -1;
static int hf_ncsi_lstat_1000THD = -1;
static int hf_ncsi_lstat_100T4 = -1;
static int hf_ncsi_lstat_100TXFD = -1;
static int hf_ncsi_lstat_100TXHD = -1;
static int hf_ncsi_lstat_10TFD = -1;
static int hf_ncsi_lstat_10THD = -1;
static int hf_ncsi_lstat_tx_flow = -1;
static int hf_ncsi_lstat_rx_flow = -1;
static int hf_ncsi_lstat_partner_flow = -1;
static int hf_ncsi_lstat_serdes = -1;
static int hf_ncsi_lstat_oem_speed_valid = -1;

static gint ett_ncsi = -1;
static gint ett_ncsi_type = -1;
static gint ett_ncsi_payload = -1;
static gint ett_ncsi_lstat = -1;

#define NCSI_MIN_LENGTH 8

enum ncsi_type {
    NCSI_TYPE_GLS = 0x0a,
    NCSI_TYPE_AEN = 0x7f,
};

static const value_string ncsi_type_vals[] = {
    { 0x00,		"Clear initial state" },
    { 0x01,		"Select package" },
    { 0x02,		"Deselect package" },
    { 0x03,		"Enable channel" },
    { 0x04,		"Disable channel" },
    { 0x05,		"Reset channel" },
    { 0x06,		"Enable channel TX" },
    { 0x07,		"Disable channel TX" },
    { 0x08,		"AEN enable" },
    { NCSI_TYPE_GLS,	"Get link status" },
    { 0x0d,		"Disable VLAN" },
    { 0x0e,		"Set MAC address" },
    { 0x10,		"Enable broadcast filter" },
    { 0x11,		"Disable broadcast filter" },
    { NCSI_TYPE_AEN,	"Async Event Notification" },
    { 0, NULL },
};

static const value_string ncsi_type_resp_vals[] = {
    { 0x00,	"request" },
    { 0x01,	"response" },
    { 0, NULL },
};

static const value_string ncsi_aen_type_vals[] = {
    { 0x00,	"Link status change" },
    { 0x01,	"Configuration required" },
    { 0x02,	"Host NC driver status change" },
    { 0, NULL },
};

static const value_string ncsi_lstat_flag_vals[] = {
    { 0x00, "Link down" },
    { 0x01, "Link up" },
    { 0, NULL },
};

static const value_string ncsi_lstat_speed_duplex_vals[] = {
    { 0x00, "Auto-negotiate not complete" },
    { 0x01, "10BaseT half duplex" },
    { 0x02, "10BaseT full duplex" },
    { 0x03, "100BaseT half duplex" },
    { 0x04, "100BaseT4" },
    { 0x05, "100BaseTX full duplex" },
    { 0x06, "1000BaseT half duplex" },
    { 0x07, "1000BaseT full duplex" },
    { 0x08, "10GBaseT support" },
    { 0, NULL },
};

static const value_string ncsi_enable_vals[] = {
    { 0x00, "disabled" },
    { 0x01, "enabled" },
    { 0, NULL },
};

static const value_string ncsi_valid_vals[] = {
    { 0x00, "invalid" },
    { 0x01, "valid" },
    { 0, NULL },
};

static const value_string ncsi_autoneg_complete_vals[] = {
    { 0x00, "disabled/in-progress" },
    { 0x01, "complete" },
    { 0, NULL },
};

static const value_string ncsi_partner_flow_vals[] = {
    { 0x00, "Not pause capable" },
    { 0x01, "Symmetric pause" },
    { 0x02, "Assymmetric pause" },
    { 0x03, "Symmetric & Assymetric pause" },
    { 0, NULL },
};

static const value_string ncsi_used_vals[] = {
    { 0x00, "unused" },
    { 0x01, "used" },
    { 0, NULL },
};

static const value_string ncsi_capable_vals[] = {
    { 0x00, "not capable" },
    { 0x01, "capable" },
    { 0, NULL },
};

static const value_string ncsi_aen_hcstat_vals[] = {
    { 0x00, "not running" },
    { 0x01, "running" },
    { 0, NULL },
};

static const value_string ncsi_sm_at_vals[] = {
    { 0x00, "unicast" },
    { 0x01, "multicast" },
    { 0, NULL },
};

static const value_string ncsi_bf_filter_vals[] = {
    { 0x00, "drop" },
    { 0x01, "forward" },
    { 0, NULL },
};


static void
ncsi_proto_tree_add_lstat(tvbuff_t *tvb, proto_tree *tree, int offset)
{
    static const int *lstat_fields[] = {
        &hf_ncsi_lstat_flag,
        &hf_ncsi_lstat_speed_duplex,
        &hf_ncsi_lstat_autoneg,
        &hf_ncsi_lstat_autoneg_complete,
        &hf_ncsi_lstat_parallel_detection,
        &hf_ncsi_lstat_1000TFD,
        &hf_ncsi_lstat_1000THD,
        &hf_ncsi_lstat_100T4,
        &hf_ncsi_lstat_100TXFD,
        &hf_ncsi_lstat_100TXHD,
        &hf_ncsi_lstat_10TFD,
        &hf_ncsi_lstat_10THD,
        &hf_ncsi_lstat_tx_flow,
        &hf_ncsi_lstat_rx_flow,
        &hf_ncsi_lstat_partner_flow,
        &hf_ncsi_lstat_serdes,
        &hf_ncsi_lstat_oem_speed_valid,
        NULL,
    };

    proto_tree_add_bitmask_with_flags(tree, tvb, offset, hf_ncsi_lstat,
            ett_ncsi_lstat, lstat_fields, ENC_BIG_ENDIAN, BMT_NO_APPEND);
}

static void
dissect_ncsi_aen(tvbuff_t *tvb, proto_tree *tree)
{
    guint8 type = tvb_get_guint8(tvb, 19);

    proto_tree_add_item(tree, hf_ncsi_aen_type, tvb, 19, 1, ENC_NA);

    switch (type) {
    case 0x00:
        ncsi_proto_tree_add_lstat(tvb, tree, 20);
        proto_tree_add_item(tree, hf_ncsi_aen_lsc_oemstat, tvb, 24, 4, ENC_NA);
        break;
    case 0x02:
        proto_tree_add_item(tree, hf_ncsi_aen_lsc_hcstat, tvb, 20, 4, ENC_NA);
        break;
    }
}

/* Code to actually dissect the packets */
static int
dissect_ncsi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
        void *data _U_)
{
    proto_tree *ncsi_tree, *ncsi_payload_tree;
    proto_item *ti, *pti;
    guint8 type, plen;
    static const int *type_fields[] = {
        &hf_ncsi_type_code,
        &hf_ncsi_type_resp,
        NULL,
    };

    /* Check that the packet is long enough for it to belong to us. */
    if (tvb_reported_length(tvb) < NCSI_MIN_LENGTH)
        return 0;

    col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCSI");

    type = tvb_get_guint8(tvb, 4);
    plen = tvb_get_guint8(tvb, 7);

    col_clear(pinfo->cinfo, COL_INFO);
    if (type == 0xff) {
        col_add_fstr(pinfo->cinfo, COL_INFO,
                "Async Event Notification, chan %x",
                tvb_get_guint8(tvb, 5));
    } else {
        col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s, id %x, chan %x",
                val_to_str(type & 0x7f, ncsi_type_vals, "Unknown type %x"),
                type & 0x80 ? "response" : "request ",
                tvb_get_guint8(tvb, 3),
                tvb_get_guint8(tvb, 5));
    }


    /* Top-level NCSI protocol item & tree */
    ti = proto_tree_add_item(tree, proto_ncsi, tvb, 0, -1, ENC_NA);

    ncsi_tree = proto_item_add_subtree(ti, ett_ncsi);

    /* Standard header fields */
    proto_tree_add_item(ncsi_tree, hf_ncsi_mc_id, tvb, 0, 1, ENC_NA);
    proto_tree_add_item(ncsi_tree, hf_ncsi_revision, tvb, 1, 1, ENC_NA);
    proto_tree_add_item(ncsi_tree, hf_ncsi_iid, tvb, 3, 1, ENC_NA);
    proto_tree_add_bitmask(ncsi_tree, tvb, 4, hf_ncsi_type,
            ett_ncsi_type, type_fields, ENC_NA);
    proto_tree_add_item(ncsi_tree, hf_ncsi_chan, tvb, 5, 1, ENC_NA);
    proto_tree_add_item(ncsi_tree, hf_ncsi_plen, tvb, 7, 1, ENC_NA);

    if (!plen)
        return 16;

    /* Payload tree */
    ncsi_payload_tree = proto_tree_add_subtree(ncsi_tree, tvb, 16,
            plen, ett_ncsi_payload, &pti, "Payload");

    /* All responses start with response code & reason data */
    if (type & 0x80) {
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_resp, tvb,
                16, 2, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_reason, tvb,
                18, 2, ENC_NA);
    }

    switch (type) {
    case 0x01:
        proto_item_set_text(pti, "Select package request");
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_sp_hwarb, tvb,
                19, 1, ENC_NA);
        break;
    case 0x04:
        proto_item_set_text(pti, "Disable channel request");
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_dc_ald, tvb,
                19, 1, ENC_NA);
        break;
    case 0x08:
        proto_item_set_text(pti, "AEN enable request");
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_aene_mc, tvb,
                19, 1, ENC_NA);
        break;
    case 0x0e:
        proto_item_set_text(pti, "Set MAC address request");
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_sm_mac, tvb,
                16, 6, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_sm_macno, tvb,
                22, 1, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_sm_at, tvb,
                23, 1, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_sm_e, tvb,
                23, 1, ENC_NA);
        break;
    case 0x10:
        proto_item_set_text(pti, "Enable broadcast filter request");
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_bf, tvb,
                16, 4, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_bf_arp, tvb,
                16, 4, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_bf_dhcpc, tvb,
                16, 4, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_bf_dhcps, tvb,
                16, 4, ENC_NA);
        proto_tree_add_item(ncsi_payload_tree, hf_ncsi_bf_netbios, tvb,
                16, 4, ENC_NA);
        break;
    case NCSI_TYPE_GLS | 0x80:
        proto_item_set_text(pti, "Get Link Status response");
        ncsi_proto_tree_add_lstat(tvb, ncsi_payload_tree, 20);
        break;
    case NCSI_TYPE_AEN | 0x80:
        proto_item_set_text(pti, "AEN payload");
        dissect_ncsi_aen(tvb, ncsi_payload_tree);
        break;
    }

    return tvb_captured_length(tvb);
}

void
proto_register_ncsi(void)
{
    /* Field definitions */
    static hf_register_info hf[] = {
        { &hf_ncsi_mc_id,
          { "MC ID", "ncsi.mc_id",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Management controller ID", HFILL },
        },
        { &hf_ncsi_revision,
          { "Revision", "ncsi.revision",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Header revision", HFILL },
        },
        { &hf_ncsi_iid,
          { "IID", "ncsi.iid",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Instance ID", HFILL },
        },
        { &hf_ncsi_type,
          { "Type", "ncsi.type",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Packet type", HFILL },
        },
        { &hf_ncsi_type_code,
          { "Type code", "ncsi.type.code",
            FT_UINT8, BASE_HEX, VALS(ncsi_type_vals), 0x7f,
            "Packet type code", HFILL },
        },
        { &hf_ncsi_type_resp,
          { "Type req/resp", "ncsi.type.resp",
            FT_UINT8, BASE_HEX, VALS(ncsi_type_resp_vals), 0x80,
            "Packet type request/response", HFILL },
        },
        { &hf_ncsi_chan,
          { "Channel", "ncsi.chan",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "NCSI Channel", HFILL },
        },
        { &hf_ncsi_plen,
          { "Payload Length", "ncsi.plen",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_resp,
          { "Response", "ncsi.resp",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Response code", HFILL },
        },
        { &hf_ncsi_reason,
          { "Reason", "ncsi.reason",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            "Reason code", HFILL },
        },
        { &hf_ncsi_sp_hwarb,
          { "Hardware arbitration disable", "ncsi.sp.hwarb",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_dc_ald,
          { "Allow link down", "ncsi.dc.ald",
            FT_UINT8, BASE_HEX, NULL, 0x1,
            NULL, HFILL },
        },
        { &hf_ncsi_aene_mc,
          { "Management controller ID", "ncsi.aene.mc",
            FT_UINT8, BASE_HEX, NULL, 0x1,
            NULL, HFILL },
        },
        { &hf_ncsi_sm_mac,
          { "MAC address", "ncsi.sm.mac",
            FT_ETHER, BASE_NONE, NULL, 0,
            NULL, HFILL },
        },
        { &hf_ncsi_sm_macno,
          { "MAC address number", "ncsi.sm.macno",
            FT_UINT8, BASE_HEX, NULL, 0,
            NULL, HFILL },
        },
        { &hf_ncsi_sm_at,
          { "Address type", "ncsi.sm.at",
            FT_UINT8, BASE_HEX, VALS(ncsi_sm_at_vals), 0xe0,
            NULL, HFILL },
        },
        { &hf_ncsi_sm_e,
          { "Enabled", "ncsi.sm.e",
            FT_UINT8, BASE_HEX, VALS(ncsi_enable_vals), 0x1,
            NULL, HFILL },
        },

        { &hf_ncsi_aen_type,
          { "AEN type", "ncsi.aen_type",
            FT_UINT8, BASE_HEX, VALS(ncsi_aen_type_vals), 0xff,
            NULL, HFILL },
        },
        { &hf_ncsi_aen_lsc_stat,
          { "AEN link status", "ncsi.aen_lsc_stat",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_aen_lsc_oemstat,
          { "AEN link OEM status", "ncsi.aen_lsc_oemstat",
            FT_UINT8, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_aen_lsc_hcstat,
          { "AEN link HC status", "ncsi.aen_lsc_hcstat",
            FT_UINT8, BASE_HEX, VALS(ncsi_aen_hcstat_vals), 0x0,
            "AEN link host controller status", HFILL },
        },
        /* Broadcast filter */
        { &hf_ncsi_bf,
          { "Broadcast filter settings", "ncsi.bf.settings",
            FT_UINT32, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_bf_arp,
          { "ARP", "ncsi.bf.settings.arp",
            FT_UINT32, BASE_HEX, VALS(ncsi_bf_filter_vals), 1 << 0,
            NULL, HFILL },
        },
        { &hf_ncsi_bf_dhcpc,
          { "DHCP Client", "ncsi.bf.settings.dhcpc",
            FT_UINT32, BASE_HEX, VALS(ncsi_bf_filter_vals), 1 << 1,
            NULL, HFILL },
        },
        { &hf_ncsi_bf_dhcps,
          { "DHCP Server", "ncsi.bf.settings.dhcps",
            FT_UINT32, BASE_HEX, VALS(ncsi_bf_filter_vals), 1 << 2,
            NULL, HFILL },
        },
        { &hf_ncsi_bf_netbios,
          { "NetBIOS", "ncsi.bf.settings.netbios",
            FT_UINT32, BASE_HEX, VALS(ncsi_bf_filter_vals), 1 << 3,
            NULL, HFILL },
        },

        /* generic link status */
        { &hf_ncsi_lstat,
          { "Link status", "ncsi.lstat",
            FT_UINT32, BASE_HEX, NULL, 0x0,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_flag,
          { "Link flag", "ncsi.lstat.flag",
            FT_UINT32, BASE_HEX, VALS(ncsi_lstat_flag_vals), 0x1,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_speed_duplex,
          { "Speed & duplex", "ncsi.lstat.speed_duplex",
            FT_UINT32, BASE_HEX, VALS(ncsi_lstat_speed_duplex_vals), 0x1e,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_autoneg,
          { "Autonegotiation", "ncsi.lstat.autoneg",
            FT_UINT32, BASE_HEX, VALS(ncsi_enable_vals), 1<<5,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_autoneg_complete,
          { "Autonegotiation complete", "ncsi.lstat.autoneg_complete",
            FT_UINT32, BASE_HEX, VALS(ncsi_autoneg_complete_vals), 1<<6,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_parallel_detection,
          { "Parallel detection", "ncsi.lstat.parallel_detection",
            FT_UINT32, BASE_HEX, VALS(ncsi_used_vals), 1<<7,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_1000TFD,
          { "1000TFD", "ncsi.lstat.1000tfd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<9,
            "Partner advertised 1000TFD", HFILL },
        },
        { &hf_ncsi_lstat_1000THD,
          { "1000THD", "ncsi.lstat.1000thd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<10,
            "Partner advertised 1000THD", HFILL },
        },
        { &hf_ncsi_lstat_100T4,
          { "100T4", "ncsi.lstat.100t4",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<11,
            "Partner advertised 100T4", HFILL },
        },
        { &hf_ncsi_lstat_100TXFD,
          { "100TXFD", "ncsi.lstat.100txfd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<12,
            "Partner advertised 100TXFD", HFILL },
        },
        { &hf_ncsi_lstat_100TXHD,
          { "100TXHD", "ncsi.lstat.100txhd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<13,
            "Partner advertised 100TXHD", HFILL },
        },
        { &hf_ncsi_lstat_10TFD,
          { "10TFD", "ncsi.lstat.10tfd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<14,
            "Partner advertised 10TFD", HFILL },
        },
        { &hf_ncsi_lstat_10THD,
          { "10THD", "ncsi.lstat.10thd",
            FT_UINT32, BASE_HEX, VALS(ncsi_capable_vals), 1<<15,
            "Partner advertised 10THD", HFILL },
        },
        { &hf_ncsi_lstat_tx_flow,
          { "TX flow", "ncsi.lstat.tx_flow",
            FT_UINT32, BASE_HEX, VALS(ncsi_enable_vals), 1<<16,
            "TX flow control", HFILL },
        },
        { &hf_ncsi_lstat_rx_flow,
          { "RX flow", "ncsi.lstat.rx_flow",
            FT_UINT32, BASE_HEX, VALS(ncsi_enable_vals), 1<<17,
            "RX flow control", HFILL },
        },
        { &hf_ncsi_lstat_partner_flow,
          { "Partner flow", "ncsi.lstat.partner_flow",
            FT_UINT32, BASE_HEX, VALS(ncsi_partner_flow_vals), 3<<18,
            "Partner-advertised flow control", HFILL },
        },
        { &hf_ncsi_lstat_serdes,
          { "SerDes", "ncsi.lstat.serdes",
            FT_UINT32, BASE_HEX, VALS(ncsi_used_vals), 1<<20,
            NULL, HFILL },
        },
        { &hf_ncsi_lstat_oem_speed_valid,
          { "OEM Speed", "ncsi.lstat.oem_speed_valid",
            FT_UINT32, BASE_HEX, VALS(ncsi_valid_vals), 1<<21,
            NULL, HFILL },
        },
    };

    /* Setup protocol subtree array */
    static gint *ett[] = {
        &ett_ncsi,
        &ett_ncsi_type,
        &ett_ncsi_payload,
        &ett_ncsi_lstat,
    };

    /* Register the protocol name and description */
    proto_ncsi = proto_register_protocol("NCSI", "NCSI", "ncsi");

    /* Required function calls to register the header fields and subtrees */
    proto_register_field_array(proto_ncsi, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_ncsi(void)
{
    dissector_handle_t ncsi_handle;
    ncsi_handle = create_dissector_handle(dissect_ncsi, proto_ncsi);
    dissector_add_uint("ethertype", ETHERTYPE_NCSI, ncsi_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:
 */
