From Roland Knall: openSAFETY dissector.
[obnox/wireshark/wip.git] / epan / dissectors / packet-opensafety.c
1 /* packet-opensafety.c
2  *   openSAFETY is a machine-safety protocol, encapsulated in modern fieldbus
3  *   and industrial ethernet solutions.
4  *   For more information see http://www.open-safety.org
5  * By Roland Knall <roland.knall@br-automation.com>
6  * Copyright 2011 Bernecker + Rainer Industrie-Elektronik Ges.m.b.H.
7  *
8  * $Id$
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <glib.h>
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36 #include <epan/etypes.h>
37 #include <epan/tfs.h>
38 #include <epan/proto.h>
39 #include <epan/emem.h>
40 #include <epan/dissectors/packet-udp.h>
41
42 #include <stdio.h>
43 #include <string.h>
44
45
46 /* General definitions */
47
48 /* openSAFETY UDP Port */
49 #ifndef UDP_PORT_OPENSAFETY
50 #define UDP_PORT_OPENSAFETY   9877
51 #endif
52
53 /* SercosIII UDP Port */
54 #ifndef UDP_PORT_SIII
55 #define UDP_PORT_SIII         8755
56 #endif
57
58 /* Under linux, this get's defined in netinet/in.h */
59 #ifndef IPPROTO_UDP
60 #define IPPROTO_UDP 0x11
61 #endif
62
63 #ifndef OPENSAFETY_PINFO_CONST_DATA
64 #define OPENSAFETY_PINFO_CONST_DATA 0xAABBCCDD
65 #endif
66
67 /* openSAFETY CRC types */
68 #define OPENSAFETY_CHECKSUM_CRC8   0x01
69 #define OPENSAFETY_CHECKSUM_CRC16  0x02
70 #define OPENSAFETY_CHECKSUM_CRC32  0x04
71
72 static const value_string message_crc_type[] = {
73         { OPENSAFETY_CHECKSUM_CRC8,  "CRC8" },
74         { OPENSAFETY_CHECKSUM_CRC16, "CRC16" },
75         { OPENSAFETY_CHECKSUM_CRC32, "CRC32" },
76         { 0, NULL }
77 };
78
79 /* openSAFETY Message Types */
80 #define OPENSAFETY_SPDO_MESSAGE_TYPE           0xC0
81 #define OPENSAFETY_SSDO_MESSAGE_TYPE           0xE0
82 #define OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE      0xE8
83 #define OPENSAFETY_SNMT_MESSAGE_TYPE           0xA0
84
85 static const value_string message_id_values[] = {
86           { OPENSAFETY_SPDO_MESSAGE_TYPE,      "openSAFETY SPDO" },
87           { OPENSAFETY_SSDO_MESSAGE_TYPE,      "openSAFETY SSDO" },
88           { OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE, "openSAFETY Slim SSDO" },
89           { OPENSAFETY_SNMT_MESSAGE_TYPE,      "openSAFETY SNMT" },
90           { 0, NULL }
91 };
92
93 /* openSAFETY Message IDs */
94 #define OPENSAFETY_MSG_SPDO_DATA_ONLY                  0xC0
95 #define OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST     0xC8
96 #define OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE    0xD0
97 #define OPENSAFETY_MSG_SPDO_RESERVED                   0xD8
98
99 #define OPENSAFETY_MSG_SSDO_SERVICE_REQUEST            0xE0
100 #define OPENSAFETY_MSG_SSDO_SERVICE_RESPONSE           0xE4
101 #define OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST       0xE8
102 #define OPENSAFETY_MSG_SSDO_SLIM_SERVICE_RESPONSE      0xEC
103
104 #define OPENSAFETY_MSG_SNMT_REQUEST_UDID               0xA0
105 #define OPENSAFETY_MSG_SNMT_RESPONSE_UDID              0xA4
106 #define OPENSAFETY_MSG_SNMT_ASSIGN_SADR                0xA8
107 #define OPENSAFETY_MSG_SNMT_SADR_ASSIGNED              0xAC
108 #define OPENSAFETY_MSG_SNMT_SERVICE_REQUEST            0xB0
109 #define OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE           0xB4
110 #define OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM      0xBC
111
112 static const value_string message_type_values[] = {
113         { OPENSAFETY_MSG_SPDO_DATA_ONLY,               "SPDO Data only" },
114         { OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST,  "SPDO Data with Time Request" },
115         { OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE, "SPDO Data with Time Response" },
116         { OPENSAFETY_MSG_SPDO_RESERVED,                "SPDO Reserved" },
117
118         { OPENSAFETY_MSG_SSDO_SERVICE_REQUEST,         "SSDO Service Request" },
119         { OPENSAFETY_MSG_SSDO_SERVICE_RESPONSE,        "SSDO Service Response" },
120         { OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST,    "SSDO Slim Service Request" },
121         { OPENSAFETY_MSG_SSDO_SLIM_SERVICE_RESPONSE,   "SSDO Slim Service Response" },
122
123         { OPENSAFETY_MSG_SNMT_REQUEST_UDID,            "SNMT Request UDID" },
124         { OPENSAFETY_MSG_SNMT_RESPONSE_UDID,           "SNMT Response UDID" },
125         { OPENSAFETY_MSG_SNMT_ASSIGN_SADR,             "SNMT Assign SADR" },
126         { OPENSAFETY_MSG_SNMT_SADR_ASSIGNED,           "SNMT SADR Assigned" },
127         { OPENSAFETY_MSG_SNMT_SERVICE_REQUEST,         "SNMT Service Request" },
128         { OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE,        "SNMT Service Response" },
129         { OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM,   "SNMT SN reset guarding SCM" },
130         {0, NULL }
131 };
132
133 /* SNTM extended Services */
134 #define OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_PRE_OP              0x00
135 #define OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_OP                  0x02
136 #define OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP               0x04
137 #define OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP                 0x06
138 #define OPENSAFETY_MSG_SNMT_EXT_SCM_GUARD_SN                  0x08
139 #define OPENSAFETY_MSG_SNMT_EXT_ASSIGN_ADDITIONAL_SADR        0x0A
140 #define OPENSAFETY_MSG_SNMT_EXT_SN_ACKNOWLEDGE                0x0C
141 #define OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGN_UDID_SCM            0x0E
142 #define OPENSAFETY_MSG_SNMT_EXT_SN_STATUS_PRE_OP              0x01
143 #define OPENSAFETY_MSG_SNMT_EXT_SN_STATUS_OP                  0x03
144 #define OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_ADDITIONAL_SADR   0x05
145 #define OPENSAFETY_MSG_SNMT_EXT_SN_FAIL                       0x07
146 #define OPENSAFETY_MSG_SNMT_EXT_SN_BUSY                       0x09
147 #define OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_UDID_SCM          0x0F
148
149 static const value_string message_service_type[] = {
150         { OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_PRE_OP,             "SN set to pre Operational" },
151         { OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_OP,                 "SN set to Operational" },
152         { OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP,              "SCM set to Stop" },
153         { OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP,                "SCM set to Operational" },
154         { OPENSAFETY_MSG_SNMT_EXT_SCM_GUARD_SN,                 "SCM guard SN" },
155         { OPENSAFETY_MSG_SNMT_EXT_ASSIGN_ADDITIONAL_SADR,       "Assign additional SADR" },
156         { OPENSAFETY_MSG_SNMT_EXT_SN_ACKNOWLEDGE,               "SN Acknowledge" },
157         { OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGN_UDID_SCM,           "SN assign UDID SCM" },
158         { OPENSAFETY_MSG_SNMT_EXT_SN_STATUS_PRE_OP,             "SN status pre Operational" },
159         { OPENSAFETY_MSG_SNMT_EXT_SN_STATUS_OP,                 "SN status Operational" },
160         { OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_ADDITIONAL_SADR,  "Assigned additional SADR" },
161         { OPENSAFETY_MSG_SNMT_EXT_SN_FAIL,                      "SN Fail" },
162         { OPENSAFETY_MSG_SNMT_EXT_SN_BUSY,                      "SN Busy" },
163         { OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_UDID_SCM,         "SN assigned UDID SCM" },
164         { 0, NULL }
165 };
166
167
168 /* SNTM extended Services */
169 #define OPENSAFETY_MSG_SSDO_ABORT                           0x04
170 #define OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_MIDDLE           0x08
171 #define OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_MIDDLE         0x09
172 #define OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_EXPEDITED       0x20
173 #define OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_EXPEDITED     0x21
174 #define OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_SEGMENTED       0x28
175 #define OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_SEGMENTED     0x29
176 #define OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_END              0x48
177 #define OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_END            0x49
178 /*
179 #define OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_SEGMENT_MIDDLE     0x88
180 #define OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_SEGMENT_MIDDLE   0x89
181 #define OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_INITIATE           0xA8
182 #define OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_INITIATE         0xA9
183 #define OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_INITIATE_EXPEDITED 0xC0
184 #define OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_SEGMENT_END        0x40
185 #define OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_SEGMENT_END      0xC9
186 */
187 static const value_string ssdo_sacmd_values[] = {
188     /*    { OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_SEGMENT_END,     "Block Download Segment End" },
189         { OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_INITIATE,          "Block Upload Expedited Initiate" },
190         { OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_INITIATE_EXPEDITED,"Block Upload Initiate" },
191         { OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_INITIATE,        "Block Download Initiate" },
192         { OPENSAFETY_MSG_SSDO_BLOCK_DOWNLOAD_SEGMENT_MIDDLE,  "Block Download Middle Segment" },
193         { OPENSAFETY_MSG_SSDO_BLOCK_UPLOAD_SEGMENT_END,       "Block Upload End Segment" },*/
194         { OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_END,           "Download End Segment" },
195         { OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_END,             "Upload End Segment" },
196         { OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_EXPEDITED,    "Download Expedited Initiate" },
197         { OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_SEGMENTED,      "Upload Initiate Segmented" },
198         { OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_SEGMENTED,    "Download Initiate Segmented" },
199         { OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_EXPEDITED,      "Upload Expedited Initiate" },
200         { OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_MIDDLE,        "Download Middle Segment" },
201         { OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_MIDDLE,          "Upload Middle Segment" },
202         { OPENSAFETY_MSG_SSDO_ABORT,                           "Abort" },
203         { 0, NULL }
204 };
205
206 #define OPENSAFETY_SSDO_SACMD_ACC  0x01
207 #define OPENSAFETY_SSDO_SACMD_RES  0x02
208 #define OPENSAFETY_SSDO_SACMD_ABRT 0x04
209 #define OPENSAFETY_SSDO_SACMD_SEG  0x08
210 #define OPENSAFETY_SSDO_SACMD_TGL  0x10
211 #define OPENSAFETY_SSDO_SACMD_INI  0x20
212 #define OPENSAFETY_SSDO_SACMD_ENSG 0x40
213 #define OPENSAFETY_SSDO_SACMD_BLK  0x80
214
215 static const true_false_string opensafety_sacmd_acc  = { "Write Access", "Read Access" };
216 static const true_false_string opensafety_sacmd_res  = { "Reserved", "Reserved" };
217 static const true_false_string opensafety_sacmd_abrt = { "Abort Transfer", "Successful Transfer" };
218 static const true_false_string opensafety_sacmd_seg  = { "Segmented Access", "Expedited Access" };
219 static const true_false_string opensafety_on_off     = { "On", "Off" };
220 static const true_false_string opensafety_set_notset = { "Set", "Not set" };
221 static const true_false_string opensafety_sacmd_ini  = { "Initiate", "No Initiate" };
222 static const true_false_string opensafety_sacmd_ensg = { "No more segments", "More segments" };
223 static const true_false_string opensafety_sacmd_blk  = { "Block Transfer", "Normal Transfer" };
224
225 #define OPENSAFETY_SPDO_CONNECTION_VALID  0x04
226
227
228 static const value_string abort_codes[] = {
229
230         /* SSDO abort codes */
231         { 0x05030000,  "Reserved" },
232
233         { 0x05040000,  "SSDO protocol timed out" },
234         { 0x05040001,  "Client/server Command ID not valid or unknown" },
235         { 0x05040002,  "Invalid block size" },
236         { 0x05040003,  "Invalid sequence number" },
237         { 0x05040004,  "Reserved" },
238         { 0x05040005,  "Out of memory" },
239
240         { 0x06010000,  "Unsupported access to an object" },
241         { 0x06010001,  "Attempt to read a write-only object" },
242         { 0x06010002,  "Attempt to write a read-only object" },
243
244         { 0x06020000,  "Object does not exist in the object dictionary" },
245
246         { 0x06040041,  "Object cannot be mapped to the SPDO" },
247         { 0x06040042,  "The number and length of the objects to be mapped would exceed SPDO length" },
248         { 0x06040043,  "General parameter incompatibility" },
249         { 0x06040047,  "General internal incompatibility in the device" },
250
251         { 0x06060000,  "Access failed due to a hardware error" },
252
253         { 0x06070010,  "Data type does not match, length of service parameter does not match" },
254         { 0x06070012,  "Data type does not match, length of service parameter too high" },
255         { 0x06070013,  "Data type does not match, length of service parameter too low" },
256
257         { 0x06090011,  "Sub-index does not exist" },
258         { 0x06090030,  "Value range o parameter exceeded (only for write access)" },
259         { 0x06090031,  "Value of parameter written too high" },
260         { 0x06090032,  "Value of parameter written too low" },
261         { 0x06090036,  "Maximum value is less than minimum value" },
262
263         { 0x08000000,  "General error" },
264         { 0x08000020,  "Data cannot be transferred or stored to the application" },
265         { 0x08000021,  "Data cannot be transferred or stored to the application because of local control" },
266         { 0x08000022,  "Data cannot be transferred or stored to the application because of the present device state" },
267         { 0x08000023,  "Data cannot be transferred or stored to the application because of the object data is not available now" },
268
269         { 0, NULL }
270 };
271
272 static const true_false_string opensafety_message_type = { "Request", "Response" };
273 #define OPENSAFETY_REQUEST  TRUE
274 #define OPENSAFETY_RESPONSE FALSE
275
276 #define OSS_FRAME_POS_ADDR   0
277 #define OSS_FRAME_POS_ID     1
278 #define OSS_FRAME_POS_LEN    2
279 #define OSS_FRAME_POS_CT     3
280 #define OSS_FRAME_POS_DATA   4
281
282 #define OSS_PAYLOAD_MAXSIZE_FOR_CRC8        0x08
283 #define OSS_SLIM_FRAME_WITH_CRC8_MAXSIZE    0x13   /* 19 */
284 #define OSS_SLIM_FRAME2_WITH_CRC8           0x06   /*  6 */
285 #define OSS_SLIM_FRAME2_WITH_CRC16          0x07   /*  7 */
286
287 #define OSS_FRAME_ADDR(f, offset)        (f[OSS_FRAME_POS_ADDR + offset] + ((guint8)((f[OSS_FRAME_POS_ADDR + offset + 1]) << 6) << 2))
288 #define OSS_FRAME_ID(f, offset)          ((f[OSS_FRAME_POS_ID + offset] >> 2 ) << 2)
289 #define OSS_FRAME_LENGTH(f, offset)      (f[OSS_FRAME_POS_LEN + offset])
290 #define OSS_FRAME_FIELD(f, position)       (f[position])
291
292
293 #define CRC8_POLY 0x2F /* CRC-8 Polynom */
294 #define CRC16_POLY 0x5935 /* CRC-16 Polynom */
295
296
297 static int proto_opensafety = -1;
298
299 static gint ett_opensafety = -1;
300 static gint ett_opensafety_checksum = -1;
301 static gint ett_opensafety_snmt = -1;
302 static gint ett_opensafety_ssdo = -1;
303 static gint ett_opensafety_ssdo_sacmd = -1;
304
305 static int hf_oss_msg = -1;
306 static int hf_oss_msgtype = -1;
307 static int hf_oss_msg_sender = -1;
308 static int hf_oss_msg_receiver = -1;
309 static int hf_oss_length= -1;
310 static int hf_oss_data = -1;
311 static int hf_oss_crc = -1;
312
313 static int hf_oss_crc_valid = -1;
314 static int hf_oss_crc_type  = -1;
315
316 static int hf_oss_snmt_slave         = -1;
317 static int hf_oss_snmt_master        = -1;
318 static int hf_oss_snmt_udid          = -1;
319 static int hf_oss_snmt_scm           = -1;
320 static int hf_oss_snmt_tool          = -1;
321 static int hf_oss_snmt_timestamp     = -1;
322 static int hf_oss_snmt_service_id    = -1;
323 static int hf_oss_snmt_error_group   = -1;
324 static int hf_oss_snmt_error_code    = -1;
325
326 static int hf_oss_ssdo_server        = -1;
327 static int hf_oss_ssdo_client        = -1;
328 static int hf_oss_ssdo_sano          = -1;
329 static int hf_oss_ssdo_sacmd         = -1;
330 static int hf_oss_ssdo_sod_index     = -1;
331 static int hf_oss_ssdo_sod_subindex  = -1;
332 static int hf_oss_ssdo_payload       = -1;
333 static int hf_oss_ssdo_payload_size  = -1;
334 static int hf_oss_ssdo_segment_size  = -1;
335 static int hf_oss_ssdo_inhibit_time  = -1;
336 static int hf_oss_ssdo_abort_code    = -1;
337
338 static int hf_oss_ssdo_sacmd_access_type     = -1;
339 static int hf_oss_ssdo_sacmd_reserved          = -1;
340 static int hf_oss_ssdo_sacmd_abort_transfer  = -1;
341 static int hf_oss_ssdo_sacmd_segmentation    = -1;
342 static int hf_oss_ssdo_sacmd_toggle          = -1;
343 static int hf_oss_ssdo_sacmd_initiate        = -1;
344 static int hf_oss_ssdo_sacmd_end_segment     = -1;
345 static int hf_oss_ssdo_sacmd_block_transfer  = -1;
346
347 static int hf_oss_scm_udid           = -1;
348 static int hf_oss_scm_udid_valid     = -1;
349
350 static int hf_oss_spdo_connection_valid   = -1;
351 static int hf_oss_spdo_payload            = -1;
352 static int hf_oss_spdo_producer           = -1;
353 static int hf_oss_spdo_producer_time      = -1;
354 static int hf_oss_spdo_time_value_sn      = -1;
355 static int hf_oss_spdo_time_request       = -1;
356 static int hf_oss_spdo_time_request_to    = -1;
357 static int hf_oss_spdo_time_request_from  = -1;
358
359 static const char *global_scm_udid = "00:00:00:00:00:00";
360
361 /* This is defined by the specification. The Address field is 10 bits long, and the node with the number
362  *  1 is always the SCM, therefore ( 2 ^ 10 ) - 1 nodes can be addressed. We use 2 ^ 10 here, because the
363  *  SCM can talk to himself (Assign SADR for instance ) */
364 #define MAX_NUMBER_OF_SAFETY_NODES      ( 2 ^ 10 )
365
366 /* Tracks the information that the packet pinfo has been received by receiver, and adds that information to the tree, using pos, as
367  * byte position in the PDU */
368 #define PACKET_RECEIVED_BY(pinfo, recv, pos)                       proto_tree_add_uint(opensafety_tree, hf_oss_msg_receiver, message_tvb, pos, 2, recv);
369
370 /* Tracks the information that the packet pinfo has been sent by sender, and received by receiver, and adds that information to
371  * the tree, using pos for the sender and pos2 for the receiver, as byte position in the PDU */
372 #define PACKET_SEND_FROM_TO(pinfo, send, pos, recv, pos2)         proto_tree_add_uint(opensafety_tree, hf_oss_msg_sender, message_tvb, pos, 2, send); \
373                                                                 proto_tree_add_uint(opensafety_tree, hf_oss_msg_receiver, message_tvb, pos2, 2, recv);
374
375 /* Tracks the information that the packet pinfo has been sent by sender, and received by everyone else, and adds that information to
376  * the tree, using pos, as byte position in the PDU */
377 #define PACKET_SEND_FROM_TO_ALL(pinfo, sender, pos)                proto_tree_add_uint(opensafety_tree, hf_oss_msg_sender, message_tvb, pos, 2, sender);
378
379 /* Helper Functions & Function Prototypes */
380
381 static guint8 opensafety_get_scm_udid(guint8 * scmUDID);
382 static guint16 findFrame1Position ( guint8 dataLength, guint8 byteStream[] );
383 static guint8 * unxorFrame(guint8 dataLength, guint8 byteStream[], guint16 startFrame1, guint16 startFrame2, guint8 scmUDID[]);
384
385 /*
386  * @brief Calculates a CRC8 checksum for the given buffer
387  * @param len the length of the given buffer
388  * @param pBuffer a pointer to a buffer of the given length
389  * @return the CRC8 checksum for the buffer
390  */
391 static guint8 crc8_opensafety(guint32 len, guint8 * pBuffer, guint8 initCRC);
392
393 /*
394  * @brief Calculates a CRC16 checksum for the given buffer
395  * @param len the length of the given buffer
396  * @param pBuffer a pointer to a buffer of the given length
397  * @return the CRC16 checksum for the buffer
398  */
399 static guint16 crc16_opensafety(guint32 len, guint8 * pBuffer, guint16 initCRC);
400
401 static guint stringToBytes( const char * string, guint8 * pBuffer, guint32 length );
402
403 static guint8 findSafetyFrame ( guint8 * pBuffer, guint32 length, guint8 u_Offset, guint *u_frameOffset, guint *u_frameLength );
404
405 static guint stringToBytes( const char * stringToBytes, guint8 * pBuffer, guint32 length )
406 {
407     guint k;
408     guint32 byte ;
409     char * endptr ;
410     char * str, * temp, * token;
411
412     k = 0;
413
414     str = ep_strdup(stringToBytes);
415     token = strtok( str, ":" );
416     temp = token;
417
418     byte = strtol ( temp, &endptr, 16 );
419     pBuffer[k] = byte;
420     k++;
421
422     for ( temp = token ; ; temp = NULL )
423     {
424         temp = strtok( NULL, ":" );
425         if ( temp == NULL || ( k == length ) )
426             break;
427         byte = strtol ( temp, &endptr, 16 );
428         pBuffer[k] = byte;
429         k++;
430     }
431
432     return k;
433 }
434
435 static guint16 findFrame1Position ( guint8 dataLength, guint8 byteStream[] )
436 {
437     guint16 i_wFrame1Position = 0;
438     guint16 i_payloadLength, i_calculatedLength = 0;
439     guint16 i_offset = 0;
440     guint8 b_tempByte = 0;
441
442     /*
443      * First, a normal package get's assumed. Calculation of frame 1 position is
444      * pretty easy, because, the length of the whole package is 11 + 2*n + 2*o, which
445      * results in frame 1 start at (6 + n + o), which is length / 2 + 1
446      */
447     i_wFrame1Position = dataLength / 2 + 1;
448     i_payloadLength = byteStream [ i_wFrame1Position + 2 ];
449     /* Calculating the assumed frame length, taking CRC8/CRC16 into account */
450     i_calculatedLength = i_payloadLength * 2 + 11 + 2 * (i_payloadLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? 1 : 0);
451
452     /* If the calculated length differs from the given length, a slim package is assumed */
453     if ( i_calculatedLength != dataLength )
454     {
455         /* possible slim package */
456         i_wFrame1Position = 0;
457         /*
458          * Slim packages have a fixed sublength of either 6 bytes for frame 2 in
459          * case of crc8 and 7 bytes in case of crc16
460          */
461         i_offset = OSS_SLIM_FRAME2_WITH_CRC8 + ( dataLength < (OSS_SLIM_FRAME_WITH_CRC8_MAXSIZE + 1) ? 0 : 1 );
462         /* Last 2 digits belong to addr, therefore have to be cleared */
463         b_tempByte = ( ( byteStream [ i_offset + 1 ] ) >> 2 ) << 2;
464
465         /* If the id byte xor 0xE8 is 0, we have a slim package */
466         if ( ( ( b_tempByte ^ OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST ) == 0 ) ||
467              ( ( b_tempByte ^ OPENSAFETY_MSG_SSDO_SLIM_SERVICE_RESPONSE ) == 0 ) )
468         {
469             /* Slim package found */
470             i_wFrame1Position = i_offset;
471         }
472     }
473
474     return i_wFrame1Position;
475 }
476
477 /*
478  * This function applies the given UDID to the bytestream, considering the start of frame 2
479  */
480 static guint8 * unxorFrame(guint8 dataLength, guint8 byteStream[], guint16 frameStart1, guint16 frameStart2, guint8 scmUDID[])
481 {
482     guint8 * pb_sendMemBlock;
483     guint k;
484     guint8 frame1Size;
485
486     frame1Size = ( frameStart2 > frameStart1 ? frameStart2 : dataLength - frameStart1 );
487
488
489     pb_sendMemBlock = (guint8*) ep_alloc0( sizeof(guint8) * dataLength);
490
491     memcpy ( &pb_sendMemBlock[frameStart1], &byteStream[frameStart1], frame1Size );
492
493     for ( k = 0; k < (guint)(dataLength - frame1Size); k++)
494         pb_sendMemBlock [ k + frameStart2 ] = byteStream [ k + frameStart2 ] ^ scmUDID[ ( k % 6 ) ];
495
496     return pb_sendMemBlock;
497 }
498
499 /* @brief Precompiled table for CRC8 values */
500 static const guint16 crc16_opensafety_precompiled[256] =
501 {
502         0x0000, 0x5935, 0xB26A, 0xEB5F, 0x3DE1, 0x64D4, 0x8F8B, 0xD6BE,
503         0x7BC2, 0x22F7, 0xC9A8, 0x909D, 0x4623, 0x1F16, 0xF449, 0xAD7C,
504         0xF784, 0xAEB1, 0x45EE, 0x1CDB, 0xCA65, 0x9350, 0x780F, 0x213A,
505         0x8C46, 0xD573, 0x3E2C, 0x6719, 0xB1A7, 0xE892, 0x03CD, 0x5AF8,
506         0xB63D, 0xEF08, 0x0457, 0x5D62, 0x8BDC, 0xD2E9, 0x39B6, 0x6083,
507         0xCDFF, 0x94CA, 0x7F95, 0x26A0, 0xF01E, 0xA92B, 0x4274, 0x1B41,
508         0x41B9, 0x188C, 0xF3D3, 0xAAE6, 0x7C58, 0x256D, 0xCE32, 0x9707,
509         0x3A7B, 0x634E, 0x8811, 0xD124, 0x079A, 0x5EAF, 0xB5F0, 0xECC5,
510         0x354F, 0x6C7A, 0x8725, 0xDE10, 0x08AE, 0x519B, 0xBAC4, 0xE3F1,
511         0x4E8D, 0x17B8, 0xFCE7, 0xA5D2, 0x736C, 0x2A59, 0xC106, 0x9833,
512         0xC2CB, 0x9BFE, 0x70A1, 0x2994, 0xFF2A, 0xA61F, 0x4D40, 0x1475,
513         0xB909, 0xE03C, 0x0B63, 0x5256, 0x84E8, 0xDDDD, 0x3682, 0x6FB7,
514         0x8372, 0xDA47, 0x3118, 0x682D, 0xBE93, 0xE7A6, 0x0CF9, 0x55CC,
515         0xF8B0, 0xA185, 0x4ADA, 0x13EF, 0xC551, 0x9C64, 0x773B, 0x2E0E,
516         0x74F6, 0x2DC3, 0xC69C, 0x9FA9, 0x4917, 0x1022, 0xFB7D, 0xA248,
517         0x0F34, 0x5601, 0xBD5E, 0xE46B, 0x32D5, 0x6BE0, 0x80BF, 0xD98A,
518         0x6A9E, 0x33AB, 0xD8F4, 0x81C1, 0x577F, 0x0E4A, 0xE515, 0xBC20,
519         0x115C, 0x4869, 0xA336, 0xFA03, 0x2CBD, 0x7588, 0x9ED7, 0xC7E2,
520         0x9D1A, 0xC42F, 0x2F70, 0x7645, 0xA0FB, 0xF9CE, 0x1291, 0x4BA4,
521         0xE6D8, 0xBFED, 0x54B2, 0x0D87, 0xDB39, 0x820C, 0x6953, 0x3066,
522         0xDCA3, 0x8596, 0x6EC9, 0x37FC, 0xE142, 0xB877, 0x5328, 0x0A1D,
523         0xA761, 0xFE54, 0x150B, 0x4C3E, 0x9A80, 0xC3B5, 0x28EA, 0x71DF,
524         0x2B27, 0x7212, 0x994D, 0xC078, 0x16C6, 0x4FF3, 0xA4AC, 0xFD99,
525         0x50E5, 0x09D0, 0xE28F, 0xBBBA, 0x6D04, 0x3431, 0xDF6E, 0x865B,
526         0x5FD1, 0x06E4, 0xEDBB, 0xB48E, 0x6230, 0x3B05, 0xD05A, 0x896F,
527         0x2413, 0x7D26, 0x9679, 0xCF4C, 0x19F2, 0x40C7, 0xAB98, 0xF2AD,
528         0xA855, 0xF160, 0x1A3F, 0x430A, 0x95B4, 0xCC81, 0x27DE, 0x7EEB,
529         0xD397, 0x8AA2, 0x61FD, 0x38C8, 0xEE76, 0xB743, 0x5C1C, 0x0529,
530         0xE9EC, 0xB0D9, 0x5B86, 0x02B3, 0xD40D, 0x8D38, 0x6667, 0x3F52,
531         0x922E, 0xCB1B, 0x2044, 0x7971, 0xAFCF, 0xF6FA, 0x1DA5, 0x4490,
532         0x1E68, 0x475D, 0xAC02, 0xF537, 0x2389, 0x7ABC, 0x91E3, 0xC8D6,
533         0x65AA, 0x3C9F, 0xD7C0, 0x8EF5, 0x584B, 0x017E, 0xEA21, 0xB314
534 };
535
536 /*
537  * @brief Calculates a CRC16 checksum for the given buffer with the polynom
538  *     x^16 + x^14 + x^12 + x^11 + x^8 + x^5 + x^4 + x^2 + 1
539  * @param len the length of the given buffer
540  * @param pBuffer a pointer to a buffer of the given length
541  * @return the CRC16 checksum for the buffer
542  */
543 static guint16 crc16_opensafety(guint32 len, guint8 * pBuffer, guint16 initCRC)
544 {
545     guint16 crc;
546     guint16 ulTab;
547
548     crc = initCRC;
549     while(len-- > 0)
550     {
551         ulTab = crc16_opensafety_precompiled[(*pBuffer++) ^ (crc >> 8)];
552         crc = (crc << 8) ^ ulTab;
553     }
554
555     return crc;
556 }
557
558 /* @brief Precompiled table for CRC8 values */
559 static const guint8 crc8_opensafety_precompiled[256] =
560 {
561         0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD,
562         0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A,
563         0xAE, 0x81, 0xF0, 0xDF, 0x12, 0x3D, 0x4C, 0x63,
564         0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34,
565         0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0, 0x91, 0xBE,
566         0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9,
567         0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10,
568         0x8A, 0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47,
569         0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B,
570         0xB1, 0x9E, 0xEF, 0xC0, 0x0D, 0x22, 0x53, 0x7C,
571         0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85,
572         0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD, 0xD2,
573         0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58,
574         0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F,
575         0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6,
576         0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1,
577         0xE3, 0xCC, 0xBD, 0x92, 0x5F, 0x70, 0x01, 0x2E,
578         0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79,
579         0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80,
580         0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7,
581         0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D,
582         0xC7, 0xE8, 0x99, 0xB6, 0x7B, 0x54, 0x25, 0x0A,
583         0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3,
584         0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA, 0x8B, 0xA4,
585         0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8,
586         0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F,
587         0xAB, 0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66,
588         0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31,
589         0x76, 0x59, 0x28, 0x07, 0xCA, 0xE5, 0x94, 0xBB,
590         0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC,
591         0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A, 0x15,
592         0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42
593 };
594
595 /*
596  * @brief Calculates a CRC8 checksum for the given buffer with the polynom
597  *     x^8 + x^5 + x^3 + x^2 + x + 1
598  * @param len the length of the given buffer
599  * @param pBuffer a pointer to a buffer of the given length
600  * @return the CRC8 checksum for the buffer
601  */
602 static guint8 crc8_opensafety(guint32 len, guint8 * pBuffer, guint8 initCRC)
603 {
604     guint8 crc;
605
606     crc = initCRC;
607     while(len-- > 0)
608     {
609        crc = (guint8)(*pBuffer++) ^ crc;
610         crc = crc8_opensafety_precompiled[crc];
611     }
612
613     return crc;
614 }
615
616 static guint8 findSafetyFrame ( guint8 * pBuffer, guint32 length, guint8 u_Offset, guint *u_frameOffset, guint *u_frameLength )
617 {
618     guint n;
619     guint16 crc, calcCrc;
620     guint8 b_ID, b_Length, crcOffset, leftShifted;
621     gboolean found;
622
623     found = 0;
624     DISSECTOR_ASSERT ( u_Offset < ( u_Offset + length ) );
625     for ( n = u_Offset; n < ( u_Offset + length ); n++)
626     {
627        /* The ID byte must ALWAYS be the second byte, therefore 0 is invalid */
628        if ( n == 0 )
629            continue;
630
631         *u_frameLength = 0;
632         *u_frameOffset = 0;
633
634         crcOffset = 0;
635         b_ID = pBuffer [ n ];
636         b_Length = pBuffer [ n + 1 ];
637
638         /* 0xFF is often used, but always false, otherwise start detection, if the highest
639          *  bit is set */
640         if ( ( b_ID != 0xFF ) && ( b_ID & 0x80 ) )
641         {
642            /* If the determined size could be bigger, than the data to be dissect,
643             * we have an error, return */
644             if ( ( b_Length + n ) > ( u_Offset + length ) )
645                 continue;
646
647             leftShifted = b_ID >> 4;
648             /* An openSAFETY command has to have a high-byte range between 0x0A and 0x0E
649              *  b_ID 0x80 took care of everything underneath, we check for 0x09 and 0x0F,
650              *  as they remain the only values left, which are not valid */
651             if ( ( leftShifted == 0x09 ) || ( leftShifted == 0x0F ) )
652                 continue;
653
654             /* Find CRC position and calculate checksum */
655             crc = 0;
656             calcCrc = 0;
657             crc = pBuffer [ n + 3 + b_Length ];
658             if ( b_Length > 8 ) {
659                 crc += ( ( pBuffer [ n + 4 + b_Length ] ) << 8 );
660                 crcOffset = 1;
661                 if ( crc != 0x00 )
662                     calcCrc = crc16_opensafety ( b_Length + 4, &pBuffer [ n - 1 ], 0 );
663             } else {
664                 if ( crc != 0x00 )
665                     calcCrc = crc8_opensafety ( b_Length + 4, &pBuffer [ n - 1 ], 0 );
666             }
667
668             if ( ( crc != 0x00 ) && ( crc ^ calcCrc ) == 0 )
669             {
670                 /* We have found a Slim frame. Those are not correctly identified yet */
671                 if ( ( b_ID >> 3 ) == ( OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE >> 3 ) )
672                 {
673                     *u_frameOffset = ( n - 1 );
674                     *u_frameLength = b_Length + 2 * crcOffset + 11;
675                     found = 1;
676                     break;
677                 }
678                 else
679                 {
680                     *u_frameLength = 2 * b_Length + 2 * crcOffset + 11;
681                     *u_frameOffset = ( n - 1 );
682                     /* EPL SoC messages can be falsely detected as openSAFETY frames,
683                      *  so we check if both checksums have no lower byte of 0x00. This
684                      *  check remains, although SoC and SoA messages get sorted out in
685                      *  the dissector */
686                     if ( pBuffer [ *u_frameOffset + *u_frameLength - 2 ] == 0x00 &&
687                         pBuffer [ *u_frameOffset + *u_frameLength - 1 ] == 0x00 )
688                         continue;
689
690                     found = 1;
691                     break;
692                 }
693             }
694         }
695     }
696
697     return (found ? 1 : 0);
698 }
699
700 static void
701 dissect_opensafety_spdo_message(tvbuff_t *message_tvb,  proto_tree *opensafety_tree,
702         guint8 * bytes, guint16 frameStart1, guint16 frameStart2 , gboolean validSCMUDID)
703 {
704     proto_item *item;
705     proto_tree *spdo_tree;
706     guint16 ct, taddr;
707     guint dataLength;
708     guint8 tr, b_ID;
709
710     dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + frameStart1);
711     b_ID = ( bytes[frameStart1 + 1] >> 3 ) << 3;
712
713     ct = bytes[frameStart1 + 2];
714     if ( validSCMUDID )
715         ct = (guint16)(bytes[frameStart2 + 2] << 8) + (bytes[frameStart1 + 2]);
716
717     /* taddr is the 4th octet in the second frame */
718     taddr = OSS_FRAME_ADDR(bytes, frameStart2 + 3);
719     tr = ( bytes[frameStart2 + 4] << 2 ) >> 2;
720
721     /* An SPDO get's always send by the producer, to everybody else */
722     PACKET_SEND_FROM_TO_ALL( pinfo, OSS_FRAME_ADDR(bytes, frameStart1), OSS_FRAME_POS_ADDR + frameStart1 );
723
724     if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE )
725         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_RESPONSE);
726     else if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST || b_ID == OPENSAFETY_MSG_SPDO_DATA_ONLY )
727         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_REQUEST);
728
729     item = proto_tree_add_uint_format_value(opensafety_tree, hf_oss_msg, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1,
730             b_ID, "%s", val_to_str(b_ID, message_type_values, "Unknown") );
731
732     spdo_tree = proto_item_add_subtree(item, ett_opensafety_ssdo);
733
734     proto_tree_add_uint(spdo_tree, hf_oss_spdo_producer, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
735
736     if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST )
737     {
738         item = proto_tree_add_uint_format_value(spdo_tree, hf_oss_spdo_time_value_sn, message_tvb, 0, 0, ct,
739                 "0x%04X [%d] (%s)", ct, ct, (validSCMUDID ? "Complete" : "Low byte only"));
740         PROTO_ITEM_SET_GENERATED(item);
741
742         proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request, message_tvb, OSS_FRAME_POS_ADDR + frameStart2 + 4, 1, tr);
743         proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request_from, message_tvb, OSS_FRAME_POS_ADDR + frameStart2 + 3, 2, taddr);
744     }
745     else
746     {
747         item = proto_tree_add_uint_format_value(spdo_tree, hf_oss_spdo_producer_time, message_tvb, 0, 0, ct,
748                 "0x%04X [%d] (%s)", ct, ct, (validSCMUDID ? "Complete" : "Low byte only"));
749         PROTO_ITEM_SET_GENERATED(item);
750
751         if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE )
752         {
753             proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request, message_tvb, OSS_FRAME_POS_ADDR + frameStart2 + 4, 1, tr);
754             proto_tree_add_uint(spdo_tree, hf_oss_spdo_time_request_to, message_tvb, OSS_FRAME_POS_ADDR + frameStart2 + 3, 2, taddr);
755         }
756     }
757
758     if ( dataLength > 0 )
759     {
760         proto_tree_add_bytes(spdo_tree, hf_oss_spdo_payload, message_tvb, OSS_FRAME_POS_ID + 3,
761                             dataLength, &bytes[frameStart1 + 4]);
762
763     }
764 }
765
766
767 static void
768 dissect_opensafety_ssdo_message(tvbuff_t *message_tvb , packet_info * pinfo, proto_tree *opensafety_tree ,
769         guint8 * bytes, guint16 frameStart1, guint16 frameStart2 , gboolean validSCMUDID)
770 {
771     proto_item *item;
772     proto_tree *ssdo_tree, *ssdo_sacmd_tree;
773     guint16 taddr = 0    ;
774     guint32 abortcode;
775     guint8 db0Offset, db0, sacmd, payloadOffset, payloadSize, n;
776     guint dataLength;
777     gboolean isRequest;
778     guint8 * payload;
779
780     dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + frameStart1);
781
782     db0Offset = frameStart1 + OSS_FRAME_POS_DATA;
783     db0 = bytes[db0Offset];
784     sacmd = db0;
785
786     if ( ( sacmd & OPENSAFETY_SSDO_SACMD_TGL ) == OPENSAFETY_SSDO_SACMD_TGL )
787         sacmd = sacmd & ( ~OPENSAFETY_SSDO_SACMD_TGL );
788
789     isRequest = FALSE;
790     if ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SERVICE_REQUEST || OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST )
791         isRequest = TRUE;
792
793     if ( validSCMUDID )
794     {
795         /* taddr is the 4th octet in the second frame */
796         taddr = OSS_FRAME_ADDR(bytes, frameStart2 + 3);
797
798         PACKET_SEND_FROM_TO( pinfo, OSS_FRAME_ADDR(bytes, frameStart1), frameStart1, taddr, frameStart2 + 3);
799     }
800     else if ( ! isRequest )
801     {
802         PACKET_RECEIVED_BY(pinfo, OSS_FRAME_ADDR(bytes, frameStart1), frameStart1 );
803     }
804
805     if ( ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SERVICE_RESPONSE ) ||
806          ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SLIM_SERVICE_RESPONSE ) )
807         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_RESPONSE);
808     else
809         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_REQUEST);
810
811     item = proto_tree_add_uint_format_value(opensafety_tree, hf_oss_msg, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1,
812             OSS_FRAME_ID(bytes, frameStart1), "%s", val_to_str(OSS_FRAME_ID(bytes, frameStart1), message_type_values, "Unknown") );
813
814     ssdo_tree = proto_item_add_subtree(item, ett_opensafety_ssdo);
815
816     if ( isRequest )
817     {
818         if ( validSCMUDID )
819         {
820             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
821             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_client, message_tvb, frameStart2 + 3, 2, taddr);
822         }
823         else
824         {
825             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
826         }
827     }
828     else if ( ! isRequest )
829     {
830         if ( validSCMUDID )
831         {
832             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_client, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
833             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, frameStart2 + 3, 2, taddr);
834         }
835         else
836         {
837             item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_client, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
838         }
839     }
840
841     item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_sacmd, message_tvb, db0Offset, 1, sacmd);
842     col_append_fstr(pinfo->cinfo, COL_INFO, ", SACMD: %s", val_to_str(sacmd, ssdo_sacmd_values, " "));
843
844     ssdo_sacmd_tree = proto_item_add_subtree(item, ett_opensafety_ssdo_sacmd);
845     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_block_transfer, message_tvb, db0Offset, 1, db0);
846     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_end_segment, message_tvb, db0Offset, 1, db0);
847     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_initiate, message_tvb, db0Offset, 1, db0);
848     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_toggle, message_tvb, db0Offset, 1, db0);
849     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_segmentation, message_tvb, db0Offset, 1, db0);
850     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_abort_transfer, message_tvb, db0Offset, 1, db0);
851     item = proto_tree_add_boolean(ssdo_sacmd_tree, hf_oss_ssdo_sacmd_access_type, message_tvb, db0Offset, 1, db0);
852
853     if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM) == 0 )
854     {
855         item = proto_tree_add_uint(ssdo_tree, hf_oss_snmt_master, message_tvb, frameStart1 + OSS_FRAME_POS_ADDR, 2, OSS_FRAME_ADDR(bytes, frameStart1));
856         if ( validSCMUDID )
857             item = proto_tree_add_uint(ssdo_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + OSS_FRAME_POS_ADDR + 2, 2, taddr);
858     }
859
860     payloadOffset = db0Offset + 1;
861     /* When the following clause is met, DB1,2 contain the SOD index, and DB3 the SOD subindex */
862     if ( ( ( sacmd & OPENSAFETY_SSDO_SACMD_INI ) == OPENSAFETY_SSDO_SACMD_INI ) ||
863             ( sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_MIDDLE ) ||
864             ( sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_END ) ||
865             ( sacmd == OPENSAFETY_MSG_SSDO_ABORT )
866     )
867     {
868         item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_sod_index, message_tvb, db0Offset + 1, 2,
869                 ((guint16)(bytes[db0Offset + 2] << 8) + bytes[db0Offset + 1]));
870         item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_sod_subindex, message_tvb, db0Offset + 3, 1, bytes[db0Offset + 3]);
871         payloadOffset += 3;
872     }
873
874     if ( sacmd == OPENSAFETY_MSG_SSDO_ABORT )
875     {
876         abortcode = 0;
877         for ( n = 0; n < 4; n++ )
878             abortcode += ( bytes[frameStart1 + OSS_FRAME_POS_DATA + 4 + n] ) << (8 * n);
879
880         item = proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_abort_code, message_tvb, payloadOffset, 4, abortcode,
881                 "0x%04X %04X - %s", (guint16)(abortcode >> 16), (guint16)(abortcode),
882                 val_to_str(abortcode, abort_codes, "Unknown"));
883
884
885     } else if ( ( isRequest && (sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_SEGMENTED ||
886             sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_EXPEDITED ||
887             sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_MIDDLE ||
888             sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_SEGMENT_END
889                                 ) ) ||
890          ( !isRequest && (sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_EXPEDITED ||
891             sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_SEGMENTED ||
892             sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_MIDDLE ||
893             sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_SEGMENT_END
894                                  ) )
895     )
896     {
897         if ( ( sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_SEGMENTED ) || ( sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_SEGMENTED ) )
898         {
899             payloadOffset += 4;
900             /* using payloadSize as helper var for for-loop */
901             payloadSize = dataLength - (payloadOffset - db0Offset);
902             payload = (guint8*)ep_alloc(sizeof(guint8*)*payloadSize);
903             for ( n = 0; n < payloadSize; n++)
904                 payload[payloadSize - n - 1] = bytes[frameStart1 + OSS_FRAME_POS_DATA + (payloadOffset - db0Offset) + n];
905
906             /* reading real size */
907             payloadSize = 0;
908             for ( n = 0; n < 4; n++ )
909             {
910                 payloadSize += ( bytes[frameStart1 + OSS_FRAME_POS_DATA + 4 + n] ) << (8 * n);
911             }
912
913
914             item = proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_payload_size, message_tvb, payloadOffset - 4, 4,
915                     payloadSize, "%d octets total (%d octets in this frame)", payloadSize, dataLength - (payloadOffset - db0Offset));
916             item = proto_tree_add_bytes(ssdo_tree, hf_oss_ssdo_payload, message_tvb, payloadOffset,
917                     dataLength - (payloadOffset - db0Offset), payload );
918         }
919         else
920         {
921             payloadSize = dataLength - (payloadOffset - db0Offset);
922             payload = (guint8*)ep_alloc(sizeof(guint8*)*payloadSize);
923             for ( n = 0; n < payloadSize; n++)
924                 payload[payloadSize - n - 1] = bytes[frameStart1 + OSS_FRAME_POS_DATA + (payloadOffset - db0Offset) + n];
925
926             item = proto_tree_add_uint_format_value(ssdo_tree, hf_oss_ssdo_payload_size, message_tvb, 0, 0, payloadSize,
927                     "%d octets", payloadSize);
928             PROTO_ITEM_SET_GENERATED(item);
929             item = proto_tree_add_bytes(ssdo_tree, hf_oss_ssdo_payload, message_tvb, payloadOffset, payloadSize, payload );
930
931         }
932     }
933 }
934
935 static void
936 dissect_opensafety_snmt_message(tvbuff_t *message_tvb, packet_info *pinfo , proto_tree *opensafety_tree,
937         guint8 * bytes, guint16 frameStart1, guint16 frameStart2 )
938 {
939     proto_item *item;
940     proto_tree *snmt_tree ;
941     guint16 addr, taddr;
942     guint8 db0;
943     guint dataLength;
944
945     dataLength = OSS_FRAME_LENGTH(bytes, frameStart1);
946
947     /* addr is the first field, as well as the recipient of the message */
948     addr = OSS_FRAME_ADDR(bytes, frameStart1);
949     /* taddr is the 4th octet in the second frame */
950     taddr = OSS_FRAME_ADDR(bytes, frameStart2 + 3);
951
952     db0 = -1;
953     if (dataLength > 0)
954         db0 = bytes[OSS_FRAME_POS_DATA];
955
956     if ( ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE) == 0 ) &&
957          ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP) == 0 || (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP) == 0 ) )
958     {
959         PACKET_RECEIVED_BY( pinfo, addr, OSS_FRAME_POS_ADDR + frameStart1 );
960     }
961     else
962     {
963         PACKET_SEND_FROM_TO ( pinfo, taddr, frameStart2 + 3, addr, OSS_FRAME_POS_ADDR + frameStart1 );
964     }
965
966     if ( ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SNMT_RESPONSE_UDID ) ||
967          ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SNMT_SADR_ASSIGNED ) ||
968          ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE ) )
969         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_RESPONSE);
970     else
971         proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_REQUEST);
972
973     item = proto_tree_add_uint_format_value(opensafety_tree, hf_oss_msg, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1,
974             OSS_FRAME_ID(bytes, frameStart1), "%s", val_to_str(OSS_FRAME_ID(bytes, frameStart1), message_type_values, "Unknown") );
975
976     snmt_tree = proto_item_add_subtree(item, ett_opensafety_snmt);
977
978     if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM) == 0 )
979     {
980         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
981         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
982     }
983     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE) == 0 )
984     {
985         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + frameStart1, 1, db0);
986         col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str(db0, message_service_type, " "));
987
988         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
989         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
990         if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_FAIL) == 0 )
991         {
992             item = proto_tree_add_item(snmt_tree, hf_oss_snmt_error_group, message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1, 1, FALSE);
993             item = proto_tree_add_item(snmt_tree, hf_oss_snmt_error_code, message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 2, 1, FALSE);
994         }
995         else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_UDID_SCM) == 0 )
996         {
997             proto_tree_add_ether(snmt_tree, hf_oss_snmt_udid, message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1,
998                     6, tvb_get_ptr(message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1, 6));
999         }
1000
1001     }
1002     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SERVICE_REQUEST) == 0 )
1003     {
1004         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_service_id, message_tvb, OSS_FRAME_POS_DATA + frameStart1, 1, db0);
1005         col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str(db0, message_service_type, " "));
1006
1007         if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP) == 0 || (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP) == 0 )
1008         {
1009             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_scm, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1010             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_tool, message_tvb, frameStart2 + 3, 2, taddr);
1011         }
1012         else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGN_UDID_SCM) == 0 )
1013         {
1014             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1015             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
1016             proto_tree_add_ether(snmt_tree, hf_oss_snmt_udid, message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1,
1017                     6, tvb_get_ptr(message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1, 6));
1018         }
1019         else
1020         {
1021             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1022             item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
1023             if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_SET_TO_OP) == 0 )
1024             {
1025                 item = proto_tree_add_bytes(snmt_tree, hf_oss_snmt_timestamp, message_tvb, OSS_FRAME_POS_DATA + frameStart1 + 1, 4,
1026                                 (bytes + frameStart1 + OSS_FRAME_POS_DATA + 1));
1027             }
1028         }
1029     }
1030     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SADR_ASSIGNED) == 0 )
1031     {
1032         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1033         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
1034
1035         if (dataLength > 0)
1036             proto_tree_add_ether(snmt_tree, hf_oss_snmt_udid, message_tvb,
1037                     OSS_FRAME_POS_DATA + frameStart1, 6, tvb_get_ptr(message_tvb, OSS_FRAME_POS_DATA + frameStart1, 6));
1038
1039     }
1040     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_ASSIGN_SADR) == 0 )
1041     {
1042         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1043         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, frameStart2 + 3, 2, taddr);
1044
1045         if (dataLength > 0)
1046             proto_tree_add_ether(snmt_tree, hf_oss_snmt_udid, message_tvb,
1047                     OSS_FRAME_POS_DATA + frameStart1, 6, tvb_get_ptr(message_tvb, OSS_FRAME_POS_DATA + frameStart1, 6));
1048
1049     }
1050     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_RESPONSE_UDID) == 0 )
1051     {
1052         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1053         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + 3, 2, taddr);
1054
1055         if (dataLength > 0)
1056             proto_tree_add_ether(snmt_tree, hf_oss_snmt_udid, message_tvb,
1057                     OSS_FRAME_POS_DATA + frameStart1, 6, tvb_get_ptr(message_tvb, OSS_FRAME_POS_DATA + frameStart1, 6));
1058
1059     }
1060     else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_REQUEST_UDID) == 0 )
1061     {
1062         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_slave, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, addr);
1063         item = proto_tree_add_uint(snmt_tree, hf_oss_snmt_master, message_tvb, frameStart2 + 3, 2, taddr);
1064     }
1065
1066 }
1067
1068 static guint8 opensafety_get_scm_udid(guint8 * scmUDID )
1069 {
1070     if ( strlen(global_scm_udid) != (2*6 + 5) )
1071         return 0;
1072
1073     return stringToBytes(global_scm_udid, scmUDID, 6);
1074 }
1075
1076 static void
1077 dissect_opensafety_checksum(tvbuff_t *message_tvb, proto_tree *opensafety_tree ,
1078         guint8 * bytes, guint16 frameStart1 )
1079 {
1080     guint16 frameCrc;
1081     guint16 calcCrc;
1082     guint dataLength;
1083     proto_item * item;
1084     proto_tree *checksum_tree;
1085     gint start;
1086     gint length;
1087
1088     dataLength = OSS_FRAME_LENGTH(bytes, frameStart1);
1089     start = OSS_FRAME_POS_DATA + dataLength  + frameStart1;
1090     frameCrc = bytes[start];
1091
1092     if (OSS_FRAME_LENGTH(bytes, frameStart1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8)
1093         frameCrc += (bytes[start + 1] << 8);
1094
1095     length = (dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? OPENSAFETY_CHECKSUM_CRC16 : OPENSAFETY_CHECKSUM_CRC8);
1096     item = proto_tree_add_uint(opensafety_tree, hf_oss_crc, message_tvb, start, length, frameCrc);
1097
1098     checksum_tree = proto_item_add_subtree(item, ett_opensafety_checksum);
1099
1100     if ( dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 )
1101         calcCrc = crc16_opensafety(dataLength + 4, &bytes[frameStart1], 0);
1102     else
1103         calcCrc = crc8_opensafety(dataLength + 4, &bytes[frameStart1], 0);
1104
1105     item = proto_tree_add_boolean(checksum_tree, hf_oss_crc_valid, message_tvb, start, length, (frameCrc == calcCrc));
1106     PROTO_ITEM_SET_GENERATED(item);
1107     /* using the defines, as the values can change */
1108     item = proto_tree_add_uint(checksum_tree, hf_oss_crc_type, message_tvb, start, length,
1109             ( dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? OPENSAFETY_CHECKSUM_CRC16 : OPENSAFETY_CHECKSUM_CRC8 ) );
1110
1111 }
1112
1113 static gboolean
1114 dissect_opensafety_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree, gboolean b_frame2First, guint8 u_nrInPackage)
1115 {
1116     guint8 type, b_ID;
1117     guint length;
1118     guint16 frameStart1, frameStart2;
1119     guint8 * bytes, *scmUDID;
1120     gboolean validSCMUDID;
1121     proto_item * item;
1122
1123     length = tvb_length(message_tvb);
1124
1125     bytes = (guint8 *)ep_tvb_memdup(message_tvb, 0, length);
1126
1127     if ( b_frame2First )
1128     {
1129         frameStart1 = findFrame1Position (length, bytes );
1130         frameStart2 = 0;
1131     }
1132     else
1133     {
1134         frameStart1 = 0;
1135         frameStart2 = ((OSS_FRAME_LENGTH(bytes, frameStart1) - 1) +
1136                 (OSS_FRAME_LENGTH(bytes, frameStart1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 ? OSS_SLIM_FRAME2_WITH_CRC16 : OSS_SLIM_FRAME2_WITH_CRC8));
1137     }
1138
1139     if ( ( OSS_FRAME_ID(bytes, frameStart1) & OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
1140         type = OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE;
1141     else if ( ( OSS_FRAME_ID(bytes, frameStart1) & OPENSAFETY_SSDO_MESSAGE_TYPE ) == OPENSAFETY_SSDO_MESSAGE_TYPE )
1142         type = OPENSAFETY_SSDO_MESSAGE_TYPE;
1143     else if ( ( OSS_FRAME_ID(bytes, frameStart1) & OPENSAFETY_SPDO_MESSAGE_TYPE ) == OPENSAFETY_SPDO_MESSAGE_TYPE )
1144         type = OPENSAFETY_SPDO_MESSAGE_TYPE;
1145     else if ( ( OSS_FRAME_ID(bytes, frameStart1) & OPENSAFETY_SNMT_MESSAGE_TYPE ) == OPENSAFETY_SNMT_MESSAGE_TYPE )
1146         type = OPENSAFETY_SNMT_MESSAGE_TYPE;
1147     else
1148     {
1149         /* This is an invalid openSAFETY package, we return */
1150         return FALSE;
1151     }
1152
1153     b_ID = OSS_FRAME_ID(bytes, frameStart1);
1154     /* Clearing connection valid bit */
1155     if ( type == OPENSAFETY_SPDO_MESSAGE_TYPE )
1156         b_ID = ( b_ID >> 3 ) << 3;
1157
1158     col_append_fstr(pinfo->cinfo, COL_INFO, (u_nrInPackage > 1 ? " | %s" : "%s" ),
1159             val_to_str(b_ID, message_type_values, "Unknown Message (0x%02X) "));
1160
1161     if (opensafety_tree)
1162     {
1163         if ( type == OPENSAFETY_SNMT_MESSAGE_TYPE )
1164         {
1165             validSCMUDID = TRUE;
1166             dissect_opensafety_snmt_message ( message_tvb, pinfo, opensafety_tree, bytes, frameStart1, frameStart2 );
1167         }
1168         else
1169         {
1170             validSCMUDID = FALSE;
1171             scmUDID = (guint8*)g_malloc(sizeof(guint8)*6);
1172             memset(scmUDID, 0, 6);
1173             if ( opensafety_get_scm_udid(scmUDID) == 6 )
1174             {
1175                 validSCMUDID = TRUE;
1176                 bytes = unxorFrame(length, bytes, frameStart1, frameStart2, scmUDID);
1177                 /* Now confirm, that the xor operation was successful
1178                  *  The ID fields of both frames have to be the same, otherwise
1179                  *  perform the xor again to revert the change
1180                  */
1181                 if ( ( OSS_FRAME_ID(bytes, frameStart1) ^ OSS_FRAME_ID(bytes, frameStart2 ) ) != 0 )
1182                 {
1183                     validSCMUDID = FALSE;
1184                     bytes = unxorFrame(length, bytes, frameStart1, frameStart2, scmUDID);
1185                 }
1186             }
1187             g_free ( scmUDID );
1188
1189             item = proto_tree_add_string(opensafety_tree, hf_oss_scm_udid, message_tvb, 0, 0, global_scm_udid);
1190             PROTO_ITEM_SET_GENERATED(item);
1191             item = proto_tree_add_boolean(opensafety_tree, hf_oss_scm_udid_valid, message_tvb, 0, 0, validSCMUDID);
1192             PROTO_ITEM_SET_GENERATED(item);
1193
1194             if ( type == OPENSAFETY_SSDO_MESSAGE_TYPE || type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
1195             {
1196                 dissect_opensafety_ssdo_message ( message_tvb, pinfo, opensafety_tree, bytes, frameStart1, frameStart2, validSCMUDID );
1197             }
1198             else if ( type == OPENSAFETY_SPDO_MESSAGE_TYPE )
1199             {
1200                 dissect_opensafety_spdo_message ( message_tvb, opensafety_tree, bytes, frameStart1, frameStart2, validSCMUDID );
1201             }
1202         }
1203
1204         proto_tree_add_uint(opensafety_tree, hf_oss_length, message_tvb, OSS_FRAME_POS_LEN + frameStart1, 1, OSS_FRAME_LENGTH(bytes, frameStart1));
1205
1206         dissect_opensafety_checksum ( message_tvb, opensafety_tree, bytes, frameStart1 );
1207     }
1208
1209     return TRUE;
1210 }
1211
1212 static gboolean
1213 dissect_opensafety_frame(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, gboolean b_frame2First, guint8 u_nrInPackage)
1214 {
1215     proto_item *opensafety_item;
1216     proto_tree *opensafety_tree;
1217
1218     /* if the tree is NULL, we are called for the overview, otherwise for the
1219      more detailed view of the package */
1220     if (tree) {
1221         /* create the opensafety protocol tree */
1222         opensafety_item = proto_tree_add_item(tree, proto_opensafety, message_tvb, 0, -1, FALSE);
1223         opensafety_tree = proto_item_add_subtree(opensafety_item, ett_opensafety);
1224     } else {
1225         opensafety_tree = NULL;
1226     };
1227
1228     /* dissect the message */
1229     return dissect_opensafety_message(message_tvb, pinfo, opensafety_tree, b_frame2First, u_nrInPackage);
1230 }
1231
1232 static gboolean
1233 dissect_opensafety(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree)
1234 {
1235     /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1236     if (pinfo)
1237     {
1238         col_set_str(pinfo->cinfo, COL_PROTOCOL,
1239                 (pinfo->destport == UDP_PORT_SIII ? "openSAFETY/SercosIII" : "openSAFETY" )
1240                 );
1241         col_clear(pinfo->cinfo,COL_INFO);
1242     }
1243
1244     /* dissect the message, we are called using UDP SHNF, therefore Frame2 allways comes first */
1245     return dissect_opensafety_frame(message_tvb, pinfo, tree, TRUE, 1);
1246 }
1247
1248 static gboolean
1249 dissect_opensafety_epl(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1250 {
1251     tvbuff_t *next_tvb;
1252     guint length, frameOffset, frameLength;
1253     guint8 *bytes;
1254     gboolean handled, dissectorCalled;
1255     guint8 firstByte, found;
1256     gint len, reported_len;
1257     dissector_handle_t epl_handle;
1258     guint8 packageCounter;
1259     handled = FALSE;
1260     dissectorCalled = FALSE;
1261
1262     epl_handle = find_dissector("epl");
1263     if ( epl_handle == NULL )
1264         epl_handle = find_dissector("data");
1265
1266     firstByte = ( tvb_get_guint8(message_tvb, 0) << 1 );
1267     /* No frames can be sent in SoA and SoC messages, therefore those get filtered right away */
1268     if ( firstByte == 0x02 || firstByte == 0x0A )
1269     {
1270         call_dissector(epl_handle, message_tvb, pinfo, tree);
1271         return TRUE;
1272     }
1273
1274     len = tvb_length_remaining(message_tvb, 0);
1275     reported_len = tvb_reported_length_remaining(message_tvb, 0);
1276     length = tvb_length(message_tvb);
1277     bytes = (guint8 *) ep_tvb_memdup(message_tvb, 0, length);
1278
1279     frameOffset = 0;
1280     frameLength = 0;
1281     found = 0;
1282     packageCounter = 0;
1283     while ( frameOffset < length )
1284     {
1285         if ( findSafetyFrame(bytes, length - frameOffset, frameOffset, &frameOffset, &frameLength) )
1286         {
1287             if ((frameOffset + frameLength) > (guint)reported_len )
1288                 break;
1289             found++;
1290
1291             /* Freeing memory before dissector, as otherwise we would waste it */
1292             next_tvb = tvb_new_subset(message_tvb, frameOffset, frameLength, reported_len);
1293             if ( ! dissectorCalled )
1294             {
1295                 call_dissector(epl_handle, message_tvb, pinfo, tree);
1296                 dissectorCalled = TRUE;
1297
1298                 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1299                 if (pinfo)
1300                 {
1301                     col_set_str(pinfo->cinfo, COL_PROTOCOL, "openSAFETY/Powerlink");
1302                     col_clear(pinfo->cinfo,COL_INFO);
1303                 }
1304             }
1305
1306             /* Only engage, if we are not called strictly for the overview */
1307             if ( tree )
1308             {
1309                 if ( dissect_opensafety_frame(next_tvb, pinfo, tree, FALSE, found) == TRUE )
1310                     packageCounter++;
1311             }
1312             handled = TRUE;
1313         }
1314         else
1315             break;
1316
1317         frameOffset += frameLength;
1318     }
1319
1320     if ( handled == TRUE && packageCounter == 0 )
1321         handled = FALSE;
1322
1323     if ( ! handled )
1324     {
1325         call_dissector(epl_handle, message_tvb, pinfo, tree);
1326         handled = TRUE;
1327     }
1328     return ( handled ? TRUE : FALSE );
1329 }
1330
1331 static gboolean
1332 dissect_heur_opensafety_epl(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1333 {
1334     guint32 constData;
1335
1336     constData = 0x0;
1337     if ( pinfo->private_data != NULL )
1338         memcpy(&constData, pinfo->private_data, sizeof(guint32));
1339
1340     /* We will call the epl dissector by using call_dissector(). The epl dissector will then call
1341      * the heuristic openSAFETY dissector again. By setting this information, we prevent a dissector
1342      * loop */
1343     if ( pinfo->private_data == NULL || ( constData != OPENSAFETY_PINFO_CONST_DATA ) )
1344     {
1345         constData = OPENSAFETY_PINFO_CONST_DATA;
1346         pinfo->private_data = (void*)ep_alloc(sizeof(guint32));
1347         memcpy(pinfo->private_data, &constData, sizeof(guint32));
1348         return dissect_opensafety_epl(message_tvb, pinfo, tree );
1349     }
1350
1351     return FALSE;
1352 }
1353
1354 static gboolean
1355 dissect_opensafety_siii(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1356 {
1357     tvbuff_t *next_tvb;
1358     guint length, frameOffset, frameLength;
1359     guint8 *bytes;
1360     gboolean handled, dissectorCalled, udpDissectorCalled;
1361     guint8 firstByte, found;
1362     gint len, reported_len;
1363     dissector_handle_t siii_handle;
1364     guint8 packageCounter = 0;
1365
1366     handled = FALSE;
1367     dissectorCalled = FALSE;
1368     udpDissectorCalled = FALSE;
1369
1370     siii_handle = find_dissector("sercosiii");
1371     if ( siii_handle == NULL )
1372         siii_handle = find_dissector("data");
1373
1374     /* We have a SERCOS III package, whether encapsulated in UDP or
1375        directly atop Ethernet */
1376     firstByte = ( tvb_get_guint8(message_tvb, 0) << 1 );
1377     /* No frames can be sent in AT messages, therefore those get filtered right away */
1378     if ( ( (!firstByte) & 0x40 ) == 0x40 )
1379     {
1380         if ( pinfo->ipproto != IPPROTO_UDP )
1381              call_dissector(siii_handle, message_tvb, pinfo, tree);
1382         return TRUE;
1383     }
1384
1385     len = tvb_length_remaining(message_tvb, 0);
1386     reported_len = tvb_reported_length_remaining(message_tvb, 0);
1387     length = tvb_length(message_tvb);
1388     bytes = (guint8 *) ep_tvb_memdup(message_tvb, 0, length);
1389
1390     frameOffset = 0;
1391     frameLength = 0;
1392     found = 0;
1393     while ( frameOffset < length )
1394     {
1395         if ( findSafetyFrame(bytes, length - frameOffset, frameOffset, &frameOffset, &frameLength) )
1396         {
1397             if ((frameOffset + frameLength) > (guint)reported_len )
1398                 break;
1399             found++;
1400
1401             /* Freeing memory before dissector, as otherwise we would waste it */
1402             next_tvb = tvb_new_subset(message_tvb, frameOffset, frameLength, reported_len);
1403
1404             /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1405             if ( ( ! udpDissectorCalled ) && ( pinfo->ipproto == IPPROTO_UDP ) && pinfo )
1406             {
1407                  col_set_str(pinfo->cinfo, COL_PROTOCOL,
1408                          ( pinfo->ipproto != IPPROTO_UDP ? "openSAFETY/SercosIII" : "openSAFETY/SercosIII UDP" ) );
1409                    col_clear(pinfo->cinfo,COL_INFO);
1410                    udpDissectorCalled = TRUE;
1411             }
1412
1413             /* Call the dissector */
1414             if ( ( ! dissectorCalled ) && ( pinfo->ipproto != IPPROTO_UDP ) )
1415             {
1416                 call_dissector(siii_handle, message_tvb, pinfo, tree);
1417                 dissectorCalled = TRUE;
1418
1419                 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1420                 if (pinfo)
1421                 {
1422                     col_set_str(pinfo->cinfo, COL_PROTOCOL, "openSAFETY/SercosIII");
1423                     col_clear(pinfo->cinfo,COL_INFO);
1424                 }
1425             }
1426
1427             /* Only engage, if we are not called strictly for the overview */
1428             if ( tree )
1429             {
1430                 if ( dissect_opensafety_frame(next_tvb, pinfo, tree, FALSE, found) == TRUE )
1431                     packageCounter++;
1432             }
1433             handled = TRUE;
1434         }
1435         else
1436             break;
1437
1438         frameOffset += frameLength;
1439     }
1440
1441     if ( handled == TRUE && packageCounter == 0 )
1442         handled = FALSE;
1443
1444     if ( ! handled )
1445     {
1446         if ( pinfo->ipproto != IPPROTO_UDP )
1447             call_dissector(siii_handle, message_tvb, pinfo, tree);
1448         handled = TRUE;
1449     }
1450     return ( handled ? TRUE : FALSE );
1451 }
1452
1453 static gboolean
1454 dissect_heur_opensafety_siii(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1455 {
1456     guint32 constData;
1457     guint8 firstByte;
1458
1459     /* We can assume to have a SercosIII package, as the SercosIII dissector won't detect
1460      * SercosIII-UDP packages, this is most likely SercosIII-over-ethernet */
1461
1462     /* No frames can be sent in AT messages, therefore those get filtered right away */
1463     firstByte = ( tvb_get_guint8(message_tvb, 0) << 1 );
1464     if ( ( (!firstByte) & 0x40 ) == 0x40 )
1465         return FALSE;
1466
1467     constData = 0x0;
1468     if ( pinfo->private_data != NULL )
1469         memcpy(&constData, pinfo->private_data, sizeof(guint32));
1470
1471     /* We will call the SercosIII dissector by using call_dissector(). The SercosIII dissector will
1472      * then call the heuristic openSAFETY dissector again. By setting this information, we prevent
1473      * a dissector loop */
1474     if ( pinfo->private_data == NULL || ( constData != OPENSAFETY_PINFO_CONST_DATA ) )
1475     {
1476         constData = OPENSAFETY_PINFO_CONST_DATA;
1477         pinfo->private_data = (void*)ep_alloc(sizeof(guint32));
1478         memcpy(pinfo->private_data, &constData, sizeof(guint32));
1479         return dissect_opensafety_siii(message_tvb, pinfo, tree);
1480     }
1481
1482     return FALSE;
1483 }
1484
1485 void
1486 proto_register_opensafety(void)
1487 {
1488     /* Setup list of header fields */
1489     static hf_register_info hf[] = {
1490             /* General fields for subframe 1 */
1491
1492             { &hf_oss_scm_udid,     { "SCM UDID Configured",    "opensafety.scm_udid",     FT_STRING,   BASE_NONE, NULL,   0x0, NULL, HFILL } },
1493             { &hf_oss_scm_udid_valid,     { "SCM UDID Valid",    "opensafety.scm_udid_valid",     FT_BOOLEAN,   BASE_NONE, NULL,   0x0, NULL, HFILL } },
1494
1495             { &hf_oss_msg,     { "Message",    "opensafety.msg.id",     FT_UINT8,   BASE_HEX, VALS(message_type_values),   0x0, NULL, HFILL } },
1496             { &hf_oss_msgtype, { "Type",  "opensafety.msg.type", FT_BOOLEAN,   BASE_NONE, TFS(&opensafety_message_type),   0x0, NULL, HFILL } },
1497             { &hf_oss_msg_sender, { "Sender",  "opensafety.msg.sender", FT_UINT16,   BASE_HEX, NULL,   0x0, NULL, HFILL } },
1498             { &hf_oss_msg_receiver, { "Receiver",  "opensafety.msg.receiver", FT_UINT16,   BASE_HEX, NULL,   0x0, NULL, HFILL } },
1499             { &hf_oss_length,  { "Length",    "opensafety.length",  FT_UINT8,   BASE_DEC, NULL,     0x0, NULL, HFILL } },
1500             { &hf_oss_data,    { "Data",      "opensafety.data",    FT_BYTES,   BASE_NONE, NULL,    0x0, NULL, HFILL } },
1501             { &hf_oss_crc,     { "CRC",       "opensafety.crc.data",     FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1502
1503             { &hf_oss_crc_valid,   { "Is Valid", "opensafety.crc.valid", FT_BOOLEAN, BASE_NONE, NULL,    0x0, NULL, HFILL } },
1504             { &hf_oss_crc_type,    { "CRC Type",  "opensafety.crc.type",  FT_UINT8,   BASE_DEC, VALS(message_crc_type),    0x0, NULL, HFILL } },
1505
1506             /* SNMT Specific fields */
1507             { &hf_oss_snmt_slave,  { "SNMT Slave",    "opensafety.snmt.slave", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1508             { &hf_oss_snmt_master, { "SNMT Master",   "opensafety.snmt.master", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1509             { &hf_oss_snmt_scm,  { "SCM",    "opensafety.snmt.scm", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1510             { &hf_oss_snmt_tool, { "Tool ID",   "opensafety.snmt.tool_id", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1511             { &hf_oss_snmt_udid,   { "UDID for SN",   "opensafety.snmt.udid", FT_ETHER,  BASE_NONE, NULL,    0x0, NULL, HFILL } },
1512             { &hf_oss_snmt_timestamp,   { "Parameter Timestamp",   "opensafety.snmt.timestamp", FT_BYTES,  BASE_NONE, NULL,    0x0, NULL, HFILL } },
1513             { &hf_oss_snmt_service_id,   { "Extended Service ID",   "opensafety.snmt.service_id", FT_UINT8,  BASE_HEX, VALS(message_service_type),    0x0, NULL, HFILL } },
1514             { &hf_oss_snmt_error_group,   { "Error Group",   "opensafety.snmt.error_group", FT_UINT8,  BASE_DEC, NULL,    0x0, NULL, HFILL } },
1515             { &hf_oss_snmt_error_code,   { "Error Code",   "opensafety.snmt.error_code", FT_UINT8,  BASE_DEC, NULL,   0x0, NULL, HFILL } },
1516
1517             /* SSDO Specific fields */
1518             { &hf_oss_ssdo_server, { "SSDO Server", "opensafety.ssdo.master", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1519             { &hf_oss_ssdo_client, { "SSDO Client", "opensafety.ssdo.client", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
1520             { &hf_oss_ssdo_sano, { "SOD Access Request Number", "opensafety.ssdo.sano", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1521             { &hf_oss_ssdo_sacmd, { "SOD Access Command", "opensafety.ssdo.sacmd", FT_UINT8,  BASE_HEX, VALS(ssdo_sacmd_values),    0x0, NULL, HFILL } },
1522             { &hf_oss_ssdo_sod_index, { "SOD Index", "opensafety.ssdo.sod_index", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1523             { &hf_oss_ssdo_sod_subindex, { "SOD Sub Index", "opensafety.ssdo.sod_subindex", FT_UINT8,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1524             { &hf_oss_ssdo_payload, { "SOD Payload", "opensafety.ssdo.payload", FT_BYTES,  BASE_NONE, NULL,    0x0, NULL, HFILL } },
1525             { &hf_oss_ssdo_payload_size, { "SOD Payload Size", "opensafety.ssdo.payloadsize", FT_UINT32,  BASE_DEC, NULL,    0x0, NULL, HFILL } },
1526             { &hf_oss_ssdo_segment_size, { "SOD Segment Size", "opensafety.ssdo.segmentsize", FT_UINT32,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1527             { &hf_oss_ssdo_inhibit_time, { "Inhibit Time", "opensafety.ssdo.inhibittime", FT_UINT32,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1528             { &hf_oss_ssdo_abort_code, { "Abort Code", "opensafety.ssdo.abortcode", FT_UINT32,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1529
1530             /* SSDO SACmd specific fields */
1531             { &hf_oss_ssdo_sacmd_access_type, { "Access Type", "opensafety.ssdo.sacmd.access", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_acc), OPENSAFETY_SSDO_SACMD_ACC, NULL, HFILL } },
1532             { &hf_oss_ssdo_sacmd_reserved, { "Reserved", "opensafety.ssdo.sacmd.reserved", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_res), OPENSAFETY_SSDO_SACMD_RES, NULL, HFILL } },
1533             { &hf_oss_ssdo_sacmd_abort_transfer, { "Abort Transfer", "opensafety.ssdo.sacmd.abort_transfer", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_abrt), OPENSAFETY_SSDO_SACMD_ABRT, NULL, HFILL } },
1534             { &hf_oss_ssdo_sacmd_segmentation, { "Segmentation", "opensafety.ssdo.sacmd.segmentation", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_seg), OPENSAFETY_SSDO_SACMD_SEG, NULL, HFILL } },
1535             { &hf_oss_ssdo_sacmd_toggle, { "Toggle Bit", "opensafety.ssdo.sacmd.toggle", FT_BOOLEAN,  8, TFS(&opensafety_on_off), OPENSAFETY_SSDO_SACMD_TGL, NULL, HFILL } },
1536             { &hf_oss_ssdo_sacmd_initiate, { "Initiate Transfer", "opensafety.ssdo.sacmd.initiate", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_ini), OPENSAFETY_SSDO_SACMD_INI, NULL, HFILL } },
1537             { &hf_oss_ssdo_sacmd_end_segment, { "End Segment", "opensafety.ssdo.sacmd.end_segment", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_ensg), OPENSAFETY_SSDO_SACMD_ENSG, NULL, HFILL } },
1538             { &hf_oss_ssdo_sacmd_block_transfer, { "Block Transfer", "opensafety.ssdo.sacmd.block_transfer", FT_BOOLEAN,  8, TFS(&opensafety_sacmd_blk), OPENSAFETY_SSDO_SACMD_BLK, NULL, HFILL } },
1539
1540             /* SPDO Specific fields */
1541             { &hf_oss_spdo_connection_valid, { "Connection Valid Bit", "opensafety.spdo.connection_valid", FT_BOOLEAN,  8, TFS(&opensafety_set_notset),  0x0, NULL, HFILL } },
1542             { &hf_oss_spdo_payload, { "SPDO Payload", "opensafety.spdo.payload", FT_BYTES,  BASE_NONE, NULL,    0x0, NULL, HFILL } },
1543             { &hf_oss_spdo_producer, { "Producer", "opensafety.spdo.producer", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1544             { &hf_oss_spdo_producer_time, { "Internal Time Producer", "opensafety.spdo.time.producer", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1545             { &hf_oss_spdo_time_value_sn, { "Internal Time SN", "opensafety.spdo.time.sn", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1546             { &hf_oss_spdo_time_request, { "Time Request Counter", "opensafety.spdo.time.request_counter", FT_UINT8,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1547             { &hf_oss_spdo_time_request_to, { "Time Request from", "opensafety.spdo.time.request_from", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1548             { &hf_oss_spdo_time_request_from, { "Time Request by", "opensafety.spdo.time.request_to", FT_UINT16,  BASE_HEX, NULL,    0x0, NULL, HFILL } },
1549
1550     };
1551
1552     /* Setup protocol subtree array */
1553     static gint *ett[] = {
1554             &ett_opensafety,
1555             &ett_opensafety_checksum,
1556             &ett_opensafety_snmt,
1557             &ett_opensafety_ssdo,
1558             &ett_opensafety_ssdo_sacmd,
1559     };
1560
1561     module_t *opensafety_module;
1562
1563     /* Register the protocol name and description */
1564     proto_opensafety = proto_register_protocol("openSAFETY", "openSAFETY",  "opensafety");
1565     opensafety_module = prefs_register_protocol(proto_opensafety, NULL);
1566
1567     /* Required function calls to register the header fields and subtrees used */
1568     proto_register_field_array(proto_opensafety, hf, array_length(hf));
1569     proto_register_subtree_array(ett, array_length(ett));
1570
1571     /* register user preferences */
1572     prefs_register_string_preference(opensafety_module, "scm_udid",
1573                  "SCM UDID (xx:xx:xx:xx:xx:xx)",
1574                  "To be able to fully dissect SSDO and SPDO packages, a valid UDID for the SCM has to be provided",
1575                  &global_scm_udid);
1576
1577     new_register_dissector("opensafety", dissect_opensafety, proto_opensafety);
1578     new_register_dissector("opensafety_siii", dissect_opensafety_siii, proto_opensafety);
1579 }
1580
1581 void
1582 proto_reg_handoff_opensafety(void)
1583 {
1584     static int opensafety_inited = FALSE;
1585
1586     if ( !opensafety_inited )
1587     {
1588         /* Default UDP only based dissector */
1589         dissector_add_uint("udp.port", UDP_PORT_OPENSAFETY, find_dissector("opensafety"));
1590
1591         /* Sercos III dissector does not handle UDP transport, has to be handled
1592          *  separately, everything else should be caught by the heuristic dissector
1593          */
1594         dissector_add_uint("udp.port", UDP_PORT_SIII, find_dissector("opensafety_siii"));
1595
1596         heur_dissector_add("epl", dissect_heur_opensafety_epl, proto_opensafety);
1597
1598         /* For SercosIII we have to register as a heuristic dissector, as SercosIII
1599          *  is implemented as a plugin, and therefore the heuristic dissector is not
1600          *  added by the time this method is being called
1601          */
1602         heur_dissector_add("sercosiii", dissect_heur_opensafety_siii, proto_opensafety);
1603     }
1604 }