2 * Routines for CIP (Common Industrial Protocol) Motion dissection
3 * CIP Motion Home: www.odva.org
6 * Benjamin M. Stocks <bmstocks@ra.rockwell.com>
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.
33 #include <epan/packet.h>
34 #include <epan/emem.h>
35 #include <epan/expert.h>
36 #include "packet-cip.h"
38 /* The entry point to the actual disection is: dissect_cipmotion */
40 /* Protocol handle for CIP Motion */
41 static int proto_cipmotion = -1;
43 /* Header field identifiers, these are registered in the
44 * proto_register_cipmotion function along with the bites/bytes
46 static int hf_cip_format = -1;
47 static int hf_cip_revision = -1;
48 static int hf_cip_class1_seqnum = -1;
49 static int hf_cip_updateid = -1;
50 static int hf_cip_instance_cnt = -1;
51 static int hf_cip_last_update = -1;
52 static int hf_cip_node_status = -1;
53 static int hf_cip_node_control = -1;
54 static int hf_cip_node_control_remote = -1;
55 static int hf_cip_node_control_sync = -1;
56 static int hf_cip_node_data_valid = -1;
57 static int hf_cip_node_fault_reset = -1;
58 static int hf_cip_node_device_faulted = -1;
59 static int hf_cip_time_data_set = -1;
60 static int hf_cip_time_data_stamp = -1;
61 static int hf_cip_time_data_offset = -1;
62 static int hf_cip_time_data_diag = -1;
63 static int hf_cip_time_data_time_diag = -1;
64 static int hf_cip_cont_time_stamp = -1;
65 static int hf_cip_cont_time_offset = -1;
66 static int hf_cip_devc_time_stamp = -1;
67 static int hf_cip_devc_time_offset = -1;
68 static int hf_cip_lost_update = -1;
69 static int hf_cip_late_update = -1;
70 static int hf_cip_data_rx_time_stamp = -1;
71 static int hf_cip_data_tx_time_stamp = -1;
72 static int hf_cip_node_fltalarms = -1;
73 static int hf_cip_motor_cntrl = -1;
74 static int hf_cip_fdbk_config = -1;
75 static int hf_cip_axis_control = -1;
76 static int hf_cip_control_status = -1;
77 static int hf_cip_axis_response = -1;
78 static int hf_cip_axis_resp_stat = -1;
79 static int hf_cip_cmd_data_pos_cmd = -1;
80 static int hf_cip_cmd_data_vel_cmd = -1;
81 static int hf_cip_cmd_data_acc_cmd = -1;
82 static int hf_cip_cmd_data_trq_cmd = -1;
83 static int hf_cip_cmd_data_pos_trim_cmd = -1;
84 static int hf_cip_cmd_data_vel_trim_cmd = -1;
85 static int hf_cip_cmd_data_acc_trim_cmd = -1;
86 static int hf_cip_cmd_data_trq_trim_cmd = -1;
87 static int hf_cip_act_data_pos = -1;
88 static int hf_cip_act_data_vel = -1;
89 static int hf_cip_act_data_acc = -1;
90 static int hf_cip_act_data_trq = -1;
91 static int hf_cip_act_data_crnt = -1;
92 static int hf_cip_act_data_vltg = -1;
93 static int hf_cip_act_data_fqcy = -1;
94 static int hf_cip_sts_flt = -1;
95 static int hf_cip_sts_alrm = -1;
96 static int hf_cip_sts_sts = -1;
97 static int hf_cip_sts_iosts = -1;
98 static int hf_cip_sts_safety = -1;
99 static int hf_cip_intrp = -1;
100 static int hf_cip_position_data_type = -1;
101 static int hf_cip_axis_state = -1;
102 static int hf_cip_evnt_ctrl_reg1_pos = -1;
103 static int hf_cip_evnt_ctrl_reg1_neg = -1;
104 static int hf_cip_evnt_ctrl_reg2_pos = -1;
105 static int hf_cip_evnt_ctrl_reg2_neg = -1;
106 static int hf_cip_evnt_ctrl_reg1_posrearm = -1;
107 static int hf_cip_evnt_ctrl_reg1_negrearm = -1;
108 static int hf_cip_evnt_ctrl_reg2_posrearm = -1;
109 static int hf_cip_evnt_ctrl_reg2_negrearm = -1;
110 static int hf_cip_evnt_ctrl_marker_pos = -1;
111 static int hf_cip_evnt_ctrl_marker_neg = -1;
112 static int hf_cip_evnt_ctrl_home_pos = -1;
113 static int hf_cip_evnt_ctrl_home_neg = -1;
114 static int hf_cip_evnt_ctrl_home_pp = -1;
115 static int hf_cip_evnt_ctrl_home_pm = -1;
116 static int hf_cip_evnt_ctrl_home_mp = -1;
117 static int hf_cip_evnt_ctrl_home_mm = -1;
118 static int hf_cip_evnt_ctrl_acks = -1;
119 static int hf_cip_evnt_extend_format = -1;
120 static int hf_cip_evnt_sts_reg1_pos = -1;
121 static int hf_cip_evnt_sts_reg1_neg = -1;
122 static int hf_cip_evnt_sts_reg2_pos = -1;
123 static int hf_cip_evnt_sts_reg2_neg = -1;
124 static int hf_cip_evnt_sts_reg1_posrearm = -1;
125 static int hf_cip_evnt_sts_reg1_negrearm = -1;
126 static int hf_cip_evnt_sts_reg2_posrearm = -1;
127 static int hf_cip_evnt_sts_reg2_negrearm = -1;
128 static int hf_cip_evnt_sts_marker_pos = -1;
129 static int hf_cip_evnt_sts_marker_neg = -1;
130 static int hf_cip_evnt_sts_home_pos = -1;
131 static int hf_cip_evnt_sts_home_neg = -1;
132 static int hf_cip_evnt_sts_home_pp = -1;
133 static int hf_cip_evnt_sts_home_pm = -1;
134 static int hf_cip_evnt_sts_home_mp = -1;
135 static int hf_cip_evnt_sts_home_mm = -1;
136 static int hf_cip_evnt_sts_nfs = -1;
137 static int hf_cip_evnt_sts_stat = -1;
138 static int hf_cip_evnt_type = -1;
139 static int hf_cip_svc_code = -1;
140 static int hf_cip_svc_sts = -1;
141 static int hf_cip_svc_set_axis_attr_sts = -1;
142 static int hf_cip_svc_get_axis_attr_sts = -1;
143 static int hf_cip_svc_transction = -1;
144 static int hf_cip_svc_ext_status = -1;
145 static int hf_cip_svc_data = -1;
146 static int hf_cip_ptp_grandmaster = -1;
147 static int hf_cip_axis_alarm = -1;
148 static int hf_cip_axis_fault = -1;
149 static int hf_cip_axis_sts_local_ctrl = -1;
150 static int hf_cip_axis_sts_alarm = -1;
151 static int hf_cip_axis_sts_dc_bus = -1;
152 static int hf_cip_axis_sts_pwr_struct = -1;
153 static int hf_cip_axis_sts_tracking = -1;
154 static int hf_cip_axis_sts_pos_lock = -1;
155 static int hf_cip_axis_sts_vel_lock = -1;
156 static int hf_cip_axis_sts_vel_standstill = -1;
157 static int hf_cip_axis_sts_vel_threshold = -1;
158 static int hf_cip_axis_sts_vel_limit = -1;
159 static int hf_cip_axis_sts_acc_limit = -1;
160 static int hf_cip_axis_sts_dec_limit = -1;
161 static int hf_cip_axis_sts_torque_threshold = -1;
162 static int hf_cip_axis_sts_torque_limit = -1;
163 static int hf_cip_axis_sts_cur_limit = -1;
164 static int hf_cip_axis_sts_therm_limit = -1;
165 static int hf_cip_axis_sts_feedback_integ = -1;
166 static int hf_cip_axis_sts_shutdown = -1;
167 static int hf_cip_axis_sts_in_process = -1;
168 static int hf_cip_cyclic_wrt_data = -1;
169 static int hf_cip_cyclic_rd_data = -1;
170 static int hf_cip_cyclic_write_blk = -1;
171 static int hf_cip_cyclic_read_blk = -1;
172 static int hf_cip_cyclic_write_sts = -1;
173 static int hf_cip_cyclic_read_sts = -1;
174 static int hf_cip_attribute_data = -1;
175 static int hf_cip_event_checking = -1;
176 static int hf_cip_event_ack = -1;
177 static int hf_cip_event_status = -1;
178 static int hf_cip_event_id = -1;
179 static int hf_cip_event_pos = -1;
180 static int hf_cip_event_ts = -1;
181 static int hf_cip_pos_cmd = -1;
182 static int hf_cip_pos_cmd_int = -1;
183 static int hf_cip_vel_cmd = -1;
184 static int hf_cip_accel_cmd = -1;
185 static int hf_cip_trq_cmd = -1;
186 static int hf_cip_pos_trim = -1;
187 static int hf_cip_vel_trim = -1;
188 static int hf_cip_accel_trim = -1;
189 static int hf_cip_trq_trim = -1;
190 static int hf_cip_act_pos = -1;
191 static int hf_cip_act_vel = -1;
192 static int hf_cip_act_accel = -1;
193 static int hf_cip_act_trq = -1;
194 static int hf_cip_act_crnt = -1;
195 static int hf_cip_act_volts = -1;
196 static int hf_cip_act_freq = -1;
197 static int hf_cip_fault_type = -1;
198 static int hf_cip_fault_sub_code = -1;
199 static int hf_cip_fault_action = -1;
200 static int hf_cip_fault_time_stamp = -1;
201 static int hf_cip_alarm_type = -1;
202 static int hf_cip_alarm_sub_code = -1;
203 static int hf_cip_alarm_state = -1;
204 static int hf_cip_alarm_time_stamp = -1;
205 static int hf_cip_axis_status = -1;
206 static int hf_cip_axis_status_mfg = -1;
207 static int hf_cip_axis_io_status = -1;
208 static int hf_cip_axis_io_status_mfg = -1;
209 static int hf_cip_safety_status = -1;
210 static int hf_cip_cmd_data_set = -1;
211 static int hf_cip_act_data_set = -1;
212 static int hf_cip_sts_data_set = -1;
213 static int hf_cip_group_sync = -1;
214 static int hf_cip_command_control = -1;
216 static int hf_get_axis_attr_list_attribute_cnt = -1;
217 static int hf_get_axis_attr_list_attribute_id = -1;
218 static int hf_get_axis_attr_list_dimension = -1;
219 static int hf_get_axis_attr_list_element_size = -1;
220 static int hf_get_axis_attr_list_start_index = -1;
221 static int hf_get_axis_attr_list_data_elements = -1;
222 static int hf_set_axis_attr_list_attribute_cnt = -1;
223 static int hf_set_axis_attr_list_attribute_id = -1;
224 static int hf_set_axis_attr_list_dimension = -1;
225 static int hf_set_axis_attr_list_element_size = -1;
226 static int hf_set_axis_attr_list_start_index = -1;
227 static int hf_set_axis_attr_list_data_elements = -1;
228 static int hf_var_devce_instance = -1;
229 static int hf_var_devce_instance_block_size = -1;
230 static int hf_var_devce_cyclic_block_size = -1;
231 static int hf_var_devce_cyclic_data_block_size = -1;
232 static int hf_var_devce_cyclic_rw_block_size = -1;
233 static int hf_var_devce_event_block_size = -1;
234 static int hf_var_devce_service_block_size = -1;
236 /* Subtree pointers for the dissection */
237 static gint ett_cipmotion = -1;
238 static gint ett_cont_dev_header = -1;
239 static gint ett_node_control = -1;
240 static gint ett_node_status = -1;
241 static gint ett_time_data_set = -1;
242 static gint ett_inst_data_header = -1;
243 static gint ett_cyclic_data_block = -1;
244 static gint ett_control_mode = -1;
245 static gint ett_feedback_config = -1;
246 static gint ett_command_data_set = -1;
247 static gint ett_actual_data_set = -1;
248 static gint ett_status_data_set = -1;
249 static gint ett_interp_control = -1;
250 static gint ett_cyclic_rd_wt = -1;
251 static gint ett_event = -1;
252 static gint ett_event_check_ctrl = -1;
253 static gint ett_event_check_sts = -1;
254 static gint ett_service = -1;
255 static gint ett_get_axis_attribute = -1;
256 static gint ett_set_axis_attribute = -1;
257 static gint ett_get_axis_attr_list = -1;
258 static gint ett_set_axis_attr_list = -1;
259 static gint ett_group_sync = -1;
260 static gint ett_axis_status_set = -1;
261 static gint ett_command_control = -1;
263 /* These are the BITMASKS for the Time Data Set header field */
264 #define TIME_DATA_SET_TIME_STAMP 0x1
265 #define TIME_DATA_SET_TIME_OFFSET 0x2
266 #define TIME_DATA_SET_UPDATE_DIAGNOSTICS 0x4
267 #define TIME_DATA_SET_TIME_DIAGNOSTICS 0x8
269 /* These are the BITMASKS for the Command Data Set cyclic field */
270 #define COMMAND_DATA_SET_POSITION 0x01
271 #define COMMAND_DATA_SET_VELOCITY 0x02
272 #define COMMAND_DATA_SET_ACCELERATION 0x04
273 #define COMMAND_DATA_SET_TORQUE 0x08
274 #define COMMAND_DATA_SET_POSITION_TRIM 0x10
275 #define COMMAND_DATA_SET_VELOCITY_TRIM 0x20
276 #define COMMAND_DATA_SET_ACCELERATION_TRIM 0x40
277 #define COMMAND_DATA_SET_TORQUE_TRIM 0x80
279 /* These are the BITMASKS for the Actual Data Set cyclic field */
280 #define ACTUAL_DATA_SET_POSITION 0x01
281 #define ACTUAL_DATA_SET_VELOCITY 0x02
282 #define ACTUAL_DATA_SET_ACCELERATION 0x04
283 #define ACTUAL_DATA_SET_TORQUE 0x08
284 #define ACTUAL_DATA_SET_CURRENT 0x10
285 #define ACTUAL_DATA_SET_VOLTAGE 0x20
286 #define ACTUAL_DATA_SET_FREQUENCY 0x40
288 /* These are the BITMASKS for the Status Data Set cyclic field */
289 #define STATUS_DATA_SET_AXIS_FAULT 0x01
290 #define STATUS_DATA_SET_AXIS_ALARM 0x02
291 #define STATUS_DATA_SET_AXIS_STATUS 0x04
292 #define STATUS_DATA_SET_AXIS_IO_STATUS 0x08
293 #define STATUS_DATA_SET_AXIS_SAFETY 0x80
295 /* These are the BITMASKS for the Command Control cyclic field */
296 #define COMMAND_CONTROL_TARGET_UPDATE 0x03
297 #define COMMAND_CONTROL_POSITION_DATA_TYPE 0x0C
299 /* These are the VALUES of the connection format header field of the
300 * CIP Motion protocol */
301 #define FORMAT_FIXED_CONTROL_TO_DEVICE 2
302 #define FORMAT_FIXED_DEVICE_TO_CONTROL 3
303 #define FORMAT_VAR_CONTROL_TO_DEVICE 6
304 #define FORMAT_VAR_DEVICE_TO_CONTROL 7
306 /* Translate function to string - connection format values */
307 static const value_string cip_con_format_vals[] = {
308 { FORMAT_FIXED_CONTROL_TO_DEVICE, "Fixed Controller-to-Device" },
309 { FORMAT_FIXED_DEVICE_TO_CONTROL, "Fixed Device-to-Controller" },
310 { FORMAT_VAR_CONTROL_TO_DEVICE, "Variable Controller-to-Device" },
311 { FORMAT_VAR_DEVICE_TO_CONTROL, "Variable Device-to-Controller" },
315 /* Translate function to string - motor control mode values */
316 static const value_string cip_motor_control_vals[] = {
318 { 1, "Position Control" },
319 { 2, "Velocity Control" },
320 { 3, "Acceleration Control" },
321 { 4, "Torque Control" },
322 { 5, "Current Control" },
326 /* Translate function to string - feedback config values */
327 static const value_string cip_fdbk_config_vals[] = {
328 { 0, "No Feedback" },
329 { 1, "Master Feedback" },
330 { 2, "Motor Feedback" },
331 { 3, "Load Feedback" },
332 { 4, "Dual Feedback" },
336 /* Translate function to string - axis control values */
337 static const value_string cip_axis_control_vals[] =
340 { 1, "Enable Request" },
341 { 2, "Disble Request" },
342 { 3, "Shutdown Request" },
343 { 4, "Shutdown Reset Request" },
344 { 5, "Abort Request" },
345 { 6, "Fault Reset Request" },
346 { 7, "Stop Process" },
347 { 8, "Change Actual Pos" },
348 { 9, "Change Command Pos Ref" },
349 { 127, "Cancel Request" },
353 /* Translate function to string - control status values */
354 static const value_string cip_control_status_vals[] =
356 { 1, "Configuration Complete" },
360 /* Translate function to string - group sync Status */
361 static const value_string cip_sync_status_vals[] =
363 { 0, "Synchronized" },
364 { 1, "Not Synchronized" },
365 { 2, "Wrong Grandmaster" },
369 /* Translate function to string - command target update */
370 static const value_string cip_interpolation_vals[] = {
372 { 1, "Extrapolate (+1)" },
373 { 2, "Interpolate (+2)" },
377 /* These are the VALUES for the Command Position Data Type */
378 #define POSITION_DATA_LREAL 0x00
379 #define POSITION_DATA_DINT 0x01
381 /* Translate function to string - position data type */
382 static const value_string cip_pos_data_type_vals[] = {
383 { POSITION_DATA_LREAL, "LREAL (64-bit Float)" },
384 { POSITION_DATA_DINT, "DINT (32-bit Integer)" },
388 /* Translate function to string - axis response values */
389 static const value_string cip_axis_response_vals[] = {
390 { 0, "No Acknowlede" },
391 { 1, "Enable Acknowledge" },
392 { 2, "Disable Acknowledge" },
393 { 3, "Shutdown Acknowledge" },
394 { 4, "Shutdown Reset Acknowledge" },
395 { 5, "Abort Acknowledge" },
396 { 6, "Fault Reset Acknowledge" },
400 /* Translate function to string - axis state values */
401 static const value_string cip_axis_state_vals[] = {
402 { 0, "Initializing" },
403 { 1, "Pre-charging" },
410 { 8, "Major Faulted" },
411 { 9, "Start Inhibited" },
416 /* Translate function to string - event type values */
417 static const value_string cip_event_type_vals[] = {
418 { 0, "Registration 1 Positive Edge" },
419 { 1, "Registration 1 Negative Edge" },
420 { 2, "Registration 2 Positive Edge" },
421 { 3, "Registration 2 Negative Edge" },
422 { 4, "Marker Positive Edge" },
423 { 5, "Marker Negative Edge" },
424 { 6, "Home Switch Positive Edge" },
425 { 7, "Home Switch Negative Edge" },
426 { 8, "Home Switch Marker ++" },
427 { 9, "Home Switch Marker +-" },
428 { 10, "Home Switch Marker -+" },
429 { 11, "Home Switch Marker --" },
433 #define SC_GET_AXIS_ATTRIBUTE_LIST 0x4B
434 #define SC_SET_AXIS_ATTRIBUTE_LIST 0x4C
435 #define SC_SET_CYCLIC_WRITE_LIST 0x4D
436 #define SC_SET_CYCLIC_READ_LIST 0x4E
437 #define SC_RUN_MOTOR_TEST 0x4F
438 #define SC_GET_MOTOR_TEST_DATA 0x50
439 #define SC_RUN_INERTIA_TEST 0x51
440 #define SC_GET_INERTIA_TEST_DATA 0x52
441 #define SC_RUN_HOOKUP_TEST 0x53
442 #define SC_GET_HOOKUP_TEST_DATA 0x53
444 /* Translate function to string - CIP Service codes */
445 static const value_string cip_sc_vals[] = {
447 { SC_GET_AXIS_ATTRIBUTE_LIST, "Get Axis Attribute List" },
448 { SC_SET_AXIS_ATTRIBUTE_LIST, "Set Axis Attribute List" },
449 { SC_SET_CYCLIC_WRITE_LIST, "Set Cyclic Write List" },
450 { SC_SET_CYCLIC_READ_LIST, "Set Cyclic Read List" },
451 { SC_RUN_MOTOR_TEST, "Run Motor Test" },
452 { SC_GET_MOTOR_TEST_DATA, "Get Motor Test Data" },
453 { SC_RUN_INERTIA_TEST, "Run Inertia Test" },
454 { SC_GET_INERTIA_TEST_DATA, "Get Intertia Test Data" },
455 { SC_RUN_HOOKUP_TEST, "Run Hookup Test" },
456 { SC_GET_HOOKUP_TEST_DATA, "Get Hookup Test Data" },
461 * Function name: dissect_cmd_data_set
463 * Purpose: Dissect the command data set field of the cyclic data block header and if any
464 * of the command value bits are set to retrieve and display those command values
466 * Returns: The number of bytes in the cyclic data used
469 dissect_cmd_data_set(guint32 cmd_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset, gboolean lreal_pos)
471 guint32 bytes_used = 0;
473 /* The order of these if statements is VERY important, this is the order the values will
474 * appear in the cyclic data */
475 if ( (cmd_data_set & COMMAND_DATA_SET_POSITION) == COMMAND_DATA_SET_POSITION )
477 /* Based on the Command Position Data Type value embedded in the Command Control
478 * header field the position is either 64-bit floating or 32-bit integer */
481 /* Display the command data set position command value */
482 proto_tree_add_item(tree, hf_cip_pos_cmd, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN );
487 /* Display the command data set position command value */
488 proto_tree_add_item(tree, hf_cip_pos_cmd_int, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
493 if ( (cmd_data_set & COMMAND_DATA_SET_VELOCITY) == COMMAND_DATA_SET_VELOCITY )
495 /* Display the command data set velocity command value */
496 proto_tree_add_item(tree, hf_cip_vel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
500 if ( (cmd_data_set & COMMAND_DATA_SET_ACCELERATION) == COMMAND_DATA_SET_ACCELERATION )
502 /* Display the command data set acceleration command value */
503 proto_tree_add_item(tree, hf_cip_accel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
507 if ( (cmd_data_set & COMMAND_DATA_SET_TORQUE) == COMMAND_DATA_SET_TORQUE )
509 /* Display the command data set torque command value */
510 proto_tree_add_item(tree, hf_cip_trq_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
514 if ( (cmd_data_set & COMMAND_DATA_SET_POSITION_TRIM) == COMMAND_DATA_SET_POSITION_TRIM )
516 /* Display the command data set position trim value */
517 proto_tree_add_item(tree, hf_cip_pos_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
521 if ( (cmd_data_set & COMMAND_DATA_SET_VELOCITY_TRIM) == COMMAND_DATA_SET_VELOCITY_TRIM )
523 /* Display the command data set velocity trim value */
524 proto_tree_add_item(tree, hf_cip_vel_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
528 if ( (cmd_data_set & COMMAND_DATA_SET_ACCELERATION_TRIM) == COMMAND_DATA_SET_ACCELERATION_TRIM )
530 /* Display the command data set acceleration trim value */
531 proto_tree_add_item(tree, hf_cip_accel_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
535 if ( (cmd_data_set & COMMAND_DATA_SET_TORQUE_TRIM) == COMMAND_DATA_SET_TORQUE_TRIM )
537 /* Display the command data set torque trim value */
538 proto_tree_add_item(tree, hf_cip_trq_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
547 * Function name: dissect_act_data_set
549 * Purpose: Dissect the actual data set field of the cyclic data block header and if any
550 * of the actual value bits are set to retrieve and display those feedback values
552 * Returns: The number of bytes in the cyclic data used
555 dissect_act_data_set(guint32 act_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset)
557 guint32 bytes_used = 0;
559 /* The order of these if statements is VERY important, this is the order the values will
560 * appear in the cyclic data */
561 if ( (act_data_set & ACTUAL_DATA_SET_POSITION) == ACTUAL_DATA_SET_POSITION )
563 /* Display the actual data set position feedback value */
564 proto_tree_add_item(tree, hf_cip_act_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
568 if ( (act_data_set & ACTUAL_DATA_SET_VELOCITY) == ACTUAL_DATA_SET_VELOCITY )
570 /* Display the actual data set velocity feedback value */
571 proto_tree_add_item(tree, hf_cip_act_vel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
575 if ( (act_data_set & ACTUAL_DATA_SET_ACCELERATION) == ACTUAL_DATA_SET_ACCELERATION )
577 /* Display the actual data set acceleration feedback value */
578 proto_tree_add_item(tree, hf_cip_act_accel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
582 if ( (act_data_set & ACTUAL_DATA_SET_TORQUE) == ACTUAL_DATA_SET_TORQUE )
584 /* Display the actual data set torque feedback value */
585 proto_tree_add_item(tree, hf_cip_act_trq, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
588 if ( (act_data_set & ACTUAL_DATA_SET_CURRENT) == ACTUAL_DATA_SET_CURRENT )
590 /* Display the actual data set current feedback value */
591 proto_tree_add_item(tree, hf_cip_act_crnt, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
595 if ( (act_data_set & ACTUAL_DATA_SET_VOLTAGE) == ACTUAL_DATA_SET_VOLTAGE )
597 /* Display the actual data set voltage feedback value */
598 proto_tree_add_item(tree, hf_cip_act_volts, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
602 if ( (act_data_set & ACTUAL_DATA_SET_FREQUENCY) == ACTUAL_DATA_SET_FREQUENCY )
604 /* Display the actual data set frequency feedback value */
605 proto_tree_add_item(tree, hf_cip_act_freq, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
613 * Function name: dissect_status_data_set
615 * Purpose: Dissect the status data set field of the cyclic data block header and if any
616 * of the status value bits are set to retrieve and display those status values
618 * Returns: The number of bytes in the cyclic data used
621 dissect_status_data_set(guint32 status_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset)
623 guint32 bytes_used = 0;
624 proto_item *temp_proto_item;
625 proto_tree *temp_proto_tree;
627 /* The order of these if statements is VERY important, this is the order the values will
628 * appear in the cyclic data */
629 if ( (status_data_set & STATUS_DATA_SET_AXIS_FAULT) == STATUS_DATA_SET_AXIS_FAULT )
631 /* Display the various fault codes from the device */
632 proto_tree_add_item(tree, hf_cip_fault_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
635 proto_tree_add_item(tree, hf_cip_axis_fault, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
638 proto_tree_add_item(tree, hf_cip_fault_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
641 proto_tree_add_item(tree, hf_cip_fault_action, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
644 proto_tree_add_item(tree, hf_cip_fault_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
648 if ( (status_data_set & STATUS_DATA_SET_AXIS_ALARM) == STATUS_DATA_SET_AXIS_ALARM )
650 /* Display the various alarm codes from the device */
651 proto_tree_add_item(tree, hf_cip_alarm_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
654 proto_tree_add_item(tree, hf_cip_axis_alarm, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
657 proto_tree_add_item(tree, hf_cip_alarm_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
660 proto_tree_add_item(tree, hf_cip_alarm_state, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
663 proto_tree_add_item(tree, hf_cip_alarm_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
667 if ( (status_data_set & STATUS_DATA_SET_AXIS_STATUS) == STATUS_DATA_SET_AXIS_STATUS )
669 /* Display the various axis state values from the device */
670 temp_proto_item = proto_tree_add_item(tree, hf_cip_axis_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
671 temp_proto_tree = proto_item_add_subtree( temp_proto_item, ett_axis_status_set );
672 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_local_ctrl, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
673 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_alarm, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
674 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_dc_bus, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
675 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_pwr_struct, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
676 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_tracking, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
677 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_pos_lock, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
678 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_lock, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
679 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_standstill, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
680 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_threshold, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
681 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
682 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_acc_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
683 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_dec_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
684 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_torque_threshold, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
685 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_torque_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
686 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_cur_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
687 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_therm_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
688 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_feedback_integ, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
689 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_shutdown, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
690 proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_in_process, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
693 proto_tree_add_item(tree, hf_cip_axis_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
697 if ( (status_data_set & STATUS_DATA_SET_AXIS_IO_STATUS) == STATUS_DATA_SET_AXIS_IO_STATUS )
699 proto_tree_add_item(tree, hf_cip_axis_io_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
702 proto_tree_add_item(tree, hf_cip_axis_io_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
706 if ( (status_data_set & STATUS_DATA_SET_AXIS_SAFETY) == STATUS_DATA_SET_AXIS_SAFETY )
708 proto_tree_add_item(tree, hf_cip_safety_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
716 * Function name: dissect_cntr_cyclic
718 * Purpose: Dissect the cyclic data block of a controller to device format message
720 * Returns: The new offset into the message that follow on dissections should use
721 * as their starting offset
724 dissect_cntr_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
726 proto_item *header_item, *temp_proto_item;
727 proto_tree *header_tree, *temp_proto_tree;
730 guint32 bytes_used = 0;
732 /* Create the tree for the entire instance data header */
733 header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Data Block");
734 header_tree = proto_item_add_subtree(header_item, ett_cyclic_data_block);
736 /* Add the control mode header field to the tree */
737 proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
739 /* Add the feedback config header field to the tree */
740 proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
742 /* Add the axis control field to the tree */
743 proto_tree_add_item(header_tree, hf_cip_axis_control, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
745 /* Add the control status to the tree */
746 proto_tree_add_item(header_tree, hf_cip_control_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
748 /* Read the command control header field from the packet into memory and determine if the dissector
749 * should be using an LREAL or DINT for position */
750 temp_data = tvb_get_guint8(tvb, offset + 7);
751 lreal_pos = ( (temp_data & COMMAND_CONTROL_POSITION_DATA_TYPE) == POSITION_DATA_LREAL );
753 /* Read the command data set header field from the packet into memory */
754 temp_data = tvb_get_guint8(tvb, offset + 4);
756 /* Create the tree for the command data set header field */
757 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_cmd_data_set, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
758 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_command_data_set);
759 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_pos_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
760 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_vel_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
761 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_acc_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
762 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_trq_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
763 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_pos_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
764 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_vel_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
765 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_acc_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
766 proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_trq_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
768 /* Display the command data values from the cyclic data payload within the command data set tree, the
769 * cyclic data starts immediately after the interpolation control field in the controller to device
771 bytes_used += dissect_cmd_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used, lreal_pos);
773 /* Create the tree for the actual data set header field */
774 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_act_data_set, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
775 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_actual_data_set);
776 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_pos, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
777 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vel, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
778 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_acc, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
779 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_trq, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
780 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_crnt, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
781 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vltg, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
782 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_fqcy, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
784 /* Create the tree for the status data set header field */
785 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_sts_data_set, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
786 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_status_data_set);
787 proto_tree_add_item(temp_proto_tree, hf_cip_sts_flt, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
788 proto_tree_add_item(temp_proto_tree, hf_cip_sts_alrm, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
789 proto_tree_add_item(temp_proto_tree, hf_cip_sts_sts, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
790 proto_tree_add_item(temp_proto_tree, hf_cip_sts_iosts, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
791 proto_tree_add_item(temp_proto_tree, hf_cip_sts_safety, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
793 /* Create the tree for the command control header field */
794 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_command_control, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
795 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_command_control);
797 /* Display the interpolation control and position format fields */
798 proto_tree_add_item(temp_proto_tree, hf_cip_intrp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
799 proto_tree_add_item(temp_proto_tree, hf_cip_position_data_type, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
801 /* Return the offset to the next byte in the message */
802 return offset + 8 + bytes_used;
806 * Function name: dissect_devce_cyclic
808 * Purpose: Dissect the cyclic data block of a device to controller format message
810 * Returns: The new offset into the message that follow on dissections should use
811 * as their starting offset
814 dissect_devce_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
816 proto_item *header_item, *temp_proto_item;
817 proto_tree *header_tree, *temp_proto_tree;
819 guint32 bytes_used = 0;
821 /* Create the tree for the entire instance data header */
822 header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Data Block");
823 header_tree = proto_item_add_subtree(header_item, ett_cyclic_data_block);
825 /* Add the control mode header field to the tree */
826 proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
828 /* Add the feedback config header field to the tree */
829 proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
831 /* Add the axis response field to the tree */
832 proto_tree_add_item(header_tree, hf_cip_axis_response, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
834 /* Add the axis response status to the tree */
835 proto_tree_add_item(header_tree, hf_cip_axis_resp_stat, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
837 /* Read the actual data set header field from the packet into memory */
838 temp_data = tvb_get_guint8(tvb, offset + 5);
840 /* Create the tree for the actual data set header field */
841 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_act_data_set, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
842 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_actual_data_set);
843 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_pos, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
844 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vel, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
845 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_acc, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
846 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_trq, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
847 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_crnt, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
848 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vltg, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
849 proto_tree_add_item(temp_proto_tree, hf_cip_act_data_fqcy, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
851 /* Display the actual data values from the cyclic data payload within the command data set tree, the
852 * cyclic data starts immediately after the interpolation control field in the controller to device
853 * direction and the actual data starts immediately after the cyclic data */
854 bytes_used += dissect_act_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used);
856 /* Read the status data set header field from the packet into memory */
857 temp_data = tvb_get_guint8(tvb, offset + 6);
859 /* Create the tree for the status data set header field */
860 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_sts_data_set, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
861 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_status_data_set);
862 proto_tree_add_item(temp_proto_tree, hf_cip_sts_flt, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
863 proto_tree_add_item(temp_proto_tree, hf_cip_sts_alrm, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
864 proto_tree_add_item(temp_proto_tree, hf_cip_sts_sts, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
865 proto_tree_add_item(temp_proto_tree, hf_cip_sts_iosts, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
866 proto_tree_add_item(temp_proto_tree, hf_cip_sts_safety, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
868 /* Display the status data values from the cyclic data payload within the status data set tree, the
869 * cyclic data starts immediately after the axis state field in the device to controller
870 * direction and the status data starts immediately after the cyclic data */
871 bytes_used += dissect_status_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used);
873 /* Display the axis state control field */
874 proto_tree_add_item(header_tree, hf_cip_axis_state, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
876 /* Return the offset to the next byte in the message */
877 return offset + 8 + bytes_used;
881 * Function name: dissect_cyclic_wt
883 * Purpose: Dissect the cyclic write data block in a controller to device message
885 * Returns: The new offset into the message that follow on dissections should use
886 * as their starting offset
889 dissect_cyclic_wt(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
891 proto_item *header_item;
892 proto_tree *header_tree;
894 /* Create the tree for the entire cyclic write data block */
895 header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Write Data Block");
896 header_tree = proto_item_add_subtree(header_item, ett_cyclic_rd_wt);
898 /* Display the cyclic write block id value */
899 proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
901 /* Display the cyclic read block id value */
902 proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
904 /* Display the remainder of the cyclic write data if there is any */
905 if ( (size - 4) > 0 )
907 proto_tree_add_item(header_tree, hf_cip_cyclic_wrt_data, tvb, offset + 4, size - 4, ENC_NA);
910 return offset + size;
914 * Function name: dissect_cyclic_rd
916 * Purpose: Dissect the cyclic read data block in a device to controller message
918 * Returns: The new offset into the message that follow on dissections should use
919 * as their starting offset
922 dissect_cyclic_rd(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
924 proto_item *header_item;
925 proto_tree *header_tree;
927 /* Create the tree for the entire cyclic write data block */
928 header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Read Data Block");
929 header_tree = proto_item_add_subtree(header_item, ett_cyclic_rd_wt);
931 /* Display the cyclic write block id value */
932 proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
934 /* Display the cyclic write status value */
935 proto_tree_add_item(header_tree, hf_cip_cyclic_write_sts, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
937 /* Display the cyclic read block id value */
938 proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
940 /* Display the cyclic read status value */
941 proto_tree_add_item(header_tree, hf_cip_cyclic_read_sts, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
943 /* Display the remainder of the cyclic read data if there is any*/
944 if ( (size - 4) > 0 )
946 proto_tree_add_item(header_tree, hf_cip_cyclic_rd_data, tvb, offset + 4, size - 4, ENC_NA);
949 return offset + size;
953 * Function name: dissect_cntr_event
955 * Purpose: Dissect the event data block in a controller to device message
957 * Returns: The new offset into the message that follow on dissections should use
958 * as their starting offset
961 dissect_cntr_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
963 proto_item *header_item, *temp_proto_item;
964 proto_tree *header_tree, *temp_proto_tree;
966 guint32 acks, cur_ack;
967 guint32 bytes_used = 0;
969 /* Create the tree for the entire cyclic write data block */
970 header_item = proto_tree_add_text(tree, tvb, offset, size, "Event Data Block");
971 header_tree = proto_item_add_subtree(header_item, ett_event);
973 /* Read the event checking control header field from the packet into memory */
974 temp_data = tvb_get_letohl(tvb, offset);
976 /* Create the tree for the event checking control header field */
977 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_event_checking, tvb, offset, 4, ENC_LITTLE_ENDIAN);
978 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_event_check_ctrl);
980 /* Add the individual elements of the event checking control */
981 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
982 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
983 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
984 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
985 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
986 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
987 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
988 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
989 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_marker_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
990 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_marker_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
991 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
992 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
993 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
994 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
995 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_mp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
996 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_mm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
997 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_acks, tvb, offset, 4, ENC_LITTLE_ENDIAN);
998 /* The dissector will indicate if the protocol is requesting an extended event format but will not dissect it,
999 * to date no products actually support this format */
1000 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_extend_format, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1002 /* The event checking control value is 4 bytes long */
1005 /* The final 4 bits of the event checking control value are the number of acknowledgements in the message */
1006 acks = (temp_data >> 28) & 0x0F;
1008 /* Each acknowledgement contains and id and a status value */
1009 for (cur_ack = 0; cur_ack < acks; cur_ack++)
1011 /* Display the current acknowledgement id */
1012 proto_tree_add_item(header_tree, hf_cip_event_ack, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1015 /* Display the current event status */
1016 proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1020 return offset + size;
1024 * Function name: dissect_devce_event
1026 * Purpose: Dissect the event data block in a device to controller message
1028 * Returns: The new offset into the message that follow on dissections should use
1029 * as their starting offset
1032 dissect_devce_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1034 proto_item *header_item, *temp_proto_item;
1035 proto_tree *header_tree, *temp_proto_tree;
1037 guint64 nots, cur_not;
1038 guint32 bytes_used = 0;
1040 /* Create the tree for the entire cyclic write data block */
1041 header_item = proto_tree_add_text(tree, tvb, offset, size, "Event Data Block");
1042 header_tree = proto_item_add_subtree(header_item, ett_event);
1044 /* Read the event checking control header field from the packet into memory */
1045 temp_data = tvb_get_letohl(tvb, offset);
1047 /* Create the tree for the event checking control header field */
1048 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_event_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1049 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_event_check_sts);
1051 /* Add the individual elements of the event checking control */
1052 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1053 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1054 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1055 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1056 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1057 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1058 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1059 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1060 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_marker_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1061 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_marker_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1062 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1063 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1064 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1065 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1066 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_mp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1067 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_mm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1068 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_nfs, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1069 /* The dissector will indicate if the protocol is requesting an extended event format but will not dissect it,
1070 * to date no products actually support this format */
1071 proto_tree_add_item(temp_proto_tree, hf_cip_evnt_extend_format, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1073 /* The event status control value is 4 bytes long */
1076 /* The final 4 bits of the event status control value are the number of notifications in the message */
1077 nots = (temp_data >> 28) & 0x0F;
1079 /* Each notification contains and id, status value, event type, position and time stamp */
1080 for (cur_not = 0; cur_not < nots; cur_not++)
1082 /* Display the current event id */
1083 proto_tree_add_item(header_tree, hf_cip_event_id, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1086 /* Display the current event status */
1087 proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1090 /* Display the current event type */
1091 proto_tree_add_item(header_tree, hf_cip_evnt_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1092 bytes_used += 2; /* Increment by 2 to jump the reserved byte */
1094 /* Display the event position value */
1095 proto_tree_add_item(header_tree, hf_cip_event_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1098 /* Display the event time stamp value */
1099 proto_tree_add_item(header_tree, hf_cip_event_ts, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
1103 return size + offset;
1107 * Function name: dissect_get_axis_attr_list_request
1109 * Purpose: Dissect the get axis attribute list service request
1114 dissect_get_axis_attr_list_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1116 proto_item *header_item, *attr_item;
1117 proto_tree *header_tree, *attr_tree;
1118 guint16 attribute, attribute_cnt;
1119 guint32 local_offset;
1120 guint8 increment_size, dimension;
1122 /* Create the tree for the get axis attribute list request */
1123 header_item = proto_tree_add_text(tree, tvb, offset, size, "Get Axis Attribute List Request");
1124 header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1126 /* Read the number of attributes that are contained within the request */
1127 attribute_cnt = tvb_get_letohs(tvb, offset);
1128 proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1130 /* Start the attribute loop at the beginning of the first attribute in the list */
1131 local_offset = offset + 4;
1133 /* For each attribute display the associated fields */
1134 for (attribute = 0; attribute < attribute_cnt; attribute++)
1136 /* At a minimum the local offset needs will need to be incremented by 4 bytes to reach the next attribute */
1139 /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1140 * properly displaying of the attribute is possible */
1141 dimension = tvb_get_guint8(tvb, local_offset + 2);
1143 /* Create the tree for this attribute within the request */
1144 attr_item = proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1145 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1147 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1148 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1152 /* Display the start index and start index from the request if this is an array request */
1153 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1154 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1156 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1157 increment_size += 4;
1160 /* Move the local offset to the next attribute */
1161 local_offset += increment_size;
1166 * Function name: dissect_set_axis_attr_list_request
1168 * Purpose: Dissect the set axis attribute list service request
1173 dissect_set_axis_attr_list_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1175 proto_item *header_item, *attr_item;
1176 proto_tree *header_tree, *attr_tree;
1177 guint16 attribute, attribute_cnt, data_elements;
1178 guint32 local_offset;
1179 guint32 attribute_size;
1180 guint8 dimension, attribute_start, increment_size;
1182 /* Create the tree for the set axis attribute list request */
1183 header_item = proto_tree_add_text(tree, tvb, offset, size, "Set Axis Attribute List Request");
1184 header_tree = proto_item_add_subtree(header_item, ett_set_axis_attribute);
1186 /* Read the number of attributes that are contained within the request */
1187 attribute_cnt = tvb_get_letohs(tvb, offset);
1188 proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1190 /* Start the attribute loop at the beginning of the first attribute in the list */
1191 local_offset = offset + 4;
1193 /* For each attribute display the associated fields */
1194 for (attribute = 0; attribute < attribute_cnt; attribute++)
1196 /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1199 /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1200 * properly displaying of the attribute is possible */
1201 dimension = tvb_get_guint8(tvb, local_offset + 2);
1202 attribute_size = tvb_get_guint8(tvb, local_offset + 3);
1203 attribute_start = 4;
1207 data_elements = tvb_get_letohs(tvb, local_offset + 6);
1209 /* Modify the size of the attribute data by the number of elements if the request is an array request */
1210 attribute_size *= data_elements;
1212 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1213 increment_size += 4;
1214 attribute_start += 4;
1217 /* Create the tree for this attribute in the get axis attribute list request */
1218 attr_item = proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1219 attr_tree = proto_item_add_subtree(attr_item, ett_set_axis_attr_list);
1221 proto_tree_add_item(attr_tree, hf_set_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1222 proto_tree_add_item(attr_tree, hf_set_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1226 /* Display the start index and start index from the request if the request is an array */
1227 proto_tree_add_item(attr_tree, hf_set_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1228 proto_tree_add_item(attr_tree, hf_set_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1231 /* Display the value of this attribute */
1232 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
1234 /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1235 if (attribute_size % 4 != 0)
1237 attribute_size = attribute_size + (4 - (attribute_size % 4));
1240 /* Move the local offset to the next attribute */
1241 local_offset += (attribute_size + increment_size);
1246 * Function name: dissect_group_sync_request
1248 * Purpose: Dissect the group sync service request
1253 dissect_group_sync_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1255 proto_item *header_item;
1256 proto_tree *header_tree;
1258 /* Create the tree for the group sync request */
1259 header_item = proto_tree_add_text(tree, tvb, offset, size, "Group Sync Request");
1260 header_tree = proto_item_add_subtree(header_item, ett_group_sync);
1262 /* Read the grandmaster id from the payload */
1263 proto_tree_add_item(header_tree, hf_cip_ptp_grandmaster, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1268 * Function name: dissect_cntr_service
1270 * Purpose: Dissect the service data block in a controller to device message
1272 * Returns: The new offset into the message that follow on dissections should use
1273 * as their starting offset
1276 dissect_cntr_service(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1278 proto_item *header_item;
1279 proto_tree *header_tree;
1282 /* Create the tree for the entire service data block */
1283 header_item = proto_tree_add_text(tree, tvb, offset, size, "Service Data Block");
1284 header_tree = proto_item_add_subtree(header_item, ett_service);
1286 /* Display the transaction id value */
1287 proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1289 /* Display the service code */
1290 service = tvb_get_guint8(tvb, offset + 1);
1291 proto_tree_add_item(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1293 /* If the service is a set axis, get axis attribute or group sync request dissect it as well */
1296 case SC_GET_AXIS_ATTRIBUTE_LIST:
1297 dissect_get_axis_attr_list_request(tvb, header_tree, offset + 4, size);
1299 case SC_SET_AXIS_ATTRIBUTE_LIST:
1300 dissect_set_axis_attr_list_request(tvb, header_tree, offset + 4, size);
1303 dissect_group_sync_request(tvb, header_tree, offset + 4, size);
1306 /* Display the remainder of the service channel data */
1307 proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1310 return offset + size;
1314 * Function name: dissect_set_axis_attr_list_response
1316 * Purpose: Dissect the set axis attribute list service response
1321 dissect_set_axis_attr_list_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1323 proto_item *header_item, *attr_item;
1324 proto_tree *header_tree, *attr_tree;
1325 guint16 attribute, attribute_cnt;
1326 guint32 local_offset;
1328 /* Create the tree for the set axis attribute list response */
1329 header_item = proto_tree_add_text(tree, tvb, offset, size, "Set Axis Attribute List Response");
1330 header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1332 /* Read the number of attributes that are contained within the response */
1333 attribute_cnt = tvb_get_letohs(tvb, offset);
1334 proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1336 /* Start the attribute loop at the beginning of the first attribute in the list */
1337 local_offset = offset + 4;
1339 /* For each attribute display the associated fields */
1340 for (attribute = 0; attribute < attribute_cnt; attribute++)
1342 /* Create the tree for the current attribute in the set axis attribute list response */
1343 attr_item = proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1344 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1346 /* Add the response status to the tree */
1347 proto_tree_add_item(attr_tree, hf_cip_svc_set_axis_attr_sts, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1349 /* Move the local offset to the next attribute */
1355 * Function name: dissect_get_axis_attr_list_response
1357 * Purpose: Dissect the get axis attribute list service response
1362 dissect_get_axis_attr_list_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1364 proto_item *header_item, *attr_item;
1365 proto_tree *header_tree, *attr_tree;
1366 guint16 attribute, attribute_cnt, data_elements;
1367 guint32 attribute_size;
1368 guint8 dimension, attribute_start, increment_size;
1369 guint32 local_offset;
1371 /* Create the tree for the get axis attribute list response */
1372 header_item = proto_tree_add_text(tree, tvb, offset, size, "Get Axis Attribute List Response");
1373 header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1375 /* Read the number of attributes that are contained within the request */
1376 attribute_cnt = tvb_get_letohs(tvb, offset);
1377 proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1379 /* Start the attribute loop at the beginning of the first attribute in the list */
1380 local_offset = offset + 4;
1382 /* For each attribute display the associated fields */
1383 for (attribute = 0; attribute < attribute_cnt; attribute++)
1385 /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1388 /* Pull the fields for this attribute from the payload, all fields are need to make some calculations before
1389 * properly displaying of the attribute is possible */
1390 dimension = tvb_get_guint8(tvb, local_offset + 2);
1391 attribute_size = tvb_get_guint8(tvb, local_offset + 3);
1392 attribute_start = 4;
1396 data_elements = tvb_get_letohs(tvb, local_offset + 6);
1398 /* Modify the size of the attribute data by the number of elements if the request is an array request */
1399 attribute_size *= data_elements;
1401 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1402 increment_size += 4;
1403 attribute_start += 4;
1406 /* Display the fields associated with the get axis attribute list response */
1407 attr_item = proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1408 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1410 if (dimension == 0xFF)
1412 /* Display the element size as an error code if the dimension field indicates an error */
1413 proto_tree_add_item(attr_tree, hf_cip_svc_get_axis_attr_sts, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1415 /* No attribute data so no attribute size */
1420 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1421 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1425 /* Display the start index and start indexfrom the request */
1426 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1427 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1430 /* Display the remainder of the service channel data */
1431 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, offset + attribute_start, attribute_size, ENC_NA);
1433 /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1434 if (attribute_size % 4 != 0)
1436 attribute_size = attribute_size + (4 - (attribute_size % 4));
1440 /* Move the local offset to the next attribute */
1441 local_offset += (attribute_size + increment_size);
1446 * Function name: dissect_group_sync_response
1448 * Purpose: Dissect the group sync service response
1453 dissect_group_sync_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size _U_)
1455 proto_tree_add_item(tree, hf_cip_group_sync, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1459 * Function name: dissect_devce_service
1461 * Purpose: Dissect the service data block in a device to controller message
1463 * Returns: The new offset into the message that follow on dissections should use
1464 * as their starting offset
1467 dissect_devce_service(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1469 proto_item *header_item;
1470 proto_tree *header_tree;
1472 /* Create the tree for the entire service data block */
1473 header_item = proto_tree_add_text(tree, tvb, offset, size, "Service Data Block");
1474 header_tree = proto_item_add_subtree(header_item, ett_service);
1476 /* Display the transaction id value */
1477 proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1479 /* Display the service code */
1480 proto_tree_add_item(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1482 /* Display the general status code */
1483 proto_tree_add_item(header_tree, hf_cip_svc_sts, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1485 /* Display the extended status code */
1486 proto_tree_add_item(header_tree, hf_cip_svc_ext_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1488 /* If the service is a set axis, get axis attribute response or group sync dissect it as well */
1489 switch(tvb_get_guint8(tvb, offset + 1))
1491 case SC_GET_AXIS_ATTRIBUTE_LIST:
1492 dissect_get_axis_attr_list_response(tvb, header_tree, offset + 4, size);
1494 case SC_SET_AXIS_ATTRIBUTE_LIST:
1495 dissect_set_axis_attr_list_response(tvb, header_tree, offset + 4, size);
1498 dissect_group_sync_response(tvb, header_tree, offset + 4, size);
1501 /* Display the remainder of the service channel data */
1502 proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1505 return offset + size;
1509 * Function name: dissect_var_inst_header
1511 * Purpose: Dissect the instance data header of a variable controller to device or
1512 * device to controller message
1517 dissect_var_inst_header(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint8* inst_number, guint32* cyc_size,
1518 guint32* cyc_blk_size, guint32* evnt_size, guint32* servc_size)
1521 proto_item *header_item;
1522 proto_tree *header_tree;
1524 /* Create the tree for the entire instance data header */
1525 *inst_number = tvb_get_guint8(tvb, offset);
1527 header_item = proto_tree_add_text(tree, tvb, offset, 8, "Instance Data Header - Instance: %d", *inst_number);
1528 header_tree = proto_item_add_subtree(header_item, ett_inst_data_header);
1530 /* Read the instance number field from the instance data header */
1531 proto_tree_add_item(header_tree, hf_var_devce_instance, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1533 /* The "size" fields in the instance data block header are all stored as number of 32-bit words the
1534 * block uses since all blocks should pad up to 32-bits so to convert to bytes each is mulitplied by 4 */
1536 /* Read the instance block size field in bytes from the instance data header */
1537 temp_data = tvb_get_guint8(tvb, offset + 2);
1538 proto_tree_add_uint_format_value(header_tree, hf_var_devce_instance_block_size,
1539 tvb, offset + 2, 1, temp_data, "%d words", temp_data);
1541 /* Read the cyclic block size field in bytes from the instance data header */
1542 temp_data = tvb_get_guint8(tvb, offset + 3);
1543 proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_block_size,
1544 tvb, offset + 3, 1, temp_data, "%d words", temp_data);
1546 /* Read the cyclic command block size field in bytes from the instance data header */
1547 *cyc_size = (tvb_get_guint8(tvb, offset + 4) * 4);
1548 proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_data_block_size,
1549 tvb, offset + 4, 1, (*cyc_size)/4, "%d words", (*cyc_size)/4);
1551 /* Read the cyclic write block size field in bytes from the instance data header */
1552 *cyc_blk_size = (tvb_get_guint8(tvb, offset + 5) * 4);
1553 proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_rw_block_size,
1554 tvb, offset + 5, 1, (*cyc_blk_size)/4, "%d words", (*cyc_blk_size)/4);
1556 /* Read the event block size in bytes from the instance data header */
1557 *evnt_size = (tvb_get_guint8(tvb, offset + 6) * 4);
1558 proto_tree_add_uint_format_value(header_tree, hf_var_devce_event_block_size,
1559 tvb, offset + 6, 1, (*evnt_size)/4, "%d words", (*evnt_size)/4);
1561 /* Read the service block size in bytes from the instance data header */
1562 *servc_size = (tvb_get_guint8(tvb, offset + 7) * 4);
1563 proto_tree_add_uint_format_value(header_tree, hf_var_devce_service_block_size,
1564 tvb, offset + 7, 1, (*servc_size)/4, "%d words", (*servc_size)/4);
1568 * Function name: dissect_var_cont_conn_header
1570 * Purpose: Dissect the connection header of a variable controller to device message
1572 * Returns: Offset to the start of the instance data block
1575 dissect_var_cont_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1577 guint32 header_size;
1579 proto_item *header_item, *temp_proto_item;
1580 proto_tree *header_tree, *temp_proto_tree;
1582 /* Calculate the header size, start with the basic header size */
1585 temp_data = tvb_get_guint8(tvb, offset + 7);
1587 /* Check the time data set field for enabled bits. If either update period or
1588 * update time stamp fields are set, bump the header size by the appropriate size */
1589 if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1593 if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1598 /* Create the tree for the entire connection header */
1599 header_item = proto_tree_add_text(tree, tvb, offset, header_size, "Connection Header");
1600 header_tree = proto_item_add_subtree(header_item, ett_cont_dev_header);
1602 /* Add the connection header fields that are common to all types of messages */
1603 proto_tree_add_item(header_tree, hf_cip_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1604 proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1605 proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1607 /* Create the tree for the node control header field */
1608 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_node_control, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1609 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_node_control);
1611 /* Add the individual data elements to the node control tree */
1612 proto_tree_add_item(temp_proto_tree, hf_cip_node_control_remote, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1613 proto_tree_add_item(temp_proto_tree, hf_cip_node_control_sync, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1614 proto_tree_add_item(temp_proto_tree, hf_cip_node_data_valid, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1615 proto_tree_add_item(temp_proto_tree, hf_cip_node_fault_reset, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1617 /* Read the instance count field from the packet into memory, this gets passed back out of the method */
1618 *inst_count = tvb_get_guint8(tvb, offset + 4);
1620 /* Add the instance count and last update id to the connection header tree */
1621 proto_tree_add_item(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
1622 proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
1624 /* Read the time data set from the packet into memory */
1625 temp_data = tvb_get_guint8(tvb, offset + 7);
1627 /* Create the tree for the time data set field */
1628 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_time_data_set, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1629 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_time_data_set);
1631 /* Add the individual data elements to the time data set header field */
1632 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_stamp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1633 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_offset, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1634 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1635 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_time_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1637 /* Move the offset to the byte just beyond the time data set field */
1638 offset = (offset + 7 + 1);
1640 /* Add the time values if they are present in the time data set header field */
1641 if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1643 proto_tree_add_item(header_tree, hf_cip_cont_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1644 offset = (offset + 8);
1647 if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1649 proto_tree_add_item(header_tree, hf_cip_cont_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1650 offset = (offset + 8);
1653 /* Return the number of bytes used so it can be used as an offset in the following dissections */
1658 * Function name: dissect_var_devce_conn_header
1660 * Purpose: Dissect the connection header of a variable device to controller message
1662 * Returns: Offset to the start of the instance data block
1665 dissect_var_devce_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1667 guint32 header_size;
1669 proto_item *header_item, *temp_proto_item;
1670 proto_tree *header_tree, *temp_proto_tree;
1672 /* Calculate the header size, start with the basic header size */
1675 temp_data = tvb_get_guint8(tvb, offset + 7);
1676 if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1680 if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1684 if ( (temp_data & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
1688 if ( (temp_data & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
1693 /* Create the tree for the entire connection header */
1694 header_item = proto_tree_add_text(tree, tvb, offset, header_size, "Connection Header");
1695 header_tree = proto_item_add_subtree(header_item, ett_cont_dev_header);
1697 /* Add the connection header fields that are common to all types of messages */
1698 proto_tree_add_item(header_tree, hf_cip_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1699 proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1700 proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1702 /* Create the tree for the node status header field */
1703 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_node_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1704 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_node_status);
1706 /* Add the individual data elements to the node control tree */
1707 proto_tree_add_item(temp_proto_tree, hf_cip_node_control_remote, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1708 proto_tree_add_item(temp_proto_tree, hf_cip_node_control_sync, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1709 proto_tree_add_item(temp_proto_tree, hf_cip_node_data_valid, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1710 proto_tree_add_item(temp_proto_tree, hf_cip_node_device_faulted, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1712 /* Read the instance count field from the packet into memory, this gets passed back out of the method */
1713 *inst_count = tvb_get_guint8(tvb, offset + 4);
1715 /* Add the instance count to the connection header tree */
1716 proto_tree_add_item(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
1718 /* The device to controller header contains the node alarms and node faults fields as well. */
1719 proto_tree_add_item(header_tree, hf_cip_node_fltalarms, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
1721 /* Add the last update id to the connection header tree */
1722 proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
1724 /* Read the time data set from the packet into memory */
1725 temp_data = tvb_get_guint8(tvb, offset + 7);
1727 /* Create the tree for the time data set field */
1728 temp_proto_item = proto_tree_add_item(header_tree, hf_cip_time_data_set, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1729 temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_time_data_set);
1731 /* Add the individual data elements to the time data set header field */
1732 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_stamp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1733 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_offset, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1734 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1735 proto_tree_add_item(temp_proto_tree, hf_cip_time_data_time_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1737 /* Move the offset to the byte just beyond the time data set field */
1738 offset = (offset + 7 + 1);
1740 /* Add the time values if they are present in the time data set header field */
1741 if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1743 proto_tree_add_item(header_tree, hf_cip_devc_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1744 offset = (offset + 8);
1747 if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1749 proto_tree_add_item(header_tree, hf_cip_devc_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1750 offset = (offset + 8);
1753 if ( (temp_data & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
1755 /* If the time diagnostic bit is set then the header contains the count of lost updates, late updates, data
1756 * received time stamp and data transmit time stamp */
1757 proto_tree_add_item(header_tree, hf_cip_lost_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1758 offset = (offset + 1);
1760 /* Add the reserved bytes to the offset after adding the late updates to the display */
1761 proto_tree_add_item(header_tree, hf_cip_late_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1762 offset = (offset + 3);
1765 if ( (temp_data & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
1767 proto_tree_add_item(header_tree, hf_cip_data_rx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1770 proto_tree_add_item(header_tree, hf_cip_data_tx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1774 /* Return the number of bytes used so it can be used as an offset in the following dissections */
1780 * Function name: dissect_cipmotion
1782 * Purpose: Perform the top level dissection of the CIP Motion datagram, it is called by
1783 * Wireshark when the dissection rule registered in proto_reg_handoff_cipmotion is fired
1788 dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree)
1791 /* guint32 seq_number; */
1793 proto_item *proto_item_top;
1794 proto_tree *proto_tree_top;
1797 /* Pull the CIP class 1 sequence number from the incoming message */
1798 /* seq_number = tvb_get_letohs(tvb, offset); */
1799 offset = (offset + 2);
1801 /* Pull the actual values for the connection format and update id from the
1802 * incoming message to be used in the column info */
1803 con_format = tvb_get_guint8(tvb, offset);
1804 update_id = tvb_get_guint8(tvb, offset + 2);
1806 /* Make entries in Protocol column and Info column on summary display */
1807 col_set_str(pinfo->cinfo, COL_PROTOCOL, "Motion");
1809 /* Add connection format and update number to the info column */
1810 col_add_fstr( pinfo->cinfo, COL_INFO, "%s, Update Id: %d",
1811 val_to_str(con_format, cip_con_format_vals, "Unknown connection format (%x)"), update_id );
1813 /* If tree is not NULL then Wireshark is requesting that the dissection
1814 * panel be updated with the dissected packet, if tree is NULL then only
1815 * the summary protocol and info columns need to be updated */
1818 /* Create display subtree for the protocol by creating an item and then
1819 * creating a subtree from the item, the subtree must have been registered
1820 * in proto_register_cipmotion already */
1821 proto_item_top = proto_tree_add_item( tree, proto_cipmotion, tvb, 0, -1, ENC_NA );
1822 proto_tree_top = proto_item_add_subtree( proto_item_top, ett_cipmotion );
1824 /* Add the CIP class 1 sequence number to the tree */
1825 proto_tree_add_item( proto_tree_top, hf_cip_class1_seqnum, tvb, 0, 2, ENC_LITTLE_ENDIAN );
1827 /* Attempt to classify the incoming header */
1828 if (( con_format == FORMAT_VAR_CONTROL_TO_DEVICE ) ||
1829 ( con_format == FORMAT_VAR_DEVICE_TO_CONTROL ))
1831 /* Sizes of the individual channels within the connection */
1832 guint32 cyc_size, cyc_blk_size, evnt_size, servc_size;
1833 guint32 inst_count = 0, inst;
1835 /* Dissect the header fields */
1838 case FORMAT_VAR_CONTROL_TO_DEVICE:
1839 offset = dissect_var_cont_conn_header(tvb, proto_tree_top, &inst_count, offset);
1841 case FORMAT_VAR_DEVICE_TO_CONTROL:
1842 offset = dissect_var_devce_conn_header(tvb, proto_tree_top, &inst_count, offset);
1846 /* Repeat the following dissections for each instance within the payload */
1847 for( inst = 0; inst < inst_count; inst++ )
1849 /* Actual instance number from header field */
1852 /* Dissect the instance data header */
1853 dissect_var_inst_header( tvb, proto_tree_top, offset, &instance,
1854 &cyc_size, &cyc_blk_size, &evnt_size, &servc_size );
1856 /* Increment the offset to just beyond the instance header */
1859 /* Dissect the cyclic command (actual) data if any exists */
1860 /* Dissect the cyclic write (read) data if any exists */
1861 /* Dissect the event data block if there is any event data */
1864 case FORMAT_VAR_CONTROL_TO_DEVICE:
1866 offset = dissect_cntr_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
1867 if ( cyc_blk_size > 0 )
1868 offset = dissect_cyclic_wt(tvb, proto_tree_top, offset, cyc_blk_size);
1869 if ( evnt_size > 0 )
1870 offset = dissect_cntr_event(tvb, proto_tree_top, offset, evnt_size);
1871 if ( servc_size > 0 )
1872 offset = dissect_cntr_service(tvb, proto_tree_top, offset, servc_size);
1874 case FORMAT_VAR_DEVICE_TO_CONTROL:
1876 offset = dissect_devce_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
1877 if ( cyc_blk_size > 0 )
1878 offset = dissect_cyclic_rd( tvb, proto_tree_top, offset, cyc_blk_size );
1879 if ( evnt_size > 0 )
1880 offset = dissect_devce_event(tvb, proto_tree_top, offset, evnt_size);
1881 if ( servc_size > 0 )
1882 offset = dissect_devce_service(tvb, proto_tree_top, offset, servc_size);
1886 } /* End of instance for( ) loop */
1892 dissect_cipmotion_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1895 /* The total message size is 10 bytes long at a minimum, 2 bytes for the
1896 * update id and 8 bytes for the protocol header */
1897 (tvb_length(tvb) >= 10) &&
1898 /* The connection format is between 4 and 7 (fixed format message is very unlikely) */
1899 ( (tvb_get_guint8(tvb, 2) >= 4) ||
1900 (tvb_get_guint8(tvb, 2) <= 7) ) &&
1901 /* The datagram format revision is 2 */
1902 (tvb_get_guint8(tvb, 3) == 2) &&
1903 /* The node control field is a maximum of Fh */
1904 (tvb_get_guint8(tvb, 5) <= 0x0F) &&
1905 /* If all valid bits are set in the time data set the value is 0x0F at a maximum */
1906 (tvb_get_guint8(tvb, 9) <= 0x0F) )
1908 /* ...then attempt a dissection */
1909 dissect_cipmotion(tvb, pinfo, tree);
1919 * Function name: proto_register_cipmotion
1921 * Purpose: Register the protocol with Wireshark, a script will add this protocol
1922 * to the list of protocols during the build process. This function is where the
1923 * header fields and subtree identifiers are registered.
1928 proto_register_cipmotion(void)
1930 /* This is a list of header fields that can be used in the dissection or
1931 * to use in a filter expression */
1932 static hf_register_info header_fields[] =
1934 /* Connection format header field, the first byte in the message which
1935 * determines if the message is fixed or variable, controller to device,
1936 * device to controller, etc. */
1937 { &hf_cip_format, { "Connection Format", "cipm.format", FT_UINT8, BASE_DEC, VALS(cip_con_format_vals), 0, "Message connection format", HFILL }},
1939 /* Connection format revision header field */
1940 { &hf_cip_revision, { "Format Revision", "cipm.revision", FT_UINT8, BASE_DEC, NULL, 0, "Message format revision", HFILL }},
1942 { &hf_cip_class1_seqnum, { "CIP Class 1 Sequence Number", "cipm.class1seqnum", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
1943 { &hf_cip_updateid, { "Update Id", "cipm.updateid", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Transaction Number", HFILL }},
1944 { &hf_cip_instance_cnt, { "Instance Count", "cipm.instancecount", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1945 { &hf_cip_last_update, { "Last Update Id", "cipm.lastupdate", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1946 { &hf_cip_node_status, { "Node Status", "cipm.nodestatus", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1947 { &hf_cip_node_control, { "Node Control", "cipm.nodecontrol", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1948 { &hf_cip_node_control_remote, { "Remote Control", "cipm.remote", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01, "Node Control: Remote Control", HFILL}},
1949 { &hf_cip_node_control_sync, { "Sync Control", "cipm.sync", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02, "Node Control: Synchronous Operation", HFILL}},
1950 { &hf_cip_node_data_valid, { "Data Valid", "cipm.valid", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04, "Node Control: Data Valid", HFILL}},
1951 { &hf_cip_node_fault_reset, { "Fault Reset", "cipm.fltrst", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08, "Node Control: Device Fault Reset", HFILL}},
1952 { &hf_cip_node_device_faulted, { "Faulted", "cipm.flt", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08, "Node Control: Device Faulted", HFILL}},
1953 { &hf_cip_node_fltalarms, { "Node Faults and Alarms", "cipm.fltalarms", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1954 { &hf_cip_time_data_set, { "Time Data Set", "cipm.timedataset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1955 { &hf_cip_time_data_stamp, { "Time Stamp", "cipm.time.stamp", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_STAMP, "Time Data Set: Time Stamp", HFILL}},
1956 { &hf_cip_time_data_offset, { "Time Offset", "cipm.time.offset", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_OFFSET, "Time Data Set: Time Offset", HFILL}},
1957 { &hf_cip_time_data_diag, { "Time Update Diagnostics", "cipm.time.update", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_UPDATE_DIAGNOSTICS, "Time Data Set: Time Update Diagnostics", HFILL}},
1958 { &hf_cip_time_data_time_diag, { "Time Diagnostics", "cipm.time.diag", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_DIAGNOSTICS, "Time Data Set: Time Diagnostics", HFILL}},
1960 { &hf_cip_cont_time_stamp, { "Controller Time Stamp", "cipm.ctrltimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Controller Time Stamp", HFILL}},
1961 { &hf_cip_cont_time_offset, { "Controller Time Offset", "cipm.ctrltimeoffser", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Controller Time Offset", HFILL}},
1962 { &hf_cip_data_rx_time_stamp, { "Data Received Time Stamp", "cipm.rxtimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Data Received Time Stamp", HFILL}},
1963 { &hf_cip_data_tx_time_stamp, { "Data Transmit Time Stamp", "cipm.txtimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Data Transmit Time Offset", HFILL}},
1964 { &hf_cip_devc_time_stamp, { "Device Time Stamp", "cipm.devctimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Device Time Stamp", HFILL} },
1965 { &hf_cip_devc_time_offset, { "Device Time Offset", "cipm.devctimeoffser", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Device Time Offset", HFILL}},
1966 { &hf_cip_lost_update, { "Lost Updates", "cipm.lostupdates", FT_UINT8, BASE_DEC, NULL, 0, "Time Data Set: Lost Updates", HFILL}},
1967 { &hf_cip_late_update, { "Lost Updates", "cipm.lateupdates", FT_UINT8, BASE_DEC, NULL, 0, "Time Data Set: Late Updates", HFILL}},
1969 { &hf_cip_motor_cntrl, { "Control Mode", "cipm.ctrlmode", FT_UINT8, BASE_DEC, VALS(cip_motor_control_vals), 0, "Cyclic Data Block: Motor Control Mode", HFILL }},
1970 { &hf_cip_fdbk_config, { "Feedback Config", "cipm.fdbkcfg", FT_UINT8, BASE_DEC, VALS(cip_fdbk_config_vals), 0, "Cyclic Data Block: Feedback Configuration", HFILL }},
1971 { &hf_cip_axis_control, { "Axis Control", "cipm.axisctrl", FT_UINT8, BASE_DEC, VALS(cip_axis_control_vals), 0, "Cyclic Data Block: Axis Control", HFILL }},
1972 { &hf_cip_control_status, { "Control Status", "cipm.csts", FT_UINT8, BASE_DEC, VALS(cip_control_status_vals), 0, "Cyclic Data Block: Axis Control Status", HFILL }},
1973 { &hf_cip_axis_response, { "Axis Response", "cipm.axisresp", FT_UINT8, BASE_DEC, VALS(cip_axis_response_vals), 0, "Cyclic Data Block: Axis Response", HFILL }},
1974 { &hf_cip_axis_resp_stat, { "Response Status", "cipm.respstat", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Cyclic Data Block: Axis Response Status", HFILL }},
1975 { &hf_cip_group_sync, { "Group Sync Status", "cipm.syncstatus", FT_UINT8, BASE_HEX, VALS(cip_sync_status_vals), 0, NULL, HFILL }},
1976 { &hf_cip_cmd_data_set, { "Command Data Set", "cipm.cmdset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1977 { &hf_cip_act_data_set, { "Actual Data Set", "cipm.actset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1978 { &hf_cip_sts_data_set, { "Status Data Set", "cipm.stsset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1979 { &hf_cip_cmd_data_pos_cmd, { "Command Position", "cipm.cmd.pos", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION, "Command Data Set: Command Position", HFILL}},
1980 { &hf_cip_cmd_data_vel_cmd, { "Command Velocity", "cipm.cmd.vel", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_VELOCITY, "Command Data Set: Command Velocity", HFILL}},
1981 { &hf_cip_cmd_data_acc_cmd, { "Command Acceleration", "cipm.cmd.acc", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_ACCELERATION, "Command Data Set: Command Acceleration", HFILL}},
1982 { &hf_cip_cmd_data_trq_cmd, { "Command Torque", "cipm.cmd.trq", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_TORQUE, "Command Data Set: Command Torque", HFILL}},
1983 { &hf_cip_cmd_data_pos_trim_cmd, { "Position Trim", "cipm.cmd.postrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION_TRIM, "Command Data Set: Position Trim", HFILL}},
1984 { &hf_cip_cmd_data_vel_trim_cmd, { "Velocity Trim", "cipm.cmd.veltrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_VELOCITY_TRIM, "Command Data Set: Velocity Trim", HFILL}},
1985 { &hf_cip_cmd_data_acc_trim_cmd, { "Acceleration Trim", "cipm.cmd.acctrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_ACCELERATION_TRIM, "Command Data Set: Acceleration Trim", HFILL}},
1986 { &hf_cip_cmd_data_trq_trim_cmd, { "Torque Trim", "cipm.cmd.trqtrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_TORQUE_TRIM, "Command Data Set: Torque Trim", HFILL}},
1988 { &hf_cip_act_data_pos, { "Actual Position", "cipm.act.pos", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_POSITION, "Acutal Data Set: Actual Position", HFILL}},
1989 { &hf_cip_act_data_vel, { "Actual Velocity", "cipm.act.vel", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_VELOCITY, "Actual Data Set: Actual Velocity", HFILL}},
1990 { &hf_cip_act_data_acc, { "Actual Acceleration", "cipm.act.acc", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_ACCELERATION, "Actual Data Set: Actual Acceleration", HFILL}},
1991 { &hf_cip_act_data_trq, { "Actual Torque", "cipm.act.trq", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_TORQUE, "Actual Data Set: Actual Torque", HFILL}},
1992 { &hf_cip_act_data_crnt, { "Actual Current", "cipm.act.crnt", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_CURRENT, "Actual Data Set: Actual Current", HFILL}},
1993 { &hf_cip_act_data_vltg, { "Actual Voltage", "cipm.act.vltg", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_VOLTAGE, "Actual Data Set: Actual Voltage", HFILL}},
1994 { &hf_cip_act_data_fqcy, { "Actual Frequency", "cipm.act.fqcy", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_FREQUENCY, "Actual Data Set: Actual Frequency", HFILL}},
1996 { &hf_cip_axis_fault, { "Axis Fault Code", "cipm.fault.code", FT_UINT8, BASE_DEC, NULL, 0, "Status Data Set: Fault Code", HFILL }},
1997 { &hf_cip_fault_type, { "Axis Fault Type", "cipm.flttype", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Type", HFILL}},
1998 { &hf_cip_fault_sub_code, { "Axis Fault Sub Code", "cipm.fltsubcode", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Sub Code", HFILL}},
1999 { &hf_cip_fault_action, { "Axis Fault Action", "cipm.fltaction", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Action", HFILL}},
2000 { &hf_cip_fault_time_stamp, { "Axis Fault Time Stamp", "cipm.flttimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Time Stamp", HFILL}},
2001 { &hf_cip_alarm_type, { "Axis Fault Type", "cipm.alarmtype", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Type", HFILL}},
2002 { &hf_cip_alarm_sub_code, { "Axis Alarm Sub Code", "cipm.alarmsubcode", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Sub Code", HFILL} },
2003 { &hf_cip_alarm_state, { "Axis Alarm State", "cipm.alarmstate", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Alarm State", HFILL }},
2004 { &hf_cip_alarm_time_stamp, { "Axis Fault Time Stamp", "cipm.alarmtimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Time Stamp", HFILL}},
2005 { &hf_cip_axis_status, { "Axis Status", "cipm.axisstatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2006 { &hf_cip_axis_status_mfg, { "Axis Status Mfg", "cipm.axisstatusmfg", FT_UINT32, BASE_HEX, NULL, 0, "Axis Status, Manufacturer Specific", HFILL}},
2007 { &hf_cip_axis_io_status, { "Axis I/O Status", "cipm.axisiostatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2008 { &hf_cip_axis_io_status_mfg, { "Axis I/O Status Mfg", "cipm.axisiostatusmfg", FT_UINT32, BASE_HEX, NULL, 0, "Axis I/O Status, Manufacturer Specific", HFILL}},
2009 { &hf_cip_safety_status, { "Axis Safety Status", "cipm.safetystatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2010 { &hf_cip_sts_flt, { "Axis Fault Codes", "cipm.sts.flt", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_FAULT, "Status Data Set: Axis Fault Codes", HFILL}},
2011 { &hf_cip_sts_alrm, { "Axis Alarm Codes", "cipm.sts.alarm", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_ALARM, "Status Data Set: Axis Alarm Codes", HFILL}},
2012 { &hf_cip_sts_sts, { "Axis Status", "cipm.sts.sts", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_STATUS, "Status Data Set: Axis Status", HFILL}},
2013 { &hf_cip_sts_iosts, { "Axis I/O Status", "cipm.sts.iosts", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_IO_STATUS, "Status Data Set: Axis I/O Status", HFILL}},
2014 { &hf_cip_sts_safety, { "Axis Safety Status", "cipm.sts.safety", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_SAFETY, "Status Data Set: Axis Safety Status", HFILL}},
2016 { &hf_cip_intrp, { "Interpolation Control", "cipm.intrp", FT_UINT8, BASE_DEC, VALS(cip_interpolation_vals), COMMAND_CONTROL_TARGET_UPDATE, "Cyclic Data Block: Interpolation Control", HFILL}},
2017 { &hf_cip_position_data_type, { "Position Data Type", "cipm.posdatatype", FT_UINT8, BASE_DEC, VALS(cip_pos_data_type_vals), COMMAND_CONTROL_POSITION_DATA_TYPE, "Cyclic Data Block: Position Data Type", HFILL }},
2018 { &hf_cip_axis_state, { "Axis State", "cipm.axste", FT_UINT8, BASE_DEC, VALS(cip_axis_state_vals), 0, "Cyclic Data Block: Axis State", HFILL}},
2019 { &hf_cip_command_control, { "Command Control", "cipm.cmdcontrol", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Command Control", HFILL }},
2020 { &hf_cip_cyclic_wrt_data, { "Write Data", "cipm.writedata", FT_BYTES, BASE_NONE, NULL, 0, "Cyclic Write: Data", HFILL }},
2021 { &hf_cip_cyclic_rd_data, { "Read Data", "cipm.readdata", FT_BYTES, BASE_NONE, NULL, 0, "Cyclic Read: Data", HFILL }},
2022 { &hf_cip_cyclic_write_blk, { "Write Block", "cipm.writeblk", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Write Block Id", HFILL }},
2023 { &hf_cip_cyclic_read_blk, { "Read Block", "cipm.readblk", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Read Block Id", HFILL}},
2024 { &hf_cip_cyclic_write_sts, { "Write Status", "cipm.writests", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Write Status", HFILL }},
2025 { &hf_cip_cyclic_read_sts, { "Read Status", "cipm.readsts", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Read Status", HFILL }},
2026 { &hf_cip_event_checking, { "Event Control", "cipm.evntchkcontrol", FT_UINT32, BASE_HEX, NULL, 0, "Event Channel: Event Checking Control", HFILL}},
2027 { &hf_cip_event_ack, { "Event Acknowledgement", "cipm.evntack", FT_UINT8, BASE_DEC, NULL, 0, "Event Channel: Event Acknowledgement", HFILL} },
2028 { &hf_cip_event_status, { "Event Status", "cipm.evntchkstatus", FT_UINT32, BASE_HEX, NULL, 0, "Event Channel: Event Checking Status", HFILL} },
2029 { &hf_cip_event_id, { "Event Id", "cipm.evntack", FT_UINT8, BASE_DEC, NULL, 0, "Event Channel: Event Id", HFILL }},
2030 { &hf_cip_event_pos, { "Event Position", "cipm.evntpos", FT_INT32, BASE_DEC, NULL, 0, "Event Channel: Event Position", HFILL} },
2031 { &hf_cip_event_ts, { "Event Time Stamp", "cipm.evntimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Event Channel: Time Stamp", HFILL}},
2033 { &hf_cip_evnt_ctrl_reg1_pos, { "Reg 1 Pos Edge", "cipm.evnt.ctrl.reg1posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Event Checking Control: Reg 1 Pos Edge", HFILL}},
2034 { &hf_cip_evnt_ctrl_reg1_neg, { "Reg 1 Neg Edge", "cipm.evnt.ctrl.reg1negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Event Checking Control: Reg 1 Neg Edge", HFILL}},
2035 { &hf_cip_evnt_ctrl_reg2_pos, { "Reg 2 Pos Edge", "cipm.evnt.ctrl.reg2posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Event Checking Control: Reg 2 Pos Edge", HFILL}},
2036 { &hf_cip_evnt_ctrl_reg2_neg, { "Reg 2 Neg Edge", "cipm.evnt.ctrl.reg2negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Event Checking Control: Reg 2 Neg Edge", HFILL}},
2037 { &hf_cip_evnt_ctrl_reg1_posrearm, { "Reg 1 Pos Rearm", "cipm.evnt.ctrl.reg1posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Event Checking Control: Reg 1 Pos Rearm", HFILL}},
2038 { &hf_cip_evnt_ctrl_reg1_negrearm, { "Reg 1 Neg Rearm", "cipm.evnt.ctrl.reg1negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Event Checking Control: Reg 1 Neg Rearm", HFILL}},
2039 { &hf_cip_evnt_ctrl_reg2_posrearm, { "Reg 2 Pos Rearm", "cipm.evnt.ctrl.reg2posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Event Checking Control: Reg 2 Pos Rearm", HFILL}},
2040 { &hf_cip_evnt_ctrl_reg2_negrearm, { "Reg 2 Neg Rearm", "cipm.evnt.ctrl.reg2negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Event Checking Control: Reg 2 Neg Rearm", HFILL}},
2041 { &hf_cip_evnt_ctrl_marker_pos, { "Marker Pos Edge", "cipm.evnt.ctrl.mrkrpos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Event Checking Control: Marker Pos Edge", HFILL}},
2042 { &hf_cip_evnt_ctrl_marker_neg, { "Marker Neg Edge", "cipm.evnt.ctrl.mrkrneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Event Checking Control: Marker Neg Edge", HFILL}},
2043 { &hf_cip_evnt_ctrl_home_pos, { "Home Pos Edge", "cipm.evnt.ctrl.homepos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Event Checking Control: Home Pos Edge", HFILL}},
2044 { &hf_cip_evnt_ctrl_home_neg, { "Home Neg Edge", "cipm.evnt.ctrl.homeneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Event Checking Control: Home Neg Edge", HFILL}},
2045 { &hf_cip_evnt_ctrl_home_pp, { "Home-Switch-Marker Plus Plus", "cipm.evnt.ctrl.homepp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000, "Event Checking Control: Home-Switch-Marker Plus Plus", HFILL}},
2046 { &hf_cip_evnt_ctrl_home_pm, { "Home-Switch-Marker Plus Minus", "cipm.evnt.ctrl.homepm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000, "Event Checking Control: Home-Switch-Marker Plus Minus", HFILL}},
2047 { &hf_cip_evnt_ctrl_home_mp,{ "Home-Switch-Marker Minus Plus", "cipm.evnt.ctrl.homemp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000, "Event Checking Control: Home-Switch-Marker Minus Plus", HFILL}},
2048 { &hf_cip_evnt_ctrl_home_mm, { "Home-Switch-Marker Minus Minus", "cipm.evnt.ctrl.homemm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000, "Event Checking Control: Home-Switch-Marker Minus Minus", HFILL}},
2049 { &hf_cip_evnt_ctrl_acks, { "Event Acknowledge Blocks", "cipm.evnt.ctrl.acks", FT_UINT32, BASE_DEC, NULL, 0x70000000, "Event Checking Control: Event Acknowledge Blocks", HFILL}},
2050 { &hf_cip_evnt_extend_format, { "Extended Event Format", "cipm.evnt.extend", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x80000000, "Event Checking Control: Extended Event Format", HFILL}},
2052 { &hf_cip_evnt_sts_reg1_pos,{ "Reg 1 Pos Edge", "cipm.evnt.sts.reg1posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Event Checking Status: Reg 1 Pos Edge", HFILL}},
2053 { &hf_cip_evnt_sts_reg1_neg, { "Reg 1 Neg Edge", "cipm.evnt.sts.reg1negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Event Checking Status: Reg 1 Neg Edge", HFILL }},
2054 { &hf_cip_evnt_sts_reg2_pos, { "Reg 2 Pos Edge", "cipm.evnt.sts.reg2posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Event Checking Status: Reg 2 Pos Edge", HFILL}},
2055 { &hf_cip_evnt_sts_reg2_neg, { "Reg 2 Neg Edge", "cipm.evnt.sts.reg2negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Event Checking Status: Reg 2 Neg Edge", HFILL}},
2056 { &hf_cip_evnt_sts_reg1_posrearm, { "Reg 1 Pos Rearm", "cipm.evnt.sts.reg1posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Event Checking Status: Reg 1 Pos Rearm", HFILL}},
2057 { &hf_cip_evnt_sts_reg1_negrearm, { "Reg 1 Neg Rearm", "cipm.evnt.sts.reg1negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Event Checking Status: Reg 1 Neg Rearm", HFILL}},
2058 { &hf_cip_evnt_sts_reg2_posrearm, { "Reg 2 Pos Rearm", "cipm.evnt.sts.reg2posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Event Checking Status: Reg 2 Pos Rearm", HFILL}},
2059 { &hf_cip_evnt_sts_reg2_negrearm, { "Reg 2 Neg Rearm", "cipm.evnt.sts.reg2negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Event Checking Status: Reg 2 Neg Rearm", HFILL}},
2060 { &hf_cip_evnt_sts_marker_pos, { "Marker Pos Edge", "cipm.evnt.sts.mrkrpos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Event Checking Status: Marker Pos Edge", HFILL}},
2061 { &hf_cip_evnt_sts_marker_neg, { "Marker Neg Edge", "cipm.evnt.sts.mrkrneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Event Checking Status: Marker Neg Edge", HFILL }},
2062 { &hf_cip_evnt_sts_home_pos, { "Home Pos Edge", "cipm.evnt.sts.homepos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Event Checking Status: Home Pos Edge", HFILL}},
2063 { &hf_cip_evnt_sts_home_neg, { "Home Neg Edge", "cipm.evnt.sts.homeneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Event Checking Status: Home Neg Edge", HFILL }},
2064 { &hf_cip_evnt_sts_home_pp, { "Home-Switch-Marker Plus Plus", "cipm.evnt.sts.homepp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000, "Event Checking Status: Home-Switch-Marker Plus Plus", HFILL}},
2065 { &hf_cip_evnt_sts_home_pm, { "Home-Switch-Marker Plus Minus", "cipm.evnt.sts.homepm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000, "Event Checking Status: Home-Switch-Marker Plus Minus", HFILL}},
2066 { &hf_cip_evnt_sts_home_mp, { "Home-Switch-Marker Minus Plus", "cipm.evnt.sts.homemp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000, "Event Checking Status: Home-Switch-Marker Minus Plus", HFILL}},
2067 { &hf_cip_evnt_sts_home_mm, { "Home-Switch-Marker Minus Minus", "cipm.evnt.sts.homemm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000, "Event Checking Status: Home-Switch-Marker Minus Minus", HFILL}},
2068 { &hf_cip_evnt_sts_nfs, { "Event Notification Blocks", "cipm.evnt.sts.nfs", FT_UINT32, BASE_DEC, NULL, 0x70000000, "Event Checking Status: Event Notification Blocks", HFILL}},
2070 { &hf_cip_evnt_sts_stat, { "Event Status", "cipm.evnt.stat", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Event Data Block: Event Status", HFILL }},
2071 { &hf_cip_evnt_type, { "Event Type", "cipm.evnt.type", FT_UINT8, BASE_DEC, VALS(cip_event_type_vals), 0, "Event Data Block: Event Type", HFILL}},
2072 { &hf_cip_svc_code, { "Service Code", "cipm.svc.code", FT_UINT8, BASE_DEC, VALS(cip_sc_vals), 0, "Service Data Block: Service Code", HFILL}},
2073 { &hf_cip_svc_sts, { "General Status", "cipm.svc.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Data Block: General Status", HFILL }},
2074 { &hf_cip_svc_transction, { "Transaction Id", "cipm.svc.tranid", FT_UINT8, BASE_DEC, NULL, 0, "Service Data Block: Transaction Id", HFILL }},
2075 { &hf_cip_svc_ext_status, { "Extended Status", "cipm.svc.extstatus", FT_UINT8, BASE_DEC, NULL, 0, "Service Data Block: Extended Status", HFILL }},
2076 { &hf_cip_svc_data, { "Service Data", "cipm.svc.data", FT_BYTES, BASE_NONE, NULL, 0, "Service Data Block: Data", HFILL }},
2077 { &hf_cip_attribute_data, { "Attribute Data", "cipm.attrdata", FT_BYTES, BASE_NONE, NULL, 0, "Attribute Service: Data", HFILL }},
2078 { &hf_cip_ptp_grandmaster, { "Grandmaster", "cipm.grandmaster", FT_UINT64, BASE_HEX, NULL, 0, "Group Sync: Grandmaster Id", HFILL}},
2080 { &hf_cip_svc_get_axis_attr_sts, { "Attribute Status", "cipm.getaxisattr.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Channel: Get Axis Attribute List Response Status", HFILL }},
2081 { &hf_get_axis_attr_list_attribute_cnt, { "Number of attributes", "cipm.getaxisattr.cnt", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Attribute Count", HFILL}},
2082 { &hf_get_axis_attr_list_attribute_id, { "Attribute ID", "cipm.getaxisattr.id", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Attribute ID", HFILL}},
2083 { &hf_get_axis_attr_list_dimension, { "Dimension", "cipm.getaxisattr.dimension", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Dimension", HFILL}},
2084 { &hf_get_axis_attr_list_element_size, { "Element size", "cipm.getaxisattr.element_size", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Element Size", HFILL}},
2085 { &hf_get_axis_attr_list_start_index, { "Start index", "cipm.getaxisattr.start_index", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Start index", HFILL}},
2086 { &hf_get_axis_attr_list_data_elements, { "Data elements", "cipm.getaxisattr.data_elements", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Data elements", HFILL}},
2088 { &hf_cip_svc_set_axis_attr_sts, { "Attribute Status", "cipm.setaxisattr.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Channel: Set Axis Attribute List Response Status", HFILL }},
2089 { &hf_set_axis_attr_list_attribute_cnt, { "Number of attributes", "cipm.setaxisattr.cnt", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Attribute Count", HFILL}},
2090 { &hf_set_axis_attr_list_attribute_id, { "Attribute ID", "cipm.setaxisattr.id", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Attribute ID", HFILL}},
2091 { &hf_set_axis_attr_list_dimension, { "Dimension", "cipm.setaxisattr.dimension", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Dimension", HFILL}},
2092 { &hf_set_axis_attr_list_element_size, { "Element size", "cipm.setaxisattr.element_size", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Element Size", HFILL}},
2093 { &hf_set_axis_attr_list_start_index, { "Start index", "cipm.setaxisattr.start_index", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Start index", HFILL}},
2094 { &hf_set_axis_attr_list_data_elements, { "Data elements", "cipm.setaxisattr.data_elements", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Data elements", HFILL}},
2096 { &hf_var_devce_instance, { "Instance Number", "cipm.var_devce.header.instance", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Instance Number", HFILL}},
2097 { &hf_var_devce_instance_block_size, { "Instance Block Size", "cipm.var_devce.header.instance_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Instance Block Size", HFILL}},
2098 { &hf_var_devce_cyclic_block_size, { "Cyclic Block Size", "cipm.var_devce.header.cyclic_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Block Size", HFILL}},
2099 { &hf_var_devce_cyclic_data_block_size, { "Cyclic Data Block Size", "cipm.var_devce.header.cyclic_data_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Data Block Size", HFILL}},
2100 { &hf_var_devce_cyclic_rw_block_size, { "Cyclic Read/Write Block Size", "cipm.var_devce.header.cyclic_rw_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Read/Write Block Size", HFILL}},
2101 { &hf_var_devce_event_block_size, { "Event Block Size", "cipm.var_devce.header.event_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Event Block Size", HFILL}},
2102 { &hf_var_devce_service_block_size, { "Service Block Size", "cipm.var_devce.header.service_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Service Block Size", HFILL}},
2104 { &hf_cip_axis_alarm, { "Axis Alarm Code", "cipm.alarm.code", FT_UINT8, BASE_DEC, NULL, 0, "Status Data Set: Alarm Code", HFILL }},
2105 { &hf_cip_axis_sts_local_ctrl, { "Local Control", "cipm.axis.local", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Axis Status Data Set: Local Contol", HFILL }},
2106 { &hf_cip_axis_sts_alarm, { "Alarm", "cipm.axis.alarm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Axis Status Data Set: Alarm", HFILL }},
2107 { &hf_cip_axis_sts_dc_bus, { "DC Bus", "cipm.axis.bus", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Axis Status Data Set: DC Bus", HFILL }},
2108 { &hf_cip_axis_sts_pwr_struct, { "Power Struct", "cipm.axis.pwr", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Axis Status Data Set: Power Struct", HFILL }},
2109 { &hf_cip_axis_sts_tracking, { "Tracking", "cipm.axis.track", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000020, "Axis Status Data Set: Tracking", HFILL }},
2110 { &hf_cip_axis_sts_pos_lock, { "Pos Lock", "cipm.axis.poslock", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000040, "Axis Status Data Set: Pos Lock", HFILL }},
2111 { &hf_cip_axis_sts_vel_lock, { "Vel Lock", "cipm.axis.vellock", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000080, "Axis Status Data Set: Vel Lock", HFILL }},
2112 { &hf_cip_axis_sts_vel_standstill, { "Standstill", "cipm.axis.nomo", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Axis Status Data Set: Standstill", HFILL }},
2113 { &hf_cip_axis_sts_vel_threshold, { "Vel Threshold", "cipm.axis.vthresh", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Axis Status Data Set: Vel Threshold", HFILL }},
2114 { &hf_cip_axis_sts_vel_limit, { "Vel Limit", "cipm.axis.vlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Axis Status Data Set: Vel Limit", HFILL }},
2115 { &hf_cip_axis_sts_acc_limit, { "Acc Limit", "cipm.axis.alim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Axis Status Data Set: Acc Limit", HFILL }},
2116 { &hf_cip_axis_sts_dec_limit, { "Dec Limit", "cipm.axis.dlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00001000, "Axis Status Data Set: Dec Limit", HFILL }},
2117 { &hf_cip_axis_sts_torque_threshold, { "Torque Threshold", "cipm.axis.tthresh", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00002000, "Axis Status Data Set: Torque Threshold", HFILL }},
2118 { &hf_cip_axis_sts_torque_limit, { "Torque Limit", "cipm.axis.tlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00004000, "Axis Status Data Set: Torque Limit", HFILL }},
2119 { &hf_cip_axis_sts_cur_limit, { "Current Limit", "cipm.axis.ilim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00008000, "Axis Status Data Set: Current Limit", HFILL }},
2120 { &hf_cip_axis_sts_therm_limit, { "Thermal Limit", "cipm.axis.hot", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Axis Status Data Set: Thermal Limit", HFILL }},
2121 { &hf_cip_axis_sts_feedback_integ, { "Feedback Integrity", "cipm.axis.fgood", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Axis Status Data Set: Feedback Integrity", HFILL }},
2122 { &hf_cip_axis_sts_shutdown, { "Shutdown", "cipm.axis.sdwn", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Axis Status Data Set: Shutdown", HFILL }},
2123 { &hf_cip_axis_sts_in_process, { "In Process", "cipm.axis.inp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Axis Status Data Set: In Process", HFILL }},
2125 { &hf_cip_act_pos, { "Actual Position", "cipm.actpos", FT_INT32, BASE_DEC, NULL, 0, "Cyclic Data Set: Actual Position", HFILL }},
2126 { &hf_cip_act_vel, { "Actual Velocity", "cipm.actvel", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Velocity", HFILL }},
2127 { &hf_cip_act_accel, { "Actual Acceleration", "cipm.actaccel", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Acceleration", HFILL }},
2128 { &hf_cip_act_trq, { "Actual Torque", "cipm.acttrq", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Torque", HFILL }},
2129 { &hf_cip_act_crnt, { "Actual Current", "cipm.actcrnt", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Current", HFILL }},
2130 { &hf_cip_act_volts, { "Actual Volts", "cipm.actvolts", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Volts", HFILL }},
2131 { &hf_cip_act_freq, { "Actual Frequency", "cipm.actfreq", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Frequency", HFILL }},
2132 { &hf_cip_pos_cmd, { "Position Command", "cipm.posfcmd", FT_DOUBLE, BASE_NONE, NULL, 0, "Cyclic Data Set: Position Command (LREAL)", HFILL }},
2133 { &hf_cip_pos_cmd_int, { "Position Command", "cipm.posicmd", FT_INT32, BASE_DEC, NULL, 0, "Cyclic Data Set: Position Command (DINT)", HFILL }},
2134 { &hf_cip_vel_cmd, { "Velocity Command", "cipm.velcmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Velocity Command", HFILL }},
2135 { &hf_cip_accel_cmd, { "Acceleration Command", "cipm.accelcmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Acceleration Command", HFILL }},
2136 { &hf_cip_trq_cmd, { "Torque Command", "cipm.torquecmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Torque Command", HFILL }},
2137 { &hf_cip_pos_trim, { "Position Trim", "cipm.postrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Position Trim", HFILL }},
2138 { &hf_cip_vel_trim, { "Velocity Trim", "cipm.veltrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Velocity Trim", HFILL }},
2139 { &hf_cip_accel_trim, { "Acceleration Trim", "cipm.acceltrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Acceleration Trim", HFILL }},
2140 { &hf_cip_trq_trim, { "Torque Trim", "cipm.trqtrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Torque Trim", HFILL }}
2143 /* Setup protocol subtree array, these will help Wireshark remember
2144 * if the subtree should be expanded as the user moves through packets */
2145 static gint *cip_subtree[] = {
2147 &ett_cont_dev_header,
2151 &ett_inst_data_header,
2152 &ett_cyclic_data_block,
2154 &ett_feedback_config,
2155 &ett_command_data_set,
2156 &ett_actual_data_set,
2157 &ett_status_data_set,
2158 &ett_interp_control,
2161 &ett_event_check_ctrl,
2162 &ett_event_check_sts,
2164 &ett_get_axis_attribute,
2165 &ett_set_axis_attribute,
2166 &ett_get_axis_attr_list,
2167 &ett_set_axis_attr_list,
2169 &ett_axis_status_set,
2170 &ett_command_control
2173 /* Create a CIP Motion protocol handle */
2174 proto_cipmotion = proto_register_protocol(
2175 "Common Industrial Protocol, Motion", /* Full name of protocol */
2176 "CIP Motion", /* Short name of protocol */
2177 "cipm"); /* Abbreviated name of protocol */
2179 /* Register the header fields with the protocol */
2180 proto_register_field_array(proto_cipmotion, header_fields, array_length(header_fields));
2182 /* Register the subtrees for the protocol dissection */
2183 proto_register_subtree_array(cip_subtree, array_length(cip_subtree));
2187 * Function name: proto_reg_handoff_cipmotion
2189 * Purpose: This function will setup the automatic dissection of the CIP Motion datagram,
2190 * it is called by Wireshark when the protocol is registered
2195 proto_reg_handoff_cipmotion(void)
2197 heur_dissector_add("enip.cpf.conndata", dissect_cipmotion_heur, proto_cipmotion);
2202 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2207 * indent-tabs-mode: nil
2210 * ex: set shiftwidth=3 tabstop=8 expandtab:
2211 * :indentSize=3:tabSize=8:noTabs=true: