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