/* packet-dcerpc-pn-io.c
 * Routines for PROFINET IO dissection.
 *
 * $Id: packet-dcerpc-pn-io.c 14248 2005-04-30 20:52:20Z ulfl $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * Copyright 1998 Gerald Combs
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * The PN-IO protocol is a field bus protocol related to decentralized 
 * periphery and is developed by the PROFIBUS Nutzerorganisation e.V. (PNO), 
 * see: www.profibus.com
 *
 *
 * PN-IO is based on the common DCE-RPC and the "lightweight" PN-RT 
 * (ethernet type 0x8892) protocols.
 *
 * The context manager (CM) part is handling context information 
 * (like establishing, ...) and is using DCE-RPC as it's underlying 
 * protocol.
 *
 * The actual cyclic data transfer and acyclic notification uses the 
 * "lightweight" PN-RT protocol.
 *
 * There are some other related PROFINET protocols (e.g. PN-DCP, which is 
 * handling addressing topics).
 *
 * Please note: the PROFINET CBA protocol is independant of the PN-IO protocol!
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif


#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <string.h>

#include <glib.h>
#include <epan/packet.h>
#include <epan/dissectors/packet-dcerpc.h>



static int proto_pn_io = -1;

static int hf_pn_io_opnum = -1;
static int hf_pn_io_reserved16 = -1;

static int hf_pn_io_array = -1;
static int hf_pn_io_status = -1;
static int hf_pn_io_args_max = -1;
static int hf_pn_io_args_len = -1;
static int hf_pn_io_array_max_count = -1;
static int hf_pn_io_array_offset = -1;
static int hf_pn_io_array_act_count = -1;

static int hf_pn_io_data = -1;

static int hf_pn_io_ar_uuid = -1;
static int hf_pn_io_api = -1;
static int hf_pn_io_slot_nr = -1;
static int hf_pn_io_subslot_nr = -1;
static int hf_pn_io_index = -1;
static int hf_pn_io_seq_number = -1;
static int hf_pn_io_record_data_length = -1;
static int hf_pn_io_padding = -1;
static int hf_pn_io_add_val1 = -1;
static int hf_pn_io_add_val2 = -1;

static int hf_pn_io_block = -1;
static int hf_pn_io_block_type = -1;
static int hf_pn_io_block_length = -1;
static int hf_pn_io_block_version_high = -1;
static int hf_pn_io_block_version_low = -1;

static int hf_pn_io_sessionkey = -1;
static int hf_pn_io_control_command = -1;
static int hf_pn_io_control_command_prmend = -1;
static int hf_pn_io_control_command_applready = -1;
static int hf_pn_io_control_command_release = -1;
static int hf_pn_io_control_command_done = -1;
static int hf_pn_io_control_block_properties = -1;

static int hf_pn_io_error_code = -1;
static int hf_pn_io_error_decode = -1;
static int hf_pn_io_error_code1 = -1;
static int hf_pn_io_error_code2 = -1;
static int hf_pn_io_error_code1_pniorw = -1;
static int hf_pn_io_error_code1_pnio = -1;

static int hf_pn_io_alarm_type = -1;
static int hf_pn_io_alarm_specifier = -1;
static int hf_pn_io_alarm_specifier_sequence = -1;
static int hf_pn_io_alarm_specifier_channel = -1;
static int hf_pn_io_alarm_specifier_manufacturer = -1;
static int hf_pn_io_alarm_specifier_submodule = -1;
static int hf_pn_io_alarm_specifier_ardiagnosis = -1;

static int hf_pn_io_alarm_dst_endpoint = -1;
static int hf_pn_io_alarm_src_endpoint = -1;
static int hf_pn_io_pdu_type = -1;
static int hf_pn_io_pdu_type_type = -1;
static int hf_pn_io_pdu_type_version = -1;
static int hf_pn_io_add_flags = -1;
static int hf_pn_io_window_size = -1;
static int hf_pn_io_tack = -1;
static int hf_pn_io_send_seq_num = -1;
static int hf_pn_io_ack_seq_num = -1;
static int hf_pn_io_var_part_len = -1;

static int hf_pn_io_module_ident_number = -1;
static int hf_pn_io_submodule_ident_number = -1;

static int hf_pn_io_ioxs = -1;
static int hf_pn_io_ioxs_extension = -1;
static int hf_pn_io_ioxs_res14 = -1;
static int hf_pn_io_ioxs_instance = -1;
static int hf_pn_io_ioxs_datastate = -1;


static gint ett_pn_io = -1;
static gint ett_pn_io_block = -1;
static gint ett_pn_io_status = -1;
static gint ett_pn_io_rtc = -1;
static gint ett_pn_io_rta = -1;
static gint ett_pn_io_pdu_type = -1;
static gint ett_pn_io_add_flags = -1;
static gint ett_pn_io_control_command = -1;
static gint ett_pn_io_ioxs = -1;


static e_uuid_t uuid_pn_io_device = { 0xDEA00001, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
static guint16  ver_pn_io_device = 1;

static e_uuid_t uuid_pn_io_controller = { 0xDEA00002, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
static guint16  ver_pn_io_controller = 1;

static e_uuid_t uuid_pn_io_supervisor = { 0xDEA00003, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
static guint16  ver_pn_io_supervisor = 1;

static e_uuid_t uuid_pn_io_parameterserver = { 0xDEA00004, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
static guint16  ver_pn_io_parameterserver = 1;


static const value_string pn_io_block_type[] = {
	{ 0x0000, "Reserved" },
	{ 0x0001, "Alarm Notification High"},
	{ 0x0002, "Alarm Notification Low"},
	{ 0x0008, "WriteRecordReq"},
	{ 0x8008, "WriteRecordRes"},
	{ 0x0009, "ReadRecordReq"},
	{ 0x8009, "ReadRecordRes"},
	{ 0x0010, "ManufacturerSpecificDiagnosisBlock"},
	{ 0x0011, "ChannelDiagnosisBlock"},
	{ 0x0012, "ExpectedIdentificationDataBlock"},
	{ 0x0014, "SubstituteValue RecordDataRead"},
	{ 0x0015, "RecordInputDataObjectElement"},
	{ 0x0016, "RecordOutputDataObjectElement"},
	{ 0x0017, "RecordOutputDataSubstituteObjectElement"},
	{ 0x0018, "ARData"},
	{ 0x0019, "LogData"},
	{ 0x001A, "APIData"},
	{ 0x0020, "I&M0"},
	{ 0x0021, "I&M1"},
	{ 0x0022, "I&M2"},
	{ 0x0023, "I&M3"},
	{ 0x0024, "I&M4"},
	{ 0x8001, "Alarm Ack High"},
	{ 0x8002, "Alarm Ack Low"},
	{ 0x0101, "ARBlockReq"},
	{ 0x8101, "ARBlockRes"},
	{ 0x0102, "IOCRBlockReq"},
	{ 0x8102, "IOCRBlockRes"},
	{ 0x0103, "AlarmCRBlockReq"},
	{ 0x8103, "AlarmCRBlockRes"},
	{ 0x0104, "ExpectedSubmoduleBlockReq"},
	{ 0x8104, "ModuleDiffBlock"},
	{ 0x0105, "PrmServerBlockReq"},
	{ 0x8105, "PrmServerBlockRes"},
	{ 0x0110, "IODBlockReq"},
	{ 0x8110, "IODBlockRes"},
	{ 0x0111, "IODBlockReq"},
	{ 0x8111, "IODBlockRes"},
	{ 0x0112, "IOXBlockReq"},
	{ 0x8112, "IOXBlockRes"},
	{ 0x0113, "IOXBlockReq"},
	{ 0x8113, "IOXBlockRes"},
	{ 0x0114, "ReleaseBlockReq"},
	{ 0x8114, "ReleaseBlockRes"},
	{ 0, NULL }
};

static const value_string pn_io_alarm_type[] = {
	{ 0x0000, "Reserved" },
	{ 0x0001, "Diagnosis" },
	{ 0x0002, "Process" },
	{ 0x0003, "Pull" },
	{ 0x0004, "Plug" },
	{ 0x0005, "Status" },
	{ 0x0006, "Update" },
	{ 0x0007, "Redundancy" },
	{ 0x0008, "Controlled by supervisor" },
	{ 0x0009, "Released by supervisor" },
	{ 0x000A, "Plug wrong submodule" },
	{ 0x000B, "Return of submodule" },
    /* 0x000C - 0x001F reserved */
    /* 0x0020 - 0x007F manufacturer specific */
    /* 0x0080 - 0x00FF reserved for profiles */
    /* 0x0100 - 0xFFFF reserved */
    { 0, NULL }
};

static const value_string pn_io_pdu_type[] = {
	{ 0x01, "Data-RTA-PDU" },
	{ 0x02, "NACK-RTA-PDU" },
	{ 0x03, "ACK-RTA-PDU" },
	{ 0x04, "ERR-RTA-PDU" },
    { 0, NULL }
};

static const value_string pn_io_error_code[] = {
	{ 0x00, "OK" },
	{ 0x81, "PNIO" },
	{ 0xCF, "RTA error" },
	{ 0xDA, "AlarmAck" },
	{ 0xDB, "IODConnectRes" },
	{ 0xDC, "IODReleaseRes" },
	{ 0xDD, "IODControlRes" },
	{ 0xDE, "IODReadRes" },
	{ 0xDF, "IODWriteRes" },
    { 0, NULL }
};

static const value_string pn_io_error_decode[] = {
	{ 0x00, "OK" },
	{ 0x80, "PNIORW" },
	{ 0x81, "PNIO" },
    { 0, NULL }
};

/*
XXX: the next 2 are dependant on error_code and error_decode

e.g.: CL-RPC error:
error_code .. see above
error_decode .. 0x81
error_code1 .. 0x69
error_code2 ..
1 RPC_ERR_REJECTED
2 RPC_ERR_FAULTED
3 RPC_ERR_TIMEOUT
4 RPC_ERR_IN_ARGS
5 RPC_ERR_OUT_ARGS
6 RPC_ERR_DECODE
7 RPC_ERR_PNIO_OUT_ARGS
8 Application Timeout
*/

/* XXX: add some more error codes here */
static const value_string pn_io_error_code1[] = {
	{ 0x00, "OK" },
    { 0, NULL }
};

/* XXX: add some more error codes here */
static const value_string pn_io_error_code2[] = {
	{ 0x00, "OK" },
    { 0, NULL }
};

static const value_string pn_io_error_code1_pniorw[] = {
	{ 0x0a /* 10*/, "application" },
	{ 0x0b /* 11*/, "access" },
	{ 0x0c /* 12*/, "resource" },
	{ 0x0d /* 13*/, "user specific(13)" },
	{ 0x0e /* 14*/, "user specific(14)" },
	{ 0x0f /* 15*/, "user specific(15)" },
    { 0, NULL }
};

static const value_string pn_io_error_code1_pnio[] = {
	{ 0x00 /*  0*/, "Reserved" },
	{ 0x01 /*  1*/, "Connect: Faulty ARBlockReq" },
	{ 0x02 /*  2*/, "Connect: Faulty IOCRBlockReq" },
	{ 0x03 /*  3*/, "Connect: Faulty ExpectedSubmoduleBlockReq" },
	{ 0x04 /*  4*/, "Connect: Faulty AlarmCRBlockReq" },
	{ 0x05 /*  5*/, "Connect: Faulty PrmServerBlockReq" },

	{ 0x14 /* 20*/, "IODControl: Faulty ControlBlockConnect" },
	{ 0x15 /* 21*/, "IODControl: Faulty ControlBlockPlug" },
	{ 0x16 /* 22*/, "IOXControl: Faulty ControlBlock after a connect est." },
	{ 0x17 /* 23*/, "IOXControl: Faulty ControlBlock a plug alarm" },

    { 0x28 /* 40*/, "Release: Faulty ReleaseBlock" },

    { 0x3c /* 60*/, "AlarmAck Error Codes" },
    { 0x3d /* 61*/, "CMDEV" },
    { 0x3e /* 62*/, "CMCTL" },
    { 0x3f /* 63*/, "NRPM" },
    { 0x40 /* 64*/, "RMPM" },
    { 0x41 /* 65*/, "ALPMI" },
    { 0x42 /* 66*/, "ALPMR" },
    { 0x43 /* 67*/, "LMPM" },
    { 0x44 /* 68*/, "MMAC" },
    { 0x45 /* 69*/, "RPC" },
    { 0x46 /* 70*/, "APMR" },
    { 0x47 /* 71*/, "APMS" },
    { 0x48 /* 72*/, "CPM" },
    { 0x49 /* 73*/, "PPM" },
    { 0x4a /* 74*/, "DCPUCS" },
    { 0x4b /* 75*/, "DCPUCR" },
    { 0x4c /* 76*/, "DCPMCS" },
    { 0x4d /* 77*/, "DCPMCR" },
    { 0x4e /* 78*/, "FSPM" },
	{ 0xfd /*253*/, "RTA_ERR_CLS_PROTOCOL" },
    { 0, NULL }
};

static const value_string pn_io_ioxs[] = {
	{ 0x00 /*  0*/, "detected by subslot" },
	{ 0x01 /*  1*/, "detected by slot" },
	{ 0x02 /*  2*/, "detected by IO device" },
	{ 0x03 /*  3*/, "detected by IO controller" },
};


/* dissect the four status (error) fields */
static int
dissect_PNIO_status(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint8  u8ErrorCode;
    guint8  u8ErrorDecode;
    guint8  u8ErrorCode1;
    guint8  u8ErrorCode2;

    proto_item *sub_item;
	proto_tree *sub_tree;
	guint32 u32SubStart;
    int bytemask = (drep[0] & 0x10) ? 3 : 0;
    const value_string *error_code1_vals;



    /* status */
    sub_item = proto_tree_add_item(tree, hf_pn_io_status, tvb, offset, 0, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_status);
    u32SubStart = offset;

    /* the PNIOStatus field is existing in both the RPC and the application data,
     * depending on the current PDU.
     * As the byte representation of these layers are different, this has to be handled
     * in a somewhat different way than elsewhere. */

    dissect_dcerpc_uint8(tvb, offset+(0^bytemask), pinfo, sub_tree, drep, 
                        hf_pn_io_error_code, &u8ErrorCode);
	dissect_dcerpc_uint8(tvb, offset+(1^bytemask), pinfo, sub_tree, drep, 
                        hf_pn_io_error_decode, &u8ErrorDecode);

    switch(u8ErrorDecode) {
    case(0x80): /* PNIORW */
	    dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
                            hf_pn_io_error_code1_pniorw, &u8ErrorCode1);
        error_code1_vals = pn_io_error_code1_pniorw;
        break;
    case(0x81): /* PNIO */
	    dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
                            hf_pn_io_error_code1_pnio, &u8ErrorCode1);
        error_code1_vals = pn_io_error_code1_pnio;
        break;
    default:
	    dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
                            hf_pn_io_error_code1, &u8ErrorCode1);
        error_code1_vals = pn_io_error_code1;
    }

    /* XXX - this has to be decode specific too */
	dissect_dcerpc_uint8(tvb, offset+(3^bytemask), pinfo, sub_tree, drep, 
                        hf_pn_io_error_code2, &u8ErrorCode2);

    offset +=4;

    if(u8ErrorCode == 0 && u8ErrorDecode == 0 && u8ErrorCode1 == 0 && u8ErrorCode2 == 0) {
        proto_item_append_text(sub_item, ": OK");
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_str(pinfo->cinfo, COL_INFO, ", OK");
    } else {
        proto_item_append_text(sub_item, ": Error Code: \"%s\", Decode: \"%s\", Code1: \"%s\" Code2: 0x%x", 
            val_to_str(u8ErrorCode, pn_io_error_code, "(0x%x)"),
            val_to_str(u8ErrorDecode, pn_io_error_decode, "(0x%x)"),
            val_to_str(u8ErrorCode1, error_code1_vals, "(0x%x)"),
            u8ErrorCode2);
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", Error Code: %s, Decode: %s, Code1: 0x%x Code2: 0x%x",
            val_to_str(u8ErrorCode, pn_io_error_code, "(0x%x)"),
            val_to_str(u8ErrorDecode, pn_io_error_decode, "(0x%x)"),
            u8ErrorCode1,
            u8ErrorCode2);
    }
	proto_item_set_len(sub_item, offset - u32SubStart);

    return offset;
}


/* dissect the alarm specifier */
static int
dissect_Alarm_specifier(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint16 u16AlarmSpecifierSequence;
    guint16 u16AlarmSpecifierChannel;
    guint16 u16AlarmSpecifierManufacturer;
    guint16 u16AlarmSpecifierSubmodule;
    guint16 u16AlarmSpecifierAR;
    proto_item *sub_item;
	proto_tree *sub_tree;

    /* alarm specifier */
	sub_item = proto_tree_add_item(tree, hf_pn_io_alarm_specifier, tvb, offset, 2, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_pdu_type);

	dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_alarm_specifier_sequence, &u16AlarmSpecifierSequence);
    u16AlarmSpecifierSequence &= 0x07FF;
	dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_alarm_specifier_channel, &u16AlarmSpecifierChannel);
    u16AlarmSpecifierChannel = (u16AlarmSpecifierChannel &0x0800) >> 11;
	dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_alarm_specifier_manufacturer, &u16AlarmSpecifierManufacturer);
    u16AlarmSpecifierManufacturer = (u16AlarmSpecifierManufacturer &0x1000) >> 12;
	dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_alarm_specifier_submodule, &u16AlarmSpecifierSubmodule);
    u16AlarmSpecifierSubmodule = (u16AlarmSpecifierSubmodule & 0x2000) >> 13;
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_alarm_specifier_ardiagnosis, &u16AlarmSpecifierAR);
    u16AlarmSpecifierAR = (u16AlarmSpecifierAR & 0x8000) >> 15;


    proto_item_append_text(sub_item, ", Sequence: %u, Channel: %u, Manuf: %u, Submodule: %u AR: %u", 
        u16AlarmSpecifierSequence, u16AlarmSpecifierChannel, 
        u16AlarmSpecifierManufacturer, u16AlarmSpecifierSubmodule, u16AlarmSpecifierAR);

    return offset;
}


/* dissect the alarm header */
static int
dissect_Alarm_header(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint16 u16AlarmType;
    guint32 u32Api;
    guint16 u16SlotNr;
    guint16 u16SubslotNr;

	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_alarm_type, &u16AlarmType);
	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_api, &u32Api);
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_slot_nr, &u16SlotNr);
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_subslot_nr, &u16SubslotNr);

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_fstr(pinfo->cinfo, COL_INFO, ", %s, Slot: %u/%u", 
        val_to_str(u16AlarmType, pn_io_alarm_type, "Unknown"),
        u16SlotNr, u16SubslotNr);

    return offset;
}


/* dissect the alarm note block */
static int
dissect_Alarm_note_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep, guint16 body_length)
{
    guint32 u32ModuleIdentNumber;
    guint32 u32SubmoduleIdentNumber;

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_str(pinfo->cinfo, COL_INFO, ", Alarm Notification");

    offset = dissect_Alarm_header(tvb, offset, pinfo, tree, drep);
    
	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_module_ident_number, &u32ModuleIdentNumber);
	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_submodule_ident_number, &u32SubmoduleIdentNumber);

    offset = dissect_Alarm_specifier(tvb, offset, pinfo, tree, drep);

    /* XXX - dissect AlarmItem */
    body_length -= 20;
    proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, body_length, "data", 
        "Alarm Item Data: %u bytes", body_length);
    offset += body_length;

    return offset;
}


/* dissect the alarm acknowledge block */
static int
dissect_Alarm_ack_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_str(pinfo->cinfo, COL_INFO, ", Alarm Ack");

    offset = dissect_Alarm_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_Alarm_specifier(tvb, offset, pinfo, tree, drep);

    offset = dissect_PNIO_status(tvb, offset, pinfo, tree, drep);

    return offset;
}


/* dissect the read/write header */
static int
dissect_ReadWrite_header(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    e_uuid_t uuid;
    guint32 u32Api;
    guint16 u16SlotNr;
    guint16 u16SubslotNr;
    guint16 u16Index;
    guint16 u16SeqNr;

	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_seq_number, &u16SeqNr);

    offset = dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_ar_uuid, &uuid);

	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_api, &u32Api);
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_slot_nr, &u16SlotNr);
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_subslot_nr, &u16SubslotNr);
    proto_tree_add_string_format(tree, hf_pn_io_padding, tvb, offset, 2, "padding", "Padding: 2 bytes");
    offset += 2;
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_index, &u16Index);

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_fstr(pinfo->cinfo, COL_INFO, ", Api: %u, Slot: %u/%u",
            u32Api, u16SlotNr, u16SubslotNr);

    return offset;
}


/* dissect the read/write request block */
static int
dissect_ReadWrite_rqst_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint32 u32RecDataLen;


    offset = dissect_ReadWrite_header(tvb, offset, pinfo, tree, drep);

	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_record_data_length, &u32RecDataLen);
    /* XXX: don't know how to handle the optional TargetARUUID */

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_fstr(pinfo->cinfo, COL_INFO, ", %u bytes",
            u32RecDataLen);

    return offset;
}


/* dissect the read/write response block */
static int
dissect_ReadWrite_resp_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint32 u32RecDataLen;
    guint16 u16AddVal1;
    guint16 u16AddVal2;


    offset = dissect_ReadWrite_header(tvb, offset, pinfo, tree, drep);

	offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_record_data_length, &u32RecDataLen);

	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_add_val1, &u16AddVal1);

	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_add_val2, &u16AddVal2);

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_fstr(pinfo->cinfo, COL_INFO, ", %u bytes",
            u32RecDataLen);

    return offset;
}


/* dissect the control/connect block */
static int
dissect_ControlConnect_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    e_uuid_t    ar_uuid;
	proto_item *sub_item;
	proto_tree *sub_tree;
    guint16     u16PrmEnd;
    guint16     u16ApplReady;
    guint16     u16Release;
    guint16     u16CmdDone;


    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
                        hf_pn_io_reserved16, NULL);

    offset = dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_ar_uuid, &ar_uuid);

    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
                        hf_pn_io_sessionkey, NULL);

    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
                        hf_pn_io_reserved16, NULL);

    sub_item = proto_tree_add_item(tree, hf_pn_io_control_command, tvb, offset, 2, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_control_command);

    dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
                        hf_pn_io_control_command_prmend, &u16PrmEnd);
    if(u16PrmEnd & 0x0001) {
        proto_item_append_text(sub_item, ", Parameter End");
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Parameter End\"");
    }
    dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
                        hf_pn_io_control_command_applready, &u16ApplReady);
    if((u16ApplReady >> 1) & 0x0001) {
        proto_item_append_text(sub_item, ", Application Ready");
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Application Ready\"");
    }
    dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
                        hf_pn_io_control_command_release, &u16Release);
    if((u16Release >> 2) & 0x0001) {
        proto_item_append_text(sub_item, ", Release");
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Release\"");
    }
    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
                        hf_pn_io_control_command_done, &u16CmdDone);
    if((u16CmdDone >> 3) & 0x0001) {
        proto_item_append_text(sub_item, ", Done");
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Done\"");
    }

    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
                        hf_pn_io_control_block_properties, NULL);

    return offset;
}


/* dissect one PN-IO block (depending on the block type) */
static int
dissect_block(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep, guint32 u32Idx)
{
    guint16 u16BlockType;
    guint16 u16BlockLength;
    guint8 u8BlockVersionHigh;
    guint8 u8BlockVersionLow;
	proto_item *sub_item;
	proto_tree *sub_tree;
	guint32 u32SubStart;
    guint16 u16BodyLength;


    /* from here, we only have big endian (network byte ordering) */
    drep[0] &= ~0x10;

    sub_item = proto_tree_add_item(tree, hf_pn_io_block, tvb, offset, 0, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_block);
    u32SubStart = offset;

	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_block_type, &u16BlockType);
	offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_block_length, &u16BlockLength);
	offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_block_version_high, &u8BlockVersionHigh);
	offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_block_version_low, &u8BlockVersionLow);

    /* block length is without type and length fields, but with version field */
    /* as it's already dissected, remove it */
    u16BodyLength = u16BlockLength - 2;

    switch(u16BlockType) {
    case(0x0001):
    case(0x0002):
        dissect_Alarm_note_block(tvb, offset, pinfo, sub_tree, drep, u16BodyLength);
        break;
    case(0x0110):
    case(0x0112):
    case(0x0114):
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
            val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
        dissect_ControlConnect_block(tvb, offset, pinfo, sub_tree, drep);
        break;
    case(0x0008):
    case(0x0009):
        dissect_ReadWrite_rqst_block(tvb, offset, pinfo, sub_tree, drep);
        break;
    case(0x8001):
    case(0x8002):
        dissect_Alarm_ack_block(tvb, offset, pinfo, sub_tree, drep);
        break;
    case(0x8008):
    case(0x8009):
        dissect_ReadWrite_resp_block(tvb, offset, pinfo, sub_tree, drep);
        break;
    case(0x8110):
    case(0x8112):
    case(0x8114):
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
            val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
        dissect_ControlConnect_block(tvb, offset, pinfo, sub_tree, drep);
        break;
    default:
        if (check_col(pinfo->cinfo, COL_INFO) && u32Idx < 3)
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
            val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
    	proto_tree_add_string_format(sub_tree, hf_pn_io_data, tvb, offset, u16BodyLength, "undecoded", "Undecoded Data: %d bytes", u16BodyLength);
    }
    offset += u16BodyLength;

	proto_item_append_text(sub_item, "[%u]: Type=\"%s\" (0x%04x), Length=%u(+4), Version=%u.%u", 
		u32Idx, val_to_str(u16BlockType, pn_io_block_type, "Unknown"), u16BlockType,
        u16BlockLength, u8BlockVersionHigh, u8BlockVersionLow);
	proto_item_set_len(sub_item, offset - u32SubStart);

    return offset;
}


/* dissect any number of PN-IO blocks */
static int
dissect_blocks(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint32 u32Idx = 0;
    

    while(tvb_length(tvb) > (guint) offset) {
        offset = dissect_block(tvb, offset, pinfo, tree, drep, u32Idx);
        u32Idx++;
    }

    if(u32Idx > 3) {
        if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_fstr(pinfo->cinfo, COL_INFO, ", ... (%u blocks)",
            u32Idx);
    }
	return offset;
}


/* dissect a PN-IO (DCE-RPC) request header */
static int
dissect_IPNIO_rqst_header(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint32 u32ArgsMax;
    guint32 u32ArgsLen;
    guint32 u32MaxCount;
    guint32 u32Offset;
    guint32 u32ArraySize;

	proto_item *sub_item;
	proto_tree *sub_tree;
	guint32 u32SubStart;


	if (check_col(pinfo->cinfo, COL_PROTOCOL))
	    col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-CM");

    /* args_max */
	offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_args_max, &u32ArgsMax);
    /* args_len */
	offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_args_len, &u32ArgsLen);

    sub_item = proto_tree_add_item(tree, hf_pn_io_array, tvb, offset, 0, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io);
    u32SubStart = offset;

    /* RPC array header */
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_max_count, &u32MaxCount);
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_offset, &u32Offset);
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_act_count, &u32ArraySize);

	proto_item_append_text(sub_item, ": Max: %u, Offset: %u, Size: %u", 
        u32MaxCount, u32Offset, u32ArraySize);
	proto_item_set_len(sub_item, offset - u32SubStart);

    return offset;
}


/* dissect a PN-IO (DCE-RPC) response header */
static int
dissect_IPNIO_resp_header(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint32 u32ArgsLen;
    guint32 u32MaxCount;
    guint32 u32Offset;
    guint32 u32ArraySize;

	proto_item *sub_item;
	proto_tree *sub_tree;
	guint32 u32SubStart;


	if (check_col(pinfo->cinfo, COL_PROTOCOL))
	    col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-CM");

    offset = dissect_PNIO_status(tvb, offset, pinfo, tree, drep);

    /* args_len */
	offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
                        hf_pn_io_args_len, &u32ArgsLen);

    sub_item = proto_tree_add_item(tree, hf_pn_io_array, tvb, offset, 0, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io);
    u32SubStart = offset;

    /* RPC array header */
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_max_count, &u32MaxCount);
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_offset, &u32Offset);
	offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
                        hf_pn_io_array_act_count, &u32ArraySize);

    proto_item_append_text(sub_item, ": Max: %u, Offset: %u, Size: %u", 
        u32MaxCount, u32Offset, u32ArraySize);
	proto_item_set_len(sub_item, offset - u32SubStart);

    return offset;
}


/* dissect a PN-IO connect request */
static int
dissect_IPNIO_Connect_rqst(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    
    offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

	return offset;
}


/* dissect a PN-IO connect response */
static int
dissect_IPNIO_Connect_resp(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{

    offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

	return offset;
}


/* dissect a PN-IO release request */
static int
dissect_IPNIO_Release_rqst(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    
    offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

	return offset;
}


/* dissect a PN-IO release response */
static int
dissect_IPNIO_Release_resp(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{

    offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

	return offset;
}


/* dissect a PN-IO control request */
static int
dissect_IPNIO_Control_rqst(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    
    offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

    return offset;
}


/* dissect a PN-IO control response */
static int
dissect_IPNIO_Control_resp(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{

    offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_blocks(tvb, offset, pinfo, tree, drep);

    return offset;
}


/* dissect a PN-IO read request */
static int
dissect_IPNIO_Read_rqst(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    
    offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);

	return offset;
}


/* dissect a PN-IO read response */
static int
dissect_IPNIO_Read_resp(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    gint remain;

    offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);

    /* XXX - remaining bytes: dissection not yet implemented */
    remain = tvb_length_remaining(tvb, offset);
    proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, remain, "data", "User Data: %d bytes", remain);
    offset += remain;

	return offset;
}


/* dissect a PN-IO write request */
static int
dissect_IPNIO_Write_rqst(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    gint remain;

    offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);

    /* XXX - remaining bytes: dissection not yet implemented */
    remain = tvb_length_remaining(tvb, offset);
    proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, remain, "data", "User Data: %d bytes", remain);
    offset += remain;

	return offset;
}


/* dissect a PN-IO write response */
static int
dissect_IPNIO_Write_resp(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{

    offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);

    offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);

	return offset;
}


/* dissect the IOxS (IOCS, IOPS) field */
static int
dissect_PNIO_IOxS(tvbuff_t *tvb, int offset,
	packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_)
{
    guint8 u8IOxS;
    proto_item *ioxs_item = NULL;
    proto_tree *ioxs_tree = NULL;


    u8IOxS = tvb_get_guint8(tvb, offset);

    /* add ioxs subtree */
	ioxs_item = proto_tree_add_uint_format(tree, hf_pn_io_ioxs, 
		tvb, offset, 1, u8IOxS,
		"IOxS: 0x%02x (%s%s)", 
		u8IOxS, 
		(u8IOxS & 0x01) ? "another IOxS follows " : "",
		(u8IOxS & 0x80) ? "good" : "bad");
	ioxs_tree = proto_item_add_subtree(ioxs_item, ett_pn_io_ioxs);

	proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_extension, tvb, offset, 1, u8IOxS);
	proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_res14, tvb, offset, 1, u8IOxS);
	proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_instance, tvb, offset, 1, u8IOxS);
	proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_datastate, tvb, offset, 1, u8IOxS);

    return offset + 1;
}


/* dissect a PN-IO Cyclic Service Data Unit (on top of PN-RT protocol) */
static int
dissect_PNIO_C_SDU(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    proto_item *data_item;
	proto_tree *data_tree;


    if (check_col(pinfo->cinfo, COL_PROTOCOL))
	    col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO");

    if(tree) {
	    data_item = proto_tree_add_protocol_format(tree, proto_pn_io, tvb, offset, tvb_length(tvb),
				    "PROFINET IO Cyclic Service Data Unit: %u bytes", tvb_length(tvb));
        data_tree = proto_item_add_subtree(data_item, ett_pn_io_rtc);

        offset = dissect_PNIO_IOxS(tvb, offset, pinfo, data_tree, drep);

        /* XXX - dissect the remaining data */
        /* this will be one or more DataItems followed by an optional GAP and RTCPadding */
        /* as we don't have the required context information to dissect the specific DataItems, this will be tricky :-( */
	    data_item = proto_tree_add_protocol_format(data_tree, proto_pn_io, tvb, offset, tvb_length_remaining(tvb, offset),
				    "Data: %u bytes (including GAP and RTCPadding)", tvb_length_remaining(tvb, offset));
    }

    return offset;
}


/* dissect a PN-IO RTA PDU (on top of PN-RT protocol) */
static int
dissect_PNIO_RTA(tvbuff_t *tvb, int offset,
	packet_info *pinfo, proto_tree *tree, guint8 *drep)
{
    guint16 u16AlarmDstEndpoint;
    guint16 u16AlarmSrcEndpoint;
    guint8  u8PDUType;
    guint8  u8PDUVersion;
    guint8  u8WindowSize;
    guint8  u8Tack;
    guint16 u16SendSeqNum;
    guint16 u16AckSeqNum;
    guint16 u16VarPartLen;
    int     start_offset = offset;

    proto_item *rta_item;
	proto_tree *rta_tree;

    proto_item *sub_item;
	proto_tree *sub_tree;


	if (check_col(pinfo->cinfo, COL_PROTOCOL))
	    col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-AL");

	rta_item = proto_tree_add_protocol_format(tree, proto_pn_io, tvb, offset, tvb_length(tvb), 
        "PROFINET IO Alarm");
	rta_tree = proto_item_add_subtree(rta_item, ett_pn_io_rta);

    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
                    hf_pn_io_alarm_dst_endpoint, &u16AlarmDstEndpoint);
    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
                    hf_pn_io_alarm_src_endpoint, &u16AlarmSrcEndpoint);

    if (check_col(pinfo->cinfo, COL_INFO))
	    col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: 0x%x, Dst: 0x%x",
        u16AlarmSrcEndpoint, u16AlarmDstEndpoint);

    /* PDU type */
	sub_item = proto_tree_add_item(rta_tree, hf_pn_io_pdu_type, tvb, offset, 1, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_pdu_type);
    dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                    hf_pn_io_pdu_type_type, &u8PDUType);
    u8PDUType &= 0x0F;
    offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                    hf_pn_io_pdu_type_version, &u8PDUVersion);
    u8PDUVersion >>= 4;
    proto_item_append_text(sub_item, ", Type: %s, Version: %u", 
        val_to_str(u8PDUType, pn_io_pdu_type, "Unknown"),
        u8PDUVersion);

    /* additional flags */
	sub_item = proto_tree_add_item(rta_tree, hf_pn_io_add_flags, tvb, offset, 1, FALSE);
	sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_add_flags);
    dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                    hf_pn_io_window_size, &u8WindowSize);
    u8WindowSize &= 0x0F;
    offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
                    hf_pn_io_tack, &u8Tack);
    u8Tack >>= 4;
    proto_item_append_text(sub_item, ", Window Size: %u, Tack: %u", 
        u8WindowSize, u8Tack);

    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
                    hf_pn_io_send_seq_num, &u16SendSeqNum);
    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
                    hf_pn_io_ack_seq_num, &u16AckSeqNum);
    offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
                    hf_pn_io_var_part_len, &u16VarPartLen);

    switch(u8PDUType & 0x0F) {
    case(1):    /* Data-RTA */
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_str(pinfo->cinfo, COL_INFO, ", Data-RTA");
        offset = dissect_block(tvb, offset, pinfo, rta_tree, drep, 0);
        break;
    case(2):    /* NACK-RTA */
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_str(pinfo->cinfo, COL_INFO, ", NACK-RTA");
        /* no additional data */
        break;
    case(3):    /* ACK-RTA */
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_str(pinfo->cinfo, COL_INFO, ", ACK-RTA");
        /* no additional data */
        break;
    case(4):    /* ERR-RTA */
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_append_str(pinfo->cinfo, COL_INFO, ", ERR-RTA");
        offset = dissect_PNIO_status(tvb, offset, pinfo, rta_tree, drep);
        break;
    default:
        proto_tree_add_string_format(tree, hf_pn_io_data, tvb, 0, tvb_length(tvb), "data", 
            "PN-IO Alarm: unknown PDU type 0x%x", u8PDUType);    
    }

    proto_item_set_len(rta_item, offset - start_offset);

    return offset;
}


/* possibly dissect a PN-IO related PN-RT packet */
static gboolean
dissect_PNIO_heur(tvbuff_t *tvb, 
	packet_info *pinfo, proto_tree *tree)
{
    guint8  drep_data = 0;
    guint8  *drep = &drep_data;
	guint8  u8CBAVersion;
    guint16 u16FrameID;


    /* the sub tvb will NOT contain the frame_id here! */
    u16FrameID = GPOINTER_TO_UINT(pinfo->private_data);

    u8CBAVersion = tvb_get_guint8 (tvb, 0);

    /* is this a PNIO class 2 data packet? */
	/* frame id must be in valid range (cyclic Real-Time, class=2) */
	if (u16FrameID >= 0x8000 && u16FrameID < 0xbf00) {
        dissect_PNIO_C_SDU(tvb, 0, pinfo, tree, drep);
        return TRUE;
    }

    /* is this a PNIO class 1 data packet? */
	/* frame id must be in valid range (cyclic Real-Time, class=1) and
     * first byte (CBA version field) has to be != 0x11 */
	if (u16FrameID >= 0xc000 && u16FrameID < 0xfb00 && u8CBAVersion != 0x11) {
        dissect_PNIO_C_SDU(tvb, 0, pinfo, tree, drep);
        return TRUE;
    }

    /* is this a PNIO high priority alarm packet? */
    if(u16FrameID == 0xfc01) {
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_add_str(pinfo->cinfo, COL_INFO, "Alarm High");

        dissect_PNIO_RTA(tvb, 0, pinfo, tree, drep);
        return TRUE;
    }

    /* is this a PNIO low priority alarm packet? */
    if(u16FrameID == 0xfe01) {
    	if (check_col(pinfo->cinfo, COL_INFO))
	        col_add_str(pinfo->cinfo, COL_INFO, "Alarm Low");

        dissect_PNIO_RTA(tvb, 0, pinfo, tree, drep);
        return TRUE;
    }

    /* this PN-RT packet doesn't seem to be PNIO specific */
    return FALSE;
}


/* the PNIO dcerpc interface table */
static dcerpc_sub_dissector pn_io_dissectors[] = {
{ 0, "Connect", dissect_IPNIO_Connect_rqst, dissect_IPNIO_Connect_resp },
{ 1, "Release", dissect_IPNIO_Release_rqst, dissect_IPNIO_Release_resp },
{ 2, "Read",    dissect_IPNIO_Read_rqst,    dissect_IPNIO_Read_resp },
{ 3, "Write",   dissect_IPNIO_Write_rqst,   dissect_IPNIO_Write_resp },
{ 4, "Control", dissect_IPNIO_Control_rqst, dissect_IPNIO_Control_resp },
	{ 0, NULL, NULL, NULL }
};


void
proto_register_pn_io (void)
{
	static hf_register_info hf[] = {
	{ &hf_pn_io_opnum,
		{ "Operation", "pn_io.opnum", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_reserved16,
		{ "Reserved", "pn_io.reserved16", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_array,
        { "Array", "pn_io.array", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_status,
		{ "Status", "pn_io.status", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_args_max,
		{ "ArgsMaximum", "pn_io.args_max", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_args_len,
		{ "ArgsLength", "pn_io.args_len", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_array_max_count,
		{ "MaximumCount", "pn_io.array_max_count", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_array_offset,
		{ "Offset", "pn_io.array_offset", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
	{ &hf_pn_io_array_act_count,
		{ "ActualCount", "pn_io.array_act_count", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_ar_uuid,
      { "ARUUID", "pn_io.ar_uuid", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_api,
      { "API", "pn_io.api", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_slot_nr,
      { "SlotNumber", "pn_io.slot_nr", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_subslot_nr,
      { "SubslotNumber", "pn_io.subslot_nr", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_index,
      { "Index", "pn_io.index", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_seq_number,
      { "SeqNumber", "pn_io.seq_number", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_record_data_length,
      { "RecordDataLength", "pn_io.record_data_length", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_padding,
      { "Padding", "pn_io.padding", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_add_val1,
      { "AdditionalValue1", "pn_io.add_val1", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_add_val2,
      { "AdditionalValue2", "pn_io.add_val2", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_block_type,
      { "BlockType", "pn_io.block_type", FT_UINT16, BASE_HEX, VALS(pn_io_block_type), 0x0, "", HFILL }},
    { &hf_pn_io_block_length,
      { "BlockLength", "pn_io.block_length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_block_version_high,
      { "BlockVersionHigh", "pn_io.block_version_high", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_block_version_low,
      { "BlockVersionLow", "pn_io.block_version_low", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_sessionkey,
      { "SessionKey", "pn_io.session_key", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_control_command,
      { "ControlCommand", "pn_io.control_command", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_control_command_prmend,
      { "PrmEnd", "pn_io.control_command.prmend", FT_UINT16, BASE_DEC, NULL, 0x0001, "", HFILL }},
    { &hf_pn_io_control_command_applready,
      { "ApplicationReady", "pn_io.control_command.applready", FT_UINT16, BASE_DEC, NULL, 0x0002, "", HFILL }},
    { &hf_pn_io_control_command_release,
      { "Release", "pn_io.control_command.release", FT_UINT16, BASE_DEC, NULL, 0x0004, "", HFILL }},
    { &hf_pn_io_control_command_done,
      { "Done", "pn_io.control_command.done", FT_UINT16, BASE_DEC, NULL, 0x0008, "", HFILL }},
    { &hf_pn_io_control_block_properties,
      { "ControlBlockProperties", "pn_io.control_block_properties", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_pn_io_error_code,
      { "ErrorCode", "pn_io.error_code", FT_UINT8, BASE_HEX, VALS(pn_io_error_code), 0x0, "", HFILL }},
    { &hf_pn_io_error_decode,
      { "ErrorDecode", "pn_io.error_decode", FT_UINT8, BASE_HEX, VALS(pn_io_error_decode), 0x0, "", HFILL }},
    { &hf_pn_io_error_code1,
      { "ErrorCode1", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1), 0x0, "", HFILL }},
    { &hf_pn_io_error_code2,
      { "ErrorCode2", "pn_io.error_code2", FT_UINT8, BASE_HEX, VALS(pn_io_error_code2), 0x0, "", HFILL }},
    { &hf_pn_io_error_code1_pniorw,
      { "ErrorCode1 (PNIORW)", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1_pniorw), 0x0, "", HFILL }},
    { &hf_pn_io_error_code1_pnio,
      { "ErrorCode1 (PNIO)", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1_pnio), 0x0, "", HFILL }},
	{ &hf_pn_io_block,
    { "Block", "pn_io.block", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_data,
      { "Undecoded Data", "pn_io.data", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},

    { &hf_pn_io_alarm_type,
      { "AlarmType", "pn_io.alarm_type", FT_UINT16, BASE_HEX, VALS(pn_io_alarm_type), 0x0, "", HFILL }},

    { &hf_pn_io_alarm_specifier,
      { "AlarmSpecifier", "pn_io.alarm_specifier", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_alarm_specifier_sequence,
      { "SequenceNumber", "pn_io.alarm_specifier.sequence", FT_UINT16, BASE_HEX, NULL, 0x07FF, "", HFILL }},
    { &hf_pn_io_alarm_specifier_channel,
      { "ChannelDiagnosis", "pn_io.alarm_specifier.channel", FT_UINT16, BASE_HEX, NULL, 0x0800, "", HFILL }},
    { &hf_pn_io_alarm_specifier_manufacturer,
      { "ManufacturerSpecificDiagnosis", "pn_io.alarm_specifier.manufacturer", FT_UINT16, BASE_HEX, NULL, 0x1000, "", HFILL }},
    { &hf_pn_io_alarm_specifier_submodule,
      { "SubmoduleDiagnosisState", "pn_io.alarm_specifier.submodule", FT_UINT16, BASE_HEX, NULL, 0x2000, "", HFILL }},
    { &hf_pn_io_alarm_specifier_ardiagnosis,
      { "ARDiagnosisState", "pn_io.alarm_specifier.ardiagnosis", FT_UINT16, BASE_HEX, NULL, 0x8000, "", HFILL }},

    { &hf_pn_io_alarm_dst_endpoint,
      { "AlarmDstEndpoint", "pn_io.alarm_dst_endpoint", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_alarm_src_endpoint,
      { "AlarmSrcEndpoint", "pn_io.alarm_src_endpoint", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_pdu_type,
      { "PDUType", "pn_io.pdu_type", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_pdu_type_type,
      { "Type", "pn_io.pdu_type.type", FT_UINT8, BASE_HEX, VALS(pn_io_pdu_type), 0x0F, "", HFILL }},
    { &hf_pn_io_pdu_type_version,
      { "Version", "pn_io.pdu_type.version", FT_UINT8, BASE_HEX, NULL, 0xF0, "", HFILL }},
    { &hf_pn_io_add_flags,
      { "AddFlags", "pn_io.add_flags", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_window_size,
      { "WindowSize", "pn_io.window_size", FT_UINT8, BASE_DEC, NULL, 0x0F, "", HFILL }},
    { &hf_pn_io_tack,
      { "TACK", "pn_io.tack", FT_UINT8, BASE_HEX, NULL, 0xF0, "", HFILL }},
    { &hf_pn_io_send_seq_num,
      { "SendSeqNum", "pn_io.send_seq_num", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_ack_seq_num,
      { "AckSeqNum", "pn_io.ack_seq_num", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_var_part_len,
      { "VarPartLen", "pn_io.var_part_len", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_module_ident_number,
      { "ModuleIdentNumber", "pn_io.module_ident_number", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_submodule_ident_number,
      { "SubmoduleIdentNumber", "pn_io.submodule_ident_number", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},

    { &hf_pn_io_ioxs,
      { "IOxS", "pn_io.ioxs", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
    { &hf_pn_io_ioxs_extension,
      { "Extension (1:another IOxS follows/0:no IOxS follows)", "pn_io.ioxs.extension", FT_UINT8, BASE_HEX, NULL, 0x01, "", HFILL }},
    { &hf_pn_io_ioxs_res14,
      { "Reserved (should be zero)", "pn_io.ioxs.res14", FT_UINT8, BASE_HEX, NULL, 0x1E, "", HFILL }},
    { &hf_pn_io_ioxs_instance,
      { "Instance (only valid, if DataState is bad)", "pn_io.ioxs.instance", FT_UINT8, BASE_HEX, VALS(pn_io_ioxs), 0x60, "", HFILL }},
    { &hf_pn_io_ioxs_datastate,
      { "DataState (1:good/0:bad)", "pn_io.ioxs.datastate", FT_UINT8, BASE_HEX, NULL, 0x80, "", HFILL }}
    };

	static gint *ett[] = {
		&ett_pn_io,
        &ett_pn_io_block,
        &ett_pn_io_status,
        &ett_pn_io_rtc,
        &ett_pn_io_rta,
		&ett_pn_io_pdu_type,
        &ett_pn_io_add_flags,
        &ett_pn_io_control_command,
        &ett_pn_io_ioxs
	};

	proto_pn_io = proto_register_protocol ("PROFINET IO", "PNIO", "pn_io");
	proto_register_field_array (proto_pn_io, hf, array_length (hf));
	proto_register_subtree_array (ett, array_length (ett));
}

void
proto_reg_handoff_pn_io (void)
{
	/* Register the protocols as dcerpc */
	dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_device, ver_pn_io_device, pn_io_dissectors, hf_pn_io_opnum);
	dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_controller, ver_pn_io_controller, pn_io_dissectors, hf_pn_io_opnum);
	dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_supervisor, ver_pn_io_supervisor, pn_io_dissectors, hf_pn_io_opnum);
	dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_parameterserver, ver_pn_io_parameterserver, pn_io_dissectors, hf_pn_io_opnum);

	heur_dissector_add("pn_rt", dissect_PNIO_heur, proto_pn_io);
}
