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.
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
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.
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.
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.
34 #include <epan/packet.h>
35 #include <epan/prefs.h>
36 #include <epan/etypes.h>
38 #include <epan/proto.h>
39 #include <epan/emem.h>
40 #include <epan/dissectors/packet-udp.h>
46 /* General definitions */
48 /* openSAFETY UDP Port */
49 #ifndef UDP_PORT_OPENSAFETY
50 #define UDP_PORT_OPENSAFETY 9877
53 /* SercosIII UDP Port */
55 #define UDP_PORT_SIII 8755
58 /* Under linux, this get's defined in netinet/in.h */
60 #define IPPROTO_UDP 0x11
63 #ifndef OPENSAFETY_PINFO_CONST_DATA
64 #define OPENSAFETY_PINFO_CONST_DATA 0xAABBCCDD
67 /* openSAFETY CRC types */
68 #define OPENSAFETY_CHECKSUM_CRC8 0x01
69 #define OPENSAFETY_CHECKSUM_CRC16 0x02
70 #define OPENSAFETY_CHECKSUM_CRC32 0x04
72 static const value_string message_crc_type[] = {
73 { OPENSAFETY_CHECKSUM_CRC8, "CRC8" },
74 { OPENSAFETY_CHECKSUM_CRC16, "CRC16" },
75 { OPENSAFETY_CHECKSUM_CRC32, "CRC32" },
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
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" },
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
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
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
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" },
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" },
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" },
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
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" },
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
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
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" },
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
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" };
225 #define OPENSAFETY_SPDO_CONNECTION_VALID 0x04
228 static const value_string abort_codes[] = {
230 /* SSDO abort codes */
231 { 0x05030000, "Reserved" },
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" },
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" },
244 { 0x06020000, "Object does not exist in the object dictionary" },
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" },
251 { 0x06060000, "Access failed due to a hardware error" },
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" },
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" },
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" },
272 static const true_false_string opensafety_message_type = { "Request", "Response" };
273 #define OPENSAFETY_REQUEST TRUE
274 #define OPENSAFETY_RESPONSE FALSE
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
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 */
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])
293 #define CRC8_POLY 0x2F /* CRC-8 Polynom */
294 #define CRC16_POLY 0x5935 /* CRC-16 Polynom */
297 static int proto_opensafety = -1;
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;
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;
313 static int hf_oss_crc_valid = -1;
314 static int hf_oss_crc_type = -1;
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;
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;
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;
347 static int hf_oss_scm_udid = -1;
348 static int hf_oss_scm_udid_valid = -1;
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;
359 static const char *global_scm_udid = "00:00:00:00:00:00";
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 )
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);
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);
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);
379 /* Helper Functions & Function Prototypes */
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[]);
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
391 static guint8 crc8_opensafety(guint32 len, guint8 * pBuffer, guint8 initCRC);
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
399 static guint16 crc16_opensafety(guint32 len, guint8 * pBuffer, guint16 initCRC);
401 static guint stringToBytes( const char * string, guint8 * pBuffer, guint32 length );
403 static guint8 findSafetyFrame ( guint8 * pBuffer, guint32 length, guint8 u_Offset, guint *u_frameOffset, guint *u_frameLength );
405 static guint stringToBytes( const char * stringToBytes, guint8 * pBuffer, guint32 length )
410 char * str, * temp, * token;
414 str = ep_strdup(stringToBytes);
415 token = strtok( str, ":" );
418 byte = strtol ( temp, &endptr, 16 );
422 for ( temp = token ; ; temp = NULL )
424 temp = strtok( NULL, ":" );
425 if ( temp == NULL || ( k == length ) )
427 byte = strtol ( temp, &endptr, 16 );
435 static guint16 findFrame1Position ( guint8 dataLength, guint8 byteStream[] )
437 guint16 i_wFrame1Position = 0;
438 guint16 i_payloadLength, i_calculatedLength = 0;
439 guint16 i_offset = 0;
440 guint8 b_tempByte = 0;
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
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);
452 /* If the calculated length differs from the given length, a slim package is assumed */
453 if ( i_calculatedLength != dataLength )
455 /* possible slim package */
456 i_wFrame1Position = 0;
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
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;
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 ) )
469 /* Slim package found */
470 i_wFrame1Position = i_offset;
474 return i_wFrame1Position;
478 * This function applies the given UDID to the bytestream, considering the start of frame 2
480 static guint8 * unxorFrame(guint8 dataLength, guint8 byteStream[], guint16 frameStart1, guint16 frameStart2, guint8 scmUDID[])
482 guint8 * pb_sendMemBlock;
486 frame1Size = ( frameStart2 > frameStart1 ? frameStart2 : dataLength - frameStart1 );
489 pb_sendMemBlock = (guint8*) ep_alloc0( sizeof(guint8) * dataLength);
491 memcpy ( &pb_sendMemBlock[frameStart1], &byteStream[frameStart1], frame1Size );
493 for ( k = 0; k < (guint)(dataLength - frame1Size); k++)
494 pb_sendMemBlock [ k + frameStart2 ] = byteStream [ k + frameStart2 ] ^ scmUDID[ ( k % 6 ) ];
496 return pb_sendMemBlock;
499 /* @brief Precompiled table for CRC8 values */
500 static const guint16 crc16_opensafety_precompiled[256] =
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
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
543 static guint16 crc16_opensafety(guint32 len, guint8 * pBuffer, guint16 initCRC)
551 ulTab = crc16_opensafety_precompiled[(*pBuffer++) ^ (crc >> 8)];
552 crc = (crc << 8) ^ ulTab;
558 /* @brief Precompiled table for CRC8 values */
559 static const guint8 crc8_opensafety_precompiled[256] =
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
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
602 static guint8 crc8_opensafety(guint32 len, guint8 * pBuffer, guint8 initCRC)
609 crc = (guint8)(*pBuffer++) ^ crc;
610 crc = crc8_opensafety_precompiled[crc];
616 static guint8 findSafetyFrame ( guint8 * pBuffer, guint32 length, guint8 u_Offset, guint *u_frameOffset, guint *u_frameLength )
619 guint16 crc, calcCrc;
620 guint8 b_ID, b_Length, crcOffset, leftShifted;
624 DISSECTOR_ASSERT ( u_Offset < ( u_Offset + length ) );
625 for ( n = u_Offset; n < ( u_Offset + length ); n++)
627 /* The ID byte must ALWAYS be the second byte, therefore 0 is invalid */
635 b_ID = pBuffer [ n ];
636 b_Length = pBuffer [ n + 1 ];
638 /* 0xFF is often used, but always false, otherwise start detection, if the highest
640 if ( ( b_ID != 0xFF ) && ( b_ID & 0x80 ) )
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 ) )
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 ) )
654 /* Find CRC position and calculate checksum */
657 crc = pBuffer [ n + 3 + b_Length ];
658 if ( b_Length > 8 ) {
659 crc += ( ( pBuffer [ n + 4 + b_Length ] ) << 8 );
662 calcCrc = crc16_opensafety ( b_Length + 4, &pBuffer [ n - 1 ], 0 );
665 calcCrc = crc8_opensafety ( b_Length + 4, &pBuffer [ n - 1 ], 0 );
668 if ( ( crc != 0x00 ) && ( crc ^ calcCrc ) == 0 )
670 /* We have found a Slim frame. Those are not correctly identified yet */
671 if ( ( b_ID >> 3 ) == ( OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE >> 3 ) )
673 *u_frameOffset = ( n - 1 );
674 *u_frameLength = b_Length + 2 * crcOffset + 11;
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
686 if ( pBuffer [ *u_frameOffset + *u_frameLength - 2 ] == 0x00 &&
687 pBuffer [ *u_frameOffset + *u_frameLength - 1 ] == 0x00 )
697 return (found ? 1 : 0);
701 dissect_opensafety_spdo_message(tvbuff_t *message_tvb, proto_tree *opensafety_tree,
702 guint8 * bytes, guint16 frameStart1, guint16 frameStart2 , gboolean validSCMUDID)
705 proto_tree *spdo_tree;
710 dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + frameStart1);
711 b_ID = ( bytes[frameStart1 + 1] >> 3 ) << 3;
713 ct = bytes[frameStart1 + 2];
715 ct = (guint16)(bytes[frameStart2 + 2] << 8) + (bytes[frameStart1 + 2]);
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;
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 );
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);
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") );
732 spdo_tree = proto_item_add_subtree(item, ett_opensafety_ssdo);
734 proto_tree_add_uint(spdo_tree, hf_oss_spdo_producer, message_tvb, OSS_FRAME_POS_ADDR + frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
736 if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_REQUEST )
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);
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);
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);
751 if ( b_ID == OPENSAFETY_MSG_SPDO_DATA_WITH_TIME_RESPONSE )
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);
758 if ( dataLength > 0 )
760 proto_tree_add_bytes(spdo_tree, hf_oss_spdo_payload, message_tvb, OSS_FRAME_POS_ID + 3,
761 dataLength, &bytes[frameStart1 + 4]);
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)
772 proto_tree *ssdo_tree, *ssdo_sacmd_tree;
775 guint8 db0Offset, db0, sacmd, payloadOffset, payloadSize, n;
780 dataLength = tvb_get_guint8(message_tvb, OSS_FRAME_POS_LEN + frameStart1);
782 db0Offset = frameStart1 + OSS_FRAME_POS_DATA;
783 db0 = bytes[db0Offset];
786 if ( ( sacmd & OPENSAFETY_SSDO_SACMD_TGL ) == OPENSAFETY_SSDO_SACMD_TGL )
787 sacmd = sacmd & ( ~OPENSAFETY_SSDO_SACMD_TGL );
790 if ( OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SERVICE_REQUEST || OSS_FRAME_ID(bytes, frameStart1) == OPENSAFETY_MSG_SSDO_SLIM_SERVICE_REQUEST )
795 /* taddr is the 4th octet in the second frame */
796 taddr = OSS_FRAME_ADDR(bytes, frameStart2 + 3);
798 PACKET_SEND_FROM_TO( pinfo, OSS_FRAME_ADDR(bytes, frameStart1), frameStart1, taddr, frameStart2 + 3);
800 else if ( ! isRequest )
802 PACKET_RECEIVED_BY(pinfo, OSS_FRAME_ADDR(bytes, frameStart1), frameStart1 );
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);
809 proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_REQUEST);
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") );
814 ssdo_tree = proto_item_add_subtree(item, ett_opensafety_ssdo);
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);
825 item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_server, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
828 else if ( ! isRequest )
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);
837 item = proto_tree_add_uint(ssdo_tree, hf_oss_ssdo_client, message_tvb, frameStart1, 2, OSS_FRAME_ADDR(bytes, frameStart1));
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, " "));
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);
853 if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM) == 0 )
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));
857 item = proto_tree_add_uint(ssdo_tree, hf_oss_snmt_slave, message_tvb, frameStart2 + OSS_FRAME_POS_ADDR + 2, 2, taddr);
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 )
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]);
874 if ( sacmd == OPENSAFETY_MSG_SSDO_ABORT )
877 for ( n = 0; n < 4; n++ )
878 abortcode += ( bytes[frameStart1 + OSS_FRAME_POS_DATA + 4 + n] ) << (8 * n);
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"));
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
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
897 if ( ( sacmd == OPENSAFETY_MSG_SSDO_DOWNLOAD_INITIATE_SEGMENTED ) || ( sacmd == OPENSAFETY_MSG_SSDO_UPLOAD_INITIATE_SEGMENTED ) )
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];
906 /* reading real size */
908 for ( n = 0; n < 4; n++ )
910 payloadSize += ( bytes[frameStart1 + OSS_FRAME_POS_DATA + 4 + n] ) << (8 * n);
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 );
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];
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 );
936 dissect_opensafety_snmt_message(tvbuff_t *message_tvb, packet_info *pinfo , proto_tree *opensafety_tree,
937 guint8 * bytes, guint16 frameStart1, guint16 frameStart2 )
940 proto_tree *snmt_tree ;
945 dataLength = OSS_FRAME_LENGTH(bytes, frameStart1);
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);
954 db0 = bytes[OSS_FRAME_POS_DATA];
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 ) )
959 PACKET_RECEIVED_BY( pinfo, addr, OSS_FRAME_POS_ADDR + frameStart1 );
963 PACKET_SEND_FROM_TO ( pinfo, taddr, frameStart2 + 3, addr, OSS_FRAME_POS_ADDR + frameStart1 );
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);
971 proto_tree_add_boolean(opensafety_tree, hf_oss_msgtype, message_tvb, OSS_FRAME_POS_ID + frameStart1, 1, OPENSAFETY_REQUEST);
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") );
976 snmt_tree = proto_item_add_subtree(item, ett_opensafety_snmt);
978 if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SN_RESET_GUARDING_SCM) == 0 )
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);
983 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SERVICE_RESPONSE) == 0 )
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, " "));
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 )
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);
995 else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGNED_UDID_SCM) == 0 )
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));
1002 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SERVICE_REQUEST) == 0 )
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, " "));
1007 if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_STOP) == 0 || (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SCM_SET_TO_OP) == 0 )
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);
1012 else if ( (db0 ^ OPENSAFETY_MSG_SNMT_EXT_SN_ASSIGN_UDID_SCM) == 0 )
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));
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 )
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));
1030 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_SADR_ASSIGNED) == 0 )
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);
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));
1040 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_ASSIGN_SADR) == 0 )
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);
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));
1050 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_RESPONSE_UDID) == 0 )
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);
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));
1060 else if ( (OSS_FRAME_ID(bytes, frameStart1) ^ OPENSAFETY_MSG_SNMT_REQUEST_UDID) == 0 )
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);
1068 static guint8 opensafety_get_scm_udid(guint8 * scmUDID )
1070 if ( strlen(global_scm_udid) != (2*6 + 5) )
1073 return stringToBytes(global_scm_udid, scmUDID, 6);
1077 dissect_opensafety_checksum(tvbuff_t *message_tvb, proto_tree *opensafety_tree ,
1078 guint8 * bytes, guint16 frameStart1 )
1084 proto_tree *checksum_tree;
1088 dataLength = OSS_FRAME_LENGTH(bytes, frameStart1);
1089 start = OSS_FRAME_POS_DATA + dataLength + frameStart1;
1090 frameCrc = bytes[start];
1092 if (OSS_FRAME_LENGTH(bytes, frameStart1) > OSS_PAYLOAD_MAXSIZE_FOR_CRC8)
1093 frameCrc += (bytes[start + 1] << 8);
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);
1098 checksum_tree = proto_item_add_subtree(item, ett_opensafety_checksum);
1100 if ( dataLength > OSS_PAYLOAD_MAXSIZE_FOR_CRC8 )
1101 calcCrc = crc16_opensafety(dataLength + 4, &bytes[frameStart1], 0);
1103 calcCrc = crc8_opensafety(dataLength + 4, &bytes[frameStart1], 0);
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 ) );
1114 dissect_opensafety_message(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *opensafety_tree, gboolean b_frame2First, guint8 u_nrInPackage)
1118 guint16 frameStart1, frameStart2;
1119 guint8 * bytes, *scmUDID;
1120 gboolean validSCMUDID;
1123 length = tvb_length(message_tvb);
1125 bytes = (guint8 *)ep_tvb_memdup(message_tvb, 0, length);
1127 if ( b_frame2First )
1129 frameStart1 = findFrame1Position (length, bytes );
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));
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;
1149 /* This is an invalid openSAFETY package, we return */
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;
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) "));
1161 if (opensafety_tree)
1163 if ( type == OPENSAFETY_SNMT_MESSAGE_TYPE )
1165 validSCMUDID = TRUE;
1166 dissect_opensafety_snmt_message ( message_tvb, pinfo, opensafety_tree, bytes, frameStart1, frameStart2 );
1170 validSCMUDID = FALSE;
1171 scmUDID = (guint8*)g_malloc(sizeof(guint8)*6);
1172 memset(scmUDID, 0, 6);
1173 if ( opensafety_get_scm_udid(scmUDID) == 6 )
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
1181 if ( ( OSS_FRAME_ID(bytes, frameStart1) ^ OSS_FRAME_ID(bytes, frameStart2 ) ) != 0 )
1183 validSCMUDID = FALSE;
1184 bytes = unxorFrame(length, bytes, frameStart1, frameStart2, scmUDID);
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);
1194 if ( type == OPENSAFETY_SSDO_MESSAGE_TYPE || type == OPENSAFETY_SLIM_SSDO_MESSAGE_TYPE )
1196 dissect_opensafety_ssdo_message ( message_tvb, pinfo, opensafety_tree, bytes, frameStart1, frameStart2, validSCMUDID );
1198 else if ( type == OPENSAFETY_SPDO_MESSAGE_TYPE )
1200 dissect_opensafety_spdo_message ( message_tvb, opensafety_tree, bytes, frameStart1, frameStart2, validSCMUDID );
1204 proto_tree_add_uint(opensafety_tree, hf_oss_length, message_tvb, OSS_FRAME_POS_LEN + frameStart1, 1, OSS_FRAME_LENGTH(bytes, frameStart1));
1206 dissect_opensafety_checksum ( message_tvb, opensafety_tree, bytes, frameStart1 );
1213 dissect_opensafety_frame(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree, gboolean b_frame2First, guint8 u_nrInPackage)
1215 proto_item *opensafety_item;
1216 proto_tree *opensafety_tree;
1218 /* if the tree is NULL, we are called for the overview, otherwise for the
1219 more detailed view of the package */
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);
1225 opensafety_tree = NULL;
1228 /* dissect the message */
1229 return dissect_opensafety_message(message_tvb, pinfo, opensafety_tree, b_frame2First, u_nrInPackage);
1233 dissect_opensafety(tvbuff_t *message_tvb, packet_info *pinfo, proto_tree *tree)
1235 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1238 col_set_str(pinfo->cinfo, COL_PROTOCOL,
1239 (pinfo->destport == UDP_PORT_SIII ? "openSAFETY/SercosIII" : "openSAFETY" )
1241 col_clear(pinfo->cinfo,COL_INFO);
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);
1249 dissect_opensafety_epl(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1252 guint length, frameOffset, frameLength;
1254 gboolean handled, dissectorCalled;
1255 guint8 firstByte, found;
1256 gint len, reported_len;
1257 dissector_handle_t epl_handle;
1258 guint8 packageCounter;
1260 dissectorCalled = FALSE;
1262 epl_handle = find_dissector("epl");
1263 if ( epl_handle == NULL )
1264 epl_handle = find_dissector("data");
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 )
1270 call_dissector(epl_handle, message_tvb, pinfo, tree);
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);
1283 while ( frameOffset < length )
1285 if ( findSafetyFrame(bytes, length - frameOffset, frameOffset, &frameOffset, &frameLength) )
1287 if ((frameOffset + frameLength) > (guint)reported_len )
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 )
1295 call_dissector(epl_handle, message_tvb, pinfo, tree);
1296 dissectorCalled = TRUE;
1298 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1301 col_set_str(pinfo->cinfo, COL_PROTOCOL, "openSAFETY/Powerlink");
1302 col_clear(pinfo->cinfo,COL_INFO);
1306 /* Only engage, if we are not called strictly for the overview */
1309 if ( dissect_opensafety_frame(next_tvb, pinfo, tree, FALSE, found) == TRUE )
1317 frameOffset += frameLength;
1320 if ( handled == TRUE && packageCounter == 0 )
1325 call_dissector(epl_handle, message_tvb, pinfo, tree);
1328 return ( handled ? TRUE : FALSE );
1332 dissect_heur_opensafety_epl(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1337 if ( pinfo->private_data != NULL )
1338 memcpy(&constData, pinfo->private_data, sizeof(guint32));
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
1343 if ( pinfo->private_data == NULL || ( constData != OPENSAFETY_PINFO_CONST_DATA ) )
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 );
1355 dissect_opensafety_siii(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
1358 guint length, frameOffset, frameLength;
1360 gboolean handled, dissectorCalled, udpDissectorCalled;
1361 guint8 firstByte, found;
1362 gint len, reported_len;
1363 dissector_handle_t siii_handle;
1364 guint8 packageCounter = 0;
1367 dissectorCalled = FALSE;
1368 udpDissectorCalled = FALSE;
1370 siii_handle = find_dissector("sercosiii");
1371 if ( siii_handle == NULL )
1372 siii_handle = find_dissector("data");
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 )
1380 if ( pinfo->ipproto != IPPROTO_UDP )
1381 call_dissector(siii_handle, message_tvb, pinfo, tree);
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);
1393 while ( frameOffset < length )
1395 if ( findSafetyFrame(bytes, length - frameOffset, frameOffset, &frameOffset, &frameLength) )
1397 if ((frameOffset + frameLength) > (guint)reported_len )
1401 /* Freeing memory before dissector, as otherwise we would waste it */
1402 next_tvb = tvb_new_subset(message_tvb, frameOffset, frameLength, reported_len);
1404 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1405 if ( ( ! udpDissectorCalled ) && ( pinfo->ipproto == IPPROTO_UDP ) && pinfo )
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;
1413 /* Call the dissector */
1414 if ( ( ! dissectorCalled ) && ( pinfo->ipproto != IPPROTO_UDP ) )
1416 call_dissector(siii_handle, message_tvb, pinfo, tree);
1417 dissectorCalled = TRUE;
1419 /* pinfo is NULL only if dissect_opensafety_message is called from dissect_error cause */
1422 col_set_str(pinfo->cinfo, COL_PROTOCOL, "openSAFETY/SercosIII");
1423 col_clear(pinfo->cinfo,COL_INFO);
1427 /* Only engage, if we are not called strictly for the overview */
1430 if ( dissect_opensafety_frame(next_tvb, pinfo, tree, FALSE, found) == TRUE )
1438 frameOffset += frameLength;
1441 if ( handled == TRUE && packageCounter == 0 )
1446 if ( pinfo->ipproto != IPPROTO_UDP )
1447 call_dissector(siii_handle, message_tvb, pinfo, tree);
1450 return ( handled ? TRUE : FALSE );
1454 dissect_heur_opensafety_siii(tvbuff_t *message_tvb , packet_info *pinfo , proto_tree *tree )
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 */
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 )
1468 if ( pinfo->private_data != NULL )
1469 memcpy(&constData, pinfo->private_data, sizeof(guint32));
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 ) )
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);
1486 proto_register_opensafety(void)
1488 /* Setup list of header fields */
1489 static hf_register_info hf[] = {
1490 /* General fields for subframe 1 */
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 } },
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 } },
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 } },
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 } },
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 } },
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 } },
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 } },
1552 /* Setup protocol subtree array */
1553 static gint *ett[] = {
1555 &ett_opensafety_checksum,
1556 &ett_opensafety_snmt,
1557 &ett_opensafety_ssdo,
1558 &ett_opensafety_ssdo_sacmd,
1561 module_t *opensafety_module;
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);
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));
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",
1577 new_register_dissector("opensafety", dissect_opensafety, proto_opensafety);
1578 new_register_dissector("opensafety_siii", dissect_opensafety_siii, proto_opensafety);
1582 proto_reg_handoff_opensafety(void)
1584 static int opensafety_inited = FALSE;
1586 if ( !opensafety_inited )
1588 /* Default UDP only based dissector */
1589 dissector_add_uint("udp.port", UDP_PORT_OPENSAFETY, find_dissector("opensafety"));
1591 /* Sercos III dissector does not handle UDP transport, has to be handled
1592 * separately, everything else should be caught by the heuristic dissector
1594 dissector_add_uint("udp.port", UDP_PORT_SIII, find_dissector("opensafety_siii"));
1596 heur_dissector_add("epl", dissect_heur_opensafety_epl, proto_opensafety);
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
1602 heur_dissector_add("sercosiii", dissect_heur_opensafety_siii, proto_opensafety);