1 /* packet-cipmotion.c
2 * Routines for CIP (Common Industrial Protocol) Motion dissection
3 * CIP Motion Home: www.odva.org
4 *
5 * This dissector includes items from:
6 * CIP Volume 9: CIP Motion, Edition 1.7
7 *
8 * Copyright 2006-2007
9 * Benjamin M. Stocks <bmstocks@ra.rockwell.com>
10 *
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
14 *
15 * SPDX-License-Identifier: GPL-2.0-or-later
16 */
17
18 #include "config.h"
19
20 #include <epan/packet.h>
21 #include <epan/expert.h>
22
23 #include "packet-cipmotion.h"
24
25 #include "packet-cip.h"
26 #include "packet-enip.h"
27
28 void proto_register_cipmotion(void);
29 /* The entry point to the actual dissection is: dissect_cipmotion */
30 void proto_reg_handoff_cipmotion(void);
31
32 /* Protocol handle for CIP Motion */
33 static int proto_cipmotion = -1;
34 static int proto_cipmotion3 = -1;
35
36 /* Header field identifiers, these are registered in the
37 * proto_register_cipmotion function along with the bites/bytes
38 * they represent */
39 static int hf_cip_format = -1;
40 static int hf_cip_revision = -1;
41 static int hf_cip_class1_seqnum = -1;
42 static int hf_configuration_block_format_rev = -1;
43 static int hf_configuration_block_drive_power_struct_id = -1;
44 static int hf_cip_updateid = -1;
45 static int hf_cip_instance_cnt = -1;
46 static int hf_cip_last_update = -1;
47 static int hf_cip_node_status = -1;
48 static int hf_cip_node_control = -1;
49 static int hf_cip_node_control_remote = -1;
50 static int hf_cip_node_control_sync = -1;
51 static int hf_cip_node_data_valid = -1;
52 static int hf_cip_node_fault_reset = -1;
53 static int hf_cip_node_device_faulted = -1;
54 static int hf_cip_time_data_set = -1;
55 static int hf_cip_time_data_stamp = -1;
56 static int hf_cip_time_data_offset = -1;
57 static int hf_cip_time_data_diag = -1;
58 static int hf_cip_time_data_time_diag = -1;
59 static int hf_cip_cont_time_stamp = -1;
60 static int hf_cip_cont_time_offset = -1;
61 static int hf_cip_devc_time_stamp = -1;
62 static int hf_cip_devc_time_offset = -1;
63 static int hf_cip_lost_update = -1;
64 static int hf_cip_late_update = -1;
65 static int hf_cip_data_rx_time_stamp = -1;
66 static int hf_cip_data_tx_time_stamp = -1;
67 static int hf_cip_node_fltalarms = -1;
68 static int hf_cip_motor_cntrl = -1;
69 static int hf_cip_feedback = -1;
70 static int hf_cip_feedback_mode = -1;
71 static int hf_cip_feedback_data_type = -1;
72
73 static int hf_connection_configuration_bits = -1;
74 static int hf_connection_configuration_bits_power = -1;
75 static int hf_connection_configuration_bits_safety_bit_valid = -1;
76 static int hf_connection_configuration_bits_allow_network_safety = -1;
77
78 static int hf_cip_axis_control = -1;
79 static int hf_cip_control_status = -1;
80 static int hf_cip_control_status_complete = -1;
81 static int hf_cip_control_status_bus_up = -1;
82 static int hf_cip_control_status_bus_unload = -1;
83 static int hf_cip_control_status_power_loss = -1;
84 static int hf_cip_axis_response = -1;
85 static int hf_cip_axis_resp_stat = -1;
86 static int hf_cip_cmd_data_pos_cmd = -1;
87 static int hf_cip_cmd_data_vel_cmd = -1;
88 static int hf_cip_cmd_data_acc_cmd = -1;
89 static int hf_cip_cmd_data_trq_cmd = -1;
90 static int hf_cip_cmd_data_unwind_cycle_count = -1;
91 static int hf_cip_cmd_data_pos_displacement = -1;
92 static int hf_cip_act_data_pos = -1;
93 static int hf_cip_act_data_vel = -1;
94 static int hf_cip_act_data_acc = -1;
95 static int hf_cip_act_unwind_cycle_count = -1;
96 static int hf_cip_act_pos_displacement = -1;
97 static int hf_cip_sts_flt = -1;
98 static int hf_cip_sts_alrm = -1;
99 static int hf_cip_sts_sts = -1;
100 static int hf_cip_sts_iosts = -1;
101 static int hf_cip_sts_axis_safety = -1;
102 static int hf_cip_intrp = -1;
103 static int hf_cip_position_data_type = -1;
104 static int hf_cip_axis_state = -1;
105 static int hf_cip_evnt_ctrl_reg1_pos = -1;
106 static int hf_cip_evnt_ctrl_reg1_neg = -1;
107 static int hf_cip_evnt_ctrl_reg2_pos = -1;
108 static int hf_cip_evnt_ctrl_reg2_neg = -1;
109 static int hf_cip_evnt_ctrl_reg1_posrearm = -1;
110 static int hf_cip_evnt_ctrl_reg1_negrearm = -1;
111 static int hf_cip_evnt_ctrl_reg2_posrearm = -1;
112 static int hf_cip_evnt_ctrl_reg2_negrearm = -1;
113 static int hf_cip_evnt_ctrl_marker_pos = -1;
114 static int hf_cip_evnt_ctrl_marker_neg = -1;
115 static int hf_cip_evnt_ctrl_home_pos = -1;
116 static int hf_cip_evnt_ctrl_home_neg = -1;
117 static int hf_cip_evnt_ctrl_home_pp = -1;
118 static int hf_cip_evnt_ctrl_home_pm = -1;
119 static int hf_cip_evnt_ctrl_home_mp = -1;
120 static int hf_cip_evnt_ctrl_home_mm = -1;
121 static int hf_cip_evnt_ctrl_acks = -1;
122 static int hf_cip_evnt_extend_format = -1;
123 static int hf_cip_evnt_sts_reg1_pos = -1;
124 static int hf_cip_evnt_sts_reg1_neg = -1;
125 static int hf_cip_evnt_sts_reg2_pos = -1;
126 static int hf_cip_evnt_sts_reg2_neg = -1;
127 static int hf_cip_evnt_sts_reg1_posrearm = -1;
128 static int hf_cip_evnt_sts_reg1_negrearm = -1;
129 static int hf_cip_evnt_sts_reg2_posrearm = -1;
130 static int hf_cip_evnt_sts_reg2_negrearm = -1;
131 static int hf_cip_evnt_sts_marker_pos = -1;
132 static int hf_cip_evnt_sts_marker_neg = -1;
133 static int hf_cip_evnt_sts_home_pos = -1;
134 static int hf_cip_evnt_sts_home_neg = -1;
135 static int hf_cip_evnt_sts_home_pp = -1;
136 static int hf_cip_evnt_sts_home_pm = -1;
137 static int hf_cip_evnt_sts_home_mp = -1;
138 static int hf_cip_evnt_sts_home_mm = -1;
139 static int hf_cip_evnt_sts_nfs = -1;
140 static int hf_cip_evnt_sts_stat = -1;
141 static int hf_cip_evnt_type = -1;
142 static int hf_cip_svc_code = -1;
143 static int hf_cip_svc_sts = -1;
144 static int hf_cip_svc_set_axis_attr_sts = -1;
145 static int hf_cip_svc_get_axis_attr_sts = -1;
146 static int hf_cip_svc_transction = -1;
147 static int hf_cip_svc_ext_status = -1;
148 static int hf_cip_svc_data = -1;
149 static int hf_cip_ptp_grandmaster = -1;
150 static int hf_cip_axis_alarm = -1;
151 static int hf_cip_axis_fault = -1;
152 static int hf_cip_axis_sts_local_ctrl = -1;
153 static int hf_cip_axis_sts_alarm = -1;
154 static int hf_cip_axis_sts_dc_bus = -1;
155 static int hf_cip_axis_sts_pwr_struct = -1;
156 static int hf_cip_axis_sts_flux_up = -1;
157 static int hf_cip_axis_sts_tracking = -1;
158 static int hf_cip_axis_sts_pos_lock = -1;
159 static int hf_cip_axis_sts_vel_lock = -1;
160 static int hf_cip_axis_sts_vel_standstill = -1;
161 static int hf_cip_axis_sts_vel_threshold = -1;
162 static int hf_cip_axis_sts_vel_limit = -1;
163 static int hf_cip_axis_sts_acc_limit = -1;
164 static int hf_cip_axis_sts_dec_limit = -1;
165 static int hf_cip_axis_sts_torque_threshold = -1;
166 static int hf_cip_axis_sts_torque_limit = -1;
167 static int hf_cip_axis_sts_cur_limit = -1;
168 static int hf_cip_axis_sts_therm_limit = -1;
169 static int hf_cip_axis_sts_feedback_integ = -1;
170 static int hf_cip_axis_sts_shutdown = -1;
171 static int hf_cip_axis_sts_in_process = -1;
172 static int hf_cip_axis_sts_dc_bus_unload = -1;
173 static int hf_cip_axis_sts_ac_pwr_loss = -1;
174 static int hf_cip_axis_sts_pos_cntrl_mode = -1;
175 static int hf_cip_axis_sts_vel_cntrl_mode = -1;
176 static int hf_cip_axis_sts_trq_cntrl_mode = -1;
177
178 static int hf_cip_axis_status2 = -1;
179 static int hf_cip_axis_sts2_motor = -1;
180 static int hf_cip_axis_sts2_regenerate = -1;
181 static int hf_cip_axis_sts2_ride_thru = -1;
182 static int hf_cip_axis_sts2_ac_line_sync = -1;
183 static int hf_cip_axis_sts2_bus_volt_lock = -1;
184 static int hf_cip_axis_sts2_react_pwr_only = -1;
185 static int hf_cip_axis_sts2_volt_ctrl_mode = -1;
186 static int hf_cip_axis_sts2_pwr_loss = -1;
187 static int hf_cip_axis_sts2_ac_volt_sag = -1;
188 static int hf_cip_axis_sts2_ac_phase_loss = -1;
189 static int hf_cip_axis_sts2_ac_freq_change = -1;
190 static int hf_cip_axis_sts2_ac_sync_loss = -1;
191 static int hf_cip_axis_sts2_single_phase = -1;
192 static int hf_cip_axis_sts2_bus_volt_limit = -1;
193 static int hf_cip_axis_sts2_bus_volt_rate_limit = -1;
194 static int hf_cip_axis_sts2_active_current_rate_limit = -1;
195 static int hf_cip_axis_sts2_reactive_current_rate_limit = -1;
196 static int hf_cip_axis_sts2_reactive_pwr_limit = -1;
197 static int hf_cip_axis_sts2_reactive_pwr_rate_limit = -1;
198 static int hf_cip_axis_sts2_active_current_limit = -1;
199 static int hf_cip_axis_sts2_reactive_current_limit = -1;
200 static int hf_cip_axis_sts2_motor_pwr_limit = -1;
201 static int hf_cip_axis_sts2_regen_pwr_limit = -1;
202 static int hf_cip_axis_sts2_convert_therm_limit = -1;
203
204 static int hf_cip_cyclic_wrt_data = -1;
205 static int hf_cip_cyclic_rd_data = -1;
206 static int hf_cip_cyclic_write_blk = -1;
207 static int hf_cip_cyclic_read_blk = -1;
208 static int hf_cip_cyclic_write_sts = -1;
209 static int hf_cip_cyclic_read_sts = -1;
210 static int hf_cip_attribute_data = -1;
211 static int hf_cip_event_checking = -1;
212 static int hf_cip_event_ack = -1;
213 static int hf_cip_event_status = -1;
214 static int hf_cip_event_id = -1;
215 static int hf_cip_event_pos = -1;
216 static int hf_cip_event_ts = -1;
217 static int hf_cip_pos_cmd = -1;
218 static int hf_cip_pos_cmd_int = -1;
219 static int hf_cip_vel_cmd = -1;
220 static int hf_cip_accel_cmd = -1;
221 static int hf_cip_trq_cmd = -1;
222 static int hf_cip_pos_trim = -1;
223 static int hf_cip_vel_trim = -1;
224 static int hf_cip_accel_trim = -1;
225 static int hf_cip_trq_trim = -1;
226 static int hf_cip_act_pos = -1;
227 static int hf_cip_act_pos_64 = -1;
228 static int hf_cip_act_vel = -1;
229 static int hf_cip_act_accel = -1;
230 static int hf_cip_fault_type = -1;
231 static int hf_cip_fault_sub_code = -1;
232 static int hf_cip_fault_action = -1;
233 static int hf_cip_fault_time_stamp = -1;
234 static int hf_cip_alarm_type = -1;
235 static int hf_cip_alarm_sub_code = -1;
236 static int hf_cip_alarm_state = -1;
237 static int hf_cip_alarm_time_stamp = -1;
238 static int hf_cip_axis_status = -1;
239 static int hf_cip_axis_status_mfg = -1;
240 static int hf_cip_axis_io_status = -1;
241 static int hf_cip_axis_io_status_mfg = -1;
242 static int hf_cip_axis_safety_status = -1;
243 static int hf_cip_axis_safety_status_mfg = -1;
244 static int hf_cip_axis_safety_state = -1;
245 static int hf_cip_cmd_data_set = -1;
246 static int hf_cip_act_data_set = -1;
247 static int hf_cip_sts_data_set = -1;
248 static int hf_cip_group_sync = -1;
249 static int hf_cip_command_control = -1;
250
251 static int hf_get_axis_attr_list_attribute_cnt = -1;
252 static int hf_get_axis_attr_list_attribute_id = -1;
253 static int hf_get_axis_attr_list_dimension = -1;
254 static int hf_get_axis_attr_list_element_size = -1;
255 static int hf_get_axis_attr_list_start_index = -1;
256 static int hf_get_axis_attr_list_data_elements = -1;
257 static int hf_set_axis_attr_list_attribute_cnt = -1;
258 static int hf_set_axis_attr_list_attribute_id = -1;
259 static int hf_set_axis_attr_list_dimension = -1;
260 static int hf_set_axis_attr_list_element_size = -1;
261 static int hf_set_axis_attr_list_start_index = -1;
262 static int hf_set_axis_attr_list_data_elements = -1;
263 static int hf_set_cyclic_list_attribute_cnt = -1;
264 static int hf_set_cyclic_list_attribute_id = -1;
265 static int hf_set_cyclic_list_read_block_id = -1;
266 static int hf_set_cyclic_list_attr_sts = -1;
267 static int hf_var_devce_instance = -1;
268 static int hf_var_devce_instance_block_size = -1;
269 static int hf_var_devce_cyclic_block_size = -1;
270 static int hf_var_devce_cyclic_data_block_size = -1;
271 static int hf_var_devce_cyclic_rw_block_size = -1;
272 static int hf_var_devce_event_block_size = -1;
273 static int hf_var_devce_service_block_size = -1;
274 static int hf_cip_data = -1;
275
276 /* Subtree pointers for the dissection */
277 static gint ett_cipmotion = -1;
278 static gint ett_cont_dev_header = -1;
279 static gint ett_control_status = -1;
280 static gint ett_node_control = -1;
281 static gint ett_node_status = -1;
282 static gint ett_time_data_set = -1;
283 static gint ett_inst_data_header = -1;
284 static gint ett_cyclic_data_block = -1;
285 static gint ett_cyclic_command_data = -1;
286 static gint ett_feedback_mode = -1;
287 static gint ett_connection_configuration_bits = -1;
288 static gint ett_control_mode = -1;
289 static gint ett_feedback_config = -1;
290 static gint ett_command_data_set = -1;
291 static gint ett_actual_data_set = -1;
292 static gint ett_status_data_set = -1;
293 static gint ett_interp_control = -1;
294 static gint ett_cyclic_rd_wt = -1;
295 static gint ett_event = -1;
296 static gint ett_event_check_ctrl = -1;
297 static gint ett_event_check_sts = -1;
298 static gint ett_service = -1;
299 static gint ett_get_axis_attribute = -1;
300 static gint ett_set_axis_attribute = -1;
301 static gint ett_get_axis_attr_list = -1;
302 static gint ett_set_axis_attr_list = -1;
303 static gint ett_set_cyclic_list = -1;
304 static gint ett_group_sync = -1;
305 static gint ett_axis_status_set = -1;
306 static gint ett_command_control = -1;
307 static gint ett_configuration_block = -1;
308
309 static expert_field ei_format_rev_conn_pt = EI_INIT;
310
311 static dissector_handle_t cipmotion_handle;
312 static dissector_handle_t cipmotion3_handle;
313
314 static gboolean display_full_attribute_data = FALSE;
315
316 /* These are the BITMASKS for the Time Data Set header field */
317 #define TIME_DATA_SET_TIME_STAMP 0x1
318 #define TIME_DATA_SET_TIME_OFFSET 0x2
319 #define TIME_DATA_SET_UPDATE_DIAGNOSTICS 0x4
320 #define TIME_DATA_SET_TIME_DIAGNOSTICS 0x8
321
322 /* These are the BITMASKS for the Command Data Set cyclic field */
323 #define COMMAND_DATA_SET_POSITION 0x01
324 #define COMMAND_DATA_SET_VELOCITY 0x02
325 #define COMMAND_DATA_SET_ACCELERATION 0x04
326 #define COMMAND_DATA_SET_TORQUE 0x08
327 #define COMMAND_DATA_SET_UNWIND_CYCLE_COUNT 0x40
328 #define COMMAND_DATA_SET_POSITION_DISPLACE 0x80
329
330 /* These are the BITMASKS for the Actual Data Set cyclic field */
331 #define ACTUAL_DATA_SET_POSITION 0x01
332 #define ACTUAL_DATA_SET_VELOCITY 0x02
333 #define ACTUAL_DATA_SET_ACCELERATION 0x04
334 #define ACTUAL_DATA_SET_UNWIND_CYCLE_COUNT 0x40
335 #define ACTUAL_DATA_SET_POSITION_DISPLACE 0x80
336
337 /* These are the BITMASKS for the Status Data Set cyclic field */
338 #define STATUS_DATA_SET_AXIS_FAULT 0x01
339 #define STATUS_DATA_SET_AXIS_ALARM 0x02
340 #define STATUS_DATA_SET_AXIS_STATUS 0x04
341 #define STATUS_DATA_SET_AXIS_IO_STATUS 0x08
342 #define STATUS_DATA_SET_AXIS_SAFETY 0x10
343
344 /* These are the BITMASKS for the Command Control cyclic field */
345 #define COMMAND_CONTROL_TARGET_UPDATE 0x03
346 #define COMMAND_CONTROL_POSITION_DATA_TYPE 0x0C
347
348 /* These are the VALUES of the connection format header field of the
349 * CIP Motion protocol */
350 #define FORMAT_FIXED_CONTROL_TO_DEVICE 2
351 #define FORMAT_FIXED_DEVICE_TO_CONTROL 3
352 #define FORMAT_VAR_CONTROL_TO_DEVICE 6
353 #define FORMAT_VAR_DEVICE_TO_CONTROL 7
354
355 #define FEEDBACK_MODE_BITS 0x0F
356 #define FEEDBACK_DATA_TYPE_BITS 0x30
357
358 /* Translate function to string - connection format values */
359 static const value_string cip_con_format_vals[] = {
360 { FORMAT_FIXED_CONTROL_TO_DEVICE, "Fixed Controller-to-Device" },
361 { FORMAT_FIXED_DEVICE_TO_CONTROL, "Fixed Device-to-Controller" },
362 { FORMAT_VAR_CONTROL_TO_DEVICE, "Variable Controller-to-Device" },
363 { FORMAT_VAR_DEVICE_TO_CONTROL, "Variable Device-to-Controller" },
364 { 0, NULL }
365 };
366
367 /* Translate function to string - motor control mode values */
368 static const value_string cip_motor_control_vals[] = {
369 { 0, "No Control" },
370 { 1, "Position Control" },
371 { 2, "Velocity Control" },
372 { 3, "Acceleration Control" },
373 { 4, "Torque Control" },
374 { 0, NULL }
375 };
376
377 /* Translate function to string - feedback mode values */
378 static const value_string cip_feedback_mode_vals[] = {
379 { 0, "No Feedback" },
380 { 1, "Master Feedback" },
381 { 2, "Motor Feedback" },
382 { 3, "Load Feedback" },
383 { 4, "Dual Feedback" },
384 { 0, NULL }
385 };
386
387 static const value_string cip_feedback_type_vals[] = {
388 { 0, "DINT" },
389 { 1, "LINT" },
390 { 0, NULL }
391 };
392
393 /* Translate function to string - axis control values */
394 static const value_string cip_axis_control_vals[] =
395 {
396 { 0, "No Request" },
397 { 1, "Enable Request" },
398 { 2, "Disable Request" },
399 { 3, "Shutdown Request" },
400 { 4, "Shutdown Reset Request" },
401 { 5, "Abort Request" },
402 { 6, "Fault Reset Request" },
403 { 7, "Stop Process" },
404 { 8, "Change Actual Pos" },
405 { 9, "Change Command Pos Ref" },
406 { 127, "Cancel Request" },
407 { 0, NULL }
408 };
409
410 /* Translate function to string - group sync Status */
411 static const value_string cip_sync_status_vals[] =
412 {
413 { 0, "Synchronized" },
414 { 1, "Not Synchronized" },
415 { 2, "Wrong Grandmaster" },
416 { 3, "Clock Skew Detected" },
417 { 0, NULL }
418 };
419
420 /* Translate function to string - command target update */
421 static const value_string cip_interpolation_vals[] = {
422 { 0, "Immediate" },
423 { 1, "Extrapolate (+1)" },
424 { 2, "Interpolate (+2)" },
425 { 0, NULL }
426 };
427
428 /* These are the VALUES for the Command Position Data Type */
429 #define POSITION_DATA_LREAL 0x00
430 #define POSITION_DATA_DINT 0x01
431
432 /* Translate function to string - position data type */
433 static const value_string cip_pos_data_type_vals[] = {
434 { POSITION_DATA_LREAL, "LREAL (64-bit Float)" },
435 { POSITION_DATA_DINT, "DINT (32-bit Integer)" },
436 { 0, NULL }
437 };
438
439 /* Translate function to string - axis response values */
440 static const value_string cip_axis_response_vals[] = {
441 { 0, "No Acknowledge" },
442 { 1, "Enable Acknowledge" },
443 { 2, "Disable Acknowledge" },
444 { 3, "Shutdown Acknowledge" },
445 { 4, "Shutdown Reset Acknowledge" },
446 { 5, "Abort Acknowledge" },
447 { 6, "Fault Reset Acknowledge" },
448 { 7, "Stop Process Acknowledge" },
449 { 8, "Change Actual Position Reference Acknowledge" },
450 { 9, "Change Command Position Reference Acknowledge" },
451 { 127, "Cancel Acknowledge" },
452 { 0, NULL }
453 };
454
455 /* Translate function to string - axis state values */
456 static const value_string cip_axis_state_vals[] = {
457 { 0, "Initializing" },
458 { 1, "Pre-Charge" },
459 { 2, "Stopped" },
460 { 3, "Starting" },
461 { 4, "Running" },
462 { 5, "Testing" },
463 { 6, "Stopping" },
464 { 7, "Aborting" },
465 { 8, "Major Faulted" },
466 { 9, "Start Inhibited" },
467 { 10, "Shutdown" },
468 { 0, NULL }
469 };
470
471 /* Translate function to string - event type values */
472 static const value_string cip_event_type_vals[] = {
473 { 0, "Registration 1 Positive Edge" },
474 { 1, "Registration 1 Negative Edge" },
475 { 2, "Registration 2 Positive Edge" },
476 { 3, "Registration 2 Negative Edge" },
477 { 4, "Marker Positive Edge" },
478 { 5, "Marker Negative Edge" },
479 { 6, "Home Switch Positive Edge" },
480 { 7, "Home Switch Negative Edge" },
481 { 8, "Home Switch Marker ++" },
482 { 9, "Home Switch Marker +-" },
483 { 10, "Home Switch Marker -+" },
484 { 11, "Home Switch Marker --" },
485 { 0, NULL }
486 };
487
488 #define SC_GET_AXIS_ATTRIBUTE_LIST 0x4B
489 #define SC_SET_AXIS_ATTRIBUTE_LIST 0x4C
490 #define SC_SET_CYCLIC_WRITE_LIST 0x4D
491 #define SC_SET_CYCLIC_READ_LIST 0x4E
492 #define SC_RUN_MOTOR_TEST 0x4F
493 #define SC_GET_MOTOR_TEST_DATA 0x50
494 #define SC_RUN_INERTIA_TEST 0x51
495 #define SC_GET_INERTIA_TEST_DATA 0x52
496 #define SC_RUN_HOOKUP_TEST 0x53
497 #define SC_GET_HOOKUP_TEST_DATA 0x54
498
499 /* Translate function to string - CIP Service codes */
500 static const value_string cip_sc_vals[] = {
501 GENERIC_SC_LIST
502 { SC_GET_AXIS_ATTRIBUTE_LIST, "Get Axis Attribute List" },
503 { SC_SET_AXIS_ATTRIBUTE_LIST, "Set Axis Attribute List" },
504 { SC_SET_CYCLIC_WRITE_LIST, "Set Cyclic Write List" },
505 { SC_SET_CYCLIC_READ_LIST, "Set Cyclic Read List" },
506 { SC_RUN_MOTOR_TEST, "Run Motor Test" },
507 { SC_GET_MOTOR_TEST_DATA, "Get Motor Test Data" },
508 { SC_RUN_INERTIA_TEST, "Run Inertia Test" },
509 { SC_GET_INERTIA_TEST_DATA, "Get Inertia Test Data" },
510 { SC_RUN_HOOKUP_TEST, "Run Hookup Test" },
511 { SC_GET_HOOKUP_TEST_DATA, "Get Hookup Test Data" },
512 { 0, NULL }
513 };
514
dissect_axis_status(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)515 static int dissect_axis_status(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
516 int offset, int total_len _U_)
517 {
518 static int* const bits[] = {
519 &hf_cip_axis_sts_local_ctrl,
520 &hf_cip_axis_sts_alarm,
521 &hf_cip_axis_sts_dc_bus,
522 &hf_cip_axis_sts_pwr_struct,
523 &hf_cip_axis_sts_flux_up,
524 &hf_cip_axis_sts_tracking,
525 &hf_cip_axis_sts_pos_lock,
526 &hf_cip_axis_sts_vel_lock,
527 &hf_cip_axis_sts_vel_standstill,
528 &hf_cip_axis_sts_vel_threshold,
529 &hf_cip_axis_sts_vel_limit,
530 &hf_cip_axis_sts_acc_limit,
531 &hf_cip_axis_sts_dec_limit,
532 &hf_cip_axis_sts_torque_threshold,
533 &hf_cip_axis_sts_torque_limit,
534 &hf_cip_axis_sts_cur_limit,
535 &hf_cip_axis_sts_therm_limit,
536 &hf_cip_axis_sts_feedback_integ,
537 &hf_cip_axis_sts_shutdown,
538 &hf_cip_axis_sts_in_process,
539 &hf_cip_axis_sts_dc_bus_unload,
540 &hf_cip_axis_sts_ac_pwr_loss,
541 &hf_cip_axis_sts_pos_cntrl_mode,
542 &hf_cip_axis_sts_vel_cntrl_mode,
543 &hf_cip_axis_sts_trq_cntrl_mode,
544 NULL
545 };
546
547 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_axis_status, ett_axis_status_set, bits, ENC_LITTLE_ENDIAN);
548
549 return 4;
550 }
551
dissect_axis_status2(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)552 static int dissect_axis_status2(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
553 int offset, int total_len _U_)
554 {
555 static int* const bits[] = {
556 &hf_cip_axis_sts2_motor,
557 &hf_cip_axis_sts2_regenerate,
558 &hf_cip_axis_sts2_ride_thru,
559 &hf_cip_axis_sts2_ac_line_sync,
560 &hf_cip_axis_sts2_bus_volt_lock,
561 &hf_cip_axis_sts2_react_pwr_only,
562 &hf_cip_axis_sts2_volt_ctrl_mode,
563 &hf_cip_axis_sts2_pwr_loss,
564 &hf_cip_axis_sts2_ac_volt_sag,
565 &hf_cip_axis_sts2_ac_phase_loss,
566 &hf_cip_axis_sts2_ac_freq_change,
567 &hf_cip_axis_sts2_ac_sync_loss,
568 &hf_cip_axis_sts2_single_phase,
569 &hf_cip_axis_sts2_bus_volt_limit,
570 &hf_cip_axis_sts2_bus_volt_rate_limit,
571 &hf_cip_axis_sts2_active_current_rate_limit,
572 &hf_cip_axis_sts2_reactive_current_rate_limit,
573 &hf_cip_axis_sts2_reactive_pwr_limit,
574 &hf_cip_axis_sts2_reactive_pwr_rate_limit,
575 &hf_cip_axis_sts2_active_current_limit,
576 &hf_cip_axis_sts2_reactive_current_limit,
577 &hf_cip_axis_sts2_motor_pwr_limit,
578 &hf_cip_axis_sts2_regen_pwr_limit,
579 &hf_cip_axis_sts2_convert_therm_limit,
580 NULL
581 };
582
583 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_axis_status2, ett_axis_status_set, bits, ENC_LITTLE_ENDIAN);
584
585 return 4;
586 }
587
dissect_event_checking_control(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)588 static int dissect_event_checking_control(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
589 int offset, int total_len _U_)
590 {
591 static int* const bits[] = {
592 &hf_cip_evnt_ctrl_reg1_pos,
593 &hf_cip_evnt_ctrl_reg1_neg,
594 &hf_cip_evnt_ctrl_reg2_pos,
595 &hf_cip_evnt_ctrl_reg2_neg,
596 &hf_cip_evnt_ctrl_reg1_posrearm,
597 &hf_cip_evnt_ctrl_reg1_negrearm,
598 &hf_cip_evnt_ctrl_reg2_posrearm,
599 &hf_cip_evnt_ctrl_reg2_negrearm,
600 &hf_cip_evnt_ctrl_marker_pos,
601 &hf_cip_evnt_ctrl_marker_neg,
602 &hf_cip_evnt_ctrl_home_pos,
603 &hf_cip_evnt_ctrl_home_neg,
604 &hf_cip_evnt_ctrl_home_pp,
605 &hf_cip_evnt_ctrl_home_pm,
606 &hf_cip_evnt_ctrl_home_mp,
607 &hf_cip_evnt_ctrl_home_mm,
608 &hf_cip_evnt_ctrl_acks,
609 // The dissector will indicate if the protocol is requesting an extended event format but will not dissect it.
610 &hf_cip_evnt_extend_format,
611 NULL
612 };
613
614 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_event_checking, ett_event_check_ctrl, bits, ENC_LITTLE_ENDIAN);
615
616 return 4;
617 }
618
dissect_event_checking_status(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)619 static int dissect_event_checking_status(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
620 int offset, int total_len _U_)
621 {
622 static int* const bits[] = {
623 &hf_cip_evnt_sts_reg1_pos,
624 &hf_cip_evnt_sts_reg1_neg,
625 &hf_cip_evnt_sts_reg2_pos,
626 &hf_cip_evnt_sts_reg2_neg,
627 &hf_cip_evnt_sts_reg1_posrearm,
628 &hf_cip_evnt_sts_reg1_negrearm,
629 &hf_cip_evnt_sts_reg2_posrearm,
630 &hf_cip_evnt_sts_reg2_negrearm,
631 &hf_cip_evnt_sts_marker_pos,
632 &hf_cip_evnt_sts_marker_neg,
633 &hf_cip_evnt_sts_home_pos,
634 &hf_cip_evnt_sts_home_neg,
635 &hf_cip_evnt_sts_home_pp,
636 &hf_cip_evnt_sts_home_pm,
637 &hf_cip_evnt_sts_home_mp,
638 &hf_cip_evnt_sts_home_mm,
639 &hf_cip_evnt_sts_nfs,
640 // The dissector will indicate if the protocol is requesting an extended event format but will not dissect it.
641 &hf_cip_evnt_extend_format,
642 NULL
643 };
644
645 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_event_status, ett_event_check_sts, bits, ENC_LITTLE_ENDIAN);
646
647 return 4;
648 }
649
dissect_actual_data_set_bits(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)650 static int dissect_actual_data_set_bits(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
651 int offset, int total_len _U_)
652 {
653 static int* const bits[] = {
654 &hf_cip_act_data_pos,
655 &hf_cip_act_data_vel,
656 &hf_cip_act_data_acc,
657 &hf_cip_act_unwind_cycle_count,
658 &hf_cip_act_pos_displacement,
659 NULL
660 };
661
662 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_act_data_set, ett_actual_data_set, bits, ENC_LITTLE_ENDIAN);
663
664 return 1;
665 }
666
dissect_command_data_set_bits(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)667 static int dissect_command_data_set_bits(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
668 int offset, int total_len _U_)
669 {
670 static int* const bits[] = {
671 &hf_cip_cmd_data_pos_cmd,
672 &hf_cip_cmd_data_vel_cmd,
673 &hf_cip_cmd_data_acc_cmd,
674 &hf_cip_cmd_data_trq_cmd,
675 &hf_cip_cmd_data_unwind_cycle_count,
676 &hf_cip_cmd_data_pos_displacement,
677 NULL
678 };
679
680 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_cmd_data_set, ett_command_data_set, bits, ENC_LITTLE_ENDIAN);
681
682 return 1;
683 }
684
dissect_command_control(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)685 static int dissect_command_control(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
686 int offset, int total_len _U_)
687 {
688 static int* const bits[] = {
689 &hf_cip_intrp,
690 &hf_cip_position_data_type,
691 NULL
692 };
693
694 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_command_control, ett_command_control, bits, ENC_LITTLE_ENDIAN);
695
696 return 1;
697 }
698
dissect_status_data_set_bits(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)699 static int dissect_status_data_set_bits(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
700 int offset, int total_len _U_)
701 {
702 static int* const bits[] = {
703 &hf_cip_sts_flt,
704 &hf_cip_sts_alrm,
705 &hf_cip_sts_sts,
706 &hf_cip_sts_iosts,
707 &hf_cip_sts_axis_safety,
708 NULL
709 };
710
711 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_sts_data_set, ett_status_data_set, bits, ENC_LITTLE_ENDIAN);
712
713 return 1;
714 }
715
dissect_node_control(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)716 static int dissect_node_control(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
717 int offset, int total_len _U_)
718 {
719 static int* const bits[] = {
720 &hf_cip_node_control_remote,
721 &hf_cip_node_control_sync,
722 &hf_cip_node_data_valid,
723 &hf_cip_node_fault_reset,
724 NULL
725 };
726
727 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_node_control, ett_node_control, bits, ENC_LITTLE_ENDIAN);
728
729 return 1;
730 }
731
dissect_node_status(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)732 static int dissect_node_status(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
733 int offset, int total_len _U_)
734 {
735 static int* const bits[] = {
736 &hf_cip_node_control_remote,
737 &hf_cip_node_control_sync,
738 &hf_cip_node_data_valid,
739 &hf_cip_node_device_faulted,
740 NULL
741 };
742
743 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_node_status, ett_node_status, bits, ENC_LITTLE_ENDIAN);
744
745 return 1;
746 }
747
dissect_time_data_set(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)748 static int dissect_time_data_set(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
749 int offset, int total_len _U_)
750 {
751 static int* const bits[] = {
752 &hf_cip_time_data_stamp,
753 &hf_cip_time_data_offset,
754 &hf_cip_time_data_diag,
755 &hf_cip_time_data_time_diag,
756 NULL
757 };
758
759 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_time_data_set, ett_time_data_set, bits, ENC_LITTLE_ENDIAN);
760
761 return 1;
762 }
763
dissect_control_status(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)764 static int dissect_control_status(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
765 int offset, int total_len _U_)
766 {
767 static int* const bits[] = {
768 &hf_cip_control_status_complete,
769 &hf_cip_control_status_bus_up,
770 &hf_cip_control_status_bus_unload,
771 &hf_cip_control_status_power_loss,
772 NULL
773 };
774
775 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_control_status, ett_control_status, bits, ENC_LITTLE_ENDIAN);
776
777 return 1;
778 }
779
dissect_feedback_mode(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)780 static int dissect_feedback_mode(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
781 int offset, int total_len _U_)
782 {
783 static int* const bits[] = {
784 &hf_cip_feedback_mode,
785 &hf_cip_feedback_data_type,
786 NULL
787 };
788
789 proto_tree_add_bitmask(tree, tvb, offset, hf_cip_feedback, ett_feedback_mode, bits, ENC_LITTLE_ENDIAN);
790
791 return 1;
792 }
793
dissect_connection_configuration_bits(packet_info * pinfo _U_,proto_tree * tree,proto_item * item _U_,tvbuff_t * tvb,int offset,int total_len _U_)794 static int dissect_connection_configuration_bits(packet_info* pinfo _U_, proto_tree* tree, proto_item* item _U_, tvbuff_t* tvb,
795 int offset, int total_len _U_)
796 {
797 static int* const bits[] = {
798 &hf_connection_configuration_bits_power,
799 &hf_connection_configuration_bits_safety_bit_valid,
800 &hf_connection_configuration_bits_allow_network_safety,
801 NULL
802 };
803
804 proto_tree_add_bitmask(tree, tvb, offset, hf_connection_configuration_bits, ett_connection_configuration_bits, bits, ENC_LITTLE_ENDIAN);
805
806 return 1;
807 }
808
809 attribute_info_t cip_motion_attribute_vals[] = {
810 { CI_CLS_MOTION, CIP_ATTR_CLASS, 14, -1, "Node Control", cip_dissector_func, NULL, dissect_node_control },
811 { CI_CLS_MOTION, CIP_ATTR_CLASS, 15, -1, "Node Status", cip_dissector_func, NULL, dissect_node_status },
812 { CI_CLS_MOTION, CIP_ATTR_CLASS, 31, -1, "Time Data Set", cip_dissector_func, NULL, dissect_time_data_set },
813 { CI_CLS_MOTION, CIP_ATTR_CLASS, 34, -1, "Drive Power Structure Class ID", cip_udint, &hf_configuration_block_drive_power_struct_id, NULL },
814 { CI_CLS_MOTION, CIP_ATTR_CLASS, 36, -1, "Connection Configuration Bits", cip_dissector_func, NULL, dissect_connection_configuration_bits },
815 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 40, -1, "Control Mode", cip_usint, &hf_cip_motor_cntrl, NULL },
816 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 42, -1, "Feedback Mode", cip_dissector_func, NULL, dissect_feedback_mode },
817 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 60, -1, "Event Checking Control", cip_dissector_func, NULL, dissect_event_checking_control },
818 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 61, -1, "Event Checking Status", cip_dissector_func, NULL, dissect_event_checking_status },
819 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 89, -1, "Control Status", cip_dissector_func, NULL, dissect_control_status },
820 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 90, -1, "Actual Data Set", cip_dissector_func, NULL, dissect_actual_data_set_bits },
821 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 91, -1, "Command Data Set", cip_dissector_func, NULL, dissect_command_data_set_bits },
822 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 92, -1, "Command Control", cip_dissector_func, NULL, dissect_command_control },
823 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 94, -1, "Status Data Set", cip_dissector_func, NULL, dissect_status_data_set_bits },
824 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 431, -1, "Position Trim", cip_dint, &hf_cip_pos_trim, NULL },
825 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 451, -1, "Velocity Trim", cip_real, &hf_cip_vel_trim, NULL },
826 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 481, -1, "Acceleration Trim", cip_real, &hf_cip_accel_trim, NULL },
827 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 491, -1, "Torque Trim", cip_real, &hf_cip_trq_trim, NULL },
828 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 651, -1, "Axis Status", cip_dissector_func, NULL, dissect_axis_status },
829 { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 740, -1, "Axis Status 2", cip_dissector_func, NULL, dissect_axis_status2 },
830 };
831
832 /*
833 * Function name: dissect_cmd_data_set
834 *
835 * Purpose: Dissect the "Cyclic Command Data" of a Controller-to-Device format message
836 *
837 * Based on the Command Data Set bits of the Cyclic Command Data Block header, display
838 * any of those command values.
839 *
840 * Returns: The number of bytes in the cyclic data used
841 */
842 static guint32
dissect_cmd_data_set(guint32 cmd_data_set,proto_tree * parent_tree,tvbuff_t * tvb,guint32 offset,gboolean lreal_pos)843 dissect_cmd_data_set(guint32 cmd_data_set, proto_tree* parent_tree, tvbuff_t* tvb, guint32 offset, gboolean lreal_pos)
844 {
845 // If no Command Data Set bits are set, then we don't need to display any additional data.
846 if (cmd_data_set == 0)
847 {
848 return 0;
849 }
850
851 guint32 bytes_used = 0;
852
853 proto_item* item;
854 proto_tree* tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_cyclic_command_data, &item, "Cyclic Command Data");
855
856 /* The order of these if statements is VERY important, this is the order the values will
857 * appear in the cyclic data */
858 if ( (cmd_data_set & COMMAND_DATA_SET_POSITION) == COMMAND_DATA_SET_POSITION )
859 {
860 /* Based on the Command Position Data Type value embedded in the Command Control
861 * header field the position is either 64-bit floating or 32-bit integer */
862 if (lreal_pos)
863 {
864 /* Display the command data set position command value */
865 proto_tree_add_item(tree, hf_cip_pos_cmd, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN );
866 bytes_used += 8;
867 }
868 else
869 {
870 /* Display the command data set position command value */
871 proto_tree_add_item(tree, hf_cip_pos_cmd_int, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
872 bytes_used += 4;
873 }
874 }
875
876 if ( (cmd_data_set & COMMAND_DATA_SET_VELOCITY) == COMMAND_DATA_SET_VELOCITY )
877 {
878 /* Display the command data set velocity command value */
879 proto_tree_add_item(tree, hf_cip_vel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
880 bytes_used += 4;
881 }
882
883 if ( (cmd_data_set & COMMAND_DATA_SET_ACCELERATION) == COMMAND_DATA_SET_ACCELERATION )
884 {
885 /* Display the command data set acceleration command value */
886 proto_tree_add_item(tree, hf_cip_accel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
887 bytes_used += 4;
888 }
889
890 if ( (cmd_data_set & COMMAND_DATA_SET_TORQUE) == COMMAND_DATA_SET_TORQUE )
891 {
892 /* Display the command data set torque command value */
893 proto_tree_add_item(tree, hf_cip_trq_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
894 bytes_used += 4;
895 }
896
897 proto_item_set_len(item, bytes_used);
898
899 return bytes_used;
900 }
901
902
903 /*
904 * Function name: dissect_act_data_set
905 *
906 * Purpose: Dissect the "Cyclic Actual Data" of a Device-to-Controller format message
907 *
908 * Based on the Actual Data Set bits of the "Cyclic Actual Data Block" header, display
909 * any of those those feedback values.
910 *
911 * Returns: The number of bytes in the cyclic data used
912 */
913 static guint32
dissect_act_data_set(guint32 act_data_set,proto_tree * parent_tree,tvbuff_t * tvb,guint32 offset,guint8 feedback_mode)914 dissect_act_data_set(guint32 act_data_set, proto_tree* parent_tree, tvbuff_t* tvb, guint32 offset, guint8 feedback_mode)
915 {
916 // If no Actual Data Set bits are set, then we don't need to display any additional data.
917 if (act_data_set == 0)
918 {
919 return 0;
920 }
921
922 guint32 bytes_used = 0;
923
924 proto_item* item;
925 proto_tree* tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_cyclic_command_data, &item, "Cyclic Actual Data");
926
927 /* The order of these if statements is VERY important, this is the order the values will
928 * appear in the cyclic data */
929 if ( (act_data_set & ACTUAL_DATA_SET_POSITION) == ACTUAL_DATA_SET_POSITION )
930 {
931 /* Display the actual data set position feedback value in either 32 or 64 bit */
932 gboolean is_64_bit_position = (feedback_mode & FEEDBACK_DATA_TYPE_BITS) == 0x10;
933 if (is_64_bit_position)
934 {
935 proto_tree_add_item(tree, hf_cip_act_pos_64, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
936 bytes_used += 8;
937 }
938 else
939 {
940 proto_tree_add_item(tree, hf_cip_act_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
941 bytes_used += 4;
942 }
943 }
944
945 if ( (act_data_set & ACTUAL_DATA_SET_VELOCITY) == ACTUAL_DATA_SET_VELOCITY )
946 {
947 /* Display the actual data set velocity feedback value */
948 proto_tree_add_item(tree, hf_cip_act_vel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
949 bytes_used += 4;
950 }
951
952 if ( (act_data_set & ACTUAL_DATA_SET_ACCELERATION) == ACTUAL_DATA_SET_ACCELERATION )
953 {
954 /* Display the actual data set acceleration feedback value */
955 proto_tree_add_item(tree, hf_cip_act_accel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
956 bytes_used += 4;
957 }
958
959
960 proto_item_set_len(item, bytes_used);
961
962 return bytes_used;
963 }
964
965 /*
966 * Function name: dissect_status_data_set
967 *
968 * Purpose: Dissect the "Cyclic Status Data" of a Device-to-Controller format message
969 *
970 * Based on the Status Data Set bits of the "Cyclic Actual Data Block" header, display
971 * any of those status values.
972 *
973 * Returns: The number of bytes in the cyclic data used
974 */
975 static guint32
dissect_status_data_set(guint32 status_data_set,proto_tree * parent_tree,tvbuff_t * tvb,guint32 offset)976 dissect_status_data_set(guint32 status_data_set, proto_tree* parent_tree, tvbuff_t* tvb, guint32 offset)
977 {
978 // If no Status Data Set bits are set, then we don't need to display any additional data.
979 if (status_data_set == 0)
980 {
981 return 0;
982 }
983
984 guint32 bytes_used = 0;
985
986 proto_item* item;
987 proto_tree* tree = proto_tree_add_subtree(parent_tree, tvb, offset, 0, ett_cyclic_command_data, &item, "Cyclic Status Data");
988
989 /* The order of these if statements is VERY important, this is the order the values will
990 * appear in the cyclic data */
991 if ( (status_data_set & STATUS_DATA_SET_AXIS_FAULT) == STATUS_DATA_SET_AXIS_FAULT )
992 {
993 /* Display the various fault codes from the device */
994 proto_tree_add_item(tree, hf_cip_fault_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
995 bytes_used += 1;
996
997 proto_tree_add_item(tree, hf_cip_axis_fault, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
998 bytes_used += 1;
999
1000 proto_tree_add_item(tree, hf_cip_fault_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1001 bytes_used += 1;
1002
1003 proto_tree_add_item(tree, hf_cip_fault_action, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1004 bytes_used += 1;
1005
1006 proto_tree_add_item(tree, hf_cip_fault_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
1007 bytes_used += 8;
1008 }
1009
1010 if ( (status_data_set & STATUS_DATA_SET_AXIS_ALARM) == STATUS_DATA_SET_AXIS_ALARM )
1011 {
1012 /* Display the various alarm codes from the device */
1013 proto_tree_add_item(tree, hf_cip_alarm_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1014 bytes_used += 1;
1015
1016 proto_tree_add_item(tree, hf_cip_axis_alarm, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1017 bytes_used += 1;
1018
1019 proto_tree_add_item(tree, hf_cip_alarm_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1020 bytes_used += 1;
1021
1022 proto_tree_add_item(tree, hf_cip_alarm_state, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1023 bytes_used += 1;
1024
1025 proto_tree_add_item(tree, hf_cip_alarm_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
1026 bytes_used += 8;
1027 }
1028
1029 if ( (status_data_set & STATUS_DATA_SET_AXIS_STATUS) == STATUS_DATA_SET_AXIS_STATUS )
1030 {
1031 /* Display the various axis state values from the device */
1032 bytes_used += dissect_axis_status(NULL, tree, NULL, tvb, offset + bytes_used, 4);
1033
1034 proto_tree_add_item(tree, hf_cip_axis_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1035 bytes_used += 4;
1036 }
1037
1038 if ( (status_data_set & STATUS_DATA_SET_AXIS_IO_STATUS) == STATUS_DATA_SET_AXIS_IO_STATUS )
1039 {
1040 proto_tree_add_item(tree, hf_cip_axis_io_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1041 bytes_used += 4;
1042
1043 proto_tree_add_item(tree, hf_cip_axis_io_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1044 bytes_used += 4;
1045 }
1046
1047 if ( (status_data_set & STATUS_DATA_SET_AXIS_SAFETY) == STATUS_DATA_SET_AXIS_SAFETY )
1048 {
1049 proto_tree_add_item(tree, hf_cip_axis_safety_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1050 bytes_used += 4;
1051 proto_tree_add_item(tree, hf_cip_axis_safety_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1052 bytes_used += 4;
1053 proto_tree_add_item(tree, hf_cip_axis_safety_state, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1054 bytes_used += 4;
1055 }
1056
1057 proto_item_set_len(item, bytes_used);
1058
1059 return bytes_used;
1060 }
1061
1062 /*
1063 * Function name: dissect_cntr_cyclic
1064 *
1065 * Purpose: Dissect the "Cyclic Command Data Block" of a Controller-to-Device format message
1066 *
1067 * Returns: The new offset into the message that follow on dissections should use
1068 * as their starting offset
1069 */
1070 static guint32
dissect_cntr_cyclic(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1071 dissect_cntr_cyclic(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1072 {
1073 /* Create the tree for the entire instance data header */
1074 proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_cyclic_data_block, NULL, "Cyclic Command Data Block");
1075
1076 proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1077 dissect_feedback_mode(NULL, header_tree, NULL, tvb, offset + 1, 1);
1078 proto_tree_add_item(header_tree, hf_cip_axis_control, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1079 dissect_control_status(NULL, header_tree, NULL, tvb, offset + 3, 1);
1080
1081 dissect_command_data_set_bits(NULL, header_tree, NULL, tvb, offset + 4, 1);
1082 dissect_actual_data_set_bits(NULL, header_tree, NULL, tvb, offset + 5, 1);
1083 dissect_status_data_set_bits(NULL, header_tree, NULL, tvb, offset + 6, 1);
1084 dissect_command_control(NULL, header_tree, NULL, tvb, offset + 7, 1);
1085
1086 guint32 bytes_used = 8;
1087
1088 /* Determine if the dissector should be using an LREAL or DINT for position */
1089 guint8 command_control = tvb_get_guint8(tvb, offset + 7);
1090 gboolean lreal_pos = ((command_control & COMMAND_CONTROL_POSITION_DATA_TYPE) == POSITION_DATA_LREAL);
1091
1092 /* Cyclic Command Data: Display the command data values from the cyclic data payload, the
1093 * cyclic data starts immediately after the interpolation control field in the controller to device
1094 * direction */
1095 guint32 command_data_set = tvb_get_guint8(tvb, offset + 4);
1096 bytes_used += dissect_cmd_data_set(command_data_set, header_tree, tvb, offset + bytes_used, lreal_pos);
1097
1098 /* Return the offset to the next byte in the message */
1099 return offset + bytes_used;
1100 }
1101
1102 /*
1103 * Function name: dissect_device_cyclic
1104 *
1105 * Purpose: Dissect the "Cyclic Actual Data Block" of a Device-to-Controller format message
1106 *
1107 * Returns: The new offset into the message that follow on dissections should use
1108 * as their starting offset
1109 */
1110 static guint32
dissect_device_cyclic(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1111 dissect_device_cyclic(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1112 {
1113 /* Create the tree for the entire instance data header */
1114 proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_cyclic_data_block, NULL, "Cyclic Actual Data Block");
1115
1116 proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1117 dissect_feedback_mode(NULL, header_tree, NULL, tvb, offset + 1, 1);
1118 proto_tree_add_item(header_tree, hf_cip_axis_response, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1119 proto_tree_add_item(header_tree, hf_cip_axis_resp_stat, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1120
1121 dissect_actual_data_set_bits(NULL, header_tree, NULL, tvb, offset + 5, 1);
1122 dissect_status_data_set_bits(NULL, header_tree, NULL, tvb, offset + 6, 1);
1123 proto_tree_add_item(header_tree, hf_cip_axis_state, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1124
1125 guint32 bytes_used = 8;
1126
1127 /* Display the "Cyclic Actual Data" values from the cyclic data payload. */
1128 guint8 feedback_mode = tvb_get_guint8(tvb, offset + 1);
1129 guint8 actual_data_set = tvb_get_guint8(tvb, offset + 5);
1130 bytes_used += dissect_act_data_set(actual_data_set, header_tree, tvb, offset + bytes_used, feedback_mode);
1131
1132 /* Display the "Cyclic Status Data" values from the cyclic data payload. */
1133 guint8 status_data_set = tvb_get_guint8(tvb, offset + 6);
1134 bytes_used += dissect_status_data_set(status_data_set, header_tree, tvb, offset + bytes_used);
1135
1136 /* Return the offset to the next byte in the message */
1137 return offset + bytes_used;
1138 }
1139
1140 /*
1141 * Function name: dissect_cyclic_wt
1142 *
1143 * Purpose: Dissect the "Cyclic Write Data Block" in a Controller-to-Device message
1144 *
1145 * Returns: The new offset into the message that follow on dissections should use
1146 * as their starting offset
1147 */
1148 static guint32
dissect_cyclic_wt(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1149 dissect_cyclic_wt(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1150 {
1151 proto_tree *header_tree;
1152
1153 /* Create the tree for the entire cyclic write data block */
1154 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_cyclic_rd_wt, NULL, "Cyclic Write Data Block");
1155
1156 /* Display the cyclic write block id value */
1157 proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1158
1159 /* Display the cyclic read block id value */
1160 proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1161
1162 /* Display the remainder of the cyclic write data if there is any */
1163 if ( (size - 4) > 0 )
1164 {
1165 proto_tree_add_item(header_tree, hf_cip_cyclic_wrt_data, tvb, offset + 4, size - 4, ENC_NA);
1166 }
1167
1168 return offset + size;
1169 }
1170
1171 /*
1172 * Function name: dissect_cyclic_rd
1173 *
1174 * Purpose: Dissect the "Cyclic Read Data Block" in a Device-to-Controller message
1175 *
1176 * Returns: The new offset into the message that follow on dissections should use
1177 * as their starting offset
1178 */
1179 static guint32
dissect_cyclic_rd(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1180 dissect_cyclic_rd(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1181 {
1182 proto_tree *header_tree;
1183
1184 /* Create the tree for the entire cyclic write data block */
1185 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_cyclic_rd_wt, NULL, "Cyclic Read Data Block");
1186
1187 /* Display the cyclic write block id value */
1188 proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1189
1190 /* Display the cyclic write status value */
1191 proto_tree_add_item(header_tree, hf_cip_cyclic_write_sts, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1192
1193 /* Display the cyclic read block id value */
1194 proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1195
1196 /* Display the cyclic read status value */
1197 proto_tree_add_item(header_tree, hf_cip_cyclic_read_sts, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1198
1199 /* Display the remainder of the cyclic read data if there is any*/
1200 if ( (size - 4) > 0 )
1201 {
1202 proto_tree_add_item(header_tree, hf_cip_cyclic_rd_data, tvb, offset + 4, size - 4, ENC_NA);
1203 }
1204
1205 return offset + size;
1206 }
1207
1208 /*
1209 * Function name: dissect_cntr_event
1210 *
1211 * Purpose: Dissect the "Event Data Block" in a Controller-to-Device message
1212 *
1213 * Returns: The new offset into the message that follow on dissections should use
1214 * as their starting offset
1215 */
1216 static guint32
dissect_cntr_event(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1217 dissect_cntr_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1218 {
1219 proto_tree *header_tree;
1220 guint32 acks, cur_ack;
1221 guint32 bytes_used = 0;
1222
1223 /* Create the tree for the entire cyclic write data block */
1224 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_event, NULL, "Event Data Block");
1225
1226 guint32 event_checking_control = tvb_get_letohl(tvb, offset);
1227 dissect_event_checking_control(NULL, header_tree, NULL, tvb, offset, 4);
1228
1229 /* The event checking control value is 4 bytes long */
1230 bytes_used = 4;
1231
1232 /* The final 4 bits of the event checking control value are the number of acknowledgements in the message */
1233 acks = (event_checking_control >> 28) & 0x0F;
1234
1235 /* Each acknowledgement contains and id and a status value */
1236 for (cur_ack = 0; cur_ack < acks; cur_ack++)
1237 {
1238 /* Display the current acknowledgement id */
1239 proto_tree_add_item(header_tree, hf_cip_event_ack, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1240 bytes_used += 1;
1241
1242 /* Display the current event status */
1243 proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1244 bytes_used += 1;
1245 }
1246
1247 return offset + size;
1248 }
1249
1250 /*
1251 * Function name: dissect_devce_event
1252 *
1253 * Purpose: Dissect the "Event Data Block" in a Device-to-Controller message
1254 *
1255 * Returns: The new offset into the message that follow on dissections should use
1256 * as their starting offset
1257 */
1258 static guint32
dissect_devce_event(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1259 dissect_devce_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1260 {
1261 proto_tree *header_tree;
1262 guint64 nots, cur_not;
1263 guint32 bytes_used = 0;
1264
1265 /* Create the tree for the entire cyclic write data block */
1266 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_event, NULL, "Event Data Block");
1267
1268 guint32 event_checking_status = tvb_get_letohl(tvb, offset);
1269 dissect_event_checking_status(NULL, header_tree, NULL, tvb, offset, 4);
1270
1271 /* The event status control value is 4 bytes long */
1272 bytes_used = 4;
1273
1274 /* The final 4 bits of the event status control value are the number of notifications in the message */
1275 nots = (event_checking_status >> 28) & 0x0F;
1276
1277 /* Each notification contains and id, status value, event type, position and time stamp */
1278 for (cur_not = 0; cur_not < nots; cur_not++)
1279 {
1280 /* Display the current event id */
1281 proto_tree_add_item(header_tree, hf_cip_event_id, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1282 bytes_used += 1;
1283
1284 /* Display the current event status */
1285 proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1286 bytes_used += 1;
1287
1288 /* Display the current event type */
1289 proto_tree_add_item(header_tree, hf_cip_evnt_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1290 bytes_used += 2; /* Increment by 2 to jump the reserved byte */
1291
1292 /* Display the event position value */
1293 proto_tree_add_item(header_tree, hf_cip_event_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1294 bytes_used += 4;
1295
1296 /* Display the event time stamp value */
1297 proto_tree_add_item(header_tree, hf_cip_event_ts, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
1298 bytes_used += 8;
1299 }
1300
1301 return size + offset;
1302 }
1303
1304 /*
1305 * Function name: dissect_get_axis_attr_list_request
1306 *
1307 * Purpose: Dissect the get axis attribute list service request
1308 *
1309 * Returns: None
1310 */
1311 static void
dissect_get_axis_attr_list_request(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1312 dissect_get_axis_attr_list_request(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1313 {
1314 proto_item *attr_item;
1315 proto_tree *header_tree, *attr_tree;
1316 guint32 local_offset;
1317
1318 /* Create the tree for the get axis attribute list request */
1319 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_get_axis_attribute, NULL, "Get Axis Attribute List Request");
1320
1321 /* Read the number of attributes that are contained within the request */
1322 guint32 attribute_cnt;
1323 proto_tree_add_item_ret_uint(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1324
1325 /* Start the attribute loop at the beginning of the first attribute in the list */
1326 local_offset = offset + 4;
1327
1328 /* For each attribute display the associated fields */
1329 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1330 {
1331 /* At a minimum the local offset needs will need to be incremented by 4 bytes to reach the next attribute */
1332 guint8 increment_size = 4;
1333
1334 /* Create the tree for this attribute within the request */
1335 guint32 attribute_id;
1336 attr_item = proto_tree_add_item_ret_uint(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1337 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1338
1339 guint32 dimension;
1340 proto_tree_add_item_ret_uint(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN, &dimension);
1341 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1342
1343 if (dimension == 1)
1344 {
1345 /* Display the start index and start index from the request */
1346 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1347 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1348
1349 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1350 increment_size += 4;
1351 }
1352
1353 attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
1354 if (pattribute != NULL)
1355 {
1356 proto_item_append_text(attr_item, " (%s)", pattribute->text);
1357 }
1358
1359 /* Move the local offset to the next attribute */
1360 local_offset += increment_size;
1361 }
1362 }
1363
dissect_motion_attribute(packet_info * pinfo,tvbuff_t * tvb,int offset,guint32 attribute_id,guint32 instance_id,proto_item * attr_item,proto_tree * attr_tree,guint8 dimension,guint32 attribute_size)1364 static int dissect_motion_attribute(packet_info *pinfo, tvbuff_t* tvb, int offset, guint32 attribute_id,
1365 guint32 instance_id, proto_item* attr_item, proto_tree* attr_tree, guint8 dimension, guint32 attribute_size)
1366 {
1367 attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
1368 int parsed_len = 0;
1369
1370 if (pattribute != NULL)
1371 {
1372 proto_item_append_text(attr_item, " (%s)", pattribute->text);
1373
1374 // TODO: Handle more dimensions. Unsure about the format when there is more than 1 item.
1375 if (dimension <= 1)
1376 {
1377 parsed_len = dissect_cip_attribute(pinfo, attr_tree, attr_item, tvb, pattribute, offset, attribute_size);
1378 }
1379 }
1380
1381 return parsed_len;
1382 }
1383
1384 /*
1385 * Function name: dissect_set_axis_attr_list_request
1386 *
1387 * Purpose: Dissect the set axis attribute list service request
1388 *
1389 * Returns: None
1390 */
1391 static void
dissect_set_axis_attr_list_request(packet_info * pinfo,tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1392 dissect_set_axis_attr_list_request(packet_info *pinfo, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1393 {
1394 proto_item *attr_item;
1395 proto_tree *header_tree, *attr_tree;
1396 guint32 local_offset;
1397
1398 /* Create the tree for the set axis attribute list request */
1399 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_set_axis_attribute, NULL, "Set Axis Attribute List Request");
1400
1401 /* Read the number of attributes that are contained within the request */
1402 guint32 attribute_cnt;
1403 proto_tree_add_item_ret_uint(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1404
1405 /* Start the attribute loop at the beginning of the first attribute in the list */
1406 local_offset = offset + 4;
1407
1408 /* For each attribute display the associated fields */
1409 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1410 {
1411 /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1412 guint8 increment_size = 4;
1413
1414 /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1415 * properly displaying of the attribute is possible */
1416 guint8 attribute_start = 4;
1417
1418 /* Create the tree for this attribute in the get axis attribute list request */
1419 guint32 attribute_id;
1420 attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1421 attr_tree = proto_item_add_subtree(attr_item, ett_set_axis_attr_list);
1422
1423 guint32 dimension;
1424 proto_tree_add_item_ret_uint(attr_tree, hf_set_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN, &dimension);
1425
1426 guint32 attribute_size;
1427 proto_tree_add_item_ret_uint(attr_tree, hf_set_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN, &attribute_size);
1428
1429 if (dimension == 1)
1430 {
1431 guint32 data_elements;
1432
1433 /* Display the start index and start index from the request if the request is an array */
1434 proto_tree_add_item(attr_tree, hf_set_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1435 proto_tree_add_item_ret_uint(attr_tree, hf_set_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN, &data_elements);
1436
1437 /* Modify the size of the attribute data by the number of elements if the request is an array request */
1438 attribute_size *= data_elements;
1439
1440 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1441 increment_size += 4;
1442 attribute_start += 4;
1443 }
1444
1445 int parsed_len = dissect_motion_attribute(pinfo, tvb, local_offset + attribute_start, attribute_id,
1446 instance_id, attr_item, attr_tree, dimension, attribute_size);
1447
1448 // Display the raw attribute data if configured. Otherwise, just show the remaining unparsed data.
1449 if (display_full_attribute_data)
1450 {
1451 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
1452 }
1453 else if ((attribute_size - parsed_len) > 0)
1454 {
1455 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start + parsed_len, attribute_size - parsed_len, ENC_NA);
1456 }
1457
1458 /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1459 if (attribute_size % 4 != 0)
1460 {
1461 attribute_size = attribute_size + (4 - (attribute_size % 4));
1462 }
1463
1464 /* Move the local offset to the next attribute */
1465 local_offset += (attribute_size + increment_size);
1466 }
1467 }
1468
1469 /*
1470 * Function name: dissect_group_sync_request
1471 *
1472 * Purpose: Dissect the group sync service request
1473 *
1474 * Returns: None
1475 */
1476 static void
dissect_group_sync_request(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size)1477 dissect_group_sync_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1478 {
1479 proto_tree *header_tree;
1480
1481 /* Create the tree for the group sync request */
1482 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_group_sync, NULL, "Group Sync Request");
1483
1484 /* Read the grandmaster id from the payload */
1485 proto_tree_add_item(header_tree, hf_cip_ptp_grandmaster, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1486 }
1487
dissect_set_cyclic_list_request(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id,const char * service_name)1488 static void dissect_set_cyclic_list_request(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id, const char* service_name)
1489 {
1490 proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_set_cyclic_list, NULL, service_name);
1491
1492 guint32 attribute_cnt;
1493 proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1494
1495 // Skip Number of Attributes and Reserved field.
1496 offset += 4;
1497
1498 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1499 {
1500 guint32 attribute_id;
1501 proto_item* attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1502
1503 attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
1504 if (pattribute != NULL)
1505 {
1506 proto_item_append_text(attr_item, " (%s)", pattribute->text);
1507 }
1508
1509 offset += 2;
1510 }
1511 }
1512
dissect_set_cyclic_list_respone(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id,const char * service_name)1513 static void dissect_set_cyclic_list_respone(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id, const char* service_name)
1514 {
1515 proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_set_cyclic_list, NULL, service_name);
1516
1517 guint32 attribute_cnt;
1518 proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1519
1520 proto_tree_add_item(header_tree, hf_set_cyclic_list_read_block_id, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);
1521
1522 // Skip Number of Attributes and Cyclic Read Block ID field.
1523 offset += 4;
1524
1525 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1526 {
1527 guint32 attribute_id;
1528 proto_item* attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1529
1530 attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
1531 if (pattribute != NULL)
1532 {
1533 proto_item_append_text(attr_item, " (%s)", pattribute->text);
1534 }
1535
1536 offset += 2;
1537
1538 proto_tree_add_item(header_tree, hf_set_cyclic_list_attr_sts, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1539
1540 // Skip over Attribute Status and Reserved field.
1541 offset += 2;
1542 }
1543 }
1544
1545 /*
1546 * Function name: dissect_cntr_service
1547 *
1548 * Purpose: Dissect the "Service Data Block" in a Controller-to-Device message
1549 *
1550 * Returns: The new offset into the message that follow on dissections should use
1551 * as their starting offset
1552 */
1553 static guint32
dissect_cntr_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1554 dissect_cntr_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1555 {
1556 proto_tree *header_tree;
1557 guint32 service;
1558
1559 /* Create the tree for the entire service data block */
1560 proto_item *item;
1561 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, &item, "Service Data Block");
1562
1563 /* Display the transaction id value */
1564 proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1565
1566 /* Display the service code */
1567 proto_tree_add_item_ret_uint(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN, &service);
1568
1569 /* If the service is a set axis, get axis attribute or group sync request dissect it as well */
1570 if (size > 4)
1571 {
1572 switch (service)
1573 {
1574 case SC_GET_AXIS_ATTRIBUTE_LIST:
1575 dissect_get_axis_attr_list_request(tvb, header_tree, offset + 4, size - 4, instance_id);
1576 break;
1577 case SC_SET_AXIS_ATTRIBUTE_LIST:
1578 dissect_set_axis_attr_list_request(pinfo, tvb, header_tree, offset + 4, size - 4, instance_id);
1579 break;
1580 case SC_GROUP_SYNC:
1581 dissect_group_sync_request(tvb, header_tree, offset + 4, size - 4);
1582 break;
1583 case SC_SET_CYCLIC_WRITE_LIST:
1584 dissect_set_cyclic_list_request(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Write List Request");
1585 break;
1586 case SC_SET_CYCLIC_READ_LIST:
1587 dissect_set_cyclic_list_request(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Read List Request");
1588 break;
1589 case SC_SET_ATT_LIST:
1590 {
1591 cip_simple_request_info_t motion_path;
1592 motion_path.iClass = CI_CLS_MOTION;
1593 motion_path.iInstance = instance_id;
1594
1595 tvbuff_t* tvb_set_attr = tvb_new_subset_length(tvb, offset + 4, size - 4);
1596 int parsed_len = dissect_cip_set_attribute_list_req(tvb_set_attr, pinfo, header_tree, item, 0, &motion_path);
1597
1598 // Display any remaining unparsed data.
1599 int remain_len = tvb_reported_length_remaining(tvb, offset + 4 + parsed_len);
1600 if (remain_len > 0)
1601 {
1602 proto_tree_add_item(header_tree, hf_cip_attribute_data, tvb, offset + 4 + parsed_len, size - 4 - parsed_len, ENC_NA);
1603 }
1604
1605 break;
1606 }
1607 default:
1608 /* Display the remainder of the service channel data */
1609 proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1610 }
1611 }
1612
1613 return offset + size;
1614 }
1615
1616 /*
1617 * Function name: dissect_set_axis_attr_list_response
1618 *
1619 * Purpose: Dissect the set axis attribute list service response
1620 *
1621 * Returns: None
1622 */
1623 static void
dissect_set_axis_attr_list_response(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1624 dissect_set_axis_attr_list_response(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1625 {
1626 proto_item *attr_item;
1627 proto_tree *header_tree, *attr_tree;
1628 guint32 local_offset;
1629
1630 /* Create the tree for the set axis attribute list response */
1631 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_get_axis_attribute, NULL, "Set Axis Attribute List Response");
1632
1633 /* Read the number of attributes that are contained within the response */
1634 guint32 attribute_cnt;
1635 proto_tree_add_item_ret_uint(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1636
1637 /* Start the attribute loop at the beginning of the first attribute in the list */
1638 local_offset = offset + 4;
1639
1640 /* For each attribute display the associated fields */
1641 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1642 {
1643 /* Create the tree for the current attribute in the set axis attribute list response */
1644 guint32 attribute_id;
1645 attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1646 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1647
1648 /* Add the response status to the tree */
1649 proto_tree_add_item(attr_tree, hf_cip_svc_set_axis_attr_sts, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1650
1651 attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
1652 if (pattribute != NULL)
1653 {
1654 proto_item_append_text(attr_item, " (%s)", pattribute->text);
1655 }
1656
1657 /* Move the local offset to the next attribute */
1658 local_offset += 4;
1659 }
1660 }
1661
1662 /*
1663 * Function name: dissect_get_axis_attr_list_response
1664 *
1665 * Purpose: Dissect the get axis attribute list service response
1666 *
1667 * Returns: None
1668 */
1669 static void
dissect_get_axis_attr_list_response(packet_info * pinfo,tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1670 dissect_get_axis_attr_list_response(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1671 {
1672 proto_item *attr_item;
1673 proto_tree *header_tree, *attr_tree;
1674 guint32 local_offset;
1675
1676 /* Create the tree for the get axis attribute list response */
1677 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_get_axis_attribute, NULL, "Get Axis Attribute List Response");
1678
1679 /* Read the number of attributes that are contained within the request */
1680 guint32 attribute_cnt;
1681 proto_tree_add_item_ret_uint(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
1682
1683 /* Start the attribute loop at the beginning of the first attribute in the list */
1684 local_offset = offset + 4;
1685
1686 /* For each attribute display the associated fields */
1687 for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
1688 {
1689 /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1690 guint8 increment_size = 4;
1691
1692 /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1693 * properly displaying of the attribute is possible */
1694 guint8 dimension = tvb_get_guint8(tvb, local_offset + 2);
1695 guint32 attribute_size = tvb_get_guint8(tvb, local_offset + 3);
1696 guint8 attribute_start = 4;
1697
1698 if (dimension == 1)
1699 {
1700 guint16 data_elements = tvb_get_letohs(tvb, local_offset + 6);
1701
1702 /* Modify the size of the attribute data by the number of elements if the request is an array request */
1703 attribute_size *= data_elements;
1704
1705 /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1706 increment_size += 4;
1707 attribute_start += 4;
1708 }
1709
1710 /* Display the fields associated with the get axis attribute list response */
1711 guint32 attribute_id;
1712 attr_item = proto_tree_add_item_ret_uint(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
1713 attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1714
1715 if (dimension == 0xFF)
1716 {
1717 /* Display the element size as an error code if the dimension field indicates an error */
1718 proto_tree_add_item(attr_tree, hf_cip_svc_get_axis_attr_sts, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1719
1720 /* No attribute data so no attribute size */
1721 attribute_size = 0;
1722 }
1723 else
1724 {
1725 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1726 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1727
1728 if (dimension == 1)
1729 {
1730 /* Display the start index and start index from the request */
1731 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1732 proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1733 }
1734
1735 int parsed_len = dissect_motion_attribute(pinfo, tvb, local_offset + attribute_start, attribute_id,
1736 instance_id, attr_item, attr_tree, dimension, attribute_size);
1737
1738 // Display the raw attribute data if configured. Otherwise, just show the remaining unparsed data
1739 if (display_full_attribute_data)
1740 {
1741 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
1742 }
1743 else if ((attribute_size - parsed_len) > 0)
1744 {
1745 proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start + parsed_len, attribute_size - parsed_len, ENC_NA);
1746 }
1747
1748 /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1749 if (attribute_size % 4 != 0)
1750 {
1751 attribute_size = attribute_size + (4 - (attribute_size % 4));
1752 }
1753 }
1754
1755 /* Move the local offset to the next attribute */
1756 local_offset += (attribute_size + increment_size);
1757 }
1758 }
1759
1760 /*
1761 * Function name: dissect_group_sync_response
1762 *
1763 * Purpose: Dissect the group sync service response
1764 *
1765 * Returns: None
1766 */
1767 static void
dissect_group_sync_response(tvbuff_t * tvb,proto_tree * tree,guint32 offset)1768 dissect_group_sync_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset)
1769 {
1770 proto_tree_add_item(tree, hf_cip_group_sync, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1771 }
1772
1773 /*
1774 * Function name: dissect_devce_service
1775 *
1776 * Purpose: Dissect the "Service Data Block" in a Device-to-Controller message
1777 *
1778 * Returns: The new offset into the message that follow on dissections should use
1779 * as their starting offset
1780 */
1781 static guint32
dissect_devce_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,guint32 offset,guint32 size,guint32 instance_id)1782 dissect_devce_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id)
1783 {
1784 proto_tree *header_tree;
1785
1786 /* Create the tree for the entire service data block */
1787 proto_item* item;
1788 header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, &item, "Service Data Block");
1789
1790 /* Display the transaction id value */
1791 proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1792
1793 /* Display the service code */
1794 guint32 service_code;
1795 proto_tree_add_item_ret_uint(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN, &service_code);
1796
1797 /* Display the general status code */
1798 proto_tree_add_item(header_tree, hf_cip_svc_sts, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1799
1800 /* Display the extended status code */
1801 proto_tree_add_item(header_tree, hf_cip_svc_ext_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1802
1803 /* If the service is a set axis, get axis attribute response or group sync dissect it as well */
1804 if (size > 4)
1805 {
1806 switch (service_code)
1807 {
1808 case SC_GET_AXIS_ATTRIBUTE_LIST:
1809 dissect_get_axis_attr_list_response(pinfo, tvb, header_tree, offset + 4, size - 4, instance_id);
1810 break;
1811 case SC_SET_AXIS_ATTRIBUTE_LIST:
1812 dissect_set_axis_attr_list_response(tvb, header_tree, offset + 4, size - 4, instance_id);
1813 break;
1814 case SC_GROUP_SYNC:
1815 dissect_group_sync_response(tvb, header_tree, offset + 4);
1816 break;
1817 case SC_SET_CYCLIC_WRITE_LIST:
1818 dissect_set_cyclic_list_respone(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Write List Response");
1819 break;
1820 case SC_SET_CYCLIC_READ_LIST:
1821 dissect_set_cyclic_list_respone(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Read List Response");
1822 break;
1823 case SC_SET_ATT_LIST:
1824 {
1825 cip_simple_request_info_t motion_path;
1826 motion_path.iClass = CI_CLS_MOTION;
1827 motion_path.iInstance = instance_id;
1828
1829 tvbuff_t* tvb_set_attr = tvb_new_subset_length(tvb, offset + 4, size - 4);
1830 dissect_cip_set_attribute_list_rsp(tvb_set_attr, pinfo, header_tree, item, 0, &motion_path);
1831 break;
1832 }
1833 default:
1834 /* Display the remainder of the service channel data */
1835 proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1836 break;
1837 }
1838 }
1839
1840 return offset + size;
1841 }
1842
1843 /*
1844 * Function name: dissect_var_inst_header
1845 *
1846 * Purpose: Dissect the instance data header of a variable controller to device or
1847 * device to controller message
1848 *
1849 * Returns: void
1850 */
1851 static void
dissect_var_inst_header(tvbuff_t * tvb,proto_tree * tree,guint32 offset,guint8 * inst_number,guint32 * cyc_size,guint32 * cyc_blk_size,guint32 * evnt_size,guint32 * servc_size)1852 dissect_var_inst_header(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint8* inst_number, guint32* cyc_size,
1853 guint32* cyc_blk_size, guint32* evnt_size, guint32* servc_size)
1854 {
1855 proto_tree *header_tree;
1856
1857 /* Create the tree for the entire instance data header */
1858 *inst_number = tvb_get_guint8(tvb, offset);
1859
1860 header_tree = proto_tree_add_subtree_format(tree, tvb, offset, 8, ett_inst_data_header, NULL,
1861 "Instance Data Header - Instance: %d", *inst_number);
1862
1863 /* Read the instance number field from the instance data header */
1864 proto_tree_add_item(header_tree, hf_var_devce_instance, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1865
1866 /* The "size" fields in the instance data block header are all stored as number of 32-bit words the
1867 * block uses since all blocks should pad up to 32-bits so to convert to bytes each is multiplied by 4 */
1868
1869 /* Read the instance block size field in bytes from the instance data header */
1870 proto_tree_add_item(header_tree, hf_var_devce_instance_block_size, tvb, offset + 2, 1, ENC_NA);
1871
1872 /* Read the cyclic block size field in bytes from the instance data header */
1873 proto_tree_add_item(header_tree, hf_var_devce_cyclic_block_size, tvb, offset + 3, 1, ENC_NA);
1874
1875 /* Read the cyclic command block size field in bytes from the instance data header */
1876 *cyc_size = (tvb_get_guint8(tvb, offset + 4) * 4);
1877 proto_tree_add_item(header_tree, hf_var_devce_cyclic_data_block_size, tvb, offset + 4, 1, ENC_NA);
1878
1879 /* Read the cyclic write block size field in bytes from the instance data header */
1880 *cyc_blk_size = (tvb_get_guint8(tvb, offset + 5) * 4);
1881 proto_tree_add_item(header_tree, hf_var_devce_cyclic_rw_block_size, tvb, offset + 5, 1, ENC_NA);
1882
1883 /* Read the event block size in bytes from the instance data header */
1884 *evnt_size = (tvb_get_guint8(tvb, offset + 6) * 4);
1885 proto_tree_add_item(header_tree, hf_var_devce_event_block_size, tvb, offset + 6, 1, ENC_NA);
1886
1887 /* Read the service block size in bytes from the instance data header */
1888 *servc_size = (tvb_get_guint8(tvb, offset + 7) * 4);
1889 proto_tree_add_item(header_tree, hf_var_devce_service_block_size, tvb, offset + 7, 1, ENC_NA);
1890 }
1891
1892 /*
1893 * Function name: dissect_var_cont_conn_header
1894 *
1895 * Purpose: Dissect the connection header of a variable controller to device message
1896 *
1897 * Returns: Offset to the start of the instance data block
1898 */
1899 static guint32
dissect_var_cont_conn_header(tvbuff_t * tvb,proto_tree * tree,guint32 * inst_count,guint32 offset)1900 dissect_var_cont_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1901 {
1902 guint32 header_size;
1903 proto_tree *header_tree;
1904
1905 /* Calculate the header size, start with the basic header size */
1906 header_size = 8;
1907
1908 guint32 time_data_set = tvb_get_guint8(tvb, offset + 7);
1909
1910 /* Check the time data set field for enabled bits. If either update period or
1911 * update time stamp fields are set, bump the header size by the appropriate size */
1912 if ( (time_data_set & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1913 {
1914 header_size += 8;
1915 }
1916 if ( (time_data_set & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1917 {
1918 header_size += 8;
1919 }
1920
1921 /* Create the tree for the entire connection header */
1922 header_tree = proto_tree_add_subtree(tree, tvb, offset, header_size, ett_cont_dev_header, NULL, "Connection Header");
1923
1924 /* Add the connection header fields that are common to all types of messages */
1925 proto_tree_add_item(header_tree, hf_cip_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1926 proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1927 proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1928
1929 dissect_node_control(NULL, header_tree, NULL, tvb, offset + 3, 1);
1930
1931 /* Add the instance count and last update id to the connection header tree */
1932 proto_tree_add_item_ret_uint(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN, inst_count);
1933 proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
1934
1935 dissect_time_data_set(NULL, header_tree, NULL, tvb, offset + 7, 1);
1936
1937 /* Move the offset to the byte just beyond the time data set field */
1938 offset = (offset + 7 + 1);
1939
1940 /* Add the time values if they are present in the time data set header field */
1941 if ( (time_data_set & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1942 {
1943 proto_tree_add_item(header_tree, hf_cip_cont_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1944 offset = (offset + 8);
1945 }
1946
1947 if ( (time_data_set & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1948 {
1949 proto_tree_add_item(header_tree, hf_cip_cont_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1950 offset = (offset + 8);
1951 }
1952
1953 /* Return the number of bytes used so it can be used as an offset in the following dissections */
1954 return offset;
1955 }
1956
1957 /*
1958 * Function name: dissect_var_devce_conn_header
1959 *
1960 * Purpose: Dissect the connection header of a variable device to controller message
1961 *
1962 * Returns: Offset to the start of the instance data block
1963 */
1964 static guint32
dissect_var_devce_conn_header(tvbuff_t * tvb,proto_tree * tree,guint32 * inst_count,guint32 offset)1965 dissect_var_devce_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1966 {
1967 guint32 header_size;
1968 proto_tree *header_tree;
1969
1970 /* Calculate the header size, start with the basic header size */
1971 header_size = 8;
1972
1973 guint32 time_data_set = tvb_get_guint8(tvb, offset + 7);
1974 if ( (time_data_set & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1975 {
1976 header_size += 8;
1977 }
1978 if ( (time_data_set & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1979 {
1980 header_size += 8;
1981 }
1982 if ( (time_data_set & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
1983 {
1984 header_size += 4;
1985 }
1986 if ( (time_data_set & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
1987 {
1988 header_size += 16;
1989 }
1990
1991 /* Create the tree for the entire connection header */
1992 header_tree = proto_tree_add_subtree(tree, tvb, offset, header_size, ett_cont_dev_header, NULL, "Connection Header");
1993
1994 /* Add the connection header fields that are common to all types of messages */
1995 proto_tree_add_item(header_tree, hf_cip_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1996 proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1997 proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1998
1999 dissect_node_status(NULL, header_tree, NULL, tvb, offset + 3, 1);
2000
2001 /* Add the instance count to the connection header tree */
2002 proto_tree_add_item_ret_uint(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN, inst_count);
2003
2004 /* The device to controller header contains the node alarms and node faults fields as well. */
2005 proto_tree_add_item(header_tree, hf_cip_node_fltalarms, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
2006
2007 /* Add the last update id to the connection header tree */
2008 proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
2009
2010 dissect_time_data_set(NULL, header_tree, NULL, tvb, offset + 7, 1);
2011
2012 /* Move the offset to the byte just beyond the time data set field */
2013 offset = (offset + 7 + 1);
2014
2015 /* Add the time values if they are present in the time data set header field */
2016 if ( (time_data_set & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
2017 {
2018 proto_tree_add_item(header_tree, hf_cip_devc_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
2019 offset = (offset + 8);
2020 }
2021
2022 if ( (time_data_set & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
2023 {
2024 proto_tree_add_item(header_tree, hf_cip_devc_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
2025 offset = (offset + 8);
2026 }
2027
2028 if ( (time_data_set & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
2029 {
2030 /* If the time diagnostic bit is set then the header contains the count of lost updates, late updates, data
2031 * received time stamp and data transmit time stamp */
2032 proto_tree_add_item(header_tree, hf_cip_lost_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
2033 offset = (offset + 1);
2034
2035 /* Add the reserved bytes to the offset after adding the late updates to the display */
2036 proto_tree_add_item(header_tree, hf_cip_late_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
2037 offset = (offset + 3);
2038 }
2039
2040 if ( (time_data_set & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
2041 {
2042 proto_tree_add_item(header_tree, hf_cip_data_rx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
2043 offset += 8;
2044
2045 proto_tree_add_item(header_tree, hf_cip_data_tx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
2046 offset += 8;
2047 }
2048
2049 /* Return the number of bytes used so it can be used as an offset in the following dissections */
2050 return offset;
2051 }
2052
2053
2054 /*
2055 * Function name: dissect_cipmotion
2056 *
2057 * Purpose: Perform the top level dissection of the CIP Motion datagram, it is called by
2058 * Wireshark when the dissection rule registered in proto_reg_handoff_cipmotion is fired
2059 *
2060 * Returns: void
2061 */
2062 static int
dissect_cipmotion(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data)2063 dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data)
2064 {
2065 cip_io_data_input* io_data_input = (cip_io_data_input*)data;
2066
2067 guint32 con_format;
2068 guint32 update_id;
2069 proto_item *proto_item_top;
2070 proto_tree *proto_tree_top;
2071 guint32 offset = 0;
2072
2073 guint8 ConnPoint = 2;
2074 if (io_data_input && io_data_input->conn_info)
2075 {
2076 ConnPoint = io_data_input->conn_info->ConnPoint;
2077 }
2078
2079 /* Create display subtree for the protocol by creating an item and then
2080 * creating a subtree from the item, the subtree must have been registered
2081 * in proto_register_cipmotion already */
2082 proto_item_top = proto_tree_add_item(tree, proto_cipmotion, tvb, 0, -1, ENC_NA);
2083 proto_tree_top = proto_item_add_subtree(proto_item_top, ett_cipmotion);
2084
2085 /* Add the CIP class 1 sequence number to the tree */
2086 proto_tree_add_item(proto_tree_top, hf_cip_class1_seqnum, tvb, offset, 2, ENC_LITTLE_ENDIAN);
2087 offset = (offset + 2);
2088
2089 if (ConnPoint >= 3)
2090 {
2091 dissect_cip_run_idle(tvb, offset, proto_tree_top);
2092 offset += 4;
2093 }
2094
2095 /* Pull the actual values for the connection format and update id from the
2096 * incoming message to be used in the column info */
2097 con_format = tvb_get_guint8(tvb, offset);
2098 update_id = tvb_get_guint8(tvb, offset + 2);
2099
2100 /* Make entries in Protocol column and Info column on summary display */
2101 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP Motion");
2102
2103 /* Add connection format and update number to the info column */
2104 col_add_fstr( pinfo->cinfo, COL_INFO, "%s, Update Id: %d",
2105 val_to_str(con_format, cip_con_format_vals, "Unknown connection format (%x)"), update_id );
2106
2107 /* Attempt to classify the incoming header */
2108 if (( con_format == FORMAT_VAR_CONTROL_TO_DEVICE ) ||
2109 ( con_format == FORMAT_VAR_DEVICE_TO_CONTROL ))
2110 {
2111 /* Sizes of the individual channels within the connection */
2112 guint32 cyc_size, cyc_blk_size, evnt_size, servc_size;
2113 guint32 inst_count = 0, inst;
2114 guint32 format_rev = 0;
2115
2116 /* Dissect the header fields */
2117 switch(con_format)
2118 {
2119 case FORMAT_VAR_CONTROL_TO_DEVICE:
2120 format_rev = tvb_get_guint8(tvb, offset + 1);
2121 offset = dissect_var_cont_conn_header(tvb, proto_tree_top, &inst_count, offset);
2122 break;
2123 case FORMAT_VAR_DEVICE_TO_CONTROL:
2124 format_rev = tvb_get_guint8(tvb, offset + 1);
2125 offset = dissect_var_devce_conn_header(tvb, proto_tree_top, &inst_count, offset);
2126 break;
2127 }
2128
2129 if (format_rev != ConnPoint)
2130 {
2131 expert_add_info(pinfo, proto_item_top, &ei_format_rev_conn_pt);
2132 }
2133
2134 /* Repeat the following dissections for each instance within the payload */
2135 for( inst = 0; inst < inst_count; inst++ )
2136 {
2137 /* Actual instance number from header field */
2138 guint8 instance;
2139
2140 /* Dissect the instance data header */
2141 dissect_var_inst_header( tvb, proto_tree_top, offset, &instance,
2142 &cyc_size, &cyc_blk_size, &evnt_size, &servc_size );
2143
2144 /* Increment the offset to just beyond the instance header */
2145 offset += 8;
2146
2147 /* Dissect the cyclic command (actual) data if any exists */
2148 /* Dissect the cyclic write (read) data if any exists */
2149 /* Dissect the event data block if there is any event data */
2150 switch(con_format)
2151 {
2152 case FORMAT_VAR_CONTROL_TO_DEVICE:
2153 if ( cyc_size > 0 )
2154 offset = dissect_cntr_cyclic(tvb, proto_tree_top, offset, cyc_size);
2155 if ( cyc_blk_size > 0 )
2156 offset = dissect_cyclic_wt(tvb, proto_tree_top, offset, cyc_blk_size);
2157 if ( evnt_size > 0 )
2158 offset = dissect_cntr_event(tvb, proto_tree_top, offset, evnt_size);
2159 if ( servc_size > 0 )
2160 offset = dissect_cntr_service(tvb, pinfo, proto_tree_top, offset, servc_size, instance);
2161 break;
2162 case FORMAT_VAR_DEVICE_TO_CONTROL:
2163 if ( cyc_size > 0 )
2164 offset = dissect_device_cyclic(tvb, proto_tree_top, offset, cyc_size);
2165 if ( cyc_blk_size > 0 )
2166 offset = dissect_cyclic_rd( tvb, proto_tree_top, offset, cyc_blk_size );
2167 if ( evnt_size > 0 )
2168 offset = dissect_devce_event(tvb, proto_tree_top, offset, evnt_size);
2169 if ( servc_size > 0 )
2170 offset = dissect_devce_service(tvb, pinfo, proto_tree_top, offset, servc_size, instance);
2171 break;
2172 }
2173
2174 } /* End of instance for( ) loop */
2175 }
2176
2177 // Display any remaining unparsed data.
2178 int remain_len = tvb_reported_length_remaining(tvb, offset);
2179 if (remain_len > 0)
2180 {
2181 proto_tree_add_item(proto_tree_top, hf_cip_data, tvb, offset, remain_len, ENC_NA);
2182 }
2183
2184 return tvb_captured_length(tvb);
2185 }
2186
dissect_cipmotion3(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)2187 static int dissect_cipmotion3(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_)
2188 {
2189 cip_conn_info_t conn_info;
2190 memset(&conn_info, 0, sizeof(conn_info));
2191 conn_info.ConnPoint = 3;
2192
2193 cip_io_data_input io_data_input;
2194 io_data_input.conn_info = &conn_info;
2195
2196 return dissect_cipmotion(tvb, pinfo, tree, &io_data_input);
2197 }
2198
dissect_motion_configuration_block(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * item,int offset)2199 int dissect_motion_configuration_block(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* item, int offset)
2200 {
2201 proto_item* config_item;
2202 proto_tree* config_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_configuration_block, &config_item, "Motion Configuration Block");
2203
2204 proto_tree_add_item(config_tree, hf_configuration_block_format_rev, tvb, offset, 1, ENC_LITTLE_ENDIAN);
2205 int parsed_len = 1;
2206
2207 parsed_len += dissect_connection_configuration_bits(pinfo, config_tree, item, tvb, offset + parsed_len, 1);
2208
2209 // 2 reserved bytes
2210 parsed_len += 2;
2211
2212 proto_tree_add_item(config_tree, hf_configuration_block_drive_power_struct_id, tvb, offset + parsed_len, 4, ENC_LITTLE_ENDIAN);
2213 parsed_len += 4;
2214
2215 proto_item_set_len(config_item, parsed_len);
2216
2217 return parsed_len;
2218 }
2219
2220 /*
2221 * Function name: proto_register_cipmotion
2222 *
2223 * Purpose: Register the protocol with Wireshark, a script will add this protocol
2224 * to the list of protocols during the build process. This function is where the
2225 * header fields and subtree identifiers are registered.
2226 *
2227 * Returns: void
2228 */
2229 void
proto_register_cipmotion(void)2230 proto_register_cipmotion(void)
2231 {
2232 /* This is a list of header fields that can be used in the dissection or
2233 * to use in a filter expression */
2234 static hf_register_info hf[] =
2235 {
2236 /* Connection format header field, the first byte in the message which
2237 * determines if the message is fixed or variable, controller to device,
2238 * device to controller, etc. */
2239 { &hf_cip_format,
2240 { "Connection Format", "cipm.format",
2241 FT_UINT8, BASE_DEC, VALS(cip_con_format_vals), 0,
2242 "Message connection format", HFILL }
2243 },
2244
2245 /* Connection format revision header field */
2246 { &hf_cip_revision,
2247 { "Format Revision", "cipm.revision",
2248 FT_UINT8, BASE_DEC, NULL, 0,
2249 "Message format revision", HFILL }
2250 },
2251
2252 { &hf_cip_class1_seqnum,
2253 { "CIP Class 1 Sequence Count", "cipm.class1seqnum",
2254 FT_UINT16, BASE_DEC, NULL, 0,
2255 NULL, HFILL }
2256 },
2257
2258 { &hf_configuration_block_format_rev,
2259 { "Format Revision", "cipm.config.format_rev",
2260 FT_UINT8, BASE_DEC, NULL, 0,
2261 NULL, HFILL }
2262 },
2263
2264 { &hf_configuration_block_drive_power_struct_id,
2265 { "Drive Power Structure Class ID", "cipm.config.drive_class_id",
2266 FT_UINT32, BASE_DEC, NULL, 0,
2267 NULL, HFILL }
2268 },
2269
2270 { &hf_cip_updateid,
2271 { "Update Id", "cipm.updateid",
2272 FT_UINT8, BASE_DEC, NULL, 0,
2273 "Cyclic Transaction Number", HFILL }
2274 },
2275 { &hf_cip_instance_cnt,
2276 { "Instance Count", "cipm.instancecount",
2277 FT_UINT8, BASE_DEC, NULL, 0,
2278 NULL, HFILL }
2279 },
2280 { &hf_cip_last_update,
2281 { "Last Update Id", "cipm.lastupdate",
2282 FT_UINT8, BASE_DEC, NULL, 0,
2283 NULL, HFILL }
2284 },
2285 { &hf_cip_node_status,
2286 { "Node Status", "cipm.nodestatus",
2287 FT_UINT8, BASE_HEX, NULL, 0,
2288 NULL, HFILL}
2289 },
2290 { &hf_cip_node_control,
2291 { "Node Control", "cipm.nodecontrol",
2292 FT_UINT8, BASE_HEX, NULL, 0,
2293 NULL, HFILL}
2294 },
2295 { &hf_cip_node_control_remote,
2296 { "Remote Control", "cipm.remote",
2297 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01,
2298 "Node Control: Remote Control", HFILL}
2299 },
2300 { &hf_cip_node_control_sync,
2301 { "Sync Control", "cipm.sync",
2302 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02,
2303 "Node Control: Synchronous Operation", HFILL}
2304 },
2305 { &hf_cip_node_data_valid,
2306 { "Data Valid", "cipm.valid",
2307 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04,
2308 "Node Control: Data Valid", HFILL}
2309 },
2310 { &hf_cip_node_fault_reset,
2311 { "Node Fault Reset", "cipm.fltrst",
2312 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08,
2313 "Node Control: Node Fault Reset", HFILL}
2314 },
2315 { &hf_cip_node_device_faulted,
2316 { "Faulted", "cipm.flt",
2317 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08,
2318 "Node Control: Device Faulted", HFILL}
2319 },
2320 { &hf_cip_node_fltalarms,
2321 { "Node Faults and Alarms", "cipm.fltalarms",
2322 FT_UINT8, BASE_DEC, NULL, 0,
2323 NULL, HFILL }
2324 },
2325 { &hf_cip_time_data_set,
2326 { "Time Data Set", "cipm.timedataset",
2327 FT_UINT8, BASE_HEX, NULL, 0,
2328 NULL, HFILL}
2329 },
2330 { &hf_cip_time_data_stamp,
2331 { "Time Stamp", "cipm.time.stamp",
2332 FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_STAMP,
2333 "Time Data Set: Time Stamp", HFILL}
2334 },
2335 { &hf_cip_time_data_offset,
2336 { "Time Offset", "cipm.time.offset",
2337 FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_OFFSET,
2338 "Time Data Set: Time Offset", HFILL}
2339 },
2340 { &hf_cip_time_data_diag,
2341 { "Update Diagnostics", "cipm.time.update",
2342 FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_UPDATE_DIAGNOSTICS,
2343 "Time Data Set: Update Diagnostics", HFILL}
2344 },
2345 { &hf_cip_time_data_time_diag,
2346 { "Time Diagnostics", "cipm.time.diag",
2347 FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_DIAGNOSTICS,
2348 "Time Data Set: Time Diagnostics", HFILL}
2349 },
2350
2351 { &hf_cip_cont_time_stamp,
2352 { "Controller Time Stamp", "cipm.ctrltimestamp",
2353 FT_UINT64, BASE_DEC, NULL, 0,
2354 "Time Data Set: Controller Time Stamp", HFILL}
2355 },
2356 { &hf_cip_cont_time_offset,
2357 { "Controller Time Offset", "cipm.ctrltimeoffser",
2358 FT_UINT64, BASE_DEC, NULL, 0,
2359 "Time Data Set: Controller Time Offset", HFILL}
2360 },
2361 { &hf_cip_data_rx_time_stamp,
2362 { "Data Received Time Stamp", "cipm.rxtimestamp",
2363 FT_UINT64, BASE_DEC, NULL, 0,
2364 "Time Data Set: Data Received Time Stamp", HFILL}
2365 },
2366 { &hf_cip_data_tx_time_stamp,
2367 { "Data Transmit Time Stamp", "cipm.txtimestamp",
2368 FT_UINT64, BASE_DEC, NULL, 0,
2369 "Time Data Set: Data Transmit Time Offset", HFILL}
2370 },
2371 { &hf_cip_devc_time_stamp,
2372 { "Device Time Stamp", "cipm.devctimestamp",
2373 FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_nanosecond_nanoseconds, 0,
2374 "Time Data Set: Device Time Stamp", HFILL}
2375 },
2376 { &hf_cip_devc_time_offset,
2377 { "Device Time Offset", "cipm.devctimeoffser",
2378 FT_UINT64, BASE_DEC, NULL, 0,
2379 "Time Data Set: Device Time Offset", HFILL}
2380 },
2381 { &hf_cip_lost_update,
2382 { "Lost Updates", "cipm.lostupdates",
2383 FT_UINT8, BASE_DEC, NULL, 0,
2384 "Time Data Set: Lost Updates", HFILL}
2385 },
2386 { &hf_cip_late_update,
2387 { "Lost Updates", "cipm.lateupdates",
2388 FT_UINT8, BASE_DEC, NULL, 0,
2389 "Time Data Set: Late Updates", HFILL}
2390 },
2391
2392 { &hf_cip_motor_cntrl,
2393 { "Control Mode", "cipm.ctrlmode",
2394 FT_UINT8, BASE_DEC, VALS(cip_motor_control_vals), 0,
2395 "Cyclic Data Block: Motor Control Mode", HFILL }
2396 },
2397
2398 { &hf_cip_feedback,
2399 { "Feedback Information", "cipm.feedback",
2400 FT_UINT8, BASE_HEX, NULL, 0,
2401 NULL, HFILL }
2402 },
2403 { &hf_cip_feedback_mode,
2404 { "Feedback Mode", "cipm.feedback_mode",
2405 FT_UINT8, BASE_DEC, VALS(cip_feedback_mode_vals), FEEDBACK_MODE_BITS,
2406 NULL, HFILL }
2407 },
2408 { &hf_cip_feedback_data_type,
2409 { "Feedback Data Type", "cipm.feedback_data_type",
2410 FT_UINT8, BASE_DEC, VALS(cip_feedback_type_vals), FEEDBACK_DATA_TYPE_BITS,
2411 NULL, HFILL }
2412 },
2413
2414 { &hf_connection_configuration_bits,
2415 { "Connection Configuration Bits", "cipm.ccb",
2416 FT_UINT8, BASE_DEC, NULL, 0,
2417 NULL, HFILL }
2418 },
2419 { &hf_connection_configuration_bits_power,
2420 { "Verify Power Ratings", "cipm.ccb.verify_power_ratings",
2421 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01,
2422 NULL, HFILL } },
2423 { &hf_connection_configuration_bits_safety_bit_valid,
2424 { "Networked Safety Bit Valid", "cipm.ccb.networked_safety_bit_valid",
2425 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02,
2426 NULL, HFILL } },
2427 { &hf_connection_configuration_bits_allow_network_safety,
2428 { "Allow Networked Safety", "cipm.ccb.allow_networked_safety",
2429 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04,
2430 NULL, HFILL } },
2431
2432 { &hf_cip_axis_control,
2433 { "Axis Control", "cipm.axisctrl",
2434 FT_UINT8, BASE_DEC, VALS(cip_axis_control_vals), 0,
2435 "Cyclic Data Block: Axis Control", HFILL }
2436 },
2437 { &hf_cip_control_status,
2438 { "Control Status", "cipm.csts",
2439 FT_UINT8, BASE_DEC, NULL, 0,
2440 "Cyclic Data Block: Axis Control Status", HFILL }
2441 },
2442 { &hf_cip_control_status_complete,
2443 { "Configuration Complete", "cipm.control_status.complete",
2444 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01,
2445 NULL, HFILL } },
2446 { &hf_cip_control_status_bus_up,
2447 { "Converter Bus Up", "cipm.control_status.bus_up",
2448 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04,
2449 NULL, HFILL } },
2450 { &hf_cip_control_status_bus_unload,
2451 { "Converter Bus Unload", "cipm.control_status.bus_unload",
2452 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08,
2453 NULL, HFILL } },
2454 { &hf_cip_control_status_power_loss,
2455 { "Converter AC Power Loss", "cipm.control_status.power_loss",
2456 FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x10,
2457 NULL, HFILL } },
2458 { &hf_cip_axis_response,
2459 { "Axis Response", "cipm.axisresp",
2460 FT_UINT8, BASE_DEC, VALS(cip_axis_response_vals), 0,
2461 "Cyclic Data Block: Axis Response", HFILL }
2462 },
2463 { &hf_cip_axis_resp_stat,
2464 { "Response Status", "cipm.respstat",
2465 FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0,
2466 "Cyclic Data Block: Axis Response Status", HFILL }
2467 },
2468 { &hf_cip_group_sync,
2469 { "Group Sync Status", "cipm.syncstatus",
2470 FT_UINT8, BASE_HEX, VALS(cip_sync_status_vals), 0,
2471 NULL, HFILL }
2472 },
2473 { &hf_cip_cmd_data_set,
2474 { "Command Data Set", "cipm.cmdset",
2475 FT_UINT8, BASE_HEX, NULL, 0,
2476 NULL, HFILL}
2477 },
2478 { &hf_cip_act_data_set,
2479 { "Actual Data Set", "cipm.actset",
2480 FT_UINT8, BASE_HEX, NULL, 0,
2481 NULL, HFILL}
2482 },
2483 { &hf_cip_sts_data_set,
2484 { "Status Data Set", "cipm.stsset",
2485 FT_UINT8, BASE_HEX, NULL, 0,
2486 NULL, HFILL}
2487 },
2488
2489 // Command Data Set
2490 { &hf_cip_cmd_data_pos_cmd,
2491 { "Command Position", "cipm.cmd.pos",
2492 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION,
2493 "Command Data Set: Command Position", HFILL}
2494 },
2495 { &hf_cip_cmd_data_vel_cmd,
2496 { "Command Velocity", "cipm.cmd.vel",
2497 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_VELOCITY,
2498 "Command Data Set: Command Velocity", HFILL}
2499 },
2500 { &hf_cip_cmd_data_acc_cmd,
2501 { "Command Acceleration", "cipm.cmd.acc",
2502 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_ACCELERATION,
2503 "Command Data Set: Command Acceleration", HFILL}
2504 },
2505 { &hf_cip_cmd_data_trq_cmd,
2506 { "Command Torque", "cipm.cmd.trq",
2507 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_TORQUE,
2508 "Command Data Set: Command Torque", HFILL}
2509 },
2510 { &hf_cip_cmd_data_unwind_cycle_count,
2511 { "Unwind Cycle Count", "cipm.cmd.unwind",
2512 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_UNWIND_CYCLE_COUNT,
2513 "Command Data Set: Unwind Cycle Count", HFILL}
2514 },
2515 { &hf_cip_cmd_data_pos_displacement,
2516 { "Position Displacement", "cipm.cmd.pos_displacement",
2517 FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION_DISPLACE,
2518 "Command Data Set: Position Displacement", HFILL}
2519 },
2520
2521 // Actual Data Set
2522 { &hf_cip_act_data_pos,
2523 { "Actual Position", "cipm.act.pos",
2524 FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_POSITION,
2525 "Actual Data Set: Actual Position", HFILL}
2526 },
2527 { &hf_cip_act_data_vel,
2528 { "Actual Velocity", "cipm.act.vel",
2529 FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_VELOCITY,
2530 "Actual Data Set: Actual Velocity", HFILL}
2531 },
2532 { &hf_cip_act_data_acc,
2533 { "Actual Acceleration", "cipm.act.acc",
2534 FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_ACCELERATION,
2535 "Actual Data Set: Actual Acceleration", HFILL}
2536 },
2537 { &hf_cip_act_unwind_cycle_count,
2538 { "Unwind Cycle Count", "cipm.act.unwind",
2539 FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_UNWIND_CYCLE_COUNT,
2540 "Actual Data Set: Unwind Cycle Count", HFILL}
2541 },
2542 { &hf_cip_act_pos_displacement,
2543 { "Position Displacement", "cipm.act.pos_displacement",
2544 FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_POSITION_DISPLACE,
2545 "Actual Data Set: Position Displacement", HFILL}
2546 },
2547
2548 { &hf_cip_axis_fault,
2549 { "Axis Fault Code", "cipm.fault.code",
2550 FT_UINT8, BASE_DEC, NULL, 0,
2551 "Status Data Set: Fault Code", HFILL }
2552 },
2553 { &hf_cip_fault_type,
2554 { "Axis Fault Type", "cipm.flttype",
2555 FT_UINT8, BASE_DEC, NULL, 0,
2556 "Axis Status: Axis Fault Type", HFILL}
2557 },
2558 { &hf_cip_fault_sub_code,
2559 { "Axis Fault Sub Code", "cipm.fltsubcode",
2560 FT_UINT8, BASE_DEC, NULL, 0,
2561 "Axis Status: Axis Fault Sub Code", HFILL}
2562 },
2563 { &hf_cip_fault_action,
2564 { "Axis Fault Action", "cipm.fltaction",
2565 FT_UINT8, BASE_DEC, NULL, 0,
2566 "Axis Status: Axis Fault Action", HFILL}
2567 },
2568 { &hf_cip_fault_time_stamp,
2569 { "Axis Fault Time Stamp", "cipm.flttimestamp",
2570 FT_UINT64, BASE_DEC, NULL, 0,
2571 "Axis Status: Axis Fault Time Stamp", HFILL}
2572 },
2573 { &hf_cip_alarm_type,
2574 { "Axis Fault Type", "cipm.alarmtype",
2575 FT_UINT8, BASE_DEC, NULL, 0,
2576 "Axis Status: Axis Alarm Type", HFILL}
2577 },
2578 { &hf_cip_alarm_sub_code,
2579 { "Axis Alarm Sub Code", "cipm.alarmsubcode",
2580 FT_UINT8, BASE_DEC, NULL, 0,
2581 "Axis Status: Axis Alarm Sub Code", HFILL}
2582 },
2583 { &hf_cip_alarm_state,
2584 { "Axis Alarm State", "cipm.alarmstate",
2585 FT_UINT8, BASE_DEC, NULL, 0,
2586 "Axis Status: Axis Alarm State", HFILL }
2587 },
2588 { &hf_cip_alarm_time_stamp,
2589 { "Axis Fault Time Stamp", "cipm.alarmtimestamp",
2590 FT_UINT64, BASE_DEC, NULL, 0,
2591 "Axis Status: Axis Alarm Time Stamp", HFILL}
2592 },
2593 { &hf_cip_axis_status,
2594 { "Axis Status", "cipm.axisstatus",
2595 FT_UINT32, BASE_HEX, NULL, 0,
2596 NULL, HFILL}
2597 },
2598 { &hf_cip_axis_status_mfg,
2599 { "Axis Status Mfg", "cipm.axisstatusmfg",
2600 FT_UINT32, BASE_HEX, NULL, 0,
2601 "Axis Status, Manufacturer Specific", HFILL}
2602 },
2603 { &hf_cip_axis_io_status,
2604 { "Axis I/O Status", "cipm.axisiostatus",
2605 FT_UINT32, BASE_HEX, NULL, 0,
2606 NULL, HFILL}
2607 },
2608 { &hf_cip_axis_io_status_mfg,
2609 { "Axis I/O Status Mfg", "cipm.axisiostatusmfg",
2610 FT_UINT32, BASE_HEX, NULL, 0,
2611 "Axis I/O Status, Manufacturer Specific", HFILL}
2612 },
2613 { &hf_cip_axis_safety_status,
2614 { "Axis Safety Status", "cipm.safetystatus",
2615 FT_UINT32, BASE_HEX, NULL, 0,
2616 NULL, HFILL}
2617 },
2618 { &hf_cip_axis_safety_status_mfg,
2619 { "Axis Safety Status Mfg", "cipm.safetystatusmfg",
2620 FT_UINT32, BASE_HEX, NULL, 0,
2621 "Axis Safety Status, Manufacturer Specific", HFILL}
2622 },
2623 { &hf_cip_axis_safety_state,
2624 { "Axis Safety State", "cipm.safetystate",
2625 FT_UINT8, BASE_HEX, NULL, 0,
2626 "Axis Safety Sate", HFILL}
2627 },
2628 { &hf_cip_sts_flt,
2629 { "Axis Fault Codes", "cipm.sts.flt",
2630 FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_FAULT,
2631 "Status Data Set: Axis Fault Codes", HFILL}
2632 },
2633 { &hf_cip_sts_alrm,
2634 { "Axis Alarm Codes", "cipm.sts.alarm",
2635 FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_ALARM,
2636 "Status Data Set: Axis Alarm Codes", HFILL}
2637 },
2638 { &hf_cip_sts_sts,
2639 { "Axis Status", "cipm.sts.sts",
2640 FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_STATUS,
2641 "Status Data Set: Axis Status", HFILL}
2642 },
2643 { &hf_cip_sts_iosts,
2644 { "Axis I/O Status", "cipm.sts.iosts",
2645 FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_IO_STATUS,
2646 "Status Data Set: Axis I/O Status", HFILL}
2647 },
2648 { &hf_cip_sts_axis_safety,
2649 { "Axis Safety Status", "cipm.sts.safety",
2650 FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_SAFETY,
2651 "Status Data Set: Axis Safety Status", HFILL}
2652 },
2653 { &hf_cip_intrp,
2654 { "Command Target Update", "cipm.intrp",
2655 FT_UINT8, BASE_DEC, VALS(cip_interpolation_vals), COMMAND_CONTROL_TARGET_UPDATE,
2656 "Cyclic Data Block: Command Target Update", HFILL}
2657 },
2658 { &hf_cip_position_data_type,
2659 { "Command Position Data Type", "cipm.posdatatype",
2660 FT_UINT8, BASE_DEC, VALS(cip_pos_data_type_vals), COMMAND_CONTROL_POSITION_DATA_TYPE,
2661 "Cyclic Data Block: Command Position Data Type", HFILL }
2662 },
2663 { &hf_cip_axis_state,
2664 { "Axis State", "cipm.axste",
2665 FT_UINT8, BASE_DEC, VALS(cip_axis_state_vals), 0,
2666 "Cyclic Data Block: Axis State", HFILL}
2667 },
2668 { &hf_cip_command_control,
2669 { "Command Control", "cipm.cmdcontrol",
2670 FT_UINT8, BASE_DEC, NULL, 0,
2671 "Cyclic Data Block: Command Control", HFILL }
2672 },
2673 { &hf_cip_cyclic_wrt_data,
2674 { "Write Data", "cipm.writedata",
2675 FT_BYTES, BASE_NONE, NULL, 0,
2676 "Cyclic Write: Data", HFILL }
2677 },
2678 { &hf_cip_cyclic_rd_data,
2679 { "Read Data", "cipm.readdata",
2680 FT_BYTES, BASE_NONE, NULL, 0,
2681 "Cyclic Read: Data", HFILL }
2682 },
2683 { &hf_cip_cyclic_write_blk,
2684 { "Write Block", "cipm.writeblk",
2685 FT_UINT8, BASE_DEC, NULL, 0,
2686 "Cyclic Data Block: Write Block Id", HFILL }
2687 },
2688 { &hf_cip_cyclic_read_blk,
2689 { "Read Block", "cipm.readblk",
2690 FT_UINT8, BASE_DEC, NULL, 0,
2691 "Cyclic Data Block: Read Block Id", HFILL}
2692 },
2693 { &hf_cip_cyclic_write_sts,
2694 { "Write Status", "cipm.writests",
2695 FT_UINT8, BASE_DEC, NULL, 0,
2696 "Cyclic Data Block: Write Status", HFILL }
2697 },
2698 { &hf_cip_cyclic_read_sts,
2699 { "Read Status", "cipm.readsts",
2700 FT_UINT8, BASE_DEC, NULL, 0,
2701 "Cyclic Data Block: Read Status", HFILL }
2702 },
2703 { &hf_cip_event_checking,
2704 { "Event Checking Control", "cipm.evntchkcontrol",
2705 FT_UINT32, BASE_HEX, NULL, 0,
2706 "Event Channel: Event Checking Control", HFILL}
2707 },
2708 { &hf_cip_event_ack,
2709 { "Event Acknowledgement", "cipm.evntack",
2710 FT_UINT8, BASE_DEC, NULL, 0,
2711 "Event Channel: Event Acknowledgement", HFILL}
2712 },
2713 { &hf_cip_event_status,
2714 { "Event Checking Status", "cipm.evntchkstatus",
2715 FT_UINT32, BASE_HEX, NULL, 0,
2716 "Event Channel: Event Checking Status", HFILL}
2717 },
2718 { &hf_cip_event_id,
2719 { "Event Id", "cipm.evntack",
2720 FT_UINT8, BASE_DEC, NULL, 0,
2721 "Event Channel: Event Id", HFILL }
2722 },
2723 { &hf_cip_event_pos,
2724 { "Event Position", "cipm.evntpos",
2725 FT_INT32, BASE_DEC, NULL, 0,
2726 "Event Channel: Event Position", HFILL}
2727 },
2728 { &hf_cip_event_ts,
2729 { "Event Time Stamp", "cipm.evntimestamp",
2730 FT_UINT64, BASE_DEC|BASE_UNIT_STRING, &units_nanosecond_nanoseconds, 0,
2731 "Event Channel: Time Stamp", HFILL}
2732 },
2733
2734 { &hf_cip_evnt_ctrl_reg1_pos,
2735 { "Reg 1 Pos Edge", "cipm.evnt.ctrl.reg1posedge",
2736 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001,
2737 "Event Checking Control: Reg 1 Pos Edge", HFILL}
2738 },
2739 { &hf_cip_evnt_ctrl_reg1_neg,
2740 { "Reg 1 Neg Edge", "cipm.evnt.ctrl.reg1negedge",
2741 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002,
2742 "Event Checking Control: Reg 1 Neg Edge", HFILL}
2743 },
2744 { &hf_cip_evnt_ctrl_reg2_pos,
2745 { "Reg 2 Pos Edge", "cipm.evnt.ctrl.reg2posedge",
2746 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004,
2747 "Event Checking Control: Reg 2 Pos Edge", HFILL}
2748 },
2749 { &hf_cip_evnt_ctrl_reg2_neg,
2750 { "Reg 2 Neg Edge", "cipm.evnt.ctrl.reg2negedge",
2751 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008,
2752 "Event Checking Control: Reg 2 Neg Edge", HFILL}
2753 },
2754 { &hf_cip_evnt_ctrl_reg1_posrearm,
2755 { "Reg 1 Pos Rearm", "cipm.evnt.ctrl.reg1posrearm",
2756 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100,
2757 "Event Checking Control: Reg 1 Pos Rearm", HFILL}
2758 },
2759 { &hf_cip_evnt_ctrl_reg1_negrearm,
2760 { "Reg 1 Neg Rearm", "cipm.evnt.ctrl.reg1negrearm",
2761 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200,
2762 "Event Checking Control: Reg 1 Neg Rearm", HFILL}
2763 },
2764 { &hf_cip_evnt_ctrl_reg2_posrearm,
2765 { "Reg 2 Pos Rearm", "cipm.evnt.ctrl.reg2posrearm",
2766 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400,
2767 "Event Checking Control: Reg 2 Pos Rearm", HFILL}
2768 },
2769 { &hf_cip_evnt_ctrl_reg2_negrearm,
2770 { "Reg 2 Neg Rearm", "cipm.evnt.ctrl.reg2negrearm",
2771 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800,
2772 "Event Checking Control: Reg 2 Neg Rearm", HFILL}
2773 },
2774 { &hf_cip_evnt_ctrl_marker_pos,
2775 { "Marker Pos Edge", "cipm.evnt.ctrl.mrkrpos",
2776 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000,
2777 "Event Checking Control: Marker Pos Edge", HFILL}
2778 },
2779 { &hf_cip_evnt_ctrl_marker_neg,
2780 { "Marker Neg Edge", "cipm.evnt.ctrl.mrkrneg",
2781 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000,
2782 "Event Checking Control: Marker Neg Edge", HFILL}
2783 },
2784 { &hf_cip_evnt_ctrl_home_pos,
2785 { "Home Pos Edge", "cipm.evnt.ctrl.homepos",
2786 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000,
2787 "Event Checking Control: Home Pos Edge", HFILL}
2788 },
2789 { &hf_cip_evnt_ctrl_home_neg,
2790 { "Home Neg Edge", "cipm.evnt.ctrl.homeneg",
2791 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000,
2792 "Event Checking Control: Home Neg Edge", HFILL}
2793 },
2794 { &hf_cip_evnt_ctrl_home_pp,
2795 { "Home-Switch-Marker Plus Plus", "cipm.evnt.ctrl.homepp",
2796 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000,
2797 "Event Checking Control: Home-Switch-Marker Plus Plus", HFILL}
2798 },
2799 { &hf_cip_evnt_ctrl_home_pm,
2800 { "Home-Switch-Marker Plus Minus", "cipm.evnt.ctrl.homepm",
2801 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000,
2802 "Event Checking Control: Home-Switch-Marker Plus Minus", HFILL}
2803 },
2804 { &hf_cip_evnt_ctrl_home_mp,
2805 { "Home-Switch-Marker Minus Plus", "cipm.evnt.ctrl.homemp",
2806 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000,
2807 "Event Checking Control: Home-Switch-Marker Minus Plus", HFILL}
2808 },
2809 { &hf_cip_evnt_ctrl_home_mm,
2810 { "Home-Switch-Marker Minus Minus", "cipm.evnt.ctrl.homemm",
2811 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000,
2812 "Event Checking Control: Home-Switch-Marker Minus Minus", HFILL}
2813 },
2814 { &hf_cip_evnt_ctrl_acks,
2815 { "Event Block Count", "cipm.evnt.ctrl.acks",
2816 FT_UINT32, BASE_DEC, NULL, 0x70000000,
2817 "Event Checking Control: Event Block Count", HFILL}
2818 },
2819 { &hf_cip_evnt_extend_format,
2820 { "Extended Event Format", "cipm.evnt.extend",
2821 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x80000000,
2822 "Event Checking Control: Extended Event Format", HFILL}
2823 },
2824
2825 { &hf_cip_evnt_sts_reg1_pos,
2826 { "Reg 1 Pos Edge", "cipm.evnt.sts.reg1posedge",
2827 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001,
2828 "Event Checking Status: Reg 1 Pos Edge", HFILL}
2829 },
2830 { &hf_cip_evnt_sts_reg1_neg,
2831 { "Reg 1 Neg Edge", "cipm.evnt.sts.reg1negedge",
2832 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002,
2833 "Event Checking Status: Reg 1 Neg Edge", HFILL }
2834 },
2835 { &hf_cip_evnt_sts_reg2_pos,
2836 { "Reg 2 Pos Edge", "cipm.evnt.sts.reg2posedge",
2837 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004,
2838 "Event Checking Status: Reg 2 Pos Edge", HFILL}
2839 },
2840 { &hf_cip_evnt_sts_reg2_neg,
2841 { "Reg 2 Neg Edge", "cipm.evnt.sts.reg2negedge",
2842 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008,
2843 "Event Checking Status: Reg 2 Neg Edge", HFILL}
2844 },
2845 { &hf_cip_evnt_sts_reg1_posrearm,
2846 { "Reg 1 Pos Rearm", "cipm.evnt.sts.reg1posrearm",
2847 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100,
2848 "Event Checking Status: Reg 1 Pos Rearm", HFILL}
2849 },
2850 { &hf_cip_evnt_sts_reg1_negrearm,
2851 { "Reg 1 Neg Rearm", "cipm.evnt.sts.reg1negrearm",
2852 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200,
2853 "Event Checking Status: Reg 1 Neg Rearm", HFILL}
2854 },
2855 { &hf_cip_evnt_sts_reg2_posrearm,
2856 { "Reg 2 Pos Rearm", "cipm.evnt.sts.reg2posrearm",
2857 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400,
2858 "Event Checking Status: Reg 2 Pos Rearm", HFILL}
2859 },
2860 { &hf_cip_evnt_sts_reg2_negrearm,
2861 { "Reg 2 Neg Rearm", "cipm.evnt.sts.reg2negrearm",
2862 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800,
2863 "Event Checking Status: Reg 2 Neg Rearm", HFILL}
2864 },
2865 { &hf_cip_evnt_sts_marker_pos,
2866 { "Marker Pos Edge", "cipm.evnt.sts.mrkrpos",
2867 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000,
2868 "Event Checking Status: Marker Pos Edge", HFILL}
2869 },
2870 { &hf_cip_evnt_sts_marker_neg,
2871 { "Marker Neg Edge", "cipm.evnt.sts.mrkrneg",
2872 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000,
2873 "Event Checking Status: Marker Neg Edge", HFILL }
2874 },
2875 { &hf_cip_evnt_sts_home_pos,
2876 { "Home Pos Edge", "cipm.evnt.sts.homepos",
2877 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000,
2878 "Event Checking Status: Home Pos Edge", HFILL}
2879 },
2880 { &hf_cip_evnt_sts_home_neg,
2881 { "Home Neg Edge", "cipm.evnt.sts.homeneg",
2882 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000,
2883 "Event Checking Status: Home Neg Edge", HFILL }
2884 },
2885 { &hf_cip_evnt_sts_home_pp,
2886 { "Home-Switch-Marker Plus Plus", "cipm.evnt.sts.homepp",
2887 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000,
2888 "Event Checking Status: Home-Switch-Marker Plus Plus", HFILL}
2889 },
2890 { &hf_cip_evnt_sts_home_pm,
2891 { "Home-Switch-Marker Plus Minus", "cipm.evnt.sts.homepm",
2892 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000,
2893 "Event Checking Status: Home-Switch-Marker Plus Minus", HFILL}
2894 },
2895 { &hf_cip_evnt_sts_home_mp,
2896 { "Home-Switch-Marker Minus Plus", "cipm.evnt.sts.homemp",
2897 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000,
2898 "Event Checking Status: Home-Switch-Marker Minus Plus", HFILL}
2899 },
2900 { &hf_cip_evnt_sts_home_mm,
2901 { "Home-Switch-Marker Minus Minus", "cipm.evnt.sts.homemm",
2902 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000,
2903 "Event Checking Status: Home-Switch-Marker Minus Minus", HFILL}
2904 },
2905 { &hf_cip_evnt_sts_nfs,
2906 { "Event Block Count", "cipm.evnt.sts.nfs",
2907 FT_UINT32, BASE_DEC, NULL, 0x70000000,
2908 "Event Checking Status: Event Block Count", HFILL}
2909 },
2910
2911 { &hf_cip_evnt_sts_stat,
2912 { "Event Status", "cipm.evnt.stat",
2913 FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0,
2914 "Event Data Block: Event Status", HFILL }
2915 },
2916 { &hf_cip_evnt_type,
2917 { "Event Type", "cipm.evnt.type",
2918 FT_UINT8, BASE_DEC, VALS(cip_event_type_vals), 0,
2919 "Event Data Block: Event Type", HFILL}
2920 },
2921 { &hf_cip_svc_code,
2922 { "Service Code", "cipm.svc.code",
2923 FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0,
2924 "Service Data Block: Service Code", HFILL}
2925 },
2926 { &hf_cip_svc_sts,
2927 { "General Status", "cipm.svc.sts",
2928 FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0,
2929 "Service Data Block: General Status", HFILL }
2930 },
2931 { &hf_cip_svc_transction,
2932 { "Transaction Id", "cipm.svc.tranid",
2933 FT_UINT8, BASE_DEC, NULL, 0,
2934 "Service Data Block: Transaction Id", HFILL }
2935 },
2936 { &hf_cip_svc_ext_status,
2937 { "Extended Status", "cipm.svc.extstatus",
2938 FT_UINT8, BASE_DEC, NULL, 0,
2939 "Service Data Block: Extended Status", HFILL }
2940 },
2941 { &hf_cip_svc_data,
2942 { "Service Data", "cipm.svc.data",
2943 FT_BYTES, BASE_NONE, NULL, 0,
2944 "Service Data Block: Data", HFILL }
2945 },
2946 { &hf_cip_attribute_data,
2947 { "Attribute Data", "cipm.attrdata",
2948 FT_BYTES, BASE_NONE, NULL, 0,
2949 "Attribute Service: Data", HFILL }
2950 },
2951 { &hf_cip_ptp_grandmaster,
2952 { "Grandmaster", "cipm.grandmaster",
2953 FT_UINT64, BASE_HEX, NULL, 0,
2954 "Group Sync: Grandmaster Id", HFILL}
2955 },
2956
2957 { &hf_cip_svc_get_axis_attr_sts,
2958 { "Attribute Status", "cipm.getaxisattr.sts",
2959 FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0,
2960 "Service Channel: Get Axis Attribute List Response Status", HFILL }
2961 },
2962 { &hf_get_axis_attr_list_attribute_cnt,
2963 { "Number of attributes", "cipm.getaxisattr.cnt",
2964 FT_UINT16, BASE_DEC, NULL, 0,
2965 "Service Channel: Get Axis Attribute List Attribute Count", HFILL}
2966 },
2967 { &hf_get_axis_attr_list_attribute_id,
2968 { "Attribute ID", "cipm.getaxisattr.id",
2969 FT_UINT16, BASE_DEC, NULL, 0,
2970 "Service Channel: Get Axis Attribute List Attribute ID", HFILL}
2971 },
2972 { &hf_get_axis_attr_list_dimension,
2973 { "Dimension", "cipm.getaxisattr.dimension",
2974 FT_UINT8, BASE_DEC, NULL, 0,
2975 "Service Channel: Get Axis Attribute List Dimension", HFILL}
2976 },
2977 { &hf_get_axis_attr_list_element_size,
2978 { "Element size", "cipm.getaxisattr.element_size",
2979 FT_UINT8, BASE_DEC, NULL, 0,
2980 "Service Channel: Get Axis Attribute List Element Size", HFILL}
2981 },
2982 { &hf_get_axis_attr_list_start_index,
2983 { "Start index", "cipm.getaxisattr.start_index",
2984 FT_UINT16, BASE_DEC, NULL, 0,
2985 "Service Channel: Get Axis Attribute List Start index", HFILL}
2986 },
2987 { &hf_get_axis_attr_list_data_elements,
2988 { "Data elements", "cipm.getaxisattr.data_elements",
2989 FT_UINT16, BASE_DEC, NULL, 0,
2990 "Service Channel: Get Axis Attribute List Data elements", HFILL}
2991 },
2992
2993 { &hf_cip_svc_set_axis_attr_sts,
2994 { "Attribute Status", "cipm.setaxisattr.sts",
2995 FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0,
2996 "Service Channel: Set Axis Attribute List Response Status", HFILL }
2997 },
2998 { &hf_set_axis_attr_list_attribute_cnt,
2999 { "Number of attributes", "cipm.setaxisattr.cnt",
3000 FT_UINT16, BASE_DEC, NULL, 0,
3001 "Service Channel: Set Axis Attribute List Attribute Count", HFILL}
3002 },
3003 { &hf_set_axis_attr_list_attribute_id,
3004 { "Attribute ID", "cipm.setaxisattr.id",
3005 FT_UINT16, BASE_DEC, NULL, 0,
3006 "Service Channel: Set Axis Attribute List Attribute ID", HFILL}
3007 },
3008 { &hf_set_axis_attr_list_dimension,
3009 { "Dimension", "cipm.setaxisattr.dimension",
3010 FT_UINT8, BASE_DEC, NULL, 0,
3011 "Service Channel: Set Axis Attribute List Dimension", HFILL}
3012 },
3013 { &hf_set_axis_attr_list_element_size,
3014 { "Element size", "cipm.setaxisattr.element_size",
3015 FT_UINT8, BASE_DEC, NULL, 0,
3016 "Service Channel: Set Axis Attribute List Element Size", HFILL}
3017 },
3018 { &hf_set_axis_attr_list_start_index,
3019 { "Start index", "cipm.setaxisattr.start_index",
3020 FT_UINT16, BASE_DEC, NULL, 0,
3021 "Service Channel: Set Axis Attribute List Start index", HFILL}
3022 },
3023 { &hf_set_axis_attr_list_data_elements,
3024 { "Data elements", "cipm.setaxisattr.data_elements",
3025 FT_UINT16, BASE_DEC, NULL, 0,
3026 "Service Channel: Set Axis Attribute List Data elements", HFILL}
3027 },
3028
3029 { &hf_set_cyclic_list_attribute_cnt,
3030 { "Number of attributes", "cipm.set_cyclic.cnt",
3031 FT_UINT16, BASE_DEC, NULL, 0,
3032 NULL, HFILL}
3033 },
3034 { &hf_set_cyclic_list_attribute_id,
3035 { "Attribute ID", "cipm.set_cyclic.id",
3036 FT_UINT16, BASE_DEC, NULL, 0,
3037 NULL, HFILL}
3038 },
3039 { &hf_set_cyclic_list_read_block_id,
3040 { "Cyclic Read Block ID", "cipm.set_cyclic.read_block_id",
3041 FT_UINT16, BASE_DEC, NULL, 0,
3042 NULL, HFILL}
3043 },
3044 { &hf_set_cyclic_list_attr_sts,
3045 { "Attribute Status", "cipm.set_cyclic.sts",
3046 FT_UINT8, BASE_DEC | BASE_EXT_STRING, &cip_gs_vals_ext, 0,
3047 NULL, HFILL }
3048 },
3049
3050 { &hf_var_devce_instance,
3051 { "Instance Number", "cipm.var_devce.header.instance",
3052 FT_UINT8, BASE_DEC, NULL, 0,
3053 "Variable Device Header: Instance Number", HFILL}
3054 },
3055 { &hf_var_devce_instance_block_size,
3056 { "Instance Block Size", "cipm.var_devce.header.instance_block_size",
3057 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3058 "Variable Device Header: Instance Block Size", HFILL}
3059 },
3060 { &hf_var_devce_cyclic_block_size,
3061 { "Cyclic Block Size", "cipm.var_devce.header.cyclic_block_size",
3062 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3063 "Variable Device Header: Cyclic Block Size", HFILL}
3064 },
3065 { &hf_var_devce_cyclic_data_block_size,
3066 { "Cyclic Data Block Size", "cipm.var_devce.header.cyclic_data_block_size",
3067 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3068 "Variable Device Header: Cyclic Data Block Size", HFILL}
3069 },
3070 { &hf_var_devce_cyclic_rw_block_size,
3071 { "Cyclic Read/Write Block Size", "cipm.var_devce.header.cyclic_rw_block_size",
3072 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3073 "Variable Device Header: Cyclic Read/Write Block Size", HFILL}
3074 },
3075 { &hf_var_devce_event_block_size,
3076 { "Event Block Size", "cipm.var_devce.header.event_block_size",
3077 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3078 "Variable Device Header: Event Block Size", HFILL}
3079 },
3080 { &hf_var_devce_service_block_size,
3081 { "Service Block Size", "cipm.var_devce.header.service_block_size",
3082 FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0,
3083 "Variable Device Header: Service Block Size", HFILL}
3084 },
3085
3086 { &hf_cip_axis_alarm,
3087 { "Axis Alarm Code", "cipm.alarm.code",
3088 FT_UINT8, BASE_DEC, NULL, 0,
3089 "Status Data Set: Alarm Code", HFILL }
3090 },
3091 { &hf_cip_axis_sts_local_ctrl,
3092 { "Local Control", "cipm.axis.local",
3093 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001,
3094 "Axis Status Data Set: Local Control", HFILL }
3095 },
3096 { &hf_cip_axis_sts_alarm,
3097 { "Alarm", "cipm.axis.alarm",
3098 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002,
3099 "Axis Status Data Set: Alarm", HFILL }
3100 },
3101 { &hf_cip_axis_sts_dc_bus,
3102 { "DC Bus", "cipm.axis.bus",
3103 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004,
3104 "Axis Status Data Set: DC Bus", HFILL }
3105 },
3106 { &hf_cip_axis_sts_pwr_struct,
3107 { "Power Struct", "cipm.axis.pwr",
3108 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008,
3109 "Axis Status Data Set: Power Struct", HFILL }
3110 },
3111 { &hf_cip_axis_sts_flux_up,
3112 { "Motor Flux Up", "cipm.axis.flx",
3113 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000010,
3114 "Axis Status Data Set: Motor Flux Up", HFILL }
3115 },
3116 { &hf_cip_axis_sts_tracking,
3117 { "Tracking", "cipm.axis.track",
3118 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000020,
3119 "Axis Status Data Set: Tracking", HFILL }
3120 },
3121 { &hf_cip_axis_sts_pos_lock,
3122 { "Pos Lock", "cipm.axis.poslock",
3123 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000040,
3124 "Axis Status Data Set: Pos Lock", HFILL }
3125 },
3126 { &hf_cip_axis_sts_vel_lock,
3127 { "Vel Lock", "cipm.axis.vellock",
3128 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000080,
3129 "Axis Status Data Set: Vel Lock", HFILL }
3130 },
3131 { &hf_cip_axis_sts_vel_standstill,
3132 { "Vel Standstill", "cipm.axis.nomo",
3133 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100,
3134 "Axis Status Data Set: Vel Standstill", HFILL }
3135 },
3136 { &hf_cip_axis_sts_vel_threshold,
3137 { "Vel Threshold", "cipm.axis.vthresh",
3138 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200,
3139 "Axis Status Data Set: Vel Threshold", HFILL }
3140 },
3141 { &hf_cip_axis_sts_vel_limit,
3142 { "Vel Limit", "cipm.axis.vlim",
3143 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400,
3144 "Axis Status Data Set: Vel Limit", HFILL }
3145 },
3146 { &hf_cip_axis_sts_acc_limit,
3147 { "Acc Limit", "cipm.axis.alim",
3148 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800,
3149 "Axis Status Data Set: Acc Limit", HFILL }
3150 },
3151 { &hf_cip_axis_sts_dec_limit,
3152 { "Decel Limit", "cipm.axis.dlim",
3153 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00001000,
3154 "Axis Status Data Set: Decel Limit", HFILL }
3155 },
3156 { &hf_cip_axis_sts_torque_threshold,
3157 { "Torque Threshold", "cipm.axis.tthresh",
3158 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00002000,
3159 "Axis Status Data Set: Torque Threshold", HFILL }
3160 },
3161 { &hf_cip_axis_sts_torque_limit,
3162 { "Torque Limit", "cipm.axis.tlim",
3163 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00004000,
3164 "Axis Status Data Set: Torque Limit", HFILL }
3165 },
3166 { &hf_cip_axis_sts_cur_limit,
3167 { "Current Limit", "cipm.axis.ilim",
3168 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00008000,
3169 "Axis Status Data Set: Current Limit", HFILL }
3170 },
3171 { &hf_cip_axis_sts_therm_limit,
3172 { "Thermal Limit", "cipm.axis.hot",
3173 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000,
3174 "Axis Status Data Set: Thermal Limit", HFILL }
3175 },
3176 { &hf_cip_axis_sts_feedback_integ,
3177 { "Feedback Integrity", "cipm.axis.fgood",
3178 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000,
3179 "Axis Status Data Set: Feedback Integrity", HFILL }
3180 },
3181 { &hf_cip_axis_sts_shutdown,
3182 { "Shutdown", "cipm.axis.sdwn",
3183 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000,
3184 "Axis Status Data Set: Shutdown", HFILL }
3185 },
3186 { &hf_cip_axis_sts_in_process,
3187 { "In Process", "cipm.axis.inp",
3188 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000,
3189 "Axis Status Data Set: In Process", HFILL }
3190 },
3191 { &hf_cip_axis_sts_dc_bus_unload,
3192 { "DC Bus Unload", "cipm.axis.dcunload",
3193 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000,
3194 "Axis Status Data Set: DC Bus Unload", HFILL }
3195 },
3196 { &hf_cip_axis_sts_ac_pwr_loss,
3197 { "AC Power Loss", "cipm.axis.acpwrloss",
3198 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000,
3199 "Axis Status Data Set: AC Power Loss", HFILL }
3200 },
3201 { &hf_cip_axis_sts_pos_cntrl_mode,
3202 { "Pos Control Mode", "cipm.axis.poscntrl",
3203 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000,
3204 "Axis Status Data Set: Position Control Mode", HFILL }
3205 },
3206 { &hf_cip_axis_sts_vel_cntrl_mode,
3207 { "Vel Control Mode", "cipm.axis.velcntrl",
3208 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000,
3209 "Axis Status Data Set: Velocity Control Mode", HFILL }
3210 },
3211 { &hf_cip_axis_sts_trq_cntrl_mode,
3212 { "Torque Control Mode", "cipm.axis.trqcntrl",
3213 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x01000000,
3214 "Axis Status Data Set: Torque Control Mode", HFILL }
3215 },
3216
3217 // Attribute #740 - Axis Status 2.
3218 { &hf_cip_axis_status2,
3219 { "Axis Status 2", "cipm.axisstatus2",
3220 FT_UINT32, BASE_HEX, NULL, 0,
3221 NULL, HFILL }
3222 },
3223 { &hf_cip_axis_sts2_motor,
3224 { "Motoring", "cipm.axis2.motor",
3225 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001,
3226 NULL, HFILL }
3227 },
3228 { &hf_cip_axis_sts2_regenerate,
3229 { "Regenerating", "cipm.axis2.regen",
3230 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002,
3231 NULL, HFILL }
3232 },
3233 { &hf_cip_axis_sts2_ride_thru,
3234 { "Ride Thru", "cipm.axis2.ridethru",
3235 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004,
3236 NULL, HFILL }
3237 },
3238 { &hf_cip_axis_sts2_ac_line_sync,
3239 { "AC Line Sync", "cipm.axis2.acsync",
3240 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008,
3241 NULL, HFILL }
3242 },
3243 { &hf_cip_axis_sts2_bus_volt_lock,
3244 { "Bus Voltage Lock", "cipm.axis2.voltlock",
3245 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000010,
3246 NULL, HFILL }
3247 },
3248 { &hf_cip_axis_sts2_react_pwr_only,
3249 { "Reactive Power Only Mode", "cipm.axis2.reactpwr",
3250 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000020,
3251 NULL, HFILL }
3252 },
3253 { &hf_cip_axis_sts2_volt_ctrl_mode,
3254 { "Voltage Control Mode", "cipm.axis2.voltmode",
3255 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000040,
3256 NULL, HFILL }
3257 },
3258 { &hf_cip_axis_sts2_pwr_loss,
3259 { "Power Loss", "cipm.axis2.pwrloss",
3260 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000080,
3261 NULL, HFILL }
3262 },
3263 { &hf_cip_axis_sts2_ac_volt_sag,
3264 { "AC Line Voltage Sag", "cipm.axis2.voltsag",
3265 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100,
3266 NULL, HFILL }
3267 },
3268 { &hf_cip_axis_sts2_ac_phase_loss,
3269 { "AC Line Phase Loss", "cipm.axis2.phaseloss",
3270 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200,
3271 NULL, HFILL }
3272 },
3273 { &hf_cip_axis_sts2_ac_freq_change,
3274 { "AC Line Frequency Change", "cipm.axis2.freqchange",
3275 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400,
3276 NULL, HFILL }
3277 },
3278 { &hf_cip_axis_sts2_ac_sync_loss,
3279 { "AC Line Sync Loss", "cipm.axis2.syncloss",
3280 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800,
3281 NULL, HFILL }
3282 },
3283 { &hf_cip_axis_sts2_single_phase,
3284 { "Single Phase", "cipm.axis2.singlephase",
3285 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00001000,
3286 NULL, HFILL }
3287 },
3288
3289 { &hf_cip_axis_sts2_bus_volt_limit,
3290 { "Bus Voltage Limit", "cipm.axis2.bus_volt_limit",
3291 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00002000,
3292 NULL, HFILL }
3293 },
3294 { &hf_cip_axis_sts2_bus_volt_rate_limit,
3295 { "Bus Voltage Rate Limit", "cipm.axis2.bus_volt_rate_limit",
3296 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00004000,
3297 NULL, HFILL }
3298 },
3299 { &hf_cip_axis_sts2_active_current_rate_limit,
3300 { "Active Current Rate Limit", "cipm.axis2.active_current_rate_limit",
3301 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00008000,
3302 NULL, HFILL }
3303 },
3304 { &hf_cip_axis_sts2_reactive_current_rate_limit,
3305 { "Reactive Current Rate Limit", "cipm.axis2.reactive_current_rate_limit",
3306 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000,
3307 NULL, HFILL }
3308 },
3309 { &hf_cip_axis_sts2_reactive_pwr_limit,
3310 { "Reactive Power Limit", "cipm.axis2.reactive_pwr_limit",
3311 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000,
3312 NULL, HFILL }
3313 },
3314 { &hf_cip_axis_sts2_reactive_pwr_rate_limit,
3315 { "Reactive Power Rate Limit", "cipm.axis2.reactive_pwr_rate_limit",
3316 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000,
3317 NULL, HFILL }
3318 },
3319 { &hf_cip_axis_sts2_active_current_limit,
3320 { "Active Current Limit", "cipm.axis2.active_current_limit",
3321 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000,
3322 NULL, HFILL }
3323 },
3324 { &hf_cip_axis_sts2_reactive_current_limit,
3325 { "Reactive Current Limit", "cipm.axis2.reactive_current_limit",
3326 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000,
3327 NULL, HFILL }
3328 },
3329 { &hf_cip_axis_sts2_motor_pwr_limit,
3330 { "Motoring Power Limit", "cipm.axis2.motor_pwr_limit",
3331 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000,
3332 NULL, HFILL }
3333 },
3334 { &hf_cip_axis_sts2_regen_pwr_limit,
3335 { "Regenerative Power Limit", "cipm.axis2.regen_pwr_limit",
3336 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000,
3337 NULL, HFILL }
3338 },
3339 { &hf_cip_axis_sts2_convert_therm_limit,
3340 { "Converter Thermal Limit", "cipm.axis2.convert_therm_limit",
3341 FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000,
3342 NULL, HFILL }
3343 },
3344
3345 { &hf_cip_act_pos,
3346 { "Actual Position", "cipm.actpos",
3347 FT_INT32, BASE_DEC, NULL, 0,
3348 "Cyclic Data Set: Actual Position", HFILL }
3349 },
3350 { &hf_cip_act_pos_64,
3351 { "Actual Position", "cipm.actpos_64",
3352 FT_INT64, BASE_DEC, NULL, 0,
3353 "Cyclic Data Set: Actual Position", HFILL }
3354 },
3355 { &hf_cip_act_vel,
3356 { "Actual Velocity", "cipm.actvel",
3357 FT_FLOAT, BASE_NONE, NULL, 0,
3358 "Cyclic Data Set: Actual Velocity", HFILL }
3359 },
3360 { &hf_cip_act_accel,
3361 { "Actual Acceleration", "cipm.actaccel",
3362 FT_FLOAT, BASE_NONE, NULL, 0,
3363 "Cyclic Data Set: Actual Acceleration", HFILL }
3364 },
3365 { &hf_cip_pos_cmd,
3366 { "Position Command", "cipm.posfcmd",
3367 FT_DOUBLE, BASE_NONE, NULL, 0,
3368 "Cyclic Data Set: Position Command (LREAL)", HFILL }
3369 },
3370 { &hf_cip_pos_cmd_int,
3371 { "Position Command", "cipm.posicmd",
3372 FT_INT32, BASE_DEC, NULL, 0,
3373 "Cyclic Data Set: Position Command (DINT)", HFILL }
3374 },
3375 { &hf_cip_vel_cmd,
3376 { "Velocity Command", "cipm.velcmd",
3377 FT_FLOAT, BASE_NONE, NULL, 0,
3378 "Cyclic Data Set: Velocity Command", HFILL }
3379 },
3380 { &hf_cip_accel_cmd,
3381 { "Acceleration Command", "cipm.accelcmd",
3382 FT_FLOAT, BASE_NONE, NULL, 0,
3383 "Cyclic Data Set: Acceleration Command", HFILL }
3384 },
3385 { &hf_cip_trq_cmd,
3386 { "Torque Command", "cipm.torquecmd",
3387 FT_FLOAT, BASE_NONE, NULL, 0,
3388 "Cyclic Data Set: Torque Command", HFILL }
3389 },
3390 { &hf_cip_pos_trim,
3391 { "Position Trim", "cipm.postrim",
3392 FT_INT32, BASE_DEC, NULL, 0,
3393 NULL, HFILL }
3394 },
3395 { &hf_cip_vel_trim,
3396 { "Velocity Trim", "cipm.veltrim",
3397 FT_FLOAT, BASE_NONE, NULL, 0,
3398 NULL, HFILL }
3399 },
3400 { &hf_cip_accel_trim,
3401 { "Acceleration Trim", "cipm.acceltrim",
3402 FT_FLOAT, BASE_NONE, NULL, 0,
3403 NULL, HFILL }
3404 },
3405 { &hf_cip_trq_trim,
3406 { "Torque Trim", "cipm.trqtrim",
3407 FT_FLOAT, BASE_NONE, NULL, 0,
3408 NULL, HFILL }
3409 },
3410 { &hf_cip_data,
3411 { "Data", "cipm.data",
3412 FT_BYTES, BASE_NONE, NULL, 0,
3413 NULL, HFILL }
3414 }
3415 };
3416
3417 /* Setup protocol subtree array, these will help Wireshark remember
3418 * if the subtree should be expanded as the user moves through packets */
3419 static gint *cip_subtree[] = {
3420 &ett_cipmotion,
3421 &ett_cont_dev_header,
3422 &ett_control_status,
3423 &ett_node_control,
3424 &ett_node_status,
3425 &ett_time_data_set,
3426 &ett_inst_data_header,
3427 &ett_cyclic_data_block,
3428 &ett_cyclic_command_data,
3429 &ett_feedback_mode,
3430 &ett_connection_configuration_bits,
3431 &ett_control_mode,
3432 &ett_feedback_config,
3433 &ett_command_data_set,
3434 &ett_actual_data_set,
3435 &ett_status_data_set,
3436 &ett_interp_control,
3437 &ett_cyclic_rd_wt,
3438 &ett_event,
3439 &ett_event_check_ctrl,
3440 &ett_event_check_sts,
3441 &ett_service,
3442 &ett_get_axis_attribute,
3443 &ett_set_axis_attribute,
3444 &ett_get_axis_attr_list,
3445 &ett_set_axis_attr_list,
3446 &ett_set_cyclic_list,
3447 &ett_group_sync,
3448 &ett_axis_status_set,
3449 &ett_command_control,
3450 &ett_configuration_block
3451 };
3452
3453 static ei_register_info ei[] = {
3454 { &ei_format_rev_conn_pt, { "cipm.malformed.format_revision_mismatch", PI_MALFORMED, PI_WARN, "Format Revision does not match Connection Point", EXPFILL } },
3455 };
3456
3457 /* Create a CIP Motion protocol handle */
3458 proto_cipmotion = proto_register_protocol(
3459 "Common Industrial Protocol, Motion", /* Full name of protocol */
3460 "CIP Motion", /* Short name of protocol */
3461 "cipm"); /* Abbreviated name of protocol */
3462
3463 proto_cipmotion3 = proto_register_protocol_in_name_only(
3464 "Common Industrial Protocol, Motion - Rev 3",
3465 "CIP Motion - Rev 3",
3466 "cipm3",
3467 proto_cipmotion,
3468 FT_PROTOCOL);
3469
3470 /* Register the header fields with the protocol */
3471 proto_register_field_array(proto_cipmotion, hf, array_length(hf));
3472
3473 /* Register the subtrees for the protocol dissection */
3474 proto_register_subtree_array(cip_subtree, array_length(cip_subtree));
3475
3476 expert_module_t* expert_cipm = expert_register_protocol(proto_cipmotion);
3477 expert_register_field_array(expert_cipm, ei, array_length(ei));
3478
3479 module_t* cipm_module = prefs_register_protocol(proto_cipmotion, NULL);
3480 prefs_register_bool_preference(cipm_module, "display_full_attribute_data",
3481 "Display full attribute data in the Service Data Block",
3482 "Whether the CIP Motion dissector always display the full raw attribute data bytes",
3483 &display_full_attribute_data);
3484
3485 cipmotion_handle = register_dissector("cipmotion", dissect_cipmotion, proto_cipmotion);
3486 cipmotion3_handle = register_dissector("cipmotion3", dissect_cipmotion3, proto_cipmotion3);
3487 }
3488
proto_reg_handoff_cipmotion(void)3489 void proto_reg_handoff_cipmotion(void)
3490 {
3491 dissector_add_for_decode_as("cip.io", cipmotion_handle);
3492 dissector_add_for_decode_as("cip.io", cipmotion3_handle);
3493
3494 dissector_add_uint("cip.io.iface", CI_CLS_MOTION, cipmotion_handle);
3495 }
3496
3497 /*
3498 * Editor modelines - https://www.wireshark.org/tools/modelines.html
3499 *
3500 * Local variables:
3501 * c-basic-offset: 3
3502 * tab-width: 8
3503 * indent-tabs-mode: nil
3504 * End:
3505 *
3506 * ex: set shiftwidth=3 tabstop=8 expandtab:
3507 * :indentSize=3:tabSize=8:noTabs=true:
3508 */
3509