1 /* packet-pn-rtc-one.c
2  * Routines for PROFINET IO - RTC1 dissection.
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1999 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 /*
12  * The PN-IO protocol is a field bus protocol related to decentralized
13  * periphery and is developed by the PROFIBUS Nutzerorganisation e.V. (PNO),
14  * see: www.profibus.com
15  *
16  *
17  * PN-IO is based on the common DCE-RPC and the "lightweight" PN-RT
18  * (ethernet type 0x8892) protocols.
19  *
20  * The context manager (CM) part is handling context information
21  * (like establishing, ...) and is using DCE-RPC as its underlying
22  * protocol.
23  *
24  * The actual cyclic data transfer and acyclic notification uses the
25  * "lightweight" PN-RT protocol.
26  *
27  * There are some other related PROFINET protocols (e.g. PN-DCP, which is
28  * handling addressing topics).
29  *
30  * Please note: the PROFINET CBA protocol is independent of the PN-IO protocol!
31  */
32 
33 /*
34  * Cyclic PNIO RTC1 Data Dissection:
35  *
36  * To dissect cyclic PNIO RTC1 frames, this plug-in has to collect important module
37  * information out of "Ident OK", "Connect Request" and "Write Response"
38  * frames first.
39  *
40  * The data of Stationname-, -type and -id will be gained out of
41  * packet-pn-dcp.c. The header packet-pn.h will transfer those data between
42  * those two files.
43  *
44  * This file is used as a "addon" for packet-dcerpc-pn-io.c. Within "packet-dcerpc-pn-io.c"
45  * the defined structures in "packet-pn.h" will be filled with all necessary information.
46  * Those informations will be used in thise file to dissect cyclic PNIO RTC1 and PROFIsafe
47  * frames. Furthermore since RTC1 is a special frame type of PNIO, this dissection uses the
48  * already defined protocol PNIO.
49  *
50  * Overview for cyclic PNIO RTC1 data dissection functions:
51  *   -> dissect_PNIO_C_SDU_RTC1 (general dissection of RTC1)
52  */
53 
54 #include "config.h"
55 
56 #include <stdio.h>
57 #include <string.h>
58 #include <glib.h>
59 #include <epan/packet.h>
60 #include <epan/dissectors/packet-dcerpc.h>
61 #include <epan/proto.h>
62 #include <epan/expert.h>
63 
64 #include "packet-pn.h"
65 
66 
67 #define F_MESSAGE_TRAILER_4BYTE  4      /* PROFIsafe: Defines the Amount of Bytes for CRC and Status-/Controlbyte */
68 #define PN_INPUT_CR              1      /* PROFINET Input Connect Request value */
69 #define PN_INPUT_DATADESCRITPION 1      /* PROFINET Input Data Description value */
70 
71 
72 static int proto_pn_io_rtc1 = -1;
73 
74 /* General module information */
75 static int hf_pn_io_frame_info_type = -1;
76 static int hf_pn_io_frame_info_vendor = -1;
77 static int hf_pn_io_frame_info_nameofstation = -1;
78 static int hf_pn_io_frame_info_gsd_found = -1;
79 static int hf_pn_io_frame_info_gsd_error = -1;
80 static int hf_pn_io_frame_info_gsd_path = -1;
81 static int hf_pn_io_io_data_object = -1;
82 static int hf_pn_io_io_data_object_info_module_diff = -1;
83 static int hf_pn_io_io_data_object_info_moduleidentnumber = -1;
84 static int hf_pn_io_io_data_object_info_submoduleidentnumber = -1;
85 
86 static int hf_pn_io_iocs = -1;
87 static int hf_pn_io_iops = -1;
88 static int hf_pn_io_ioxs_extension = -1;
89 static int hf_pn_io_ioxs_res14 = -1;
90 static int hf_pn_io_ioxs_instance = -1;
91 static int hf_pn_io_ioxs_datastate = -1;
92 
93 /* PROFIsafe statusbyte and controlbyte */
94 static int hf_pn_io_ps_sb = -1;
95 static int hf_pn_io_ps_sb_iparOK = -1;
96 static int hf_pn_io_ps_sb_DeviceFault = -1;
97 static int hf_pn_io_ps_sb_CECRC = -1;
98 static int hf_pn_io_ps_sb_WDtimeout = -1;
99 static int hf_pn_io_ps_sb_FVactivated = -1;
100 static int hf_pn_io_ps_sb_Toggle_d = -1;
101 static int hf_pn_io_ps_sb_ConsNr_reset = -1;
102 static int hf_pn_io_ps_sb_res = -1;
103 static int hf_pn_io_ps_sb_toggelBitChanged = -1;
104 static int hf_pn_io_ps_sb_toggelBitChange_slot_nr = -1;
105 static int hf_pn_io_ps_sb_toggelBitChange_subslot_nr = -1;
106 
107 static int hf_pn_io_ps_cb = -1;
108 static int hf_pn_io_ps_cb_iparEN = -1;
109 static int hf_pn_io_ps_cb_OAReq = -1;
110 static int hf_pn_io_ps_cb_resetConsNr = -1;
111 static int hf_pn_io_ps_cb_useTO2 = -1;
112 static int hf_pn_io_ps_cb_activateFV = -1;
113 static int hf_pn_io_ps_cb_Toggle_h = -1;
114 static int hf_pn_io_ps_cb_Chf_ACK = -1;
115 static int hf_pn_io_ps_cb_loopcheck = -1;
116 static int hf_pn_io_ps_cb_toggelBitChanged = -1;
117 static int hf_pn_io_ps_cb_toggelBitChange_slot_nr = -1;
118 static int hf_pn_io_ps_cb_toggelBitChange_subslot_nr = -1;
119 
120 /* PROFIsafe */
121 static int hf_pn_io_ps_f_dest_adr = -1;
122 static int hf_pn_io_ps_f_data = -1;
123 
124 static gint ett_pn_io_rtc = -1;
125 static gint ett_pn_io_ioxs = -1;
126 static gint ett_pn_io_io_data_object = -1;
127 
128 static expert_field ei_pn_io_too_many_data_objects = EI_INIT;
129 
130 static const value_string pn_io_ioxs_extension[] = {
131     { 0x00 /*  0*/, "No IOxS octet follows" },
132     { 0x01 /*  1*/, "One more IOxS octet follows" },
133     { 0, NULL }
134 };
135 
136 static const value_string pn_io_ioxs_instance[] = {
137     { 0x00 /*  0*/, "Detected by subslot" },
138     { 0x01 /*  1*/, "Detected by slot" },
139     { 0x02 /*  2*/, "Detected by IO device" },
140     { 0x03 /*  3*/, "Detected by IO controller" },
141     { 0, NULL }
142 };
143 
144 static const value_string pn_io_ioxs_datastate[] = {
145     { 0x00 /*  0*/, "Bad" },
146     { 0x01 /*  1*/, "Good" },
147     { 0, NULL }
148 };
149 
150 
151 static int * const ps_sb_fields[] = {
152     &hf_pn_io_ps_sb_res,
153     &hf_pn_io_ps_sb_ConsNr_reset,
154     &hf_pn_io_ps_sb_Toggle_d,
155     &hf_pn_io_ps_sb_FVactivated,
156     &hf_pn_io_ps_sb_WDtimeout,
157     &hf_pn_io_ps_sb_CECRC,
158     &hf_pn_io_ps_sb_DeviceFault,
159     &hf_pn_io_ps_sb_iparOK,
160     NULL
161 };
162 
163 static int * const ps_cb_fields[] = {
164     &hf_pn_io_ps_cb_loopcheck,
165     &hf_pn_io_ps_cb_Chf_ACK,
166     &hf_pn_io_ps_cb_Toggle_h,
167     &hf_pn_io_ps_cb_activateFV,
168     &hf_pn_io_ps_cb_useTO2,
169     &hf_pn_io_ps_cb_resetConsNr,
170     &hf_pn_io_ps_cb_OAReq,
171     &hf_pn_io_ps_cb_iparEN,
172     NULL
173 };
174 
175 static int * const ioxs_fields[] = {
176     &hf_pn_io_ioxs_datastate,
177     &hf_pn_io_ioxs_instance,
178     &hf_pn_io_ioxs_res14,
179     &hf_pn_io_ioxs_extension,
180     NULL
181 };
182 
183 
184 /* Dissector for PROFIsafe Status Byte */
185 static int
dissect_pn_io_ps_SB(tvbuff_t * tvb,int offset,packet_info * pinfo _U_,proto_tree * tree,guint8 * drep _U_,int hfindex,int * const * fields)186 dissect_pn_io_ps_SB(tvbuff_t *tvb, int offset,
187 packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_, int hfindex, int * const *fields)
188 {
189 
190     if (tree) {
191         guint8     u8StatusByte;
192         proto_item *sb_item;
193 
194         u8StatusByte = tvb_get_guint8(tvb, offset);
195 
196         /* Add Status Byte subtree */
197         sb_item = proto_tree_add_bitmask_with_flags(tree, tvb, offset, hfindex, ett_pn_io_ioxs, fields,
198             ENC_LITTLE_ENDIAN, BMT_NO_APPEND);
199         proto_item_append_text(sb_item, " (%s)", ((u8StatusByte == 0x20) || (u8StatusByte == 0x00)) ? "normal" : "unnormal");
200     }
201 
202     return offset + 1;
203 }
204 
205 
206 /* Dissector for PROFIsafe Control Byte */
207 static int
dissect_pn_io_ps_CB(tvbuff_t * tvb,int offset,packet_info * pinfo _U_,proto_tree * tree,guint8 * drep _U_,int hfindex,int * const * fields)208 dissect_pn_io_ps_CB(tvbuff_t *tvb, int offset,
209 packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_, int hfindex, int * const *fields)
210 {
211 
212     if (tree) {
213         guint8     u8ControlByte;
214         proto_item *cb_item;
215 
216         u8ControlByte = tvb_get_guint8(tvb, offset);
217 
218         /* Add Status Byte subtree */
219         cb_item = proto_tree_add_bitmask_with_flags(tree, tvb, offset, hfindex, ett_pn_io_ioxs, fields,
220             ENC_LITTLE_ENDIAN, BMT_NO_APPEND);
221         proto_item_append_text(cb_item, " (%s)", ((u8ControlByte == 0x20) || (u8ControlByte == 0x00) ||
222             (u8ControlByte == 0xa0) || (u8ControlByte == 0x80)) ? "normal" : "unnormal");
223     }
224 
225     return offset + 1;
226 }
227 
228 
229 /* Dissector for IOCS (As each IOCS stands for a specific Slot & Subslot) */
230 static int
dissect_PNIO_IOCS(tvbuff_t * tvb,int offset,packet_info * pinfo _U_,proto_tree * tree,guint8 * drep _U_,int hfindex,guint16 slotNr,guint16 subSlotNr,int * const * fields)231 dissect_PNIO_IOCS(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree,
232             guint8 *drep _U_, int hfindex, guint16 slotNr, guint16 subSlotNr, int * const *fields)
233 {
234 
235     if (tree) {
236         guint8      u8IOxS;
237         proto_item *ioxs_item;
238 
239         u8IOxS = tvb_get_guint8(tvb, offset);
240 
241         /* Add ioxs subtree */
242         ioxs_item = proto_tree_add_bitmask_with_flags(tree, tvb, offset, hfindex,
243             ett_pn_io_ioxs, fields, ENC_LITTLE_ENDIAN, BMT_NO_APPEND);
244         proto_item_append_text(ioxs_item,
245             " (%s%s), Slot: 0x%x, Subslot: 0x%x",
246             (u8IOxS & 0x01) ? "another IOxS follows " : "",
247             (u8IOxS & 0x80) ? "good" : "bad",
248             slotNr,
249             subSlotNr);
250     }
251 
252     return offset + 1;
253 }
254 
255 
256 /* dissect the IOxS (IOCS, IOPS) field */
257 static int
dissect_PNIO_IOxS(tvbuff_t * tvb,int offset,packet_info * pinfo _U_,proto_tree * tree,guint8 * drep _U_,int hfindex,int * const * fields)258 dissect_PNIO_IOxS(tvbuff_t *tvb, int offset,
259 packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_, int hfindex, int * const *fields)
260 {
261 
262     if (tree) {
263         guint8     u8IOxS;
264         proto_item *ioxs_item;
265 
266         u8IOxS = tvb_get_guint8(tvb, offset);
267 
268         /* Add ioxs subtree */
269         ioxs_item = proto_tree_add_bitmask_with_flags(tree, tvb, offset, hfindex,
270             ett_pn_io_ioxs, fields, ENC_LITTLE_ENDIAN, BMT_NO_APPEND);
271         proto_item_append_text(ioxs_item,
272             " (%s%s)",
273             (u8IOxS & 0x01) ? "another IOxS follows " : "",
274             (u8IOxS & 0x80) ? "good" : "bad");
275     }
276 
277     return offset + 1;
278 }
279 
280 
281 /* Universel dissector for flexibel PROFIsafe Data 8 to 64 Bits */
282 static int
dissect_pn_io_ps_uint(tvbuff_t * tvb,gint offset,packet_info * pinfo _U_,proto_tree * tree,guint8 * drep,int hfindex,guint8 bytelength,guint64 * pdata)283 dissect_pn_io_ps_uint(tvbuff_t *tvb, gint offset, packet_info *pinfo _U_,
284     proto_tree *tree, guint8 *drep,
285 int hfindex, guint8 bytelength, guint64 *pdata)
286 {
287     guint64  data;
288     gboolean generalDissection;
289 
290     generalDissection = FALSE;
291 
292     switch (bytelength) {
293     case 1:     /* 8 Bit Safety IO Data */
294         data = tvb_get_guint8(tvb, offset);
295         if (pdata)
296             *pdata = data;
297         break;
298 
299     case 2:     /* 16 Bit Safety IO Data */
300         data = tvb_get_letohs(tvb, offset);
301         if (pdata)
302             *pdata = data;
303         break;
304 
305     case 3:     /* 24 Bit Safety IO Data */
306         data = tvb_get_letoh24(tvb, offset);
307         if (pdata)
308             *pdata = data;
309         break;
310 
311     case 4:     /* 32 Bit Safety IO Data */
312         data = tvb_get_letohl(tvb, offset);
313         if (pdata)
314             *pdata = data;
315         break;
316 
317     case 5:     /* 40 Bit Safety IO Data */
318         data = tvb_get_letoh40(tvb, offset);
319         if (pdata)
320             *pdata = data;
321         break;
322 
323     case 6:     /* 48 Bit Safety IO Data */
324         data = tvb_get_letoh48(tvb, offset);
325         if (pdata)
326             *pdata = data;
327         break;
328 
329     case 7:     /* 56 Bit Safety IO Data */
330         data = tvb_get_letoh56(tvb, offset);
331         if (pdata)
332             *pdata = data;
333         break;
334 
335     case 8:     /* 64 Bit Safety IO Data */
336         data = tvb_get_letoh64(tvb, offset);
337         if (pdata)
338             *pdata = data;
339         break;
340 
341     default:    /* Safety IO Data is too big to save it into one variable */
342         dissect_pn_user_data(tvb, offset, pinfo, tree, bytelength, "Safety IO Data");
343         generalDissection = TRUE;
344         break;
345     }
346 
347     if (tree && generalDissection == FALSE) {
348         proto_tree_add_item(tree, hfindex, tvb, offset, bytelength, DREP_ENC_INTEGER(drep));
349     }
350 
351     return offset + bytelength;
352 }
353 
354 
355 /* dissect a PN-IO RTC1 Cyclic Service Data Unit */
356 int
dissect_PNIO_C_SDU_RTC1(tvbuff_t * tvb,int offset,packet_info * pinfo,proto_tree * tree,guint8 * drep _U_,guint16 frameid)357 dissect_PNIO_C_SDU_RTC1(tvbuff_t *tvb, int offset,
358     packet_info *pinfo, proto_tree *tree, guint8 *drep _U_, guint16 frameid)
359 {
360     proto_tree  *data_tree = NULL;
361 
362     /* Count & offset for comparation of the arrays */
363     guint16     frameOffset;
364     guint32     objectCounter;
365     gboolean    inputFlag;
366     gboolean    outputFlag;
367     gboolean    psInfoText;     /* Used to display only once per frame the info text "PROFIsafe Device" */
368 
369     proto_item *data_item;
370     proto_item *IODataObject_item;
371     proto_item *IODataObject_item_info;
372     proto_tree *IODataObject_tree;
373     proto_item *ModuleID_item;
374     proto_item *ModuleDiff_item;
375 
376     wmem_strbuf_t *moduleName;
377 
378     guint8  toggleBitSb;
379     guint8  toggleBitCb;
380     guint64 f_data;
381 
382     guint8  statusbyte;
383     guint8  controlbyte;
384 
385     guint16 number_io_data_objects_input_cr;
386     guint16 number_iocs_input_cr;
387     guint16 number_io_data_objects_output_cr;
388     guint16 number_iocs_output_cr;
389 
390     conversation_t    *conversation;
391     stationInfo       *station_info = NULL;
392     iocsObject        *iocs_object;
393     ioDataObject      *io_data_object;
394     moduleDiffInfo    *module_diff_info;
395     wmem_list_frame_t *frame;
396     wmem_list_frame_t *frame_diff;
397 
398     /* Initial */
399     frameOffset = 0;
400     f_data = 0;
401     inputFlag = FALSE;
402     outputFlag = FALSE;
403     psInfoText = FALSE;
404     number_io_data_objects_input_cr = 0;
405     number_iocs_input_cr = 0;
406     number_io_data_objects_output_cr = 0;
407     number_iocs_output_cr = 0;
408 
409     wmem_list_frame_t  *aruuid_frame;
410     ARUUIDFrame        *current_aruuid_frame = NULL;
411     guint32             current_aruuid = 0;
412 
413     col_set_str(pinfo->cinfo, COL_PROTOCOL, "PNIO");            /* set protocol name */
414 
415     data_item = proto_tree_add_protocol_format(tree, proto_pn_io_rtc1, tvb, offset, tvb_captured_length(tvb),
416             "PROFINET IO Cyclic Service Data Unit: %u bytes", tvb_captured_length(tvb));
417     data_tree = proto_item_add_subtree(data_item, ett_pn_io_rtc);
418 
419     /* dissect_dcerpc_uint16(tvb, offset, pinfo, data_tree, drep, hf_pn_io_packedframe_SFCRC, &u16SFCRC); */
420     if (!(dissect_CSF_SDU_heur(tvb, pinfo, data_tree, NULL) == FALSE))
421         return(tvb_captured_length(tvb));
422 
423     /* Only dissect cyclic RTC1 frames, if PN Connect Request has been read */
424     conversation = find_conversation(pinfo->num, &pinfo->dl_src, &pinfo->dl_dst, ENDPOINT_NONE, 0, 0, 0);
425 
426     /* Detect input data package and output data package */
427     if (conversation != NULL) {
428         if (aruuid_frame_setup_list != NULL) {
429             for (aruuid_frame = wmem_list_tail(aruuid_frame_setup_list); aruuid_frame != NULL; aruuid_frame = wmem_list_frame_prev(aruuid_frame)) {
430                 current_aruuid_frame = (ARUUIDFrame*)wmem_list_frame_data(aruuid_frame);
431                 /* There are prerequisites to dissect RTC frame data */
432                 /* Current station info must be found before RTC frame dissection starts */
433                 /* if RTC frame has setup frame and setup frame number is less than RTC frame number AND if RTC frame has release frame and release frame number is greater than RTC frame number */
434                 /* if RTC frame has setup frame and setup frame number is less than RTC frame number AND RTC frame does not have release frame yet! */
435                 /* then, get AR UUID of current station info */
436                 if ((current_aruuid_frame->setupframe && current_aruuid_frame->setupframe < pinfo->num) &&
437                    ((current_aruuid_frame->releaseframe && current_aruuid_frame->releaseframe > pinfo->num) ||
438                     !current_aruuid_frame->releaseframe)) {
439                     if (current_aruuid_frame->inputframe == frameid) {
440                         current_aruuid = current_aruuid_frame->aruuid.data1;
441                         break;
442                     }
443                     else if (current_aruuid_frame->outputframe == frameid) {
444                         current_aruuid = current_aruuid_frame->aruuid.data1;
445                         break;
446                     }
447                 }
448             }
449         }
450 
451         station_info = (stationInfo*)conversation_get_proto_data(conversation, current_aruuid);
452 
453         if (station_info != NULL) {
454             pn_find_dcp_station_info(station_info, conversation);
455 
456             if (pnio_ps_selection == TRUE) {
457                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "PNIO_PS");    /* set PROFISsafe protocol name */
458             }
459 
460             if (addresses_equal(&(pinfo->src), conversation_key_addr1(conversation->key_ptr)) && addresses_equal(&(pinfo->dst), conversation_key_addr2(conversation->key_ptr))) {
461                 inputFlag = TRUE;
462                 outputFlag = FALSE;
463                 number_io_data_objects_input_cr = station_info->ioDataObjectNr;
464                 number_iocs_input_cr = station_info->iocsNr;
465             }
466 
467             if (addresses_equal(&(pinfo->dst), conversation_key_addr1(conversation->key_ptr)) && addresses_equal(&(pinfo->src), conversation_key_addr2(conversation->key_ptr))) {
468                 outputFlag = TRUE;
469                 inputFlag = FALSE;
470                 number_io_data_objects_output_cr = station_info->ioDataObjectNr;
471                 number_iocs_output_cr = station_info->iocsNr;
472             }
473         }
474     }
475 
476     /* ------- Input (PNIO) / Response (PNIO_PS) Frame Handling ------- */
477     if (inputFlag) {
478         if (pnio_ps_selection == TRUE) {
479             proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_type, tvb,
480                 offset, 0, "Response", "Response Frame (IO_Device -> IO_Controller)");
481         }
482         else {
483             proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_type, tvb,
484                 offset, 0, "Input", "Input Frame (IO_Device -> IO_Controller)");
485         }
486 
487         if (station_info != NULL) {
488             if (station_info->typeofstation != NULL) {
489                 proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_vendor, tvb, 0,
490                     0, station_info->typeofstation, "\"%s\"", station_info->typeofstation);
491             }
492             if (station_info->nameofstation != NULL) {
493                 proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_nameofstation, tvb, 0,
494                     0, station_info->nameofstation, "\"%s\"", station_info->nameofstation);
495             }
496 
497             if (station_info->gsdPathLength == TRUE) {      /* given path isn't too long for the array */
498                 if (station_info->gsdFound == TRUE) {       /* found a GSD-file */
499                     if (station_info->gsdLocation != NULL) {
500                         IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_found, tvb, offset, 0, ENC_NA);
501                         proto_item_append_text(IODataObject_item_info, ": \"%s\"", station_info->gsdLocation);
502                     }
503                 }
504                 else {
505                     if (station_info->gsdLocation != NULL) {
506                         IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_error, tvb, offset, 0, ENC_NA);
507                         proto_item_append_text(IODataObject_item_info, " Please place relevant GSD-file under \"%s\"", station_info->gsdLocation);
508                     }
509                 }
510             }
511             else {
512                 IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_path, tvb, offset, 0, ENC_NA);
513                 proto_item_append_text(IODataObject_item_info, " Please check your GSD-file networkpath. (No Path configured)");
514             }
515         }
516 
517         /* ---- Input IOData-/IOCS-Object Handling ---- */
518         objectCounter = number_io_data_objects_input_cr + number_iocs_input_cr;
519         if (objectCounter > (guint)tvb_reported_length_remaining(tvb, offset)) {
520             expert_add_info_format(pinfo, data_item, &ei_pn_io_too_many_data_objects, "Too many data objects: %d", objectCounter);
521             return(tvb_captured_length(tvb));
522         }
523 
524         while (objectCounter--) {
525             /* ---- Input IO Data Object Handling ---- */
526             if (station_info != NULL) {
527                 for (frame = wmem_list_head(station_info->ioobject_data_in); frame != NULL; frame = wmem_list_frame_next(frame)) {
528                     io_data_object = (ioDataObject*)wmem_list_frame_data(frame);
529                     if (io_data_object->frameOffset == frameOffset) {
530                         /* Found following object */
531 
532                         IODataObject_item = proto_tree_add_item(data_tree, hf_pn_io_io_data_object, tvb, offset, 0, ENC_NA);
533                         IODataObject_tree = proto_item_add_subtree(IODataObject_item, ett_pn_io_io_data_object);
534 
535                         /* Control: the Device still uses the correct ModuleIdentNumber? */
536                         for (frame_diff = wmem_list_head(station_info->diff_module); frame_diff != NULL; frame_diff = wmem_list_frame_next(frame_diff)) {
537                             module_diff_info = (moduleDiffInfo*)wmem_list_frame_data(frame_diff);
538                             if (io_data_object->moduleIdentNr != module_diff_info->modulID) {
539                                 ModuleDiff_item = proto_tree_add_item(IODataObject_tree, hf_pn_io_io_data_object_info_module_diff, tvb, 0, 0, ENC_NA);
540                                 proto_item_append_text(ModuleDiff_item, ": Device using ModuleIdentNumber 0x%08x instead of 0x%08x", module_diff_info->modulID, io_data_object->moduleIdentNr);
541                                 break;
542                             }
543                         }
544 
545                         proto_tree_add_uint(IODataObject_tree, hf_pn_io_io_data_object_info_moduleidentnumber, tvb, 0, 0, io_data_object->moduleIdentNr);
546                         proto_tree_add_uint(IODataObject_tree, hf_pn_io_io_data_object_info_submoduleidentnumber, tvb, 0, 0, io_data_object->subModuleIdentNr);
547 
548                         /* PROFIsafe Supported Inputmodule handling */
549                         if (io_data_object->profisafeSupported == TRUE && pnio_ps_selection == TRUE) {
550                             if (io_data_object->profisafeSupported == TRUE && psInfoText == FALSE) {
551                                 /* Only add one information string per device to the infotext */
552                                 col_append_str(pinfo->cinfo, COL_INFO, ", PROFIsafe Device");    /* Add string to wireshark infotext */
553                                 psInfoText = TRUE;
554                             }
555 
556                             proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_f_dest_adr, tvb, 0, 0, io_data_object->f_dest_adr);
557 
558                             /* Get Safety IO Data */
559                             if ((io_data_object->length - F_MESSAGE_TRAILER_4BYTE) > 0) {
560                                 offset = dissect_pn_io_ps_uint(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_ps_f_data,
561                                     (io_data_object->length - F_MESSAGE_TRAILER_4BYTE), &f_data);
562                             }
563 
564                             /* ---- Check for new PNIO data using togglebit ---- */
565                             statusbyte = tvb_get_guint8(tvb, offset);
566                             toggleBitSb = statusbyte & 0x20;     /* get ToggleBit of StatusByte */
567 
568                             if (io_data_object->lastToggleBit != toggleBitSb) {    /* ToggleBit has changed --> new Data incoming */
569                                 /* Special Filter for ToggleBit within Statusbyte */
570                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_sb_toggelBitChanged, tvb, offset, 0, toggleBitSb);
571                                 proto_item_set_hidden(ModuleID_item);
572 
573                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_sb_toggelBitChange_slot_nr, tvb, offset, 0, io_data_object->slotNr);
574                                 proto_item_set_hidden(ModuleID_item);
575 
576                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_sb_toggelBitChange_subslot_nr, tvb, offset, 0, io_data_object->subSlotNr);
577                                 proto_item_set_hidden(ModuleID_item);
578                             }
579 
580                             offset = dissect_pn_io_ps_SB(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_ps_sb, ps_sb_fields);
581                             offset = dissect_pn_user_data(tvb, offset, pinfo, IODataObject_tree, io_data_object->f_crc_len, "CRC");
582 
583                             io_data_object->last_sb_cb = statusbyte;       /* save the value of current statusbyte */
584                             io_data_object->lastToggleBit = toggleBitSb;   /* save the value of current togglebit within statusbyte */
585                         }    /* END of PROFIsafe Module Handling */
586 
587                         else {
588                             /* Module is not PROFIsafe supported */
589                             offset = dissect_pn_user_data(tvb, offset, pinfo, IODataObject_tree, io_data_object->length, "IO Data");
590                         }
591 
592                         if (io_data_object->discardIOXS == FALSE) {
593                             offset = dissect_PNIO_IOxS(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_iops, ioxs_fields);
594                             proto_item_set_len(IODataObject_item, io_data_object->length + 1);     /* Length = Databytes + IOXS Byte */
595                         }
596                         else {
597                             proto_item_set_len(IODataObject_item, io_data_object->length);         /* Length = Databytes */
598                         }
599 
600                         proto_item_append_text(IODataObject_item, ": Slot: 0x%x Subslot: 0x%x",
601                             io_data_object->slotNr, io_data_object->subSlotNr);
602 
603 
604                         /* ModuleIdentNr appears not only once in GSD-file -> set module name more generally */
605                         if (io_data_object->amountInGSDML > 1) {    /* if ModuleIdentNr only appears once in GSD-file, use the found GSD-file-ModuleName, else ... */
606                             if (io_data_object->slotNr == 0) {
607                                 moduleName = wmem_strbuf_new(pinfo->pool, "Headstation");
608                             }
609                             else {
610                                 moduleName = wmem_strbuf_new(pinfo->pool, "Module");
611                             }
612 
613                             if (io_data_object->profisafeSupported == TRUE) {
614                                 /* PROFIsafe */
615                                 if (io_data_object->length >= 5) {        /* 5 due to 3 CRC bytes &  1 status byte & (at least) 1 data byte */
616                                     wmem_strbuf_append(moduleName, ", DI");
617                                 }
618                                 else {
619                                     wmem_strbuf_append(moduleName, ", DO");
620                                 }
621                             }
622                             else {
623                                 /* PROFINET */
624                                 if (io_data_object->length > 0) {
625                                     wmem_strbuf_append(moduleName, ", DI");
626                                 }
627                                 else {
628                                     wmem_strbuf_append(moduleName, ", DO");
629                                 }
630                             }
631 
632                             io_data_object->moduleNameStr = wmem_strdup(wmem_file_scope(), wmem_strbuf_get_str(moduleName));
633                         }
634 
635                         proto_item_append_text(IODataObject_item, " ModuleName: \"%s\"", io_data_object->moduleNameStr);
636 
637                         /* emphasize the PROFIsafe supported Modul */
638                         if (io_data_object->profisafeSupported == TRUE && pnio_ps_selection == TRUE) {
639                             (proto_item_append_text(IODataObject_item, " (PROFIsafe Module)"));
640                         }
641 
642 
643                         /* Set frameOffset to its new value, to find the next object */
644                         frameOffset = frameOffset + io_data_object->length;  /* frameOffset = current value + data bytes */
645                         if (io_data_object->discardIOXS == FALSE) {
646                             frameOffset = frameOffset + 1;      /* frameOffset = current value + iops byte */
647                         }
648                     }
649                 }
650             }
651 
652             /* ---- Input IOCS Object Handling ---- */
653             if (station_info != NULL) {
654                 for (frame = wmem_list_head(station_info->iocs_data_in); frame != NULL; frame = wmem_list_frame_next(frame)) {
655                     iocs_object = (iocsObject*)wmem_list_frame_data(frame);
656                     if (iocs_object->frameOffset == frameOffset) {
657                         offset = dissect_PNIO_IOCS(tvb, offset, pinfo, data_tree, drep, hf_pn_io_iocs, iocs_object->slotNr,
658                             iocs_object->subSlotNr, ioxs_fields);
659 
660                         /* Set frameOffset to its new value, to find the next object */
661                         frameOffset = frameOffset + 1;      /* frameOffset = current value + iops byte */
662 
663                         break;
664                     }
665                 }
666             }
667         }
668 
669         /* Dissect padding */
670         offset = dissect_pn_user_data(tvb, offset, pinfo, tree, tvb_captured_length_remaining(tvb, offset), "GAP and RTCPadding");
671     }   /* END of Input Frame Handling */
672 
673     /* ----- Output (PNIO) / Request (PNIO_PS) Frame Handling ------ */
674     else if (outputFlag) {
675         if (pnio_ps_selection == TRUE) {
676             proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_type, tvb,
677                 offset, 0, "Request", "Request Frame (IO_Controller -> IO_Device)");
678         }
679         else {
680             proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_type, tvb,
681                 offset, 0, "Output", "Output Frame (IO_Controller -> IO_Device)");
682         }
683 
684         if (station_info != NULL) {
685             if (station_info->typeofstation != NULL) {
686                 proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_vendor, tvb, 0,
687                     0, station_info->typeofstation, "\"%s\"", station_info->typeofstation);
688             }
689             if (station_info->nameofstation != NULL) {
690                 proto_tree_add_string_format_value(data_tree, hf_pn_io_frame_info_nameofstation, tvb, 0,
691                     0, station_info->nameofstation, "\"%s\"", station_info->nameofstation);
692             }
693 
694             if (station_info->gsdPathLength == TRUE) {      /* given path isn't too long for the array */
695                 if (station_info->gsdFound == TRUE) {       /* found a GSD-file */
696                     if (station_info->gsdLocation != NULL) {
697                         IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_found, tvb, offset, 0, ENC_NA);
698                         proto_item_append_text(IODataObject_item_info, ": \"%s\"", station_info->gsdLocation);
699                     }
700                 }
701                 else {
702                     if (station_info->gsdLocation != NULL) {
703                         IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_error, tvb, offset, 0, ENC_NA);
704                         proto_item_append_text(IODataObject_item_info, " Please place relevant GSD-file under \"%s\"", station_info->gsdLocation);
705                     }
706                 }
707             }
708             else {
709                 IODataObject_item_info = proto_tree_add_item(data_tree, hf_pn_io_frame_info_gsd_path, tvb, offset, 0, ENC_NA);
710                 proto_item_append_text(IODataObject_item_info, " Please check your GSD-file networkpath. (No Path configured)");
711             }
712         }
713 
714         /* ---- Output IOData-/IOCS-Object Handling ---- */
715         objectCounter = number_io_data_objects_output_cr + number_iocs_output_cr;
716         if (objectCounter > (guint)tvb_reported_length_remaining(tvb, offset)) {
717             expert_add_info_format(pinfo, data_item, &ei_pn_io_too_many_data_objects, "Too many data objects: %d", objectCounter);
718             return(tvb_captured_length(tvb));
719         }
720         while (objectCounter--) {
721             /* ---- Output IO Data Object Handling ---- */
722             if (station_info != NULL) {
723                 for (frame = wmem_list_head(station_info->ioobject_data_out); frame != NULL; frame = wmem_list_frame_next(frame)) {
724                     io_data_object = (ioDataObject*)wmem_list_frame_data(frame);
725                     if (io_data_object != NULL && io_data_object->frameOffset == frameOffset) {
726                         /* Found following object */
727 
728                         IODataObject_item = proto_tree_add_item(data_tree, hf_pn_io_io_data_object, tvb, offset, 0, ENC_NA);
729                         IODataObject_tree = proto_item_add_subtree(IODataObject_item, ett_pn_io_io_data_object);
730 
731                         /* Control: the Device still uses the correct ModuleIdentNumber? */
732                         for (frame_diff = wmem_list_head(station_info->diff_module); frame_diff != NULL; frame_diff = wmem_list_frame_next(frame_diff)) {
733                             module_diff_info = (moduleDiffInfo*)wmem_list_frame_data(frame_diff);
734                             if (io_data_object->moduleIdentNr != module_diff_info->modulID) {
735                                 ModuleDiff_item = proto_tree_add_item(IODataObject_tree, hf_pn_io_io_data_object_info_module_diff, tvb, 0, 0, ENC_NA);
736                                 proto_item_append_text(ModuleDiff_item, ": Device using ModuleIdentNumber 0x%08x instead of 0x%08x", module_diff_info->modulID, io_data_object->moduleIdentNr);
737                                 break;
738                             }
739                         }
740 
741                         proto_tree_add_uint(IODataObject_tree, hf_pn_io_io_data_object_info_moduleidentnumber, tvb, 0, 0, io_data_object->moduleIdentNr);
742                         proto_tree_add_uint(IODataObject_tree, hf_pn_io_io_data_object_info_submoduleidentnumber, tvb, 0, 0, io_data_object->subModuleIdentNr);
743 
744                         if (io_data_object->profisafeSupported == TRUE && pnio_ps_selection == TRUE) {
745                             if (io_data_object->profisafeSupported == TRUE && psInfoText == FALSE) {
746                                 /* Only add one information string per device to the infotext */
747                                 col_append_str(pinfo->cinfo, COL_INFO, ", PROFIsafe Device");    /* Add string to wireshark infotext */
748                                 psInfoText = TRUE;
749                             }
750 
751                             proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_f_dest_adr, tvb, 0, 0, io_data_object->f_dest_adr);
752 
753                             /* Get Safety IO Data */
754                             if ((io_data_object->length - F_MESSAGE_TRAILER_4BYTE) > 0) {
755                                 offset = dissect_pn_io_ps_uint(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_ps_f_data,
756                                     (io_data_object->length - F_MESSAGE_TRAILER_4BYTE), &f_data);
757                             }
758 
759                             /* ---- Check for new PNIO data using togglebit ---- */
760                             controlbyte = tvb_get_guint8(tvb, offset);
761                             toggleBitCb = controlbyte & 0x20;               /* get ToggleBit of Controlbyte */
762 
763                             if (io_data_object->lastToggleBit != toggleBitCb) {   /* ToggleBit has changed --> new Data incoming */
764                                 /* Special Filter for ToggleBit within Controlbyte */
765                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_cb_toggelBitChanged, tvb, offset, 0, toggleBitCb);
766                                 proto_item_set_hidden(ModuleID_item);
767 
768                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_cb_toggelBitChange_slot_nr, tvb, offset, 0, io_data_object->slotNr);
769                                 proto_item_set_hidden(ModuleID_item);
770 
771                                 ModuleID_item = proto_tree_add_uint(IODataObject_tree, hf_pn_io_ps_cb_toggelBitChange_subslot_nr, tvb, offset, 0, io_data_object->subSlotNr);
772                                 proto_item_set_hidden(ModuleID_item);
773                             }
774 
775                             offset = dissect_pn_io_ps_CB(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_ps_cb, ps_cb_fields);
776                             offset = dissect_pn_user_data(tvb, offset, pinfo, IODataObject_tree, io_data_object->f_crc_len, "CRC");
777 
778                             io_data_object->last_sb_cb = controlbyte;         /* save the value of current controlbyte */
779                             io_data_object->lastToggleBit = toggleBitCb;      /* save the value of current togglebit within controlbyte */
780                         }    /* End of PROFIsafe Module Handling */
781                         else {
782                             /* Module is not PROFIsafe supported */
783                             offset = dissect_pn_user_data(tvb, offset, pinfo, IODataObject_tree, io_data_object->length, "IO Data");
784                         }
785 
786                         if (io_data_object->discardIOXS == FALSE) {
787                             offset = dissect_PNIO_IOxS(tvb, offset, pinfo, IODataObject_tree, drep, hf_pn_io_iops, ioxs_fields);
788                             proto_item_set_len(IODataObject_item, io_data_object->length + 1);        /* Length = Databytes + IOXS Byte */
789                         }
790                         else {
791                             proto_item_set_len(IODataObject_item, io_data_object->length);            /* Length = Databytes */
792                         }
793 
794                         proto_item_append_text(IODataObject_item, ": Slot: 0x%x Subslot: 0x%x",
795                             io_data_object->slotNr, io_data_object->subSlotNr);
796 
797 
798                         /* ModuleIdentNr appears not only once in GSD-file -> set module name more generally */
799                         if (io_data_object->amountInGSDML > 1) {    /* if ModuleIdentNr only appears once in GSD-file, use the found GSD-file-ModuleName, else ... */
800                             if (io_data_object->slotNr == 0) {
801                                 moduleName = wmem_strbuf_new(pinfo->pool, "Headstation");
802                             }
803                             else {
804                                 moduleName = wmem_strbuf_new(pinfo->pool, "Module");
805                             }
806 
807                             if (io_data_object->profisafeSupported == TRUE) {
808                                 /* PROFIsafe */
809                                 if (io_data_object->length >= 5) {        /* 5 due to 3 CRC bytes &  1 status byte & (at least) 1 data byte */
810                                     wmem_strbuf_append(moduleName, ", DO");
811                                 }
812                                 else {
813                                     wmem_strbuf_append(moduleName, ", DI");
814                                 }
815                             }
816                             else {
817                                 /* PROFINET */
818                                 if (io_data_object->length > 0) {
819                                     wmem_strbuf_append(moduleName, ", DO");
820                                 }
821                                 else {
822                                     wmem_strbuf_append(moduleName, ", DI");
823                                 }
824                             }
825 
826                             io_data_object->moduleNameStr = wmem_strdup(wmem_file_scope(), wmem_strbuf_get_str(moduleName));
827                         }
828 
829                         proto_item_append_text(IODataObject_item, " ModuleName: \"%s\"", io_data_object->moduleNameStr);
830 
831                         /* emphasize the PROFIsafe supported Modul */
832                         if (io_data_object->profisafeSupported == TRUE && pnio_ps_selection == TRUE) {
833                             proto_item_append_text(IODataObject_item, " (PROFIsafe Module)");
834                         }
835 
836                         /* Set frameOffset to its new value, to find the next object */
837                         frameOffset = frameOffset + io_data_object->length; /* frameOffset = current value + data bytes */
838                         if (io_data_object->discardIOXS == FALSE) {
839                             frameOffset = frameOffset + 1;      /* frameOffset = current value + iops byte */
840                         }
841                     }
842                 }
843             }
844 
845             /* ---- Output IOCS Object Handling ---- */
846             if (station_info != NULL) {
847                 for (frame = wmem_list_head(station_info->iocs_data_out); frame != NULL; frame = wmem_list_frame_next(frame)) {
848                     iocs_object = (iocsObject*)wmem_list_frame_data(frame);
849                     if (iocs_object->frameOffset == frameOffset) {
850                         offset = dissect_PNIO_IOCS(tvb, offset, pinfo, data_tree, drep, hf_pn_io_iocs, iocs_object->slotNr,
851                             iocs_object->subSlotNr, ioxs_fields);
852 
853                         /* Set frameOffset to its new value, to find the next object */
854                         frameOffset = frameOffset + 1;      /* frameOffset = current value + iops byte */
855 
856                         break;
857                     }
858                 }
859             }
860         }
861 
862         /* Dissect padding */
863         offset = dissect_pn_user_data(tvb, offset, pinfo, tree, tvb_captured_length_remaining(tvb, offset), "GAP and RTCPadding");
864     }   /* END of Output Frame Handling */
865 
866     return offset;
867 }
868 
869 
870 void
init_pn_io_rtc1(int proto)871 init_pn_io_rtc1(int proto)
872 {
873     static hf_register_info hf[] = {
874         { &hf_pn_io_io_data_object,
875             { "IODataObject", "pn_io.io_data_object",
876             FT_NONE, BASE_NONE, NULL, 0x0,
877             NULL, HFILL }
878         },
879         { &hf_pn_io_io_data_object_info_module_diff,
880             { "Difference", "pn_io.io_data_object.diff_module",
881             FT_NONE, BASE_NONE, NULL, 0x0,
882             NULL, HFILL }
883         },
884         { &hf_pn_io_io_data_object_info_moduleidentnumber,
885             { "ModuleIdentNumber", "pn_io.io_data_object.module_nr",
886             FT_UINT32, BASE_HEX, NULL, 0x0,
887             NULL, HFILL }
888         },
889         { &hf_pn_io_io_data_object_info_submoduleidentnumber,
890             { "SubmoduleIdentNumber", "pn_io.io_data_object.submodule_nr",
891             FT_UINT32, BASE_HEX, NULL, 0x0,
892             NULL, HFILL }
893         },
894         { &hf_pn_io_frame_info_type,
895             { "PN Frame Type", "pn_io.frame_info.type",
896             FT_STRING, BASE_NONE, NULL, 0x0,
897             NULL, HFILL }
898         },
899         { &hf_pn_io_frame_info_vendor,
900             { "DeviceVendorValue", "pn_io.frame_info.vendor",
901             FT_STRING, BASE_NONE, NULL, 0x0,
902             NULL, HFILL }
903         },
904         { &hf_pn_io_frame_info_nameofstation,
905             { "NameOfStation", "pn_io.frame_info.nameofstation",
906             FT_STRING, BASE_NONE, NULL, 0x0,
907             NULL, HFILL }
908         },
909         { &hf_pn_io_frame_info_gsd_found,
910             { "GSD-file found", "pn_io.frame_info.gsd_found",
911             FT_NONE, BASE_NONE, NULL, 0x0,
912             NULL, HFILL }
913         },
914         { &hf_pn_io_frame_info_gsd_error,
915             { "GSD-file not found.", "pn_io.frame_info.gsd_error",
916             FT_NONE, BASE_NONE, NULL, 0x0,
917             NULL, HFILL }
918         },
919         { &hf_pn_io_frame_info_gsd_path,
920             { "GSD-file networkpath failure!", "pn_io.frame_info.gsd_path",
921             FT_NONE, BASE_NONE, NULL, 0x0,
922             NULL, HFILL }
923         },
924         { &hf_pn_io_iocs,
925             { "IOCS", "pn_io.ioxs",
926             FT_UINT8, BASE_HEX, NULL, 0x0,
927             NULL, HFILL }
928         },
929         { &hf_pn_io_iops,
930             { "IOPS", "pn_io.ioxs",
931             FT_UINT8, BASE_HEX, NULL, 0x0,
932             NULL, HFILL }
933         },
934         { &hf_pn_io_ioxs_extension,
935             { "Extension", "pn_io.ioxs.extension",
936             FT_UINT8, BASE_HEX, VALS(pn_io_ioxs_extension), 0x01,
937             NULL, HFILL }
938         },
939         { &hf_pn_io_ioxs_res14,
940             { "Reserved", "pn_io.ioxs.res14",
941             FT_UINT8, BASE_HEX, NULL, 0x1E,
942             NULL, HFILL }
943         },
944         { &hf_pn_io_ioxs_instance,
945             { "Instance", "pn_io.ioxs.instance",
946             FT_UINT8, BASE_HEX, VALS(pn_io_ioxs_instance), 0x60,
947             NULL, HFILL }
948         },
949         { &hf_pn_io_ioxs_datastate,
950             { "DataState", "pn_io.ioxs.datastate",
951             FT_UINT8, BASE_HEX, VALS(pn_io_ioxs_datastate), 0x80,
952             NULL, HFILL }
953         },
954         /* PROFIsafe parameter */
955         /* Status Byte & Control Byte for PROFIsafe --- dissector handle */
956         { &hf_pn_io_ps_sb,
957             { "Status Byte", "pn_io.ps.sb",
958             FT_UINT8, BASE_HEX, NULL, 0x0,
959             NULL, HFILL }
960         },
961         { &hf_pn_io_ps_sb_toggelBitChanged,
962             { "Status Byte", "pn_io.ps.sb.toggle_d_changed",
963             FT_UINT8, BASE_DEC, NULL, 0x00,
964             NULL, HFILL }
965         },
966         { &hf_pn_io_ps_sb_toggelBitChange_slot_nr,
967             { "Slot_Number", "pn_io.ps.sb.toggle_d_changed.slot",
968             FT_UINT16, BASE_DEC, NULL, 0x0,
969             NULL, HFILL }
970         },
971         { &hf_pn_io_ps_sb_toggelBitChange_subslot_nr,
972             { "Sub_Slot_Number", "pn_io.ps.sb.toggle_d_changed.subslot",
973             FT_UINT16, BASE_DEC, NULL, 0x0,
974             NULL, HFILL }
975         },
976         { &hf_pn_io_ps_cb,
977             { "Control Byte", "pn_io.ps.cb",
978             FT_UINT8, BASE_HEX, NULL, 0x0,
979             NULL, HFILL }
980         },
981         { &hf_pn_io_ps_cb_toggelBitChanged,
982             { "Control Byte", "pn_io.ps.cb.toggle_h_changed",
983             FT_UINT8, BASE_DEC, NULL, 0x00,
984             NULL, HFILL }
985         },
986         { &hf_pn_io_ps_cb_toggelBitChange_slot_nr,
987             { "Slot_Number", "pn_io.ps.cb.toggle_h_changed.slot",
988             FT_UINT16, BASE_DEC, NULL, 0x0,
989             NULL, HFILL }
990         },
991         { &hf_pn_io_ps_cb_toggelBitChange_subslot_nr,
992             { "Sub_Slot_Number", "pn_io.ps.cb.toggle_h_changed.subslot",
993             FT_UINT16, BASE_DEC, NULL, 0x0,
994             NULL, HFILL }
995         },
996         /* Structures for dissecting Status Byte & Control Byte PROFIsafe ---dissector details */
997         { &hf_pn_io_ps_sb_iparOK,
998             { "iPar_OK - F-Device has new iParameter values assigned", "pn_io.ps.sb.iPar_OK",
999             FT_UINT8, BASE_HEX, NULL, 0x01,
1000             NULL, HFILL }
1001         },
1002         { &hf_pn_io_ps_sb_DeviceFault,
1003             { "Device_Fault - Failure exists in F-Device or F-Module", "pn_io.ps.sb.DeviceFault",
1004             FT_UINT8, BASE_HEX, NULL, 0x02,
1005             NULL, HFILL }
1006         },
1007         { &hf_pn_io_ps_sb_CECRC,
1008             { "CE_CRC - CRC Communication fault", "pn_io.ps.sb.CE_CRC",
1009             FT_UINT8, BASE_HEX, NULL, 0x04,
1010             NULL, HFILL }
1011         },
1012         { &hf_pn_io_ps_sb_WDtimeout,
1013             { "WD_timeout - WatchDog timeout Communication fault", "pn_io.ps.sb.WD_timeout",
1014             FT_UINT8, BASE_HEX, NULL, 0x08,
1015             NULL, HFILL }
1016         },
1017         { &hf_pn_io_ps_sb_FVactivated,
1018             { "FV_activated - Fail-safe values (FV) activated", "pn_io.ps.sb.FV_activated",
1019             FT_UINT8, BASE_HEX, NULL, 0x10,
1020             NULL, HFILL }
1021         },
1022         { &hf_pn_io_ps_sb_Toggle_d,
1023             { "Toggle_d - Device-based Toggle Bit", "pn_io.ps.sb.Toggle_d",
1024             FT_UINT8, BASE_HEX, NULL, 0x20,
1025             NULL, HFILL }
1026         },
1027         { &hf_pn_io_ps_sb_ConsNr_reset,
1028             { "cons_nr_R - F-Device has reset its consecutive number counter", "pn_io.ps.sb.cons_nr_R",
1029             FT_UINT8, BASE_HEX, NULL, 0x40,
1030             NULL, HFILL }
1031         },
1032         { &hf_pn_io_ps_sb_res,
1033             { "Bit7 - reserved for future releases", "pn_io.ps.sb.bit7",
1034             FT_UINT8, BASE_HEX, NULL, 0x80,
1035             NULL, HFILL }
1036         },
1037         { &hf_pn_io_ps_cb_iparEN,
1038             { "iPar_EN - iParameter assignment deblocked", "pn_io.ps.cb.iparEN",
1039             FT_UINT8, BASE_HEX, NULL, 0x01,
1040             NULL, HFILL }
1041         },
1042         { &hf_pn_io_ps_cb_OAReq,
1043             { "OA_Req - Operator acknowledge requested", "pn_io.ps.cb.OA_Req",
1044             FT_UINT8, BASE_HEX, NULL, 0x02,
1045             NULL, HFILL }
1046         },
1047         { &hf_pn_io_ps_cb_resetConsNr,
1048             { "R_cons_nr - Set the Virtual Consecutive Number within the F-Device to be \"0\"", "pn_io.ps.cb.R_cons_nr",
1049             FT_UINT8, BASE_HEX, NULL, 0x04,
1050             NULL, HFILL }
1051         },
1052         { &hf_pn_io_ps_cb_useTO2,
1053             { "Bit3 - Reserved or Use the secondary watchdog (Use_TO2)", "pn_io.ps.cb.bit3",
1054             FT_UINT8, BASE_HEX, NULL, 0x08,
1055             NULL, HFILL }
1056         },
1057         { &hf_pn_io_ps_cb_activateFV,
1058             { "activate_FV - Fail-safe values (FV) to be activated", "pn_io.ps.cb.activate_FV",
1059             FT_UINT8, BASE_HEX, NULL, 0x10,
1060             NULL, HFILL }
1061         },
1062         { &hf_pn_io_ps_cb_Toggle_h,
1063             { "Toggle_h - Host-based Toggle Bit", "pn_io.ps.cb.Toggle_h",
1064             FT_UINT8, BASE_HEX, NULL, 0x20,
1065             NULL, HFILL }
1066         },
1067         { &hf_pn_io_ps_cb_Chf_ACK,
1068             { "Bit6 - Reserved or Operator acknowledge after cleared channel fault (ChF_Ack)", "pn_io.ps.cb.bit6",
1069             FT_UINT8, BASE_HEX, NULL, 0x40,
1070             NULL, HFILL }
1071         },
1072         { &hf_pn_io_ps_cb_loopcheck,
1073             { "Bit7 - Reserved or Loop-back check (Loopcheck, shall be set to 1)", "pn_io.ps.cb.bit7",
1074             FT_UINT8, BASE_HEX, NULL, 0x80,
1075             NULL, HFILL }
1076         },
1077         /* PROFIsafe */
1078         { &hf_pn_io_ps_f_dest_adr,
1079             { "F_Dest_Add", "pn_io.ps.f_dest_add",
1080             FT_UINT16, BASE_DEC, NULL, 0x0,
1081             NULL, HFILL }
1082         },
1083         { &hf_pn_io_ps_f_data,
1084             { "SafetyIO Data", "pn_io.ps.f_data",
1085             FT_UINT64, BASE_HEX, NULL, 0x0,
1086             NULL, HFILL }
1087         },
1088     };
1089 
1090     static gint *ett[] = {
1091         &ett_pn_io_rtc,
1092         &ett_pn_io_ioxs,
1093         &ett_pn_io_io_data_object
1094     };
1095 
1096     static ei_register_info ei[] = {
1097         { &ei_pn_io_too_many_data_objects, { "pn_io.too_many_data_objects", PI_MALFORMED, PI_ERROR, "Too many data objects", EXPFILL }},
1098     };
1099 
1100     expert_module_t* expert_pn_io;
1101 
1102     proto_pn_io_rtc1 = proto;
1103     proto_register_field_array(proto, hf, array_length(hf));
1104     proto_register_subtree_array(ett, array_length(ett));
1105     expert_pn_io = expert_register_protocol(proto_pn_io_rtc1);
1106     expert_register_field_array(expert_pn_io, ei, array_length(ei));
1107 }
1108 
1109 
1110 /*
1111 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1112 *
1113 * Local variables:
1114 * c-basic-offset: 4
1115 * tab-width: 8
1116 * indent-tabs-mode: nil
1117 * End:
1118 *
1119 * vi: set shiftwidth=4 tabstop=8 expandtab:
1120 * :indentSize=4:tabSize=8:noTabs=true:
1121 */
1122