fix usage of "if(tree) {" to display the right things, even if no coloring rule is set
[obnox/wireshark/wip.git] / epan / dissectors / packet-dcerpc-pn-io.c
1 /* packet-dcerpc-pn-io.c
2  * Routines for PROFINET IO dissection.
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /*
26  * The PN-IO protocol is a field bus protocol related to decentralized 
27  * periphery and is developed by the PROFIBUS Nutzerorganisation e.V. (PNO), 
28  * see: www.profibus.com
29  *
30  *
31  * PN-IO is based on the common DCE-RPC and the "lightweight" PN-RT 
32  * (ethernet type 0x8892) protocols.
33  *
34  * The context manager (CM) part is handling context information 
35  * (like establishing, ...) and is using DCE-RPC as it's underlying 
36  * protocol.
37  *
38  * The actual cyclic data transfer and acyclic notification uses the 
39  * "lightweight" PN-RT protocol.
40  *
41  * There are some other related PROFINET protocols (e.g. PN-DCP, which is 
42  * handling addressing topics).
43  *
44  * Please note: the PROFINET CBA protocol is independant of the PN-IO protocol!
45  */
46
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52
53 #ifdef HAVE_SYS_TYPES_H
54 #include <sys/types.h>
55 #endif
56
57 #include <string.h>
58
59 #include <glib.h>
60 #include <epan/packet.h>
61 #include "packet-dcerpc.h"
62
63
64
65 static int proto_pn_io = -1;
66
67 static int hf_pn_io_opnum = -1;
68 static int hf_pn_io_reserved16 = -1;
69
70 static int hf_pn_io_array = -1;
71 static int hf_pn_io_status = -1;
72 static int hf_pn_io_args_max = -1;
73 static int hf_pn_io_args_len = -1;
74 static int hf_pn_io_array_max_count = -1;
75 static int hf_pn_io_array_offset = -1;
76 static int hf_pn_io_array_act_count = -1;
77
78 static int hf_pn_io_data = -1;
79
80 static int hf_pn_io_ar_uuid = -1;
81 static int hf_pn_io_api = -1;
82 static int hf_pn_io_slot_nr = -1;
83 static int hf_pn_io_subslot_nr = -1;
84 static int hf_pn_io_index = -1;
85 static int hf_pn_io_seq_number = -1;
86 static int hf_pn_io_record_data_length = -1;
87 static int hf_pn_io_padding = -1;
88 static int hf_pn_io_add_val1 = -1;
89 static int hf_pn_io_add_val2 = -1;
90
91 static int hf_pn_io_block = -1;
92 static int hf_pn_io_block_type = -1;
93 static int hf_pn_io_block_length = -1;
94 static int hf_pn_io_block_version_high = -1;
95 static int hf_pn_io_block_version_low = -1;
96
97 static int hf_pn_io_sessionkey = -1;
98 static int hf_pn_io_control_command = -1;
99 static int hf_pn_io_control_command_prmend = -1;
100 static int hf_pn_io_control_command_applready = -1;
101 static int hf_pn_io_control_command_release = -1;
102 static int hf_pn_io_control_command_done = -1;
103 static int hf_pn_io_control_block_properties = -1;
104
105 static int hf_pn_io_error_code = -1;
106 static int hf_pn_io_error_decode = -1;
107 static int hf_pn_io_error_code1 = -1;
108 static int hf_pn_io_error_code2 = -1;
109 static int hf_pn_io_error_code1_pniorw = -1;
110 static int hf_pn_io_error_code1_pnio = -1;
111
112 static int hf_pn_io_alarm_type = -1;
113 static int hf_pn_io_alarm_specifier = -1;
114 static int hf_pn_io_alarm_specifier_sequence = -1;
115 static int hf_pn_io_alarm_specifier_channel = -1;
116 static int hf_pn_io_alarm_specifier_manufacturer = -1;
117 static int hf_pn_io_alarm_specifier_submodule = -1;
118 static int hf_pn_io_alarm_specifier_ardiagnosis = -1;
119
120 static int hf_pn_io_alarm_dst_endpoint = -1;
121 static int hf_pn_io_alarm_src_endpoint = -1;
122 static int hf_pn_io_pdu_type = -1;
123 static int hf_pn_io_pdu_type_type = -1;
124 static int hf_pn_io_pdu_type_version = -1;
125 static int hf_pn_io_add_flags = -1;
126 static int hf_pn_io_window_size = -1;
127 static int hf_pn_io_tack = -1;
128 static int hf_pn_io_send_seq_num = -1;
129 static int hf_pn_io_ack_seq_num = -1;
130 static int hf_pn_io_var_part_len = -1;
131
132 static int hf_pn_io_module_ident_number = -1;
133 static int hf_pn_io_submodule_ident_number = -1;
134
135 static int hf_pn_io_ioxs = -1;
136 static int hf_pn_io_ioxs_extension = -1;
137 static int hf_pn_io_ioxs_res14 = -1;
138 static int hf_pn_io_ioxs_instance = -1;
139 static int hf_pn_io_ioxs_datastate = -1;
140
141
142 static gint ett_pn_io = -1;
143 static gint ett_pn_io_block = -1;
144 static gint ett_pn_io_status = -1;
145 static gint ett_pn_io_rtc = -1;
146 static gint ett_pn_io_rta = -1;
147 static gint ett_pn_io_pdu_type = -1;
148 static gint ett_pn_io_add_flags = -1;
149 static gint ett_pn_io_control_command = -1;
150 static gint ett_pn_io_ioxs = -1;
151
152
153 static e_uuid_t uuid_pn_io_device = { 0xDEA00001, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
154 static guint16  ver_pn_io_device = 1;
155
156 static e_uuid_t uuid_pn_io_controller = { 0xDEA00002, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
157 static guint16  ver_pn_io_controller = 1;
158
159 static e_uuid_t uuid_pn_io_supervisor = { 0xDEA00003, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
160 static guint16  ver_pn_io_supervisor = 1;
161
162 static e_uuid_t uuid_pn_io_parameterserver = { 0xDEA00004, 0x6C97, 0x11D1, { 0x82, 0x71, 0x00, 0xA0, 0x24, 0x42, 0xDF, 0x7D } };
163 static guint16  ver_pn_io_parameterserver = 1;
164
165
166 static const value_string pn_io_block_type[] = {
167         { 0x0000, "Reserved" },
168         { 0x0001, "Alarm Notification High"},
169         { 0x0002, "Alarm Notification Low"},
170         { 0x0008, "WriteRecordReq"},
171         { 0x8008, "WriteRecordRes"},
172         { 0x0009, "ReadRecordReq"},
173         { 0x8009, "ReadRecordRes"},
174         { 0x0010, "ManufacturerSpecificDiagnosisBlock"},
175         { 0x0011, "ChannelDiagnosisBlock"},
176         { 0x0012, "ExpectedIdentificationDataBlock"},
177         { 0x0014, "SubstituteValue RecordDataRead"},
178         { 0x0015, "RecordInputDataObjectElement"},
179         { 0x0016, "RecordOutputDataObjectElement"},
180         { 0x0017, "RecordOutputDataSubstituteObjectElement"},
181         { 0x0018, "ARData"},
182         { 0x0019, "LogData"},
183         { 0x001A, "APIData"},
184         { 0x0020, "I&M0"},
185         { 0x0021, "I&M1"},
186         { 0x0022, "I&M2"},
187         { 0x0023, "I&M3"},
188         { 0x0024, "I&M4"},
189         { 0x8001, "Alarm Ack High"},
190         { 0x8002, "Alarm Ack Low"},
191         { 0x0101, "ARBlockReq"},
192         { 0x8101, "ARBlockRes"},
193         { 0x0102, "IOCRBlockReq"},
194         { 0x8102, "IOCRBlockRes"},
195         { 0x0103, "AlarmCRBlockReq"},
196         { 0x8103, "AlarmCRBlockRes"},
197         { 0x0104, "ExpectedSubmoduleBlockReq"},
198         { 0x8104, "ModuleDiffBlock"},
199         { 0x0105, "PrmServerBlockReq"},
200         { 0x8105, "PrmServerBlockRes"},
201         { 0x0110, "IODBlockReq"},
202         { 0x8110, "IODBlockRes"},
203         { 0x0111, "IODBlockReq"},
204         { 0x8111, "IODBlockRes"},
205         { 0x0112, "IOXBlockReq"},
206         { 0x8112, "IOXBlockRes"},
207         { 0x0113, "IOXBlockReq"},
208         { 0x8113, "IOXBlockRes"},
209         { 0x0114, "ReleaseBlockReq"},
210         { 0x8114, "ReleaseBlockRes"},
211         { 0, NULL }
212 };
213
214 static const value_string pn_io_alarm_type[] = {
215         { 0x0000, "Reserved" },
216         { 0x0001, "Diagnosis" },
217         { 0x0002, "Process" },
218         { 0x0003, "Pull" },
219         { 0x0004, "Plug" },
220         { 0x0005, "Status" },
221         { 0x0006, "Update" },
222         { 0x0007, "Redundancy" },
223         { 0x0008, "Controlled by supervisor" },
224         { 0x0009, "Released by supervisor" },
225         { 0x000A, "Plug wrong submodule" },
226         { 0x000B, "Return of submodule" },
227     /* 0x000C - 0x001F reserved */
228     /* 0x0020 - 0x007F manufacturer specific */
229     /* 0x0080 - 0x00FF reserved for profiles */
230     /* 0x0100 - 0xFFFF reserved */
231     { 0, NULL }
232 };
233
234 static const value_string pn_io_pdu_type[] = {
235         { 0x01, "Data-RTA-PDU" },
236         { 0x02, "NACK-RTA-PDU" },
237         { 0x03, "ACK-RTA-PDU" },
238         { 0x04, "ERR-RTA-PDU" },
239     { 0, NULL }
240 };
241
242 static const value_string pn_io_error_code[] = {
243         { 0x00, "OK" },
244         { 0x81, "PNIO" },
245         { 0xCF, "RTA error" },
246         { 0xDA, "AlarmAck" },
247         { 0xDB, "IODConnectRes" },
248         { 0xDC, "IODReleaseRes" },
249         { 0xDD, "IODControlRes" },
250         { 0xDE, "IODReadRes" },
251         { 0xDF, "IODWriteRes" },
252     { 0, NULL }
253 };
254
255 static const value_string pn_io_error_decode[] = {
256         { 0x00, "OK" },
257         { 0x80, "PNIORW" },
258         { 0x81, "PNIO" },
259     { 0, NULL }
260 };
261
262 /*
263 XXX: the next 2 are dependant on error_code and error_decode
264
265 e.g.: CL-RPC error:
266 error_code .. see above
267 error_decode .. 0x81
268 error_code1 .. 0x69
269 error_code2 ..
270 1 RPC_ERR_REJECTED
271 2 RPC_ERR_FAULTED
272 3 RPC_ERR_TIMEOUT
273 4 RPC_ERR_IN_ARGS
274 5 RPC_ERR_OUT_ARGS
275 6 RPC_ERR_DECODE
276 7 RPC_ERR_PNIO_OUT_ARGS
277 8 Application Timeout
278 */
279
280 /* XXX: add some more error codes here */
281 static const value_string pn_io_error_code1[] = {
282         { 0x00, "OK" },
283     { 0, NULL }
284 };
285
286 /* XXX: add some more error codes here */
287 static const value_string pn_io_error_code2[] = {
288         { 0x00, "OK" },
289     { 0, NULL }
290 };
291
292 static const value_string pn_io_error_code1_pniorw[] = {
293         { 0x0a /* 10*/, "application" },
294         { 0x0b /* 11*/, "access" },
295         { 0x0c /* 12*/, "resource" },
296         { 0x0d /* 13*/, "user specific(13)" },
297         { 0x0e /* 14*/, "user specific(14)" },
298         { 0x0f /* 15*/, "user specific(15)" },
299     { 0, NULL }
300 };
301
302 static const value_string pn_io_error_code1_pnio[] = {
303         { 0x00 /*  0*/, "Reserved" },
304         { 0x01 /*  1*/, "Connect: Faulty ARBlockReq" },
305         { 0x02 /*  2*/, "Connect: Faulty IOCRBlockReq" },
306         { 0x03 /*  3*/, "Connect: Faulty ExpectedSubmoduleBlockReq" },
307         { 0x04 /*  4*/, "Connect: Faulty AlarmCRBlockReq" },
308         { 0x05 /*  5*/, "Connect: Faulty PrmServerBlockReq" },
309
310         { 0x14 /* 20*/, "IODControl: Faulty ControlBlockConnect" },
311         { 0x15 /* 21*/, "IODControl: Faulty ControlBlockPlug" },
312         { 0x16 /* 22*/, "IOXControl: Faulty ControlBlock after a connect est." },
313         { 0x17 /* 23*/, "IOXControl: Faulty ControlBlock a plug alarm" },
314
315     { 0x28 /* 40*/, "Release: Faulty ReleaseBlock" },
316
317     { 0x3c /* 60*/, "AlarmAck Error Codes" },
318     { 0x3d /* 61*/, "CMDEV" },
319     { 0x3e /* 62*/, "CMCTL" },
320     { 0x3f /* 63*/, "NRPM" },
321     { 0x40 /* 64*/, "RMPM" },
322     { 0x41 /* 65*/, "ALPMI" },
323     { 0x42 /* 66*/, "ALPMR" },
324     { 0x43 /* 67*/, "LMPM" },
325     { 0x44 /* 68*/, "MMAC" },
326     { 0x45 /* 69*/, "RPC" },
327     { 0x46 /* 70*/, "APMR" },
328     { 0x47 /* 71*/, "APMS" },
329     { 0x48 /* 72*/, "CPM" },
330     { 0x49 /* 73*/, "PPM" },
331     { 0x4a /* 74*/, "DCPUCS" },
332     { 0x4b /* 75*/, "DCPUCR" },
333     { 0x4c /* 76*/, "DCPMCS" },
334     { 0x4d /* 77*/, "DCPMCR" },
335     { 0x4e /* 78*/, "FSPM" },
336         { 0xfd /*253*/, "RTA_ERR_CLS_PROTOCOL" },
337     { 0, NULL }
338 };
339
340 static const value_string pn_io_ioxs[] = {
341         { 0x00 /*  0*/, "detected by subslot" },
342         { 0x01 /*  1*/, "detected by slot" },
343         { 0x02 /*  2*/, "detected by IO device" },
344         { 0x03 /*  3*/, "detected by IO controller" },
345 };
346
347
348 /* dissect the four status (error) fields */
349 static int
350 dissect_PNIO_status(tvbuff_t *tvb, int offset,
351         packet_info *pinfo, proto_tree *tree, guint8 *drep)
352 {
353     guint8  u8ErrorCode;
354     guint8  u8ErrorDecode;
355     guint8  u8ErrorCode1;
356     guint8  u8ErrorCode2;
357
358     proto_item *sub_item;
359         proto_tree *sub_tree;
360         guint32 u32SubStart;
361     int bytemask = (drep[0] & 0x10) ? 3 : 0;
362     const value_string *error_code1_vals;
363
364
365
366     /* status */
367     sub_item = proto_tree_add_item(tree, hf_pn_io_status, tvb, offset, 0, FALSE);
368         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_status);
369     u32SubStart = offset;
370
371     /* the PNIOStatus field is existing in both the RPC and the application data,
372      * depending on the current PDU.
373      * As the byte representation of these layers are different, this has to be handled
374      * in a somewhat different way than elsewhere. */
375
376     dissect_dcerpc_uint8(tvb, offset+(0^bytemask), pinfo, sub_tree, drep, 
377                         hf_pn_io_error_code, &u8ErrorCode);
378         dissect_dcerpc_uint8(tvb, offset+(1^bytemask), pinfo, sub_tree, drep, 
379                         hf_pn_io_error_decode, &u8ErrorDecode);
380
381     switch(u8ErrorDecode) {
382     case(0x80): /* PNIORW */
383             dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
384                             hf_pn_io_error_code1_pniorw, &u8ErrorCode1);
385         error_code1_vals = pn_io_error_code1_pniorw;
386         break;
387     case(0x81): /* PNIO */
388             dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
389                             hf_pn_io_error_code1_pnio, &u8ErrorCode1);
390         error_code1_vals = pn_io_error_code1_pnio;
391         break;
392     default:
393             dissect_dcerpc_uint8(tvb, offset+(2^bytemask), pinfo, sub_tree, drep, 
394                             hf_pn_io_error_code1, &u8ErrorCode1);
395         error_code1_vals = pn_io_error_code1;
396     }
397
398     /* XXX - this has to be decode specific too */
399         dissect_dcerpc_uint8(tvb, offset+(3^bytemask), pinfo, sub_tree, drep, 
400                         hf_pn_io_error_code2, &u8ErrorCode2);
401
402     offset +=4;
403
404     if(u8ErrorCode == 0 && u8ErrorDecode == 0 && u8ErrorCode1 == 0 && u8ErrorCode2 == 0) {
405         proto_item_append_text(sub_item, ": OK");
406         if (check_col(pinfo->cinfo, COL_INFO))
407                 col_append_str(pinfo->cinfo, COL_INFO, ", OK");
408     } else {
409         proto_item_append_text(sub_item, ": Error Code: \"%s\", Decode: \"%s\", Code1: \"%s\" Code2: 0x%x", 
410             val_to_str(u8ErrorCode, pn_io_error_code, "(0x%x)"),
411             val_to_str(u8ErrorDecode, pn_io_error_decode, "(0x%x)"),
412             val_to_str(u8ErrorCode1, error_code1_vals, "(0x%x)"),
413             u8ErrorCode2);
414         if (check_col(pinfo->cinfo, COL_INFO))
415                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Error Code: %s, Decode: %s, Code1: 0x%x Code2: 0x%x",
416             val_to_str(u8ErrorCode, pn_io_error_code, "(0x%x)"),
417             val_to_str(u8ErrorDecode, pn_io_error_decode, "(0x%x)"),
418             u8ErrorCode1,
419             u8ErrorCode2);
420     }
421         proto_item_set_len(sub_item, offset - u32SubStart);
422
423     return offset;
424 }
425
426
427 /* dissect the alarm specifier */
428 static int
429 dissect_Alarm_specifier(tvbuff_t *tvb, int offset,
430         packet_info *pinfo, proto_tree *tree, guint8 *drep)
431 {
432     guint16 u16AlarmSpecifierSequence;
433     guint16 u16AlarmSpecifierChannel;
434     guint16 u16AlarmSpecifierManufacturer;
435     guint16 u16AlarmSpecifierSubmodule;
436     guint16 u16AlarmSpecifierAR;
437     proto_item *sub_item;
438         proto_tree *sub_tree;
439
440     /* alarm specifier */
441         sub_item = proto_tree_add_item(tree, hf_pn_io_alarm_specifier, tvb, offset, 2, FALSE);
442         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_pdu_type);
443
444         dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
445                         hf_pn_io_alarm_specifier_sequence, &u16AlarmSpecifierSequence);
446     u16AlarmSpecifierSequence &= 0x07FF;
447         dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
448                         hf_pn_io_alarm_specifier_channel, &u16AlarmSpecifierChannel);
449     u16AlarmSpecifierChannel = (u16AlarmSpecifierChannel &0x0800) >> 11;
450         dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
451                         hf_pn_io_alarm_specifier_manufacturer, &u16AlarmSpecifierManufacturer);
452     u16AlarmSpecifierManufacturer = (u16AlarmSpecifierManufacturer &0x1000) >> 12;
453         dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
454                         hf_pn_io_alarm_specifier_submodule, &u16AlarmSpecifierSubmodule);
455     u16AlarmSpecifierSubmodule = (u16AlarmSpecifierSubmodule & 0x2000) >> 13;
456         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
457                         hf_pn_io_alarm_specifier_ardiagnosis, &u16AlarmSpecifierAR);
458     u16AlarmSpecifierAR = (u16AlarmSpecifierAR & 0x8000) >> 15;
459
460
461     proto_item_append_text(sub_item, ", Sequence: %u, Channel: %u, Manuf: %u, Submodule: %u AR: %u", 
462         u16AlarmSpecifierSequence, u16AlarmSpecifierChannel, 
463         u16AlarmSpecifierManufacturer, u16AlarmSpecifierSubmodule, u16AlarmSpecifierAR);
464
465     return offset;
466 }
467
468
469 /* dissect the alarm header */
470 static int
471 dissect_Alarm_header(tvbuff_t *tvb, int offset,
472         packet_info *pinfo, proto_tree *tree, guint8 *drep)
473 {
474     guint16 u16AlarmType;
475     guint32 u32Api;
476     guint16 u16SlotNr;
477     guint16 u16SubslotNr;
478
479         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
480                         hf_pn_io_alarm_type, &u16AlarmType);
481         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
482                         hf_pn_io_api, &u32Api);
483         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
484                         hf_pn_io_slot_nr, &u16SlotNr);
485         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
486                         hf_pn_io_subslot_nr, &u16SubslotNr);
487
488     if (check_col(pinfo->cinfo, COL_INFO))
489             col_append_fstr(pinfo->cinfo, COL_INFO, ", %s, Slot: %u/%u", 
490         val_to_str(u16AlarmType, pn_io_alarm_type, "Unknown"),
491         u16SlotNr, u16SubslotNr);
492
493     return offset;
494 }
495
496
497 /* dissect the alarm note block */
498 static int
499 dissect_Alarm_note_block(tvbuff_t *tvb, int offset,
500         packet_info *pinfo, proto_tree *tree, guint8 *drep, guint16 body_length)
501 {
502     guint32 u32ModuleIdentNumber;
503     guint32 u32SubmoduleIdentNumber;
504
505     if (check_col(pinfo->cinfo, COL_INFO))
506             col_append_str(pinfo->cinfo, COL_INFO, ", Alarm Notification");
507
508     offset = dissect_Alarm_header(tvb, offset, pinfo, tree, drep);
509     
510         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
511                         hf_pn_io_module_ident_number, &u32ModuleIdentNumber);
512         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
513                         hf_pn_io_submodule_ident_number, &u32SubmoduleIdentNumber);
514
515     offset = dissect_Alarm_specifier(tvb, offset, pinfo, tree, drep);
516
517     /* XXX - dissect AlarmItem */
518     body_length -= 20;
519     proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, body_length, "data", 
520         "Alarm Item Data: %u bytes", body_length);
521     offset += body_length;
522
523     return offset;
524 }
525
526
527 /* dissect the alarm acknowledge block */
528 static int
529 dissect_Alarm_ack_block(tvbuff_t *tvb, int offset,
530         packet_info *pinfo, proto_tree *tree, guint8 *drep)
531 {
532     if (check_col(pinfo->cinfo, COL_INFO))
533             col_append_str(pinfo->cinfo, COL_INFO, ", Alarm Ack");
534
535     offset = dissect_Alarm_header(tvb, offset, pinfo, tree, drep);
536
537     offset = dissect_Alarm_specifier(tvb, offset, pinfo, tree, drep);
538
539     offset = dissect_PNIO_status(tvb, offset, pinfo, tree, drep);
540
541     return offset;
542 }
543
544
545 /* dissect the read/write header */
546 static int
547 dissect_ReadWrite_header(tvbuff_t *tvb, int offset,
548         packet_info *pinfo, proto_tree *tree, guint8 *drep)
549 {
550     e_uuid_t uuid;
551     guint32 u32Api;
552     guint16 u16SlotNr;
553     guint16 u16SubslotNr;
554     guint16 u16Index;
555     guint16 u16SeqNr;
556
557         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
558                         hf_pn_io_seq_number, &u16SeqNr);
559
560     offset = dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, 
561                         hf_pn_io_ar_uuid, &uuid);
562
563         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
564                         hf_pn_io_api, &u32Api);
565         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
566                         hf_pn_io_slot_nr, &u16SlotNr);
567         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
568                         hf_pn_io_subslot_nr, &u16SubslotNr);
569     proto_tree_add_string_format(tree, hf_pn_io_padding, tvb, offset, 2, "padding", "Padding: 2 bytes");
570     offset += 2;
571         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
572                         hf_pn_io_index, &u16Index);
573
574     if (check_col(pinfo->cinfo, COL_INFO))
575             col_append_fstr(pinfo->cinfo, COL_INFO, ", Api: %u, Slot: %u/%u",
576             u32Api, u16SlotNr, u16SubslotNr);
577
578     return offset;
579 }
580
581
582 /* dissect the read/write request block */
583 static int
584 dissect_ReadWrite_rqst_block(tvbuff_t *tvb, int offset,
585         packet_info *pinfo, proto_tree *tree, guint8 *drep)
586 {
587     guint32 u32RecDataLen;
588
589
590     offset = dissect_ReadWrite_header(tvb, offset, pinfo, tree, drep);
591
592         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
593                         hf_pn_io_record_data_length, &u32RecDataLen);
594     /* XXX: don't know how to handle the optional TargetARUUID */
595
596     if (check_col(pinfo->cinfo, COL_INFO))
597             col_append_fstr(pinfo->cinfo, COL_INFO, ", %u bytes",
598             u32RecDataLen);
599
600     return offset;
601 }
602
603
604 /* dissect the read/write response block */
605 static int
606 dissect_ReadWrite_resp_block(tvbuff_t *tvb, int offset,
607         packet_info *pinfo, proto_tree *tree, guint8 *drep)
608 {
609     guint32 u32RecDataLen;
610     guint16 u16AddVal1;
611     guint16 u16AddVal2;
612
613
614     offset = dissect_ReadWrite_header(tvb, offset, pinfo, tree, drep);
615
616         offset = dissect_dcerpc_uint32(tvb, offset, pinfo, tree, drep, 
617                         hf_pn_io_record_data_length, &u32RecDataLen);
618
619         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
620                         hf_pn_io_add_val1, &u16AddVal1);
621
622         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep, 
623                         hf_pn_io_add_val2, &u16AddVal2);
624
625     if (check_col(pinfo->cinfo, COL_INFO))
626             col_append_fstr(pinfo->cinfo, COL_INFO, ", %u bytes",
627             u32RecDataLen);
628
629     return offset;
630 }
631
632
633 /* dissect the control/connect block */
634 static int
635 dissect_ControlConnect_block(tvbuff_t *tvb, int offset,
636         packet_info *pinfo, proto_tree *tree, guint8 *drep)
637 {
638     e_uuid_t    ar_uuid;
639         proto_item *sub_item;
640         proto_tree *sub_tree;
641     guint16     u16PrmEnd;
642     guint16     u16ApplReady;
643     guint16     u16Release;
644     guint16     u16CmdDone;
645
646
647     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
648                         hf_pn_io_reserved16, NULL);
649
650     offset = dissect_ndr_uuid_t(tvb, offset, pinfo, tree, drep, 
651                         hf_pn_io_ar_uuid, &ar_uuid);
652
653     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
654                         hf_pn_io_sessionkey, NULL);
655
656     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
657                         hf_pn_io_reserved16, NULL);
658
659     sub_item = proto_tree_add_item(tree, hf_pn_io_control_command, tvb, offset, 2, FALSE);
660         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_control_command);
661
662     dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
663                         hf_pn_io_control_command_prmend, &u16PrmEnd);
664     if(u16PrmEnd & 0x0001) {
665         proto_item_append_text(sub_item, ", Parameter End");
666         if (check_col(pinfo->cinfo, COL_INFO))
667                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Parameter End\"");
668     }
669     dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
670                         hf_pn_io_control_command_applready, &u16ApplReady);
671     if((u16ApplReady >> 1) & 0x0001) {
672         proto_item_append_text(sub_item, ", Application Ready");
673         if (check_col(pinfo->cinfo, COL_INFO))
674                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Application Ready\"");
675     }
676     dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
677                         hf_pn_io_control_command_release, &u16Release);
678     if((u16Release >> 2) & 0x0001) {
679         proto_item_append_text(sub_item, ", Release");
680         if (check_col(pinfo->cinfo, COL_INFO))
681                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Release\"");
682     }
683     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep,
684                         hf_pn_io_control_command_done, &u16CmdDone);
685     if((u16CmdDone >> 3) & 0x0001) {
686         proto_item_append_text(sub_item, ", Done");
687         if (check_col(pinfo->cinfo, COL_INFO))
688                 col_append_fstr(pinfo->cinfo, COL_INFO, ", Command: \"Done\"");
689     }
690
691     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, tree, drep,
692                         hf_pn_io_control_block_properties, NULL);
693
694     return offset;
695 }
696
697
698 /* dissect one PN-IO block (depending on the block type) */
699 static int
700 dissect_block(tvbuff_t *tvb, int offset,
701         packet_info *pinfo, proto_tree *tree, guint8 *drep, guint32 u32Idx)
702 {
703     guint16 u16BlockType;
704     guint16 u16BlockLength;
705     guint8 u8BlockVersionHigh;
706     guint8 u8BlockVersionLow;
707         proto_item *sub_item;
708         proto_tree *sub_tree;
709         guint32 u32SubStart;
710     guint16 u16BodyLength;
711
712
713     /* from here, we only have big endian (network byte ordering) */
714     drep[0] &= ~0x10;
715
716     sub_item = proto_tree_add_item(tree, hf_pn_io_block, tvb, offset, 0, FALSE);
717         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_block);
718     u32SubStart = offset;
719
720         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
721                         hf_pn_io_block_type, &u16BlockType);
722         offset = dissect_dcerpc_uint16(tvb, offset, pinfo, sub_tree, drep, 
723                         hf_pn_io_block_length, &u16BlockLength);
724         offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
725                         hf_pn_io_block_version_high, &u8BlockVersionHigh);
726         offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
727                         hf_pn_io_block_version_low, &u8BlockVersionLow);
728
729     /* block length is without type and length fields, but with version field */
730     /* as it's already dissected, remove it */
731     u16BodyLength = u16BlockLength - 2;
732
733     switch(u16BlockType) {
734     case(0x0001):
735     case(0x0002):
736         dissect_Alarm_note_block(tvb, offset, pinfo, sub_tree, drep, u16BodyLength);
737         break;
738     case(0x0110):
739     case(0x0112):
740     case(0x0114):
741         if (check_col(pinfo->cinfo, COL_INFO))
742                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
743             val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
744         dissect_ControlConnect_block(tvb, offset, pinfo, sub_tree, drep);
745         break;
746     case(0x0008):
747     case(0x0009):
748         dissect_ReadWrite_rqst_block(tvb, offset, pinfo, sub_tree, drep);
749         break;
750     case(0x8001):
751     case(0x8002):
752         dissect_Alarm_ack_block(tvb, offset, pinfo, sub_tree, drep);
753         break;
754     case(0x8008):
755     case(0x8009):
756         dissect_ReadWrite_resp_block(tvb, offset, pinfo, sub_tree, drep);
757         break;
758     case(0x8110):
759     case(0x8112):
760     case(0x8114):
761         if (check_col(pinfo->cinfo, COL_INFO))
762                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
763             val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
764         dissect_ControlConnect_block(tvb, offset, pinfo, sub_tree, drep);
765         break;
766     default:
767         if (check_col(pinfo->cinfo, COL_INFO) && u32Idx < 3)
768                 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
769             val_to_str(u16BlockType, pn_io_block_type, "Unknown"));
770         proto_tree_add_string_format(sub_tree, hf_pn_io_data, tvb, offset, u16BodyLength, "undecoded", "Undecoded Data: %d bytes", u16BodyLength);
771     }
772     offset += u16BodyLength;
773
774         proto_item_append_text(sub_item, "[%u]: Type=\"%s\" (0x%04x), Length=%u(+4), Version=%u.%u", 
775                 u32Idx, val_to_str(u16BlockType, pn_io_block_type, "Unknown"), u16BlockType,
776         u16BlockLength, u8BlockVersionHigh, u8BlockVersionLow);
777         proto_item_set_len(sub_item, offset - u32SubStart);
778
779     return offset;
780 }
781
782
783 /* dissect any number of PN-IO blocks */
784 static int
785 dissect_blocks(tvbuff_t *tvb, int offset,
786         packet_info *pinfo, proto_tree *tree, guint8 *drep)
787 {
788     guint32 u32Idx = 0;
789     
790
791     while(tvb_length(tvb) > (guint) offset) {
792         offset = dissect_block(tvb, offset, pinfo, tree, drep, u32Idx);
793         u32Idx++;
794     }
795
796     if(u32Idx > 3) {
797         if (check_col(pinfo->cinfo, COL_INFO))
798                 col_append_fstr(pinfo->cinfo, COL_INFO, ", ... (%u blocks)",
799             u32Idx);
800     }
801         return offset;
802 }
803
804
805 /* dissect a PN-IO (DCE-RPC) request header */
806 static int
807 dissect_IPNIO_rqst_header(tvbuff_t *tvb, int offset,
808         packet_info *pinfo, proto_tree *tree, guint8 *drep)
809 {
810     guint32 u32ArgsMax;
811     guint32 u32ArgsLen;
812     guint32 u32MaxCount;
813     guint32 u32Offset;
814     guint32 u32ArraySize;
815
816         proto_item *sub_item;
817         proto_tree *sub_tree;
818         guint32 u32SubStart;
819
820
821         if (check_col(pinfo->cinfo, COL_PROTOCOL))
822             col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-CM");
823
824     /* args_max */
825         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
826                         hf_pn_io_args_max, &u32ArgsMax);
827     /* args_len */
828         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
829                         hf_pn_io_args_len, &u32ArgsLen);
830
831     sub_item = proto_tree_add_item(tree, hf_pn_io_array, tvb, offset, 0, FALSE);
832         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io);
833     u32SubStart = offset;
834
835     /* RPC array header */
836         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
837                         hf_pn_io_array_max_count, &u32MaxCount);
838         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
839                         hf_pn_io_array_offset, &u32Offset);
840         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
841                         hf_pn_io_array_act_count, &u32ArraySize);
842
843         proto_item_append_text(sub_item, ": Max: %u, Offset: %u, Size: %u", 
844         u32MaxCount, u32Offset, u32ArraySize);
845         proto_item_set_len(sub_item, offset - u32SubStart);
846
847     return offset;
848 }
849
850
851 /* dissect a PN-IO (DCE-RPC) response header */
852 static int
853 dissect_IPNIO_resp_header(tvbuff_t *tvb, int offset,
854         packet_info *pinfo, proto_tree *tree, guint8 *drep)
855 {
856     guint32 u32ArgsLen;
857     guint32 u32MaxCount;
858     guint32 u32Offset;
859     guint32 u32ArraySize;
860
861         proto_item *sub_item;
862         proto_tree *sub_tree;
863         guint32 u32SubStart;
864
865
866         if (check_col(pinfo->cinfo, COL_PROTOCOL))
867             col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-CM");
868
869     offset = dissect_PNIO_status(tvb, offset, pinfo, tree, drep);
870
871     /* args_len */
872         offset = dissect_ndr_uint32(tvb, offset, pinfo, tree, drep, 
873                         hf_pn_io_args_len, &u32ArgsLen);
874
875     sub_item = proto_tree_add_item(tree, hf_pn_io_array, tvb, offset, 0, FALSE);
876         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io);
877     u32SubStart = offset;
878
879     /* RPC array header */
880         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
881                         hf_pn_io_array_max_count, &u32MaxCount);
882         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
883                         hf_pn_io_array_offset, &u32Offset);
884         offset = dissect_ndr_uint32(tvb, offset, pinfo, sub_tree, drep, 
885                         hf_pn_io_array_act_count, &u32ArraySize);
886
887     proto_item_append_text(sub_item, ": Max: %u, Offset: %u, Size: %u", 
888         u32MaxCount, u32Offset, u32ArraySize);
889         proto_item_set_len(sub_item, offset - u32SubStart);
890
891     return offset;
892 }
893
894
895 /* dissect a PN-IO connect request */
896 static int
897 dissect_IPNIO_Connect_rqst(tvbuff_t *tvb, int offset,
898         packet_info *pinfo, proto_tree *tree, guint8 *drep)
899 {
900     
901     offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);
902
903     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
904
905         return offset;
906 }
907
908
909 /* dissect a PN-IO connect response */
910 static int
911 dissect_IPNIO_Connect_resp(tvbuff_t *tvb, int offset,
912         packet_info *pinfo, proto_tree *tree, guint8 *drep)
913 {
914
915     offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);
916
917     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
918
919         return offset;
920 }
921
922
923 /* dissect a PN-IO release request */
924 static int
925 dissect_IPNIO_Release_rqst(tvbuff_t *tvb, int offset,
926         packet_info *pinfo, proto_tree *tree, guint8 *drep)
927 {
928     
929     offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);
930
931     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
932
933         return offset;
934 }
935
936
937 /* dissect a PN-IO release response */
938 static int
939 dissect_IPNIO_Release_resp(tvbuff_t *tvb, int offset,
940         packet_info *pinfo, proto_tree *tree, guint8 *drep)
941 {
942
943     offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);
944
945     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
946
947         return offset;
948 }
949
950
951 /* dissect a PN-IO control request */
952 static int
953 dissect_IPNIO_Control_rqst(tvbuff_t *tvb, int offset,
954         packet_info *pinfo, proto_tree *tree, guint8 *drep)
955 {
956     
957     offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);
958
959     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
960
961     return offset;
962 }
963
964
965 /* dissect a PN-IO control response */
966 static int
967 dissect_IPNIO_Control_resp(tvbuff_t *tvb, int offset,
968         packet_info *pinfo, proto_tree *tree, guint8 *drep)
969 {
970
971     offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);
972
973     offset = dissect_blocks(tvb, offset, pinfo, tree, drep);
974
975     return offset;
976 }
977
978
979 /* dissect a PN-IO read request */
980 static int
981 dissect_IPNIO_Read_rqst(tvbuff_t *tvb, int offset,
982         packet_info *pinfo, proto_tree *tree, guint8 *drep)
983 {
984     
985     offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);
986
987     offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);
988
989         return offset;
990 }
991
992
993 /* dissect a PN-IO read response */
994 static int
995 dissect_IPNIO_Read_resp(tvbuff_t *tvb, int offset,
996         packet_info *pinfo, proto_tree *tree, guint8 *drep)
997 {
998     gint remain;
999
1000     offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);
1001
1002     offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);
1003
1004     /* XXX - remaining bytes: dissection not yet implemented */
1005     remain = tvb_length_remaining(tvb, offset);
1006     proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, remain, "data", "User Data: %d bytes", remain);
1007     offset += remain;
1008
1009         return offset;
1010 }
1011
1012
1013 /* dissect a PN-IO write request */
1014 static int
1015 dissect_IPNIO_Write_rqst(tvbuff_t *tvb, int offset,
1016         packet_info *pinfo, proto_tree *tree, guint8 *drep)
1017 {
1018     gint remain;
1019
1020     offset = dissect_IPNIO_rqst_header(tvb, offset, pinfo, tree, drep);
1021
1022     offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);
1023
1024     /* XXX - remaining bytes: dissection not yet implemented */
1025     remain = tvb_length_remaining(tvb, offset);
1026     proto_tree_add_string_format(tree, hf_pn_io_data, tvb, offset, remain, "data", "User Data: %d bytes", remain);
1027     offset += remain;
1028
1029         return offset;
1030 }
1031
1032
1033 /* dissect a PN-IO write response */
1034 static int
1035 dissect_IPNIO_Write_resp(tvbuff_t *tvb, int offset,
1036         packet_info *pinfo, proto_tree *tree, guint8 *drep)
1037 {
1038
1039     offset = dissect_IPNIO_resp_header(tvb, offset, pinfo, tree, drep);
1040
1041     offset = dissect_block(tvb, offset, pinfo, tree, drep, 0);
1042
1043         return offset;
1044 }
1045
1046
1047 /* dissect the IOxS (IOCS, IOPS) field */
1048 static int
1049 dissect_PNIO_IOxS(tvbuff_t *tvb, int offset,
1050         packet_info *pinfo _U_, proto_tree *tree, guint8 *drep _U_)
1051 {
1052     guint8 u8IOxS;
1053     proto_item *ioxs_item = NULL;
1054     proto_tree *ioxs_tree = NULL;
1055
1056
1057     u8IOxS = tvb_get_guint8(tvb, offset);
1058
1059     /* add ioxs subtree */
1060         ioxs_item = proto_tree_add_uint_format(tree, hf_pn_io_ioxs, 
1061                 tvb, offset, 1, u8IOxS,
1062                 "IOxS: 0x%02x (%s%s)", 
1063                 u8IOxS, 
1064                 (u8IOxS & 0x01) ? "another IOxS follows " : "",
1065                 (u8IOxS & 0x80) ? "good" : "bad");
1066         ioxs_tree = proto_item_add_subtree(ioxs_item, ett_pn_io_ioxs);
1067
1068         proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_extension, tvb, offset, 1, u8IOxS);
1069         proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_res14, tvb, offset, 1, u8IOxS);
1070         proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_instance, tvb, offset, 1, u8IOxS);
1071         proto_tree_add_uint(ioxs_tree, hf_pn_io_ioxs_datastate, tvb, offset, 1, u8IOxS);
1072
1073     return offset + 1;
1074 }
1075
1076
1077 /* dissect a PN-IO Cyclic Service Data Unit (on top of PN-RT protocol) */
1078 static int
1079 dissect_PNIO_C_SDU(tvbuff_t *tvb, int offset,
1080         packet_info *pinfo, proto_tree *tree, guint8 *drep)
1081 {
1082     proto_item *data_item;
1083         proto_tree *data_tree;
1084
1085
1086     if (check_col(pinfo->cinfo, COL_PROTOCOL))
1087             col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO");
1088
1089     if(tree) {
1090             data_item = proto_tree_add_protocol_format(tree, proto_pn_io, tvb, offset, tvb_length(tvb),
1091                                     "PROFINET IO Cyclic Service Data Unit: %u bytes", tvb_length(tvb));
1092         data_tree = proto_item_add_subtree(data_item, ett_pn_io_rtc);
1093
1094         offset = dissect_PNIO_IOxS(tvb, offset, pinfo, data_tree, drep);
1095
1096         /* XXX - dissect the remaining data */
1097         /* this will be one or more DataItems followed by an optional GAP and RTCPadding */
1098         /* as we don't have the required context information to dissect the specific DataItems, this will be tricky :-( */
1099             data_item = proto_tree_add_protocol_format(data_tree, proto_pn_io, tvb, offset, tvb_length_remaining(tvb, offset),
1100                                     "Data: %u bytes (including GAP and RTCPadding)", tvb_length_remaining(tvb, offset));
1101     }
1102
1103     return offset;
1104 }
1105
1106
1107 /* dissect a PN-IO RTA PDU (on top of PN-RT protocol) */
1108 static int
1109 dissect_PNIO_RTA(tvbuff_t *tvb, int offset,
1110         packet_info *pinfo, proto_tree *tree, guint8 *drep)
1111 {
1112     guint16 u16AlarmDstEndpoint;
1113     guint16 u16AlarmSrcEndpoint;
1114     guint8  u8PDUType;
1115     guint8  u8PDUVersion;
1116     guint8  u8WindowSize;
1117     guint8  u8Tack;
1118     guint16 u16SendSeqNum;
1119     guint16 u16AckSeqNum;
1120     guint16 u16VarPartLen;
1121     int     start_offset = offset;
1122
1123     proto_item *rta_item;
1124         proto_tree *rta_tree;
1125
1126     proto_item *sub_item;
1127         proto_tree *sub_tree;
1128
1129
1130         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1131             col_add_str(pinfo->cinfo, COL_PROTOCOL, "PNIO-AL");
1132
1133         rta_item = proto_tree_add_protocol_format(tree, proto_pn_io, tvb, offset, tvb_length(tvb), 
1134         "PROFINET IO Alarm");
1135         rta_tree = proto_item_add_subtree(rta_item, ett_pn_io_rta);
1136
1137     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
1138                     hf_pn_io_alarm_dst_endpoint, &u16AlarmDstEndpoint);
1139     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
1140                     hf_pn_io_alarm_src_endpoint, &u16AlarmSrcEndpoint);
1141
1142     if (check_col(pinfo->cinfo, COL_INFO))
1143             col_append_fstr(pinfo->cinfo, COL_INFO, ", Src: 0x%x, Dst: 0x%x",
1144         u16AlarmSrcEndpoint, u16AlarmDstEndpoint);
1145
1146     /* PDU type */
1147         sub_item = proto_tree_add_item(rta_tree, hf_pn_io_pdu_type, tvb, offset, 1, FALSE);
1148         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_pdu_type);
1149     dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
1150                     hf_pn_io_pdu_type_type, &u8PDUType);
1151     u8PDUType &= 0x0F;
1152     offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
1153                     hf_pn_io_pdu_type_version, &u8PDUVersion);
1154     u8PDUVersion >>= 4;
1155     proto_item_append_text(sub_item, ", Type: %s, Version: %u", 
1156         val_to_str(u8PDUType, pn_io_pdu_type, "Unknown"),
1157         u8PDUVersion);
1158
1159     /* additional flags */
1160         sub_item = proto_tree_add_item(rta_tree, hf_pn_io_add_flags, tvb, offset, 1, FALSE);
1161         sub_tree = proto_item_add_subtree(sub_item, ett_pn_io_add_flags);
1162     dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
1163                     hf_pn_io_window_size, &u8WindowSize);
1164     u8WindowSize &= 0x0F;
1165     offset = dissect_dcerpc_uint8(tvb, offset, pinfo, sub_tree, drep, 
1166                     hf_pn_io_tack, &u8Tack);
1167     u8Tack >>= 4;
1168     proto_item_append_text(sub_item, ", Window Size: %u, Tack: %u", 
1169         u8WindowSize, u8Tack);
1170
1171     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
1172                     hf_pn_io_send_seq_num, &u16SendSeqNum);
1173     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
1174                     hf_pn_io_ack_seq_num, &u16AckSeqNum);
1175     offset = dissect_dcerpc_uint16(tvb, offset, pinfo, rta_tree, drep, 
1176                     hf_pn_io_var_part_len, &u16VarPartLen);
1177
1178     switch(u8PDUType & 0x0F) {
1179     case(1):    /* Data-RTA */
1180         if (check_col(pinfo->cinfo, COL_INFO))
1181                 col_append_str(pinfo->cinfo, COL_INFO, ", Data-RTA");
1182         offset = dissect_block(tvb, offset, pinfo, rta_tree, drep, 0);
1183         break;
1184     case(2):    /* NACK-RTA */
1185         if (check_col(pinfo->cinfo, COL_INFO))
1186                 col_append_str(pinfo->cinfo, COL_INFO, ", NACK-RTA");
1187         /* no additional data */
1188         break;
1189     case(3):    /* ACK-RTA */
1190         if (check_col(pinfo->cinfo, COL_INFO))
1191                 col_append_str(pinfo->cinfo, COL_INFO, ", ACK-RTA");
1192         /* no additional data */
1193         break;
1194     case(4):    /* ERR-RTA */
1195         if (check_col(pinfo->cinfo, COL_INFO))
1196                 col_append_str(pinfo->cinfo, COL_INFO, ", ERR-RTA");
1197         offset = dissect_PNIO_status(tvb, offset, pinfo, rta_tree, drep);
1198         break;
1199     default:
1200         proto_tree_add_string_format(tree, hf_pn_io_data, tvb, 0, tvb_length(tvb), "data", 
1201             "PN-IO Alarm: unknown PDU type 0x%x", u8PDUType);    
1202     }
1203
1204     proto_item_set_len(rta_item, offset - start_offset);
1205
1206     return offset;
1207 }
1208
1209
1210 /* possibly dissect a PN-IO related PN-RT packet */
1211 static gboolean
1212 dissect_PNIO_heur(tvbuff_t *tvb, 
1213         packet_info *pinfo, proto_tree *tree)
1214 {
1215     guint8  drep_data = 0;
1216     guint8  *drep = &drep_data;
1217         guint8  u8CBAVersion;
1218     guint16 u16FrameID;
1219
1220
1221     /* the sub tvb will NOT contain the frame_id here! */
1222     u16FrameID = GPOINTER_TO_UINT(pinfo->private_data);
1223
1224     u8CBAVersion = tvb_get_guint8 (tvb, 0);
1225
1226     /* is this a PNIO class 2 data packet? */
1227         /* frame id must be in valid range (cyclic Real-Time, class=2) */
1228         if (u16FrameID >= 0x8000 && u16FrameID < 0xbf00) {
1229         dissect_PNIO_C_SDU(tvb, 0, pinfo, tree, drep);
1230         return TRUE;
1231     }
1232
1233     /* is this a PNIO class 1 data packet? */
1234         /* frame id must be in valid range (cyclic Real-Time, class=1) and
1235      * first byte (CBA version field) has to be != 0x11 */
1236         if (u16FrameID >= 0xc000 && u16FrameID < 0xfb00 && u8CBAVersion != 0x11) {
1237         dissect_PNIO_C_SDU(tvb, 0, pinfo, tree, drep);
1238         return TRUE;
1239     }
1240
1241     /* is this a PNIO high priority alarm packet? */
1242     if(u16FrameID == 0xfc01) {
1243         if (check_col(pinfo->cinfo, COL_INFO))
1244                 col_add_str(pinfo->cinfo, COL_INFO, "Alarm High");
1245
1246         dissect_PNIO_RTA(tvb, 0, pinfo, tree, drep);
1247         return TRUE;
1248     }
1249
1250     /* is this a PNIO low priority alarm packet? */
1251     if(u16FrameID == 0xfe01) {
1252         if (check_col(pinfo->cinfo, COL_INFO))
1253                 col_add_str(pinfo->cinfo, COL_INFO, "Alarm Low");
1254
1255         dissect_PNIO_RTA(tvb, 0, pinfo, tree, drep);
1256         return TRUE;
1257     }
1258
1259     /* this PN-RT packet doesn't seem to be PNIO specific */
1260     return FALSE;
1261 }
1262
1263
1264 /* the PNIO dcerpc interface table */
1265 static dcerpc_sub_dissector pn_io_dissectors[] = {
1266 { 0, "Connect", dissect_IPNIO_Connect_rqst, dissect_IPNIO_Connect_resp },
1267 { 1, "Release", dissect_IPNIO_Release_rqst, dissect_IPNIO_Release_resp },
1268 { 2, "Read",    dissect_IPNIO_Read_rqst,    dissect_IPNIO_Read_resp },
1269 { 3, "Write",   dissect_IPNIO_Write_rqst,   dissect_IPNIO_Write_resp },
1270 { 4, "Control", dissect_IPNIO_Control_rqst, dissect_IPNIO_Control_resp },
1271         { 0, NULL, NULL, NULL }
1272 };
1273
1274
1275 void
1276 proto_register_pn_io (void)
1277 {
1278         static hf_register_info hf[] = {
1279         { &hf_pn_io_opnum,
1280                 { "Operation", "pn_io.opnum", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1281         { &hf_pn_io_reserved16,
1282                 { "Reserved", "pn_io.reserved16", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1283         { &hf_pn_io_array,
1284         { "Array", "pn_io.array", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1285         { &hf_pn_io_status,
1286                 { "Status", "pn_io.status", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1287         { &hf_pn_io_args_max,
1288                 { "ArgsMaximum", "pn_io.args_max", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1289         { &hf_pn_io_args_len,
1290                 { "ArgsLength", "pn_io.args_len", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1291         { &hf_pn_io_array_max_count,
1292                 { "MaximumCount", "pn_io.array_max_count", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1293         { &hf_pn_io_array_offset,
1294                 { "Offset", "pn_io.array_offset", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1295         { &hf_pn_io_array_act_count,
1296                 { "ActualCount", "pn_io.array_act_count", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1297     { &hf_pn_io_ar_uuid,
1298       { "ARUUID", "pn_io.ar_uuid", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},
1299     { &hf_pn_io_api,
1300       { "API", "pn_io.api", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1301     { &hf_pn_io_slot_nr,
1302       { "SlotNumber", "pn_io.slot_nr", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1303     { &hf_pn_io_subslot_nr,
1304       { "SubslotNumber", "pn_io.subslot_nr", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1305     { &hf_pn_io_index,
1306       { "Index", "pn_io.index", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1307     { &hf_pn_io_seq_number,
1308       { "SeqNumber", "pn_io.seq_number", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1309     { &hf_pn_io_record_data_length,
1310       { "RecordDataLength", "pn_io.record_data_length", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL }},
1311     { &hf_pn_io_padding,
1312       { "Padding", "pn_io.padding", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},
1313     { &hf_pn_io_add_val1,
1314       { "AdditionalValue1", "pn_io.add_val1", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1315     { &hf_pn_io_add_val2,
1316       { "AdditionalValue2", "pn_io.add_val2", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1317     { &hf_pn_io_block_type,
1318       { "BlockType", "pn_io.block_type", FT_UINT16, BASE_HEX, VALS(pn_io_block_type), 0x0, "", HFILL }},
1319     { &hf_pn_io_block_length,
1320       { "BlockLength", "pn_io.block_length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1321     { &hf_pn_io_block_version_high,
1322       { "BlockVersionHigh", "pn_io.block_version_high", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1323     { &hf_pn_io_block_version_low,
1324       { "BlockVersionLow", "pn_io.block_version_low", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1325     { &hf_pn_io_sessionkey,
1326       { "SessionKey", "pn_io.session_key", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1327     { &hf_pn_io_control_command,
1328       { "ControlCommand", "pn_io.control_command", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1329     { &hf_pn_io_control_command_prmend,
1330       { "PrmEnd", "pn_io.control_command.prmend", FT_UINT16, BASE_DEC, NULL, 0x0001, "", HFILL }},
1331     { &hf_pn_io_control_command_applready,
1332       { "ApplicationReady", "pn_io.control_command.applready", FT_UINT16, BASE_DEC, NULL, 0x0002, "", HFILL }},
1333     { &hf_pn_io_control_command_release,
1334       { "Release", "pn_io.control_command.release", FT_UINT16, BASE_DEC, NULL, 0x0004, "", HFILL }},
1335     { &hf_pn_io_control_command_done,
1336       { "Done", "pn_io.control_command.done", FT_UINT16, BASE_DEC, NULL, 0x0008, "", HFILL }},
1337     { &hf_pn_io_control_block_properties,
1338       { "ControlBlockProperties", "pn_io.control_block_properties", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1339
1340     { &hf_pn_io_error_code,
1341       { "ErrorCode", "pn_io.error_code", FT_UINT8, BASE_HEX, VALS(pn_io_error_code), 0x0, "", HFILL }},
1342     { &hf_pn_io_error_decode,
1343       { "ErrorDecode", "pn_io.error_decode", FT_UINT8, BASE_HEX, VALS(pn_io_error_decode), 0x0, "", HFILL }},
1344     { &hf_pn_io_error_code1,
1345       { "ErrorCode1", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1), 0x0, "", HFILL }},
1346     { &hf_pn_io_error_code2,
1347       { "ErrorCode2", "pn_io.error_code2", FT_UINT8, BASE_HEX, VALS(pn_io_error_code2), 0x0, "", HFILL }},
1348     { &hf_pn_io_error_code1_pniorw,
1349       { "ErrorCode1 (PNIORW)", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1_pniorw), 0x0, "", HFILL }},
1350     { &hf_pn_io_error_code1_pnio,
1351       { "ErrorCode1 (PNIO)", "pn_io.error_code1", FT_UINT8, BASE_HEX, VALS(pn_io_error_code1_pnio), 0x0, "", HFILL }},
1352         { &hf_pn_io_block,
1353     { "Block", "pn_io.block", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1354     { &hf_pn_io_data,
1355       { "Undecoded Data", "pn_io.data", FT_STRING, BASE_DEC, NULL, 0x0, "", HFILL }},
1356
1357     { &hf_pn_io_alarm_type,
1358       { "AlarmType", "pn_io.alarm_type", FT_UINT16, BASE_HEX, VALS(pn_io_alarm_type), 0x0, "", HFILL }},
1359
1360     { &hf_pn_io_alarm_specifier,
1361       { "AlarmSpecifier", "pn_io.alarm_specifier", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1362     { &hf_pn_io_alarm_specifier_sequence,
1363       { "SequenceNumber", "pn_io.alarm_specifier.sequence", FT_UINT16, BASE_HEX, NULL, 0x07FF, "", HFILL }},
1364     { &hf_pn_io_alarm_specifier_channel,
1365       { "ChannelDiagnosis", "pn_io.alarm_specifier.channel", FT_UINT16, BASE_HEX, NULL, 0x0800, "", HFILL }},
1366     { &hf_pn_io_alarm_specifier_manufacturer,
1367       { "ManufacturerSpecificDiagnosis", "pn_io.alarm_specifier.manufacturer", FT_UINT16, BASE_HEX, NULL, 0x1000, "", HFILL }},
1368     { &hf_pn_io_alarm_specifier_submodule,
1369       { "SubmoduleDiagnosisState", "pn_io.alarm_specifier.submodule", FT_UINT16, BASE_HEX, NULL, 0x2000, "", HFILL }},
1370     { &hf_pn_io_alarm_specifier_ardiagnosis,
1371       { "ARDiagnosisState", "pn_io.alarm_specifier.ardiagnosis", FT_UINT16, BASE_HEX, NULL, 0x8000, "", HFILL }},
1372
1373     { &hf_pn_io_alarm_dst_endpoint,
1374       { "AlarmDstEndpoint", "pn_io.alarm_dst_endpoint", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1375     { &hf_pn_io_alarm_src_endpoint,
1376       { "AlarmSrcEndpoint", "pn_io.alarm_src_endpoint", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1377     { &hf_pn_io_pdu_type,
1378       { "PDUType", "pn_io.pdu_type", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1379     { &hf_pn_io_pdu_type_type,
1380       { "Type", "pn_io.pdu_type.type", FT_UINT8, BASE_HEX, VALS(pn_io_pdu_type), 0x0F, "", HFILL }},
1381     { &hf_pn_io_pdu_type_version,
1382       { "Version", "pn_io.pdu_type.version", FT_UINT8, BASE_HEX, NULL, 0xF0, "", HFILL }},
1383     { &hf_pn_io_add_flags,
1384       { "AddFlags", "pn_io.add_flags", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1385     { &hf_pn_io_window_size,
1386       { "WindowSize", "pn_io.window_size", FT_UINT8, BASE_DEC, NULL, 0x0F, "", HFILL }},
1387     { &hf_pn_io_tack,
1388       { "TACK", "pn_io.tack", FT_UINT8, BASE_HEX, NULL, 0xF0, "", HFILL }},
1389     { &hf_pn_io_send_seq_num,
1390       { "SendSeqNum", "pn_io.send_seq_num", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1391     { &hf_pn_io_ack_seq_num,
1392       { "AckSeqNum", "pn_io.ack_seq_num", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1393     { &hf_pn_io_var_part_len,
1394       { "VarPartLen", "pn_io.var_part_len", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1395     { &hf_pn_io_module_ident_number,
1396       { "ModuleIdentNumber", "pn_io.module_ident_number", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1397     { &hf_pn_io_submodule_ident_number,
1398       { "SubmoduleIdentNumber", "pn_io.submodule_ident_number", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL }},
1399
1400     { &hf_pn_io_ioxs,
1401       { "IOxS", "pn_io.ioxs", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1402     { &hf_pn_io_ioxs_extension,
1403       { "Extension (1:another IOxS follows/0:no IOxS follows)", "pn_io.ioxs.extension", FT_UINT8, BASE_HEX, NULL, 0x01, "", HFILL }},
1404     { &hf_pn_io_ioxs_res14,
1405       { "Reserved (should be zero)", "pn_io.ioxs.res14", FT_UINT8, BASE_HEX, NULL, 0x1E, "", HFILL }},
1406     { &hf_pn_io_ioxs_instance,
1407       { "Instance (only valid, if DataState is bad)", "pn_io.ioxs.instance", FT_UINT8, BASE_HEX, VALS(pn_io_ioxs), 0x60, "", HFILL }},
1408     { &hf_pn_io_ioxs_datastate,
1409       { "DataState (1:good/0:bad)", "pn_io.ioxs.datastate", FT_UINT8, BASE_HEX, NULL, 0x80, "", HFILL }}
1410     };
1411
1412         static gint *ett[] = {
1413                 &ett_pn_io,
1414         &ett_pn_io_block,
1415         &ett_pn_io_status,
1416         &ett_pn_io_rtc,
1417         &ett_pn_io_rta,
1418                 &ett_pn_io_pdu_type,
1419         &ett_pn_io_add_flags,
1420         &ett_pn_io_control_command,
1421         &ett_pn_io_ioxs
1422         };
1423
1424         proto_pn_io = proto_register_protocol ("PROFINET IO", "PNIO", "pn_io");
1425         proto_register_field_array (proto_pn_io, hf, array_length (hf));
1426         proto_register_subtree_array (ett, array_length (ett));
1427 }
1428
1429 void
1430 proto_reg_handoff_pn_io (void)
1431 {
1432         /* Register the protocols as dcerpc */
1433         dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_device, ver_pn_io_device, pn_io_dissectors, hf_pn_io_opnum);
1434         dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_controller, ver_pn_io_controller, pn_io_dissectors, hf_pn_io_opnum);
1435         dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_supervisor, ver_pn_io_supervisor, pn_io_dissectors, hf_pn_io_opnum);
1436         dcerpc_init_uuid (proto_pn_io, ett_pn_io, &uuid_pn_io_parameterserver, ver_pn_io_parameterserver, pn_io_dissectors, hf_pn_io_opnum);
1437
1438         heur_dissector_add("pn_rt", dissect_PNIO_heur, proto_pn_io);
1439 }