From Don Westman via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6889
[metze/wireshark/wip.git] / epan / dissectors / packet-cipmotion.c
1 /* packet-cipmotion.c
2  * Routines for CIP (Common Industrial Protocol) Motion dissection
3  * CIP Motion Home: www.odva.org
4  *
5  * Copyright 2006-2007
6  * Benjamin M. Stocks <bmstocks@ra.rockwell.com>
7  *
8  * $Id$
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <epan/packet.h>
34 #include <epan/emem.h>
35 #include <epan/expert.h>
36 #include "packet-cip.h"
37
38 /* The entry point to the actual disection is: dissect_cipmotion */
39
40 /* Protocol handle for CIP Motion */
41 static int proto_cipmotion = -1;
42
43 /* Header field identifiers, these are registered in the
44  * proto_register_cipmotion function along with the bites/bytes
45  * they represent */
46 static int hf_cip_format                    = -1;
47 static int hf_cip_revision                  = -1;
48 static int hf_cip_class1_seqnum             = -1;
49 static int hf_cip_updateid                  = -1;
50 static int hf_cip_instance_cnt              = -1;
51 static int hf_cip_last_update               = -1;
52 static int hf_cip_node_status               = -1;
53 static int hf_cip_node_control              = -1;
54 static int hf_cip_node_control_remote       = -1;
55 static int hf_cip_node_control_sync         = -1;
56 static int hf_cip_node_data_valid           = -1;
57 static int hf_cip_node_fault_reset          = -1;
58 static int hf_cip_node_device_faulted       = -1;
59 static int hf_cip_time_data_set             = -1;
60 static int hf_cip_time_data_stamp           = -1;
61 static int hf_cip_time_data_offset          = -1;
62 static int hf_cip_time_data_diag            = -1;
63 static int hf_cip_time_data_time_diag       = -1;
64 static int hf_cip_cont_time_stamp           = -1;
65 static int hf_cip_cont_time_offset          = -1;
66 static int hf_cip_devc_time_stamp           = -1;
67 static int hf_cip_devc_time_offset          = -1;
68 static int hf_cip_lost_update               = -1;
69 static int hf_cip_late_update               = -1;
70 static int hf_cip_data_rx_time_stamp        = -1;
71 static int hf_cip_data_tx_time_stamp        = -1;
72 static int hf_cip_node_fltalarms            = -1;
73 static int hf_cip_motor_cntrl               = -1;
74 static int hf_cip_fdbk_config               = -1;
75 static int hf_cip_axis_control              = -1;
76 static int hf_cip_control_status            = -1;
77 static int hf_cip_axis_response             = -1;
78 static int hf_cip_axis_resp_stat            = -1;
79 static int hf_cip_cmd_data_pos_cmd          = -1;
80 static int hf_cip_cmd_data_vel_cmd          = -1;
81 static int hf_cip_cmd_data_acc_cmd          = -1;
82 static int hf_cip_cmd_data_trq_cmd          = -1;
83 static int hf_cip_cmd_data_pos_trim_cmd     = -1;
84 static int hf_cip_cmd_data_vel_trim_cmd     = -1;
85 static int hf_cip_cmd_data_acc_trim_cmd     = -1;
86 static int hf_cip_cmd_data_trq_trim_cmd     = -1;
87 static int hf_cip_act_data_pos              = -1;
88 static int hf_cip_act_data_vel              = -1;
89 static int hf_cip_act_data_acc              = -1;
90 static int hf_cip_act_data_trq              = -1;
91 static int hf_cip_act_data_crnt             = -1;
92 static int hf_cip_act_data_vltg             = -1;
93 static int hf_cip_act_data_fqcy             = -1;
94 static int hf_cip_sts_flt                   = -1;
95 static int hf_cip_sts_alrm                  = -1;
96 static int hf_cip_sts_sts                   = -1;
97 static int hf_cip_sts_iosts                 = -1;
98 static int hf_cip_sts_safety                = -1;
99 static int hf_cip_intrp                     = -1;
100 static int hf_cip_position_data_type        = -1;
101 static int hf_cip_axis_state                = -1;
102 static int hf_cip_evnt_ctrl_reg1_pos        = -1;
103 static int hf_cip_evnt_ctrl_reg1_neg        = -1;
104 static int hf_cip_evnt_ctrl_reg2_pos        = -1;
105 static int hf_cip_evnt_ctrl_reg2_neg        = -1;
106 static int hf_cip_evnt_ctrl_reg1_posrearm   = -1;
107 static int hf_cip_evnt_ctrl_reg1_negrearm   = -1;
108 static int hf_cip_evnt_ctrl_reg2_posrearm   = -1;
109 static int hf_cip_evnt_ctrl_reg2_negrearm   = -1;
110 static int hf_cip_evnt_ctrl_marker_pos      = -1;
111 static int hf_cip_evnt_ctrl_marker_neg      = -1;
112 static int hf_cip_evnt_ctrl_home_pos        = -1;
113 static int hf_cip_evnt_ctrl_home_neg        = -1;
114 static int hf_cip_evnt_ctrl_home_pp         = -1;
115 static int hf_cip_evnt_ctrl_home_pm         = -1;
116 static int hf_cip_evnt_ctrl_home_mp         = -1;
117 static int hf_cip_evnt_ctrl_home_mm         = -1;
118 static int hf_cip_evnt_ctrl_acks            = -1;
119 static int hf_cip_evnt_extend_format        = -1;
120 static int hf_cip_evnt_sts_reg1_pos         = -1;
121 static int hf_cip_evnt_sts_reg1_neg         = -1;
122 static int hf_cip_evnt_sts_reg2_pos         = -1;
123 static int hf_cip_evnt_sts_reg2_neg         = -1;
124 static int hf_cip_evnt_sts_reg1_posrearm    = -1;
125 static int hf_cip_evnt_sts_reg1_negrearm    = -1;
126 static int hf_cip_evnt_sts_reg2_posrearm    = -1;
127 static int hf_cip_evnt_sts_reg2_negrearm    = -1;
128 static int hf_cip_evnt_sts_marker_pos       = -1;
129 static int hf_cip_evnt_sts_marker_neg       = -1;
130 static int hf_cip_evnt_sts_home_pos         = -1;
131 static int hf_cip_evnt_sts_home_neg         = -1;
132 static int hf_cip_evnt_sts_home_pp          = -1;
133 static int hf_cip_evnt_sts_home_pm          = -1;
134 static int hf_cip_evnt_sts_home_mp          = -1;
135 static int hf_cip_evnt_sts_home_mm          = -1;
136 static int hf_cip_evnt_sts_nfs              = -1;
137 static int hf_cip_evnt_sts_stat             = -1;
138 static int hf_cip_evnt_type                 = -1;
139 static int hf_cip_svc_code                  = -1;
140 static int hf_cip_svc_sts                   = -1;
141 static int hf_cip_svc_set_axis_attr_sts     = -1;
142 static int hf_cip_svc_get_axis_attr_sts     = -1;
143 static int hf_cip_svc_transction            = -1;
144 static int hf_cip_svc_ext_status            = -1;
145 static int hf_cip_svc_data                  = -1;
146 static int hf_cip_ptp_grandmaster           = -1;
147 static int hf_cip_axis_alarm                = -1;
148 static int hf_cip_axis_fault                = -1;
149 static int hf_cip_axis_sts_local_ctrl       = -1;
150 static int hf_cip_axis_sts_alarm            = -1;
151 static int hf_cip_axis_sts_dc_bus           = -1;
152 static int hf_cip_axis_sts_pwr_struct       = -1;
153 static int hf_cip_axis_sts_tracking         = -1;
154 static int hf_cip_axis_sts_pos_lock         = -1;
155 static int hf_cip_axis_sts_vel_lock         = -1;
156 static int hf_cip_axis_sts_vel_standstill   = -1;
157 static int hf_cip_axis_sts_vel_threshold    = -1;
158 static int hf_cip_axis_sts_vel_limit        = -1;
159 static int hf_cip_axis_sts_acc_limit        = -1;
160 static int hf_cip_axis_sts_dec_limit        = -1;
161 static int hf_cip_axis_sts_torque_threshold = -1;
162 static int hf_cip_axis_sts_torque_limit     = -1;
163 static int hf_cip_axis_sts_cur_limit        = -1;
164 static int hf_cip_axis_sts_therm_limit      = -1;
165 static int hf_cip_axis_sts_feedback_integ   = -1;
166 static int hf_cip_axis_sts_shutdown         = -1;
167 static int hf_cip_axis_sts_in_process       = -1;
168 static int hf_cip_cyclic_wrt_data           = -1;
169 static int hf_cip_cyclic_rd_data            = -1;
170 static int hf_cip_cyclic_write_blk          = -1;
171 static int hf_cip_cyclic_read_blk           = -1;
172 static int hf_cip_cyclic_write_sts          = -1;
173 static int hf_cip_cyclic_read_sts           = -1;
174 static int hf_cip_attribute_data            = -1;
175 static int hf_cip_event_checking            = -1;
176 static int hf_cip_event_ack                 = -1;
177 static int hf_cip_event_status              = -1;
178 static int hf_cip_event_id                  = -1;
179 static int hf_cip_event_pos                 = -1;
180 static int hf_cip_event_ts                  = -1;
181 static int hf_cip_pos_cmd                   = -1;
182 static int hf_cip_pos_cmd_int               = -1;
183 static int hf_cip_vel_cmd                   = -1;
184 static int hf_cip_accel_cmd                 = -1;
185 static int hf_cip_trq_cmd                   = -1;
186 static int hf_cip_pos_trim                  = -1;
187 static int hf_cip_vel_trim                  = -1;
188 static int hf_cip_accel_trim                = -1;
189 static int hf_cip_trq_trim                  = -1;
190 static int hf_cip_act_pos                   = -1;
191 static int hf_cip_act_vel                   = -1;
192 static int hf_cip_act_accel                 = -1;
193 static int hf_cip_act_trq                   = -1;
194 static int hf_cip_act_crnt                  = -1;
195 static int hf_cip_act_volts                 = -1;
196 static int hf_cip_act_freq                  = -1;
197 static int hf_cip_fault_type                = -1;
198 static int hf_cip_fault_sub_code            = -1;
199 static int hf_cip_fault_action              = -1;
200 static int hf_cip_fault_time_stamp          = -1;
201 static int hf_cip_alarm_type                = -1;
202 static int hf_cip_alarm_sub_code            = -1;
203 static int hf_cip_alarm_state               = -1;
204 static int hf_cip_alarm_time_stamp          = -1;
205 static int hf_cip_axis_status               = -1;
206 static int hf_cip_axis_status_mfg           = -1;
207 static int hf_cip_axis_io_status            = -1;
208 static int hf_cip_axis_io_status_mfg        = -1;
209 static int hf_cip_safety_status             = -1;
210 static int hf_cip_cmd_data_set              = -1;
211 static int hf_cip_act_data_set              = -1;
212 static int hf_cip_sts_data_set              = -1;
213 static int hf_cip_group_sync                = -1;
214 static int hf_cip_command_control           = -1;
215
216 static int hf_get_axis_attr_list_attribute_cnt     = -1;
217 static int hf_get_axis_attr_list_attribute_id      = -1;
218 static int hf_get_axis_attr_list_dimension         = -1;
219 static int hf_get_axis_attr_list_element_size      = -1;
220 static int hf_get_axis_attr_list_start_index       = -1;
221 static int hf_get_axis_attr_list_data_elements     = -1;
222 static int hf_set_axis_attr_list_attribute_cnt     = -1;
223 static int hf_set_axis_attr_list_attribute_id      = -1;
224 static int hf_set_axis_attr_list_dimension         = -1;
225 static int hf_set_axis_attr_list_element_size      = -1;
226 static int hf_set_axis_attr_list_start_index       = -1;
227 static int hf_set_axis_attr_list_data_elements     = -1;
228 static int hf_var_devce_instance                   = -1;
229 static int hf_var_devce_instance_block_size        = -1;
230 static int hf_var_devce_cyclic_block_size          = -1;
231 static int hf_var_devce_cyclic_data_block_size     = -1;
232 static int hf_var_devce_cyclic_rw_block_size       = -1;
233 static int hf_var_devce_event_block_size           = -1;
234 static int hf_var_devce_service_block_size         = -1;
235
236 /* Subtree pointers for the dissection */
237 static gint ett_cipmotion           = -1;
238 static gint ett_cont_dev_header     = -1;
239 static gint ett_node_control        = -1;
240 static gint ett_node_status         = -1;
241 static gint ett_time_data_set       = -1;
242 static gint ett_inst_data_header    = -1;
243 static gint ett_cyclic_data_block   = -1;
244 static gint ett_control_mode        = -1;
245 static gint ett_feedback_config     = -1;
246 static gint ett_command_data_set    = -1;
247 static gint ett_actual_data_set     = -1;
248 static gint ett_status_data_set     = -1;
249 static gint ett_interp_control      = -1;
250 static gint ett_cyclic_rd_wt        = -1;
251 static gint ett_event               = -1;
252 static gint ett_event_check_ctrl    = -1;
253 static gint ett_event_check_sts     = -1;
254 static gint ett_service             = -1;
255 static gint ett_get_axis_attribute  = -1;
256 static gint ett_set_axis_attribute  = -1;
257 static gint ett_get_axis_attr_list  = -1;
258 static gint ett_set_axis_attr_list  = -1;
259 static gint ett_group_sync          = -1;
260 static gint ett_axis_status_set     = -1;
261 static gint ett_command_control     = -1;
262
263 /* These are the BITMASKS for the Time Data Set header field */
264 #define TIME_DATA_SET_TIME_STAMP                0x1
265 #define TIME_DATA_SET_TIME_OFFSET               0x2
266 #define TIME_DATA_SET_UPDATE_DIAGNOSTICS        0x4
267 #define TIME_DATA_SET_TIME_DIAGNOSTICS          0x8
268
269 /* These are the BITMASKS for the Command Data Set cyclic field */
270 #define COMMAND_DATA_SET_POSITION           0x01
271 #define COMMAND_DATA_SET_VELOCITY           0x02
272 #define COMMAND_DATA_SET_ACCELERATION       0x04
273 #define COMMAND_DATA_SET_TORQUE             0x08
274 #define COMMAND_DATA_SET_POSITION_TRIM      0x10
275 #define COMMAND_DATA_SET_VELOCITY_TRIM      0x20
276 #define COMMAND_DATA_SET_ACCELERATION_TRIM  0x40
277 #define COMMAND_DATA_SET_TORQUE_TRIM        0x80
278
279 /* These are the BITMASKS for the Actual Data Set cyclic field */
280 #define ACTUAL_DATA_SET_POSITION        0x01
281 #define ACTUAL_DATA_SET_VELOCITY        0x02
282 #define ACTUAL_DATA_SET_ACCELERATION    0x04
283 #define ACTUAL_DATA_SET_TORQUE          0x08
284 #define ACTUAL_DATA_SET_CURRENT         0x10
285 #define ACTUAL_DATA_SET_VOLTAGE         0x20
286 #define ACTUAL_DATA_SET_FREQUENCY       0x40
287
288 /* These are the BITMASKS for the Status Data Set cyclic field */
289 #define STATUS_DATA_SET_AXIS_FAULT              0x01
290 #define STATUS_DATA_SET_AXIS_ALARM              0x02
291 #define STATUS_DATA_SET_AXIS_STATUS             0x04
292 #define STATUS_DATA_SET_AXIS_IO_STATUS          0x08
293 #define STATUS_DATA_SET_AXIS_SAFETY             0x80
294
295 /* These are the BITMASKS for the Command Control cyclic field */
296 #define COMMAND_CONTROL_TARGET_UPDATE       0x03
297 #define COMMAND_CONTROL_POSITION_DATA_TYPE  0x0C
298
299 /* These are the VALUES of the connection format header field of the
300  * CIP Motion protocol */
301 #define FORMAT_FIXED_CONTROL_TO_DEVICE      2
302 #define FORMAT_FIXED_DEVICE_TO_CONTROL      3
303 #define FORMAT_VAR_CONTROL_TO_DEVICE        6
304 #define FORMAT_VAR_DEVICE_TO_CONTROL        7
305
306 /* Translate function to string - connection format values */
307 static const value_string cip_con_format_vals[] = {
308    { FORMAT_FIXED_CONTROL_TO_DEVICE,       "Fixed Controller-to-Device"        },
309    { FORMAT_FIXED_DEVICE_TO_CONTROL,       "Fixed Device-to-Controller"        },
310    { FORMAT_VAR_CONTROL_TO_DEVICE,         "Variable Controller-to-Device"     },
311    { FORMAT_VAR_DEVICE_TO_CONTROL,         "Variable Device-to-Controller"     },
312    { 0,                                    NULL                                }
313 };
314
315 /* Translate function to string - motor control mode values */
316 static const value_string cip_motor_control_vals[] = {
317    { 0,    "No Control"            },
318    { 1,    "Position Control"      },
319    { 2,    "Velocity Control"      },
320    { 3,    "Acceleration Control"  },
321    { 4,    "Torque Control"        },
322    { 5,    "Current Control"       },
323    { 0,    NULL                    }
324 };
325
326 /* Translate function to string - feedback config values */
327 static const value_string cip_fdbk_config_vals[] = {
328    { 0,    "No Feedback"       },
329    { 1,    "Master Feedback"   },
330    { 2,    "Motor Feedback"    },
331    { 3,    "Load Feedback"     },
332    { 4,    "Dual Feedback"     },
333    { 0,    NULL                }
334 };
335
336 /* Translate function to string - axis control values */
337 static const value_string cip_axis_control_vals[] =
338 {
339    { 0,    "No Request"               },
340    { 1,    "Enable Request"           },
341    { 2,    "Disble Request"           },
342    { 3,    "Shutdown Request"         },
343    { 4,    "Shutdown Reset Request"   },
344    { 5,    "Abort Request"            },
345    { 6,    "Fault Reset Request"      },
346    { 7,    "Stop Process"             },
347    { 8,    "Change Actual Pos"        },
348    { 9,    "Change Command Pos Ref"   },
349    { 127,  "Cancel Request"           },
350    { 0,    NULL                       }
351 };
352
353 /* Translate function to string - control status values */
354 static const value_string cip_control_status_vals[] =
355 {
356    { 1,    "Configuration Complete"   },
357    { 0,    NULL                       }
358 };
359
360 /* Translate function to string - group sync Status */
361 static const value_string cip_sync_status_vals[] =
362 {
363    { 0,       "Synchronized"      },
364    { 1,       "Not Synchronized"  },
365    { 2,       "Wrong Grandmaster" },
366    { 0,       NULL }
367 };
368
369 /* Translate function to string - command target update */
370 static const value_string cip_interpolation_vals[] = {
371    { 0,  "Immediate"         },
372    { 1,  "Extrapolate (+1)"  },
373    { 2,  "Interpolate (+2)"  },
374    { 0,  NULL                }
375 };
376
377 /* These are the VALUES for the Command Position Data Type */
378 #define POSITION_DATA_LREAL 0x00
379 #define POSITION_DATA_DINT  0x01
380
381 /* Translate function to string - position data type */
382 static const value_string cip_pos_data_type_vals[] = {
383    { POSITION_DATA_LREAL, "LREAL (64-bit Float)"   },
384    { POSITION_DATA_DINT,  "DINT (32-bit Integer)"  },
385    { 0,                   NULL                     }
386 };
387
388 /* Translate function to string - axis response values */
389 static const value_string cip_axis_response_vals[] = {
390    { 0,    "No Acknowlede"                 },
391    { 1,    "Enable Acknowledge"            },
392    { 2,    "Disable Acknowledge"           },
393    { 3,    "Shutdown Acknowledge"          },
394    { 4,    "Shutdown Reset Acknowledge"    },
395    { 5,    "Abort Acknowledge"             },
396    { 6,    "Fault Reset Acknowledge"       },
397    { 0,    NULL                            }
398 };
399
400 /* Translate function to string - axis state values */
401 static const value_string cip_axis_state_vals[] = {
402    { 0,    "Initializing"      },
403    { 1,    "Pre-charging"      },
404    { 2,    "Stopped"           },
405    { 3,    "Starting"          },
406    { 4,    "Running"           },
407    { 5,    "Testing"           },
408    { 6,    "Stopping"          },
409    { 7,    "Aborting"          },
410    { 8,    "Major Faulted"     },
411    { 9,    "Start Inhibited"   },
412    { 10,   "Shutdown"          },
413    { 0,    NULL                }
414 };
415
416 /* Translate function to string - event type values */
417 static const value_string cip_event_type_vals[] = {
418    { 0,    "Registration 1 Positive Edge"  },
419    { 1,    "Registration 1 Negative Edge"  },
420    { 2,    "Registration 2 Positive Edge"  },
421    { 3,    "Registration 2 Negative Edge"  },
422    { 4,    "Marker Positive Edge"          },
423    { 5,    "Marker Negative Edge"          },
424    { 6,    "Home Switch Positive Edge"     },
425    { 7,    "Home Switch Negative Edge"     },
426    { 8,    "Home Switch Marker ++"         },
427    { 9,    "Home Switch Marker +-"         },
428    { 10,   "Home Switch Marker -+"         },
429    { 11,   "Home Switch Marker --"         },
430    { 0,    NULL                            }
431 };
432
433 #define SC_GET_AXIS_ATTRIBUTE_LIST  0x4B
434 #define SC_SET_AXIS_ATTRIBUTE_LIST  0x4C
435 #define SC_SET_CYCLIC_WRITE_LIST    0x4D
436 #define SC_SET_CYCLIC_READ_LIST     0x4E
437 #define SC_RUN_MOTOR_TEST           0x4F
438 #define SC_GET_MOTOR_TEST_DATA      0x50
439 #define SC_RUN_INERTIA_TEST         0x51
440 #define SC_GET_INERTIA_TEST_DATA    0x52
441 #define SC_RUN_HOOKUP_TEST          0x53
442 #define SC_GET_HOOKUP_TEST_DATA     0x53
443
444 /* Translate function to string - CIP Service codes */
445 static const value_string cip_sc_vals[] = {
446    GENERIC_SC_LIST
447    { SC_GET_AXIS_ATTRIBUTE_LIST,   "Get Axis Attribute List"   },
448    { SC_SET_AXIS_ATTRIBUTE_LIST,   "Set Axis Attribute List"   },
449    { SC_SET_CYCLIC_WRITE_LIST,     "Set Cyclic Write List"     },
450    { SC_SET_CYCLIC_READ_LIST,      "Set Cyclic Read List"      },
451    { SC_RUN_MOTOR_TEST,            "Run Motor Test"            },
452    { SC_GET_MOTOR_TEST_DATA,       "Get Motor Test Data"       },
453    { SC_RUN_INERTIA_TEST,          "Run Inertia Test"          },
454    { SC_GET_INERTIA_TEST_DATA,     "Get Intertia Test Data"    },
455    { SC_RUN_HOOKUP_TEST,           "Run Hookup Test"           },
456    { SC_GET_HOOKUP_TEST_DATA,      "Get Hookup Test Data"      },
457    { 0,                            NULL                        }
458 };
459
460 /*
461  * Function name: dissect_cmd_data_set
462  *
463  * Purpose: Dissect the command data set field of the cyclic data block header and if any
464  * of the command value bits are set to retrieve and display those command values
465  *
466  * Returns: The number of bytes in the cyclic data used
467  */
468 static guint32
469 dissect_cmd_data_set(guint32 cmd_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset, gboolean lreal_pos)
470 {
471    guint32 bytes_used = 0;
472
473    /* The order of these if statements is VERY important, this is the order the values will
474    * appear in the cyclic data */
475    if ( (cmd_data_set & COMMAND_DATA_SET_POSITION) == COMMAND_DATA_SET_POSITION )
476    {
477       /* Based on the Command Position Data Type value embedded in the Command Control
478       * header field the position is either 64-bit floating or 32-bit integer */
479       if (lreal_pos)
480       {
481          /* Display the command data set position command value */
482          proto_tree_add_item(tree, hf_cip_pos_cmd, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN );
483          bytes_used += 8;
484       }
485       else
486       {
487          /* Display the command data set position command value */
488          proto_tree_add_item(tree, hf_cip_pos_cmd_int, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
489          bytes_used += 4;
490       }
491    }
492
493    if ( (cmd_data_set & COMMAND_DATA_SET_VELOCITY) == COMMAND_DATA_SET_VELOCITY )
494    {
495       /* Display the command data set velocity command value */
496       proto_tree_add_item(tree, hf_cip_vel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
497       bytes_used += 4;
498    }
499
500    if ( (cmd_data_set & COMMAND_DATA_SET_ACCELERATION) == COMMAND_DATA_SET_ACCELERATION )
501    {
502       /* Display the command data set acceleration command value */
503       proto_tree_add_item(tree, hf_cip_accel_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
504       bytes_used += 4;
505    }
506
507    if ( (cmd_data_set & COMMAND_DATA_SET_TORQUE) == COMMAND_DATA_SET_TORQUE )
508    {
509       /* Display the command data set torque command value */
510       proto_tree_add_item(tree, hf_cip_trq_cmd, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
511       bytes_used += 4;
512    }
513
514    if ( (cmd_data_set & COMMAND_DATA_SET_POSITION_TRIM) == COMMAND_DATA_SET_POSITION_TRIM )
515    {
516       /* Display the command data set position trim value */
517       proto_tree_add_item(tree, hf_cip_pos_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
518       bytes_used += 4;
519    }
520
521    if ( (cmd_data_set & COMMAND_DATA_SET_VELOCITY_TRIM) == COMMAND_DATA_SET_VELOCITY_TRIM )
522    {
523       /* Display the command data set velocity trim value */
524       proto_tree_add_item(tree, hf_cip_vel_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
525       bytes_used += 4;
526    }
527
528    if ( (cmd_data_set & COMMAND_DATA_SET_ACCELERATION_TRIM) == COMMAND_DATA_SET_ACCELERATION_TRIM )
529    {
530       /* Display the command data set acceleration trim value */
531       proto_tree_add_item(tree, hf_cip_accel_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
532       bytes_used += 4;
533    }
534
535    if ( (cmd_data_set & COMMAND_DATA_SET_TORQUE_TRIM) == COMMAND_DATA_SET_TORQUE_TRIM )
536    {
537       /* Display the command data set torque trim value */
538       proto_tree_add_item(tree, hf_cip_trq_trim, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
539       bytes_used += 4;
540    }
541
542    return bytes_used;
543 }
544
545
546 /*
547  * Function name: dissect_act_data_set
548  *
549  * Purpose: Dissect the actual data set field of the cyclic data block header and if any
550  * of the actual value bits are set to retrieve and display those feedback values
551  *
552  * Returns: The number of bytes in the cyclic data used
553  */
554 static guint32
555 dissect_act_data_set(guint32 act_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset)
556 {
557    guint32 bytes_used = 0;
558
559    /* The order of these if statements is VERY important, this is the order the values will
560    * appear in the cyclic data */
561    if ( (act_data_set & ACTUAL_DATA_SET_POSITION) == ACTUAL_DATA_SET_POSITION )
562    {
563       /* Display the actual data set position feedback value */
564       proto_tree_add_item(tree, hf_cip_act_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
565       bytes_used += 4;
566    }
567
568    if ( (act_data_set & ACTUAL_DATA_SET_VELOCITY) == ACTUAL_DATA_SET_VELOCITY )
569    {
570       /* Display the actual data set velocity feedback value */
571       proto_tree_add_item(tree, hf_cip_act_vel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
572       bytes_used += 4;
573    }
574
575    if ( (act_data_set & ACTUAL_DATA_SET_ACCELERATION) == ACTUAL_DATA_SET_ACCELERATION )
576    {
577       /* Display the actual data set acceleration feedback value */
578       proto_tree_add_item(tree, hf_cip_act_accel, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
579       bytes_used += 4;
580    }
581
582    if ( (act_data_set & ACTUAL_DATA_SET_TORQUE) == ACTUAL_DATA_SET_TORQUE )
583    {
584       /* Display the actual data set torque feedback value */
585       proto_tree_add_item(tree, hf_cip_act_trq, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
586       bytes_used += 4;
587    }
588    if ( (act_data_set & ACTUAL_DATA_SET_CURRENT) == ACTUAL_DATA_SET_CURRENT )
589    {
590       /* Display the actual data set current feedback value */
591       proto_tree_add_item(tree, hf_cip_act_crnt, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
592       bytes_used += 4;
593    }
594
595    if ( (act_data_set & ACTUAL_DATA_SET_VOLTAGE) == ACTUAL_DATA_SET_VOLTAGE )
596    {
597       /* Display the actual data set voltage feedback value */
598       proto_tree_add_item(tree, hf_cip_act_volts, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
599       bytes_used += 4;
600    }
601
602    if ( (act_data_set & ACTUAL_DATA_SET_FREQUENCY) == ACTUAL_DATA_SET_FREQUENCY )
603    {
604       /* Display the actual data set frequency feedback value */
605       proto_tree_add_item(tree, hf_cip_act_freq, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
606       bytes_used += 4;
607    }
608
609    return bytes_used;
610 }
611
612 /*
613  * Function name: dissect_status_data_set
614  *
615  * Purpose: Dissect the status data set field of the cyclic data block header and if any
616  * of the status value bits are set to retrieve and display those status values
617  *
618  * Returns: The number of bytes in the cyclic data used
619  */
620 static guint32
621 dissect_status_data_set(guint32 status_data_set, proto_tree* tree, tvbuff_t* tvb, guint32 offset)
622 {
623    guint32 bytes_used = 0;
624    proto_item *temp_proto_item;
625    proto_tree *temp_proto_tree;
626
627    /* The order of these if statements is VERY important, this is the order the values will
628     * appear in the cyclic data */
629    if ( (status_data_set & STATUS_DATA_SET_AXIS_FAULT) == STATUS_DATA_SET_AXIS_FAULT )
630    {
631       /* Display the various fault codes from the device */
632       proto_tree_add_item(tree, hf_cip_fault_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
633       bytes_used += 1;
634
635       proto_tree_add_item(tree, hf_cip_axis_fault, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
636       bytes_used += 1;
637
638       proto_tree_add_item(tree, hf_cip_fault_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
639       bytes_used += 1;
640
641       proto_tree_add_item(tree, hf_cip_fault_action, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
642       bytes_used += 1;
643
644       proto_tree_add_item(tree, hf_cip_fault_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
645       bytes_used += 8;
646    }
647
648    if ( (status_data_set & STATUS_DATA_SET_AXIS_ALARM) == STATUS_DATA_SET_AXIS_ALARM )
649    {
650       /* Display the various alarm codes from the device */
651       proto_tree_add_item(tree, hf_cip_alarm_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
652       bytes_used += 1;
653
654       proto_tree_add_item(tree, hf_cip_axis_alarm, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
655       bytes_used += 1;
656
657       proto_tree_add_item(tree, hf_cip_alarm_sub_code, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
658       bytes_used += 1;
659
660       proto_tree_add_item(tree, hf_cip_alarm_state, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
661       bytes_used += 1;
662
663       proto_tree_add_item(tree, hf_cip_alarm_time_stamp, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
664       bytes_used += 8;
665    }
666
667    if ( (status_data_set & STATUS_DATA_SET_AXIS_STATUS) == STATUS_DATA_SET_AXIS_STATUS )
668    {
669       /* Display the various axis state values from the device */
670       temp_proto_item = proto_tree_add_item(tree, hf_cip_axis_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
671       temp_proto_tree = proto_item_add_subtree( temp_proto_item, ett_axis_status_set );
672       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_local_ctrl, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
673       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_alarm, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
674       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_dc_bus, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
675       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_pwr_struct, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
676       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_tracking, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
677       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_pos_lock, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
678       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_lock, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
679       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_standstill, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
680       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_threshold, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
681       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_vel_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
682       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_acc_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
683       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_dec_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
684       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_torque_threshold, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
685       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_torque_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
686       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_cur_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
687       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_therm_limit, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
688       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_feedback_integ, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
689       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_shutdown, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
690       proto_tree_add_item( temp_proto_tree, hf_cip_axis_sts_in_process, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN );
691       bytes_used += 4;
692
693       proto_tree_add_item(tree, hf_cip_axis_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
694       bytes_used += 4;
695    }
696
697    if ( (status_data_set & STATUS_DATA_SET_AXIS_IO_STATUS) == STATUS_DATA_SET_AXIS_IO_STATUS )
698    {
699       proto_tree_add_item(tree, hf_cip_axis_io_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
700       bytes_used += 4;
701
702       proto_tree_add_item(tree, hf_cip_axis_io_status_mfg, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
703       bytes_used += 4;
704    }
705
706    if ( (status_data_set & STATUS_DATA_SET_AXIS_SAFETY) == STATUS_DATA_SET_AXIS_SAFETY )
707    {
708       proto_tree_add_item(tree, hf_cip_safety_status, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
709       bytes_used += 4;
710    }
711
712    return bytes_used;
713 }
714
715 /*
716  * Function name: dissect_cntr_cyclic
717  *
718  * Purpose: Dissect the cyclic data block of a controller to device format message
719  *
720  * Returns: The new offset into the message that follow on dissections should use
721  * as their starting offset
722  */
723 static guint32
724 dissect_cntr_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
725 {
726    proto_item *header_item, *temp_proto_item;
727    proto_tree *header_tree, *temp_proto_tree;
728    guint32     temp_data;
729    gboolean    lreal_pos;
730    guint32     bytes_used = 0;
731
732    /* Create the tree for the entire instance data header */
733    header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Data Block");
734    header_tree = proto_item_add_subtree(header_item, ett_cyclic_data_block);
735
736    /* Add the control mode header field to the tree */
737    proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
738
739    /* Add the feedback config header field to the tree */
740    proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
741
742    /* Add the axis control field to the tree */
743    proto_tree_add_item(header_tree, hf_cip_axis_control, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
744
745    /* Add the control status to the tree */
746    proto_tree_add_item(header_tree, hf_cip_control_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
747
748    /* Read the command control header field from the packet into memory and determine if the dissector
749    * should be using an LREAL or DINT for position */
750    temp_data = tvb_get_guint8(tvb, offset + 7);
751    lreal_pos = ( (temp_data & COMMAND_CONTROL_POSITION_DATA_TYPE) == POSITION_DATA_LREAL );
752
753    /* Read the command data set header field from the packet into memory */
754    temp_data = tvb_get_guint8(tvb, offset + 4);
755
756    /* Create the tree for the command data set header field */
757    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_cmd_data_set, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
758    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_command_data_set);
759    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_pos_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
760    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_vel_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
761    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_acc_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
762    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_trq_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
763    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_pos_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
764    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_vel_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
765    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_acc_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
766    proto_tree_add_item(temp_proto_tree, hf_cip_cmd_data_trq_trim_cmd, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
767
768    /* Display the command data values from the cyclic data payload within the command data set tree, the
769    * cyclic data starts immediately after the interpolation control field in the controller to device
770    * direction */
771    bytes_used += dissect_cmd_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used, lreal_pos);
772
773    /* Create the tree for the actual data set header field */
774    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_act_data_set, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
775    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_actual_data_set);
776    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_pos,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
777    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vel,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
778    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_acc,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
779    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_trq,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
780    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_crnt, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
781    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vltg, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
782    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_fqcy, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
783
784    /* Create the tree for the status data set header field */
785    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_sts_data_set, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
786    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_status_data_set);
787    proto_tree_add_item(temp_proto_tree, hf_cip_sts_flt,    tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
788    proto_tree_add_item(temp_proto_tree, hf_cip_sts_alrm,   tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
789    proto_tree_add_item(temp_proto_tree, hf_cip_sts_sts,    tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
790    proto_tree_add_item(temp_proto_tree, hf_cip_sts_iosts,  tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
791    proto_tree_add_item(temp_proto_tree, hf_cip_sts_safety, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
792
793    /* Create the tree for the command control header field */
794    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_command_control, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
795    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_command_control);
796
797    /* Display the interpolation control and position format fields */
798    proto_tree_add_item(temp_proto_tree, hf_cip_intrp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
799    proto_tree_add_item(temp_proto_tree, hf_cip_position_data_type, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
800
801    /* Return the offset to the next byte in the message */
802    return offset + 8 + bytes_used;
803 }
804
805 /*
806  * Function name: dissect_devce_cyclic
807  *
808  * Purpose: Dissect the cyclic data block of a device to controller format message
809  *
810  * Returns: The new offset into the message that follow on dissections should use
811  * as their starting offset
812  */
813 static guint32
814 dissect_devce_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
815 {
816    proto_item *header_item, *temp_proto_item;
817    proto_tree *header_tree, *temp_proto_tree;
818    guint32 temp_data;
819    guint32 bytes_used = 0;
820
821    /* Create the tree for the entire instance data header */
822    header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Data Block");
823    header_tree = proto_item_add_subtree(header_item, ett_cyclic_data_block);
824
825    /* Add the control mode header field to the tree */
826    proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
827
828    /* Add the feedback config header field to the tree */
829    proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
830
831    /* Add the axis response field to the tree */
832    proto_tree_add_item(header_tree, hf_cip_axis_response, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
833
834    /* Add the axis response status to the tree */
835    proto_tree_add_item(header_tree, hf_cip_axis_resp_stat, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
836
837    /* Read the actual data set header field from the packet into memory */
838    temp_data = tvb_get_guint8(tvb, offset + 5);
839
840    /* Create the tree for the actual data set header field */
841    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_act_data_set, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
842    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_actual_data_set);
843    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_pos,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
844    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vel,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
845    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_acc,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
846    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_trq,  tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
847    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_crnt, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
848    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_vltg, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
849    proto_tree_add_item(temp_proto_tree, hf_cip_act_data_fqcy, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
850
851    /* Display the actual data values from the cyclic data payload within the command data set tree, the
852    * cyclic data starts immediately after the interpolation control field in the controller to device
853    * direction and the actual data starts immediately after the cyclic data */
854    bytes_used += dissect_act_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used);
855
856    /* Read the status data set header field from the packet into memory */
857    temp_data = tvb_get_guint8(tvb, offset + 6);
858
859    /* Create the tree for the status data set header field */
860    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_sts_data_set, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
861    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_status_data_set);
862    proto_tree_add_item(temp_proto_tree, hf_cip_sts_flt,    tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
863    proto_tree_add_item(temp_proto_tree, hf_cip_sts_alrm,   tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
864    proto_tree_add_item(temp_proto_tree, hf_cip_sts_sts,    tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
865    proto_tree_add_item(temp_proto_tree, hf_cip_sts_iosts,  tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
866    proto_tree_add_item(temp_proto_tree, hf_cip_sts_safety, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
867
868    /* Display the status data values from the cyclic data payload within the status data set tree, the
869    * cyclic data starts immediately after the axis state field in the device to controller
870    * direction and the status data starts immediately after the cyclic data */
871    bytes_used += dissect_status_data_set(temp_data, temp_proto_tree, tvb, offset + 8 + bytes_used);
872
873    /* Display the axis state control field */
874    proto_tree_add_item(header_tree, hf_cip_axis_state, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
875
876    /* Return the offset to the next byte in the message */
877    return offset + 8 + bytes_used;
878 }
879
880 /*
881  * Function name: dissect_cyclic_wt
882  *
883  * Purpose: Dissect the cyclic write data block in a controller to device message
884  *
885  * Returns: The new offset into the message that follow on dissections should use
886  * as their starting offset
887  */
888 static guint32
889 dissect_cyclic_wt(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
890 {
891    proto_item *header_item;
892    proto_tree *header_tree;
893
894    /* Create the tree for the entire cyclic write data block */
895    header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Write Data Block");
896    header_tree = proto_item_add_subtree(header_item, ett_cyclic_rd_wt);
897
898    /* Display the cyclic write block id value */
899    proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
900
901    /* Display the cyclic read block id value */
902    proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
903
904    /* Display the remainder of the cyclic write data if there is any */
905    if ( (size - 4) > 0 )
906    {
907       proto_tree_add_item(header_tree, hf_cip_cyclic_wrt_data, tvb, offset + 4, size - 4, ENC_NA);
908    }
909
910    return offset + size;
911 }
912
913 /*
914  * Function name: dissect_cyclic_rd
915  *
916  * Purpose: Dissect the cyclic read data block in a device to controller message
917  *
918  * Returns: The new offset into the message that follow on dissections should use
919  * as their starting offset
920  */
921 static guint32
922 dissect_cyclic_rd(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
923 {
924    proto_item *header_item;
925    proto_tree *header_tree;
926
927    /* Create the tree for the entire cyclic write data block */
928    header_item = proto_tree_add_text(tree, tvb, offset, size, "Cyclic Read Data Block");
929    header_tree = proto_item_add_subtree(header_item, ett_cyclic_rd_wt);
930
931    /* Display the cyclic write block id value */
932    proto_tree_add_item(header_tree, hf_cip_cyclic_write_blk, tvb, offset, 1, ENC_LITTLE_ENDIAN);
933
934    /* Display the cyclic write status value */
935    proto_tree_add_item(header_tree, hf_cip_cyclic_write_sts, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
936
937    /* Display the cyclic read block id value */
938    proto_tree_add_item(header_tree, hf_cip_cyclic_read_blk, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
939
940    /* Display the cyclic read status value */
941    proto_tree_add_item(header_tree, hf_cip_cyclic_read_sts, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
942
943    /* Display the remainder of the cyclic read data if there is any*/
944    if ( (size - 4) > 0 )
945    {
946       proto_tree_add_item(header_tree, hf_cip_cyclic_rd_data, tvb, offset + 4, size - 4, ENC_NA);
947    }
948
949    return offset + size;
950 }
951
952 /*
953  * Function name: dissect_cntr_event
954  *
955  * Purpose: Dissect the event data block in a controller to device message
956  *
957  * Returns: The new offset into the message that follow on dissections should use
958  * as their starting offset
959  */
960 static guint32
961 dissect_cntr_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
962 {
963    proto_item *header_item, *temp_proto_item;
964    proto_tree *header_tree, *temp_proto_tree;
965    guint32 temp_data;
966    guint32 acks, cur_ack;
967    guint32 bytes_used = 0;
968
969    /* Create the tree for the entire cyclic write data block */
970    header_item = proto_tree_add_text(tree, tvb, offset, size, "Event Data Block");
971    header_tree = proto_item_add_subtree(header_item, ett_event);
972
973    /* Read the event checking control header field from the packet into memory */
974    temp_data = tvb_get_letohl(tvb, offset);
975
976    /* Create the tree for the event checking control header field */
977    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_event_checking, tvb, offset, 4, ENC_LITTLE_ENDIAN);
978    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_event_check_ctrl);
979
980    /* Add the individual elements of the event checking control */
981    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
982    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
983    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
984    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
985    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
986    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg1_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
987    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
988    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_reg2_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
989    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_marker_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
990    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_marker_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
991    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pos,   tvb, offset, 4, ENC_LITTLE_ENDIAN);
992    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_neg,   tvb, offset, 4, ENC_LITTLE_ENDIAN);
993    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pp,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
994    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_pm,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
995    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_mp,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
996    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_home_mm,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
997    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_ctrl_acks,       tvb, offset, 4, ENC_LITTLE_ENDIAN);
998    /* The dissector will indicate if the protocol is requesting an extended event format but will not dissect it,
999    * to date no products actually support this format */
1000    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_extend_format,   tvb, offset, 4, ENC_LITTLE_ENDIAN);
1001
1002    /* The event checking control value is 4 bytes long */
1003    bytes_used = 4;
1004
1005    /* The final 4 bits of the event checking control value are the number of acknowledgements in the message */
1006    acks = (temp_data >> 28) & 0x0F;
1007
1008    /* Each acknowledgement contains and id and a status value */
1009    for (cur_ack = 0; cur_ack < acks; cur_ack++)
1010    {
1011      /* Display the current acknowledgement id */
1012      proto_tree_add_item(header_tree, hf_cip_event_ack, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1013      bytes_used += 1;
1014
1015      /* Display the current event status */
1016      proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1017      bytes_used += 1;
1018    }
1019
1020    return offset + size;
1021 }
1022
1023 /*
1024  * Function name: dissect_devce_event
1025  *
1026  * Purpose: Dissect the event data block in a device to controller message
1027  *
1028  * Returns: The new offset into the message that follow on dissections should use
1029  * as their starting offset
1030  */
1031 static guint32
1032 dissect_devce_event(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1033 {
1034    proto_item *header_item, *temp_proto_item;
1035    proto_tree *header_tree, *temp_proto_tree;
1036    guint64     temp_data;
1037    guint64     nots, cur_not;
1038    guint32     bytes_used = 0;
1039
1040    /* Create the tree for the entire cyclic write data block */
1041    header_item = proto_tree_add_text(tree, tvb, offset, size, "Event Data Block");
1042    header_tree = proto_item_add_subtree(header_item, ett_event);
1043
1044    /* Read the event checking control header field from the packet into memory */
1045    temp_data = tvb_get_letohl(tvb, offset);
1046
1047    /* Create the tree for the event checking control header field */
1048    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_event_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1049    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_event_check_sts);
1050
1051    /* Add the individual elements of the event checking control */
1052    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1053    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1054    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1055    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1056    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1057    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg1_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1058    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_posrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1059    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_reg2_negrearm, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1060    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_marker_pos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1061    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_marker_neg, tvb, offset, 4, ENC_LITTLE_ENDIAN);
1062    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pos,   tvb, offset, 4, ENC_LITTLE_ENDIAN);
1063    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_neg,   tvb, offset, 4, ENC_LITTLE_ENDIAN);
1064    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pp,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
1065    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_pm,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
1066    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_mp,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
1067    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_home_mm,    tvb, offset, 4, ENC_LITTLE_ENDIAN);
1068    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_sts_nfs,        tvb, offset, 4, ENC_LITTLE_ENDIAN);
1069    /* The dissector will indicate if the protocol is requesting an extended event format but will not dissect it,
1070    * to date no products actually support this format */
1071    proto_tree_add_item(temp_proto_tree, hf_cip_evnt_extend_format,  tvb, offset, 4, ENC_LITTLE_ENDIAN);
1072
1073    /* The event status control value is 4 bytes long */
1074    bytes_used = 4;
1075
1076    /* The final 4 bits of the event status control value are the number of notifications in the message */
1077    nots = (temp_data >> 28) & 0x0F;
1078
1079    /* Each notification contains and id, status value, event type, position and time stamp */
1080    for (cur_not = 0; cur_not < nots; cur_not++)
1081    {
1082       /* Display the current event id */
1083       proto_tree_add_item(header_tree, hf_cip_event_id, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1084       bytes_used += 1;
1085
1086       /* Display the current event status */
1087       proto_tree_add_item(header_tree, hf_cip_evnt_sts_stat, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1088       bytes_used += 1;
1089
1090       /* Display the current event type */
1091       proto_tree_add_item(header_tree, hf_cip_evnt_type, tvb, offset + bytes_used, 1, ENC_LITTLE_ENDIAN);
1092       bytes_used += 2;    /* Increment by 2 to jump the reserved byte */
1093
1094       /* Display the event position value */
1095       proto_tree_add_item(header_tree, hf_cip_event_pos, tvb, offset + bytes_used, 4, ENC_LITTLE_ENDIAN);
1096       bytes_used += 4;
1097
1098       /* Display the event time stamp value */
1099       proto_tree_add_item(header_tree, hf_cip_event_ts, tvb, offset + bytes_used, 8, ENC_LITTLE_ENDIAN);
1100       bytes_used += 8;
1101    }
1102
1103    return size + offset;
1104 }
1105
1106 /*
1107  * Function name: dissect_get_axis_attr_list_request
1108  *
1109  * Purpose: Dissect the get axis attribute list service request
1110  *
1111  * Returns: None
1112  */
1113 static void
1114 dissect_get_axis_attr_list_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1115 {
1116    proto_item *header_item, *attr_item;
1117    proto_tree *header_tree, *attr_tree;
1118    guint16     attribute, attribute_cnt;
1119    guint32     local_offset;
1120    guint8      increment_size, dimension;
1121
1122    /* Create the tree for the get axis attribute list request */
1123    header_item = proto_tree_add_text(tree, tvb, offset, size, "Get Axis Attribute List Request");
1124    header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1125
1126    /* Read the number of attributes that are contained within the request */
1127    attribute_cnt = tvb_get_letohs(tvb, offset);
1128    proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1129
1130    /* Start the attribute loop at the beginning of the first attribute in the list */
1131    local_offset = offset + 4;
1132
1133    /* For each attribute display the associated fields */
1134    for (attribute = 0; attribute < attribute_cnt; attribute++)
1135    {
1136       /* At a minimum the local offset needs will need to be incremented by 4 bytes to reach the next attribute */
1137       increment_size = 4;
1138
1139       /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1140       * properly displaying of the attribute is possible */
1141       dimension       = tvb_get_guint8(tvb, local_offset + 2);
1142
1143       /* Create the tree for this attribute within the request */
1144       attr_item = proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1145       attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1146
1147       proto_tree_add_item(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1148       proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1149
1150       if (dimension == 1)
1151       {
1152          /* Display the start index and start index from the request if this is an array request */
1153          proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1154          proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1155
1156          /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1157          increment_size += 4;
1158       }
1159
1160       /* Move the local offset to the next attribute */
1161       local_offset += increment_size;
1162    }
1163 }
1164
1165 /*
1166  * Function name: dissect_set_axis_attr_list_request
1167  *
1168  * Purpose: Dissect the set axis attribute list service request
1169  *
1170  * Returns: None
1171  */
1172 static void
1173 dissect_set_axis_attr_list_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1174 {
1175    proto_item *header_item, *attr_item;
1176    proto_tree *header_tree, *attr_tree;
1177    guint16     attribute, attribute_cnt, data_elements;
1178    guint32     local_offset;
1179    guint32     attribute_size;
1180    guint8      dimension, attribute_start, increment_size;
1181
1182    /* Create the tree for the set axis attribute list request */
1183    header_item = proto_tree_add_text(tree, tvb, offset, size, "Set Axis Attribute List Request");
1184    header_tree = proto_item_add_subtree(header_item, ett_set_axis_attribute);
1185
1186    /* Read the number of attributes that are contained within the request */
1187    attribute_cnt = tvb_get_letohs(tvb, offset);
1188    proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1189
1190    /* Start the attribute loop at the beginning of the first attribute in the list */
1191    local_offset = offset + 4;
1192
1193    /* For each attribute display the associated fields */
1194    for (attribute = 0; attribute < attribute_cnt; attribute++)
1195    {
1196       /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1197       increment_size = 4;
1198
1199       /* Pull the fields for this attribute from the payload, all fields are needed to make some calculations before
1200       * properly displaying of the attribute is possible */
1201       dimension       = tvb_get_guint8(tvb, local_offset + 2);
1202       attribute_size  = tvb_get_guint8(tvb, local_offset + 3);
1203       attribute_start = 4;
1204
1205       if (dimension == 1)
1206       {
1207          data_elements   = tvb_get_letohs(tvb, local_offset + 6);
1208
1209          /* Modify the size of the attribute data by the number of elements if the request is an array request */
1210          attribute_size *= data_elements;
1211
1212          /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1213          increment_size  += 4;
1214          attribute_start += 4;
1215       }
1216
1217       /* Create the tree for this attribute in the get axis attribute list request */
1218       attr_item = proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1219       attr_tree = proto_item_add_subtree(attr_item, ett_set_axis_attr_list);
1220
1221       proto_tree_add_item(attr_tree, hf_set_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1222       proto_tree_add_item(attr_tree, hf_set_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1223
1224       if (dimension == 1)
1225       {
1226          /* Display the start index and start index from the request if the request is an array */
1227          proto_tree_add_item(attr_tree, hf_set_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1228          proto_tree_add_item(attr_tree, hf_set_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1229       }
1230
1231       /* Display the value of this attribute */
1232       proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
1233
1234       /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1235       if (attribute_size % 4 != 0)
1236       {
1237          attribute_size = attribute_size + (4 - (attribute_size % 4));
1238       }
1239
1240       /* Move the local offset to the next attribute */
1241       local_offset += (attribute_size + increment_size);
1242    }
1243 }
1244
1245 /*
1246  * Function name: dissect_group_sync_request
1247  *
1248  * Purpose: Dissect the group sync service request
1249  *
1250  * Returns: None
1251  */
1252 static void
1253 dissect_group_sync_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1254 {
1255    proto_item *header_item;
1256    proto_tree *header_tree;
1257
1258    /* Create the tree for the group sync request */
1259    header_item = proto_tree_add_text(tree, tvb, offset, size, "Group Sync Request");
1260    header_tree = proto_item_add_subtree(header_item, ett_group_sync);
1261
1262    /* Read the grandmaster id from the payload */
1263    proto_tree_add_item(header_tree, hf_cip_ptp_grandmaster, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1264 }
1265
1266
1267 /*
1268  * Function name: dissect_cntr_service
1269  *
1270  * Purpose: Dissect the service data block in a controller to device message
1271  *
1272  * Returns: The new offset into the message that follow on dissections should use
1273  * as their starting offset
1274  */
1275 static guint32
1276 dissect_cntr_service(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1277 {
1278    proto_item *header_item;
1279    proto_tree *header_tree;
1280    guint8      service;
1281
1282    /* Create the tree for the entire service data block */
1283    header_item = proto_tree_add_text(tree, tvb, offset, size, "Service Data Block");
1284    header_tree = proto_item_add_subtree(header_item, ett_service);
1285
1286    /* Display the transaction id value */
1287    proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1288
1289    /* Display the service code */
1290    service = tvb_get_guint8(tvb, offset + 1);
1291    proto_tree_add_item(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1292
1293    /* If the service is a set axis, get axis attribute or group sync request dissect it as well */
1294    switch(service)
1295    {
1296    case SC_GET_AXIS_ATTRIBUTE_LIST:
1297       dissect_get_axis_attr_list_request(tvb, header_tree, offset + 4, size);
1298       break;
1299    case SC_SET_AXIS_ATTRIBUTE_LIST:
1300       dissect_set_axis_attr_list_request(tvb, header_tree, offset + 4, size);
1301       break;
1302    case SC_GROUP_SYNC:
1303       dissect_group_sync_request(tvb, header_tree, offset + 4, size);
1304       break;
1305    default:
1306       /* Display the remainder of the service channel data */
1307       proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1308    }
1309
1310    return offset + size;
1311 }
1312
1313 /*
1314  * Function name: dissect_set_axis_attr_list_response
1315  *
1316  * Purpose: Dissect the set axis attribute list service response
1317  *
1318  * Returns: None
1319  */
1320 static void
1321 dissect_set_axis_attr_list_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1322 {
1323    proto_item *header_item, *attr_item;
1324    proto_tree *header_tree, *attr_tree;
1325    guint16     attribute, attribute_cnt;
1326    guint32     local_offset;
1327
1328    /* Create the tree for the set axis attribute list response */
1329    header_item = proto_tree_add_text(tree, tvb, offset, size, "Set Axis Attribute List Response");
1330    header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1331
1332    /* Read the number of attributes that are contained within the response */
1333    attribute_cnt = tvb_get_letohs(tvb, offset);
1334    proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1335
1336    /* Start the attribute loop at the beginning of the first attribute in the list */
1337    local_offset = offset + 4;
1338
1339    /* For each attribute display the associated fields */
1340    for (attribute = 0; attribute < attribute_cnt; attribute++)
1341    {
1342       /* Create the tree for the current attribute in the set axis attribute list response */
1343       attr_item = proto_tree_add_item(header_tree, hf_set_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1344       attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1345
1346       /* Add the response status to the tree */
1347       proto_tree_add_item(attr_tree, hf_cip_svc_set_axis_attr_sts, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1348
1349       /* Move the local offset to the next attribute */
1350       local_offset += 4;
1351    }
1352 }
1353
1354 /*
1355  * Function name: dissect_get_axis_attr_list_response
1356  *
1357  * Purpose: Dissect the get axis attribute list service response
1358  *
1359  * Returns: None
1360  */
1361 static void
1362 dissect_get_axis_attr_list_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1363 {
1364    proto_item *header_item, *attr_item;
1365    proto_tree *header_tree, *attr_tree;
1366    guint16     attribute, attribute_cnt, data_elements;
1367    guint32     attribute_size;
1368    guint8      dimension, attribute_start, increment_size;
1369    guint32     local_offset;
1370
1371    /* Create the tree for the get axis attribute list response */
1372    header_item = proto_tree_add_text(tree, tvb, offset, size, "Get Axis Attribute List Response");
1373    header_tree = proto_item_add_subtree(header_item, ett_get_axis_attribute);
1374
1375    /* Read the number of attributes that are contained within the request */
1376    attribute_cnt = tvb_get_letohs(tvb, offset);
1377    proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN);
1378
1379    /* Start the attribute loop at the beginning of the first attribute in the list */
1380    local_offset = offset + 4;
1381
1382    /* For each attribute display the associated fields */
1383    for (attribute = 0; attribute < attribute_cnt; attribute++)
1384    {
1385       /* At a minimum the local offset needs to be incremented by 4 bytes to reach the next attribute */
1386       increment_size = 4;
1387
1388       /* Pull the fields for this attribute from the payload, all fields are need to make some calculations before
1389       * properly displaying of the attribute is possible */
1390       dimension       = tvb_get_guint8(tvb, local_offset + 2);
1391       attribute_size  = tvb_get_guint8(tvb, local_offset + 3);
1392       attribute_start = 4;
1393
1394       if (dimension == 1)
1395       {
1396          data_elements   = tvb_get_letohs(tvb, local_offset + 6);
1397
1398          /* Modify the size of the attribute data by the number of elements if the request is an array request */
1399          attribute_size *= data_elements;
1400
1401          /* Modify the amount to update the local offset by and the start of the data to include the index and elements field */
1402          increment_size  += 4;
1403          attribute_start += 4;
1404       }
1405
1406       /* Display the fields associated with the get axis attribute list response */
1407       attr_item = proto_tree_add_item(header_tree, hf_get_axis_attr_list_attribute_id, tvb, local_offset, 2, ENC_LITTLE_ENDIAN);
1408       attr_tree = proto_item_add_subtree(attr_item, ett_get_axis_attr_list);
1409
1410       if (dimension == 0xFF)
1411       {
1412          /* Display the element size as an error code if the dimension field indicates an error */
1413          proto_tree_add_item(attr_tree, hf_cip_svc_get_axis_attr_sts, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1414
1415          /* No attribute data so no attribute size */
1416          attribute_size = 0;
1417       }
1418       else
1419       {
1420          proto_tree_add_item(attr_tree, hf_get_axis_attr_list_dimension, tvb, local_offset + 2, 1, ENC_LITTLE_ENDIAN);
1421          proto_tree_add_item(attr_tree, hf_get_axis_attr_list_element_size, tvb, local_offset + 3, 1, ENC_LITTLE_ENDIAN);
1422
1423          if (dimension == 1)
1424          {
1425             /* Display the start index and start indexfrom the request */
1426             proto_tree_add_item(attr_tree, hf_get_axis_attr_list_start_index, tvb, local_offset + 4, 2, ENC_LITTLE_ENDIAN);
1427             proto_tree_add_item(attr_tree, hf_get_axis_attr_list_data_elements, tvb, local_offset + 6, 2, ENC_LITTLE_ENDIAN);
1428          }
1429
1430          /* Display the remainder of the service channel data */
1431          proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, offset + attribute_start, attribute_size, ENC_NA);
1432
1433          /* Round the attribute size up so the next attribute lines up on a 32-bit boundary */
1434          if (attribute_size % 4 != 0)
1435          {
1436              attribute_size = attribute_size + (4 - (attribute_size % 4));
1437          }
1438       }
1439
1440       /* Move the local offset to the next attribute */
1441       local_offset += (attribute_size + increment_size);
1442    }
1443 }
1444
1445 /*
1446  * Function name: dissect_group_sync_response
1447  *
1448  * Purpose: Dissect the group sync service response
1449  *
1450  * Returns: None
1451  */
1452 static void
1453 dissect_group_sync_response (tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size _U_)
1454 {
1455    proto_tree_add_item(tree, hf_cip_group_sync, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1456 }
1457
1458 /*
1459  * Function name: dissect_devce_service
1460  *
1461  * Purpose: Dissect the service data block in a device to controller message
1462  *
1463  * Returns: The new offset into the message that follow on dissections should use
1464  * as their starting offset
1465  */
1466 static guint32
1467 dissect_devce_service(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size)
1468 {
1469    proto_item *header_item;
1470    proto_tree *header_tree;
1471
1472    /* Create the tree for the entire service data block */
1473    header_item = proto_tree_add_text(tree, tvb, offset, size, "Service Data Block");
1474    header_tree = proto_item_add_subtree(header_item, ett_service);
1475
1476    /* Display the transaction id value */
1477    proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1478
1479    /* Display the service code */
1480    proto_tree_add_item(header_tree, hf_cip_svc_code, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1481
1482    /* Display the general status code */
1483    proto_tree_add_item(header_tree, hf_cip_svc_sts, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1484
1485    /* Display the extended status code */
1486    proto_tree_add_item(header_tree, hf_cip_svc_ext_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1487
1488    /* If the service is a set axis, get axis attribute response or group sync dissect it as well */
1489    switch(tvb_get_guint8(tvb, offset + 1))
1490    {
1491    case SC_GET_AXIS_ATTRIBUTE_LIST:
1492       dissect_get_axis_attr_list_response(tvb, header_tree, offset + 4, size);
1493       break;
1494    case SC_SET_AXIS_ATTRIBUTE_LIST:
1495       dissect_set_axis_attr_list_response(tvb, header_tree, offset + 4, size);
1496       break;
1497    case SC_GROUP_SYNC:
1498       dissect_group_sync_response(tvb, header_tree, offset + 4, size);
1499       break;
1500    default:
1501       /* Display the remainder of the service channel data */
1502       proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
1503    }
1504
1505    return offset + size;
1506 }
1507
1508 /*
1509  * Function name: dissect_var_inst_header
1510  *
1511  * Purpose: Dissect the instance data header of a variable controller to device or
1512  * device to controller message
1513  *
1514  * Returns: void
1515  */
1516 static void
1517 dissect_var_inst_header(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint8* inst_number, guint32* cyc_size,
1518                         guint32* cyc_blk_size, guint32* evnt_size, guint32* servc_size)
1519 {
1520    guint8      temp_data;
1521    proto_item *header_item;
1522    proto_tree *header_tree;
1523
1524    /* Create the tree for the entire instance data header */
1525    *inst_number = tvb_get_guint8(tvb, offset);
1526
1527    header_item = proto_tree_add_text(tree, tvb, offset, 8, "Instance Data Header - Instance: %d", *inst_number);
1528    header_tree = proto_item_add_subtree(header_item, ett_inst_data_header);
1529
1530    /* Read the instance number field from the instance data header */
1531    proto_tree_add_item(header_tree, hf_var_devce_instance, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1532
1533    /* The "size" fields in the instance data block header are all stored as number of 32-bit words the
1534    * block uses since all blocks should pad up to 32-bits so to convert to bytes each is mulitplied by 4 */
1535
1536    /* Read the instance block size field in bytes from the instance data header */
1537    temp_data = tvb_get_guint8(tvb, offset + 2);
1538    proto_tree_add_uint_format_value(header_tree, hf_var_devce_instance_block_size,
1539                                     tvb, offset + 2, 1, temp_data, "%d words", temp_data);
1540
1541    /* Read the cyclic block size field in bytes from the instance data header */
1542    temp_data = tvb_get_guint8(tvb, offset + 3);
1543    proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_block_size,
1544                                     tvb, offset + 3, 1, temp_data, "%d words", temp_data);
1545
1546    /* Read the cyclic command block size field in bytes from the instance data header */
1547    *cyc_size = (tvb_get_guint8(tvb, offset + 4) * 4);
1548    proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_data_block_size,
1549                                     tvb, offset + 4, 1, (*cyc_size)/4, "%d words", (*cyc_size)/4);
1550
1551    /* Read the cyclic write block size field in bytes from the instance data header */
1552    *cyc_blk_size = (tvb_get_guint8(tvb, offset + 5) * 4);
1553    proto_tree_add_uint_format_value(header_tree, hf_var_devce_cyclic_rw_block_size,
1554                                     tvb, offset + 5, 1, (*cyc_blk_size)/4, "%d words", (*cyc_blk_size)/4);
1555
1556    /* Read the event block size in bytes from the instance data header */
1557    *evnt_size = (tvb_get_guint8(tvb, offset + 6) * 4);
1558    proto_tree_add_uint_format_value(header_tree, hf_var_devce_event_block_size,
1559                                     tvb, offset + 6, 1, (*evnt_size)/4, "%d words", (*evnt_size)/4);
1560
1561    /* Read the service block size in bytes from the instance data header */
1562    *servc_size = (tvb_get_guint8(tvb, offset + 7) * 4);
1563    proto_tree_add_uint_format_value(header_tree, hf_var_devce_service_block_size,
1564                                     tvb, offset + 7, 1, (*servc_size)/4, "%d words", (*servc_size)/4);
1565 }
1566
1567 /*
1568  * Function name: dissect_var_cont_conn_header
1569  *
1570  * Purpose: Dissect the connection header of a variable controller to device message
1571  *
1572  * Returns: Offset to the start of the instance data block
1573  */
1574 static guint32
1575 dissect_var_cont_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1576 {
1577    guint32     header_size;
1578    guint32     temp_data;
1579    proto_item *header_item, *temp_proto_item;
1580    proto_tree *header_tree, *temp_proto_tree;
1581
1582    /* Calculate the header size, start with the basic header size */
1583    header_size = 8;
1584
1585    temp_data = tvb_get_guint8(tvb, offset + 7);
1586
1587    /* Check the time data set field for enabled bits. If either update period or
1588    * update time stamp fields are set, bump the header size by the appropriate size */
1589    if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1590    {
1591       header_size += 8;
1592    }
1593    if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1594    {
1595       header_size += 8;
1596    }
1597
1598    /* Create the tree for the entire connection header */
1599    header_item = proto_tree_add_text(tree, tvb, offset, header_size, "Connection Header");
1600    header_tree = proto_item_add_subtree(header_item, ett_cont_dev_header);
1601
1602    /* Add the connection header fields that are common to all types of messages */
1603    proto_tree_add_item(header_tree, hf_cip_format,   tvb, offset, 1, ENC_LITTLE_ENDIAN);
1604    proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1605    proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1606
1607    /* Create the tree for the node control header field */
1608    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_node_control, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1609    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_node_control);
1610
1611    /* Add the individual data elements to the node control tree */
1612    proto_tree_add_item(temp_proto_tree, hf_cip_node_control_remote, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1613    proto_tree_add_item(temp_proto_tree, hf_cip_node_control_sync, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1614    proto_tree_add_item(temp_proto_tree, hf_cip_node_data_valid, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1615    proto_tree_add_item(temp_proto_tree, hf_cip_node_fault_reset, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1616
1617    /* Read the instance count field from the packet into memory, this gets passed back out of the method */
1618    *inst_count = tvb_get_guint8(tvb, offset + 4);
1619
1620    /* Add the instance count and last update id to the connection header tree */
1621    proto_tree_add_item(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
1622    proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
1623
1624    /* Read the time data set from the packet into memory */
1625    temp_data = tvb_get_guint8(tvb, offset + 7);
1626
1627    /* Create the tree for the time data set field */
1628    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_time_data_set, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1629    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_time_data_set);
1630
1631    /* Add the individual data elements to the time data set header field */
1632    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_stamp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1633    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_offset, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1634    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1635    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_time_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1636
1637    /* Move the offset to the byte just beyond the time data set field */
1638    offset = (offset + 7 + 1);
1639
1640    /* Add the time values if they are present in the time data set header field */
1641    if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1642    {
1643       proto_tree_add_item(header_tree, hf_cip_cont_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1644       offset = (offset + 8);
1645    }
1646
1647    if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1648    {
1649       proto_tree_add_item(header_tree, hf_cip_cont_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1650       offset = (offset + 8);
1651    }
1652
1653    /* Return the number of bytes used so it can be used as an offset in the following dissections */
1654    return offset;
1655 }
1656
1657 /*
1658  * Function name: dissect_var_devce_conn_header
1659  *
1660  * Purpose: Dissect the connection header of a variable device to controller message
1661  *
1662  * Returns: Offset to the start of the instance data block
1663  */
1664 static guint32
1665 dissect_var_devce_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_count, guint32 offset)
1666 {
1667    guint32     header_size;
1668    guint32     temp_data;
1669    proto_item *header_item, *temp_proto_item;
1670    proto_tree *header_tree, *temp_proto_tree;
1671
1672    /* Calculate the header size, start with the basic header size */
1673    header_size = 8;
1674
1675    temp_data = tvb_get_guint8(tvb, offset + 7);
1676    if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1677    {
1678       header_size += 8;
1679    }
1680    if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1681    {
1682       header_size += 8;
1683    }
1684    if ( (temp_data & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
1685    {
1686       header_size += 4;
1687    }
1688    if ( (temp_data & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
1689    {
1690       header_size += 16;
1691    }
1692
1693    /* Create the tree for the entire connection header */
1694    header_item = proto_tree_add_text(tree, tvb, offset, header_size, "Connection Header");
1695    header_tree = proto_item_add_subtree(header_item, ett_cont_dev_header);
1696
1697    /* Add the connection header fields that are common to all types of messages */
1698    proto_tree_add_item(header_tree, hf_cip_format,   tvb, offset, 1, ENC_LITTLE_ENDIAN);
1699    proto_tree_add_item(header_tree, hf_cip_revision, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
1700    proto_tree_add_item(header_tree, hf_cip_updateid, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
1701
1702    /* Create the tree for the node status header field */
1703    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_node_status, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1704    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_node_status);
1705
1706    /* Add the individual data elements to the node control tree */
1707    proto_tree_add_item(temp_proto_tree, hf_cip_node_control_remote, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1708    proto_tree_add_item(temp_proto_tree, hf_cip_node_control_sync, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1709    proto_tree_add_item(temp_proto_tree, hf_cip_node_data_valid, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1710    proto_tree_add_item(temp_proto_tree, hf_cip_node_device_faulted, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
1711
1712    /* Read the instance count field from the packet into memory, this gets passed back out of the method */
1713    *inst_count = tvb_get_guint8(tvb, offset + 4);
1714
1715    /* Add the instance count to the connection header tree */
1716    proto_tree_add_item(header_tree, hf_cip_instance_cnt, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN);
1717
1718    /* The device to controller header contains the node alarms and node faults fields as well. */
1719    proto_tree_add_item(header_tree, hf_cip_node_fltalarms, tvb, offset + 5, 1, ENC_LITTLE_ENDIAN);
1720
1721    /* Add the last update id to the connection header tree */
1722    proto_tree_add_item(header_tree, hf_cip_last_update, tvb, offset + 6, 1, ENC_LITTLE_ENDIAN);
1723
1724    /* Read the time data set from the packet into memory */
1725    temp_data = tvb_get_guint8(tvb, offset + 7);
1726
1727    /* Create the tree for the time data set field */
1728    temp_proto_item = proto_tree_add_item(header_tree, hf_cip_time_data_set, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1729    temp_proto_tree = proto_item_add_subtree(temp_proto_item, ett_time_data_set);
1730
1731    /* Add the individual data elements to the time data set header field */
1732    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_stamp, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1733    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_offset, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1734    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1735    proto_tree_add_item(temp_proto_tree, hf_cip_time_data_time_diag, tvb, offset + 7, 1, ENC_LITTLE_ENDIAN);
1736
1737    /* Move the offset to the byte just beyond the time data set field */
1738    offset = (offset + 7 + 1);
1739
1740    /* Add the time values if they are present in the time data set header field */
1741    if ( (temp_data & TIME_DATA_SET_TIME_STAMP) == TIME_DATA_SET_TIME_STAMP )
1742    {
1743       proto_tree_add_item(header_tree, hf_cip_devc_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1744       offset = (offset + 8);
1745    }
1746
1747    if ( (temp_data & TIME_DATA_SET_TIME_OFFSET) == TIME_DATA_SET_TIME_OFFSET )
1748    {
1749       proto_tree_add_item(header_tree, hf_cip_devc_time_offset, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1750       offset = (offset + 8);
1751    }
1752
1753    if ( (temp_data & TIME_DATA_SET_UPDATE_DIAGNOSTICS) == TIME_DATA_SET_UPDATE_DIAGNOSTICS )
1754    {
1755       /* If the time diagnostic bit is set then the header contains the count of lost updates, late updates, data
1756       * received time stamp and data transmit time stamp */
1757       proto_tree_add_item(header_tree, hf_cip_lost_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1758       offset = (offset + 1);
1759
1760       /* Add the reserved bytes to the offset after adding the late updates to the display */
1761       proto_tree_add_item(header_tree, hf_cip_late_update, tvb, offset, 1, ENC_LITTLE_ENDIAN);
1762       offset = (offset + 3);
1763    }
1764
1765    if ( (temp_data & TIME_DATA_SET_TIME_DIAGNOSTICS) == TIME_DATA_SET_TIME_DIAGNOSTICS )
1766    {
1767       proto_tree_add_item(header_tree, hf_cip_data_rx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1768       offset += 8;
1769
1770       proto_tree_add_item(header_tree, hf_cip_data_tx_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
1771       offset += 8;
1772    }
1773
1774    /* Return the number of bytes used so it can be used as an offset in the following dissections */
1775    return offset;
1776 }
1777
1778
1779 /*
1780  * Function name: dissect_cipmotion
1781  *
1782  * Purpose: Perform the top level dissection of the CIP Motion datagram, it is called by
1783  * Wireshark when the dissection rule registered in proto_reg_handoff_cipmotion is fired
1784  *
1785  * Returns: void
1786  */
1787 static void
1788 dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree)
1789 {
1790    guint32     con_format;
1791 /*   guint32     seq_number; */
1792    guint32     update_id;
1793    proto_item *proto_item_top;
1794    proto_tree *proto_tree_top;
1795    guint32     offset = 0;
1796
1797    /* Pull the CIP class 1 sequence number from the incoming message */
1798 /*   seq_number = tvb_get_letohs(tvb, offset); */
1799    offset = (offset + 2);
1800
1801    /* Pull the actual values for the connection format and update id from the
1802     * incoming message to be used in the column info */
1803    con_format = tvb_get_guint8(tvb, offset);
1804    update_id  = tvb_get_guint8(tvb, offset + 2);
1805
1806    /* Make entries in Protocol column and Info column on summary display */
1807    col_set_str(pinfo->cinfo, COL_PROTOCOL, "Motion");
1808
1809    /* Add connection format and update number to the info column */
1810    col_add_fstr( pinfo->cinfo, COL_INFO, "%s, Update Id: %d",
1811                  val_to_str(con_format, cip_con_format_vals, "Unknown connection format (%x)"), update_id );
1812
1813    /* If tree is not NULL then Wireshark is requesting that the dissection
1814     * panel be updated with the dissected packet, if tree is NULL then only
1815     * the summary protocol and info columns need to be updated */
1816    if ( tree )
1817    {
1818       /* Create display subtree for the protocol by creating an item and then
1819        * creating a subtree from the item, the subtree must have been registered
1820        * in proto_register_cipmotion already */
1821       proto_item_top = proto_tree_add_item( tree, proto_cipmotion, tvb, 0, -1, ENC_NA );
1822       proto_tree_top = proto_item_add_subtree( proto_item_top, ett_cipmotion );
1823
1824       /* Add the CIP class 1 sequence number to the tree */
1825       proto_tree_add_item( proto_tree_top, hf_cip_class1_seqnum, tvb, 0, 2, ENC_LITTLE_ENDIAN );
1826
1827       /* Attempt to classify the incoming header */
1828       if (( con_format == FORMAT_VAR_CONTROL_TO_DEVICE ) ||
1829           ( con_format == FORMAT_VAR_DEVICE_TO_CONTROL ))
1830       {
1831          /* Sizes of the individual channels within the connection */
1832          guint32 cyc_size, cyc_blk_size, evnt_size, servc_size;
1833          guint32 inst_count = 0, inst;
1834
1835          /* Dissect the header fields */
1836          switch(con_format)
1837          {
1838          case FORMAT_VAR_CONTROL_TO_DEVICE:
1839             offset = dissect_var_cont_conn_header(tvb, proto_tree_top, &inst_count, offset);
1840             break;
1841          case FORMAT_VAR_DEVICE_TO_CONTROL:
1842             offset = dissect_var_devce_conn_header(tvb, proto_tree_top, &inst_count, offset);
1843             break;
1844          }
1845
1846          /* Repeat the following dissections for each instance within the payload */
1847          for( inst = 0; inst < inst_count; inst++ )
1848          {
1849             /* Actual instance number from header field */
1850             guint8 instance;
1851
1852             /* Dissect the instance data header */
1853             dissect_var_inst_header( tvb, proto_tree_top, offset, &instance,
1854                                      &cyc_size, &cyc_blk_size, &evnt_size, &servc_size );
1855
1856             /* Increment the offset to just beyond the instance header */
1857             offset += 8;
1858
1859             /* Dissect the cyclic command (actual) data if any exists */
1860             /* Dissect the cyclic write (read) data if any exists */
1861             /* Dissect the event data block if there is any event data */
1862             switch(con_format)
1863             {
1864             case FORMAT_VAR_CONTROL_TO_DEVICE:
1865                if ( cyc_size > 0 )
1866                   offset = dissect_cntr_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
1867                if ( cyc_blk_size > 0 )
1868                   offset = dissect_cyclic_wt(tvb, proto_tree_top, offset, cyc_blk_size);
1869                if ( evnt_size > 0 )
1870                   offset = dissect_cntr_event(tvb, proto_tree_top, offset, evnt_size);
1871                if ( servc_size > 0 )
1872                   offset = dissect_cntr_service(tvb, proto_tree_top, offset, servc_size);
1873                break;
1874             case FORMAT_VAR_DEVICE_TO_CONTROL:
1875                if ( cyc_size > 0 )
1876                   offset = dissect_devce_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
1877                if ( cyc_blk_size > 0 )
1878                   offset = dissect_cyclic_rd( tvb, proto_tree_top, offset, cyc_blk_size );
1879                if ( evnt_size > 0 )
1880                   offset = dissect_devce_event(tvb, proto_tree_top, offset, evnt_size);
1881                if ( servc_size > 0 )
1882                   offset = dissect_devce_service(tvb, proto_tree_top, offset, servc_size);
1883                break;
1884             }
1885
1886          } /* End of instance for( ) loop */
1887       }
1888    }
1889 }
1890
1891 static gboolean
1892 dissect_cipmotion_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1893 {
1894    if (
1895       /* The total message size is 10 bytes long at a minimum, 2 bytes for the
1896        * update id and 8 bytes for the protocol header */
1897       (tvb_length(tvb) >= 10) &&
1898       /* The connection format is between 4 and 7 (fixed format message is very unlikely) */
1899       ( (tvb_get_guint8(tvb, 2) >= 4) ||
1900         (tvb_get_guint8(tvb, 2) <= 7) )  &&
1901       /* The datagram format revision is 2 */
1902       (tvb_get_guint8(tvb, 3) == 2) &&
1903       /* The node control field is a maximum of Fh */
1904       (tvb_get_guint8(tvb, 5) <= 0x0F) &&
1905       /* If all valid bits are set in the time data set the value is 0x0F at a maximum  */
1906       (tvb_get_guint8(tvb, 9) <= 0x0F) )
1907    {
1908       /* ...then attempt a dissection */
1909       dissect_cipmotion(tvb, pinfo, tree);
1910       return TRUE;
1911    }
1912    else
1913    {
1914       return FALSE;
1915    }
1916 }
1917
1918 /*
1919  * Function name: proto_register_cipmotion
1920  *
1921  * Purpose: Register the protocol with Wireshark, a script will add this protocol
1922  * to the list of protocols during the build process. This function is where the
1923  * header fields and subtree identifiers are registered.
1924  *
1925  * Returns: void
1926  */
1927 void
1928 proto_register_cipmotion(void)
1929 {
1930    /* This is a list of header fields that can be used in the dissection or
1931    * to use in a filter expression */
1932    static hf_register_info header_fields[] =
1933    {
1934       /* Connection format header field, the first byte in the message which
1935       * determines if the message is fixed or variable, controller to device,
1936       * device to controller, etc. */
1937       { &hf_cip_format, { "Connection Format", "cipm.format", FT_UINT8, BASE_DEC, VALS(cip_con_format_vals), 0, "Message connection format", HFILL }},
1938
1939       /* Connection format revision header field */
1940       { &hf_cip_revision, { "Format Revision", "cipm.revision", FT_UINT8, BASE_DEC, NULL, 0, "Message format revision", HFILL }},
1941
1942       { &hf_cip_class1_seqnum, { "CIP Class 1 Sequence Number", "cipm.class1seqnum", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
1943       { &hf_cip_updateid, { "Update Id", "cipm.updateid", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Transaction Number", HFILL }},
1944       { &hf_cip_instance_cnt, { "Instance Count", "cipm.instancecount", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1945       { &hf_cip_last_update, { "Last Update Id", "cipm.lastupdate", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1946       { &hf_cip_node_status, { "Node Status", "cipm.nodestatus", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1947       { &hf_cip_node_control, { "Node Control", "cipm.nodecontrol", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1948       { &hf_cip_node_control_remote, { "Remote Control", "cipm.remote", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01, "Node Control: Remote Control", HFILL}},
1949       { &hf_cip_node_control_sync, { "Sync Control", "cipm.sync", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02, "Node Control: Synchronous Operation", HFILL}},
1950       { &hf_cip_node_data_valid, { "Data Valid", "cipm.valid", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04, "Node Control: Data Valid", HFILL}},
1951       { &hf_cip_node_fault_reset, { "Fault Reset", "cipm.fltrst", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08, "Node Control: Device Fault Reset", HFILL}},
1952       { &hf_cip_node_device_faulted, { "Faulted", "cipm.flt", FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x08, "Node Control: Device Faulted", HFILL}},
1953       { &hf_cip_node_fltalarms, { "Node Faults and Alarms", "cipm.fltalarms", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
1954       { &hf_cip_time_data_set, { "Time Data Set", "cipm.timedataset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1955       { &hf_cip_time_data_stamp, { "Time Stamp", "cipm.time.stamp", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_STAMP, "Time Data Set: Time Stamp", HFILL}},
1956       { &hf_cip_time_data_offset, { "Time Offset", "cipm.time.offset", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_OFFSET, "Time Data Set: Time Offset", HFILL}},
1957       { &hf_cip_time_data_diag, { "Time Update Diagnostics", "cipm.time.update", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_UPDATE_DIAGNOSTICS, "Time Data Set: Time Update Diagnostics", HFILL}},
1958       { &hf_cip_time_data_time_diag, { "Time Diagnostics", "cipm.time.diag", FT_BOOLEAN, 8, TFS(&tfs_true_false), TIME_DATA_SET_TIME_DIAGNOSTICS, "Time Data Set: Time Diagnostics", HFILL}},
1959
1960       { &hf_cip_cont_time_stamp, { "Controller Time Stamp", "cipm.ctrltimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Controller Time Stamp", HFILL}},
1961       { &hf_cip_cont_time_offset, { "Controller Time Offset", "cipm.ctrltimeoffser", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Controller Time Offset", HFILL}},
1962       { &hf_cip_data_rx_time_stamp, { "Data Received Time Stamp", "cipm.rxtimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Data Received Time Stamp", HFILL}},
1963       { &hf_cip_data_tx_time_stamp, { "Data Transmit Time Stamp", "cipm.txtimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Data Transmit Time Offset", HFILL}},
1964       { &hf_cip_devc_time_stamp, { "Device Time Stamp", "cipm.devctimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Device Time Stamp", HFILL} },
1965       { &hf_cip_devc_time_offset, { "Device Time Offset", "cipm.devctimeoffser", FT_UINT64, BASE_DEC, NULL, 0, "Time Data Set: Device Time Offset", HFILL}},
1966       { &hf_cip_lost_update, { "Lost Updates", "cipm.lostupdates", FT_UINT8, BASE_DEC, NULL, 0, "Time Data Set: Lost Updates", HFILL}},
1967       { &hf_cip_late_update, { "Lost Updates", "cipm.lateupdates", FT_UINT8, BASE_DEC, NULL, 0, "Time Data Set: Late Updates", HFILL}},
1968
1969       { &hf_cip_motor_cntrl, { "Control Mode", "cipm.ctrlmode", FT_UINT8, BASE_DEC, VALS(cip_motor_control_vals), 0, "Cyclic Data Block: Motor Control Mode", HFILL }},
1970       { &hf_cip_fdbk_config, { "Feedback Config", "cipm.fdbkcfg", FT_UINT8, BASE_DEC, VALS(cip_fdbk_config_vals), 0, "Cyclic Data Block: Feedback Configuration", HFILL }},
1971       { &hf_cip_axis_control, { "Axis Control", "cipm.axisctrl", FT_UINT8, BASE_DEC, VALS(cip_axis_control_vals), 0, "Cyclic Data Block: Axis Control", HFILL }},
1972       { &hf_cip_control_status, { "Control Status", "cipm.csts", FT_UINT8, BASE_DEC, VALS(cip_control_status_vals), 0, "Cyclic Data Block: Axis Control Status", HFILL }},
1973       { &hf_cip_axis_response, { "Axis Response", "cipm.axisresp", FT_UINT8, BASE_DEC, VALS(cip_axis_response_vals), 0, "Cyclic Data Block: Axis Response", HFILL }},
1974       { &hf_cip_axis_resp_stat, { "Response Status", "cipm.respstat", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Cyclic Data Block: Axis Response Status", HFILL }},
1975       { &hf_cip_group_sync, { "Group Sync Status", "cipm.syncstatus", FT_UINT8, BASE_HEX, VALS(cip_sync_status_vals), 0, NULL, HFILL }},
1976       { &hf_cip_cmd_data_set, { "Command Data Set", "cipm.cmdset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1977       { &hf_cip_act_data_set, { "Actual Data Set", "cipm.actset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1978       { &hf_cip_sts_data_set, { "Status Data Set", "cipm.stsset", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}},
1979       { &hf_cip_cmd_data_pos_cmd, { "Command Position", "cipm.cmd.pos", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION, "Command Data Set: Command Position", HFILL}},
1980       { &hf_cip_cmd_data_vel_cmd, { "Command Velocity", "cipm.cmd.vel", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_VELOCITY, "Command Data Set: Command Velocity", HFILL}},
1981       { &hf_cip_cmd_data_acc_cmd, { "Command Acceleration", "cipm.cmd.acc", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_ACCELERATION, "Command Data Set: Command Acceleration", HFILL}},
1982       { &hf_cip_cmd_data_trq_cmd, { "Command Torque", "cipm.cmd.trq", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_TORQUE, "Command Data Set: Command Torque", HFILL}},
1983       { &hf_cip_cmd_data_pos_trim_cmd, { "Position Trim", "cipm.cmd.postrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_POSITION_TRIM, "Command Data Set: Position Trim", HFILL}},
1984       { &hf_cip_cmd_data_vel_trim_cmd, { "Velocity Trim", "cipm.cmd.veltrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_VELOCITY_TRIM, "Command Data Set: Velocity Trim", HFILL}},
1985       { &hf_cip_cmd_data_acc_trim_cmd, { "Acceleration Trim", "cipm.cmd.acctrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_ACCELERATION_TRIM, "Command Data Set: Acceleration Trim", HFILL}},
1986       { &hf_cip_cmd_data_trq_trim_cmd, { "Torque Trim", "cipm.cmd.trqtrm", FT_BOOLEAN, 8, TFS(&tfs_true_false), COMMAND_DATA_SET_TORQUE_TRIM, "Command Data Set: Torque Trim", HFILL}},
1987
1988       { &hf_cip_act_data_pos, { "Actual Position", "cipm.act.pos", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_POSITION, "Acutal Data Set: Actual Position", HFILL}},
1989       { &hf_cip_act_data_vel, { "Actual Velocity", "cipm.act.vel", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_VELOCITY, "Actual Data Set: Actual Velocity", HFILL}},
1990       { &hf_cip_act_data_acc, { "Actual Acceleration", "cipm.act.acc", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_ACCELERATION, "Actual Data Set: Actual Acceleration", HFILL}},
1991       { &hf_cip_act_data_trq, { "Actual Torque", "cipm.act.trq", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_TORQUE, "Actual Data Set: Actual Torque", HFILL}},
1992       { &hf_cip_act_data_crnt, { "Actual Current", "cipm.act.crnt", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_CURRENT, "Actual Data Set: Actual Current", HFILL}},
1993       { &hf_cip_act_data_vltg, { "Actual Voltage", "cipm.act.vltg", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_VOLTAGE, "Actual Data Set: Actual Voltage", HFILL}},
1994       { &hf_cip_act_data_fqcy, { "Actual Frequency", "cipm.act.fqcy", FT_BOOLEAN, 8, TFS(&tfs_true_false), ACTUAL_DATA_SET_FREQUENCY, "Actual Data Set: Actual Frequency", HFILL}},
1995
1996       { &hf_cip_axis_fault, { "Axis Fault Code", "cipm.fault.code", FT_UINT8, BASE_DEC, NULL, 0, "Status Data Set: Fault Code", HFILL }},
1997       { &hf_cip_fault_type, { "Axis Fault Type", "cipm.flttype", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Type", HFILL}},
1998       { &hf_cip_fault_sub_code, { "Axis Fault Sub Code", "cipm.fltsubcode", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Sub Code", HFILL}},
1999       { &hf_cip_fault_action, { "Axis Fault Action", "cipm.fltaction", FT_UINT8, BASE_DEC, NULL, 0, "Axis Status: Axis Fault Action", HFILL}},
2000       { &hf_cip_fault_time_stamp, { "Axis Fault Time Stamp", "cipm.flttimestamp", FT_UINT64,  BASE_DEC, NULL, 0, "Axis Status: Axis Fault Time Stamp", HFILL}},
2001       { &hf_cip_alarm_type, { "Axis Fault Type", "cipm.alarmtype", FT_UINT8,  BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Type", HFILL}},
2002       { &hf_cip_alarm_sub_code, { "Axis Alarm Sub Code", "cipm.alarmsubcode", FT_UINT8,  BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Sub Code", HFILL} },
2003       { &hf_cip_alarm_state, { "Axis Alarm State", "cipm.alarmstate", FT_UINT8,  BASE_DEC, NULL, 0, "Axis Status: Axis Alarm State", HFILL }},
2004       { &hf_cip_alarm_time_stamp, { "Axis Fault Time Stamp", "cipm.alarmtimestamp", FT_UINT64,  BASE_DEC, NULL, 0, "Axis Status: Axis Alarm Time Stamp", HFILL}},
2005       { &hf_cip_axis_status, { "Axis Status", "cipm.axisstatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2006       { &hf_cip_axis_status_mfg, { "Axis Status Mfg", "cipm.axisstatusmfg", FT_UINT32, BASE_HEX, NULL, 0, "Axis Status, Manufacturer Specific", HFILL}},
2007       { &hf_cip_axis_io_status, { "Axis I/O Status", "cipm.axisiostatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2008       { &hf_cip_axis_io_status_mfg, { "Axis I/O Status Mfg", "cipm.axisiostatusmfg", FT_UINT32, BASE_HEX, NULL, 0, "Axis I/O Status, Manufacturer Specific", HFILL}},
2009       { &hf_cip_safety_status, { "Axis Safety Status", "cipm.safetystatus", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}},
2010       { &hf_cip_sts_flt, { "Axis Fault Codes", "cipm.sts.flt", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_FAULT, "Status Data Set: Axis Fault Codes", HFILL}},
2011       { &hf_cip_sts_alrm, { "Axis Alarm Codes", "cipm.sts.alarm", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_ALARM, "Status Data Set: Axis Alarm Codes", HFILL}},
2012       { &hf_cip_sts_sts, { "Axis Status", "cipm.sts.sts", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_STATUS, "Status Data Set: Axis Status", HFILL}},
2013       { &hf_cip_sts_iosts, { "Axis I/O Status", "cipm.sts.iosts", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_IO_STATUS, "Status Data Set: Axis I/O Status", HFILL}},
2014       { &hf_cip_sts_safety, { "Axis Safety Status", "cipm.sts.safety", FT_BOOLEAN, 8, TFS(&tfs_true_false), STATUS_DATA_SET_AXIS_SAFETY, "Status Data Set: Axis Safety Status", HFILL}},
2015
2016       { &hf_cip_intrp, { "Interpolation Control", "cipm.intrp", FT_UINT8, BASE_DEC, VALS(cip_interpolation_vals), COMMAND_CONTROL_TARGET_UPDATE, "Cyclic Data Block: Interpolation Control", HFILL}},
2017       { &hf_cip_position_data_type, { "Position Data Type", "cipm.posdatatype", FT_UINT8, BASE_DEC, VALS(cip_pos_data_type_vals), COMMAND_CONTROL_POSITION_DATA_TYPE, "Cyclic Data Block: Position Data Type", HFILL }},
2018       { &hf_cip_axis_state, { "Axis State", "cipm.axste", FT_UINT8,  BASE_DEC, VALS(cip_axis_state_vals), 0, "Cyclic Data Block: Axis State", HFILL}},
2019       { &hf_cip_command_control, { "Command Control", "cipm.cmdcontrol", FT_UINT8, BASE_DEC, NULL, 0, "Cyclic Data Block: Command Control", HFILL }},
2020       { &hf_cip_cyclic_wrt_data, { "Write Data", "cipm.writedata", FT_BYTES, BASE_NONE, NULL, 0, "Cyclic Write: Data", HFILL }},
2021       { &hf_cip_cyclic_rd_data, { "Read Data", "cipm.readdata", FT_BYTES, BASE_NONE, NULL, 0, "Cyclic Read: Data", HFILL }},
2022       { &hf_cip_cyclic_write_blk, { "Write Block", "cipm.writeblk", FT_UINT8,  BASE_DEC, NULL, 0, "Cyclic Data Block: Write Block Id", HFILL }},
2023       { &hf_cip_cyclic_read_blk, { "Read Block", "cipm.readblk", FT_UINT8,  BASE_DEC, NULL, 0, "Cyclic Data Block: Read Block Id", HFILL}},
2024       { &hf_cip_cyclic_write_sts, { "Write Status", "cipm.writests", FT_UINT8,  BASE_DEC, NULL, 0, "Cyclic Data Block: Write Status", HFILL }},
2025       { &hf_cip_cyclic_read_sts, { "Read Status", "cipm.readsts", FT_UINT8,  BASE_DEC, NULL, 0, "Cyclic Data Block: Read Status", HFILL }},
2026       { &hf_cip_event_checking, { "Event Control", "cipm.evntchkcontrol", FT_UINT32,  BASE_HEX, NULL, 0, "Event Channel: Event Checking Control", HFILL}},
2027       { &hf_cip_event_ack, { "Event Acknowledgement", "cipm.evntack", FT_UINT8,  BASE_DEC, NULL, 0, "Event Channel: Event Acknowledgement", HFILL} },
2028       { &hf_cip_event_status, { "Event Status", "cipm.evntchkstatus", FT_UINT32, BASE_HEX, NULL, 0, "Event Channel: Event Checking Status", HFILL} },
2029       { &hf_cip_event_id, { "Event Id", "cipm.evntack", FT_UINT8, BASE_DEC, NULL, 0, "Event Channel: Event Id", HFILL }},
2030       { &hf_cip_event_pos, { "Event Position", "cipm.evntpos", FT_INT32,  BASE_DEC, NULL, 0, "Event Channel: Event Position", HFILL} },
2031       { &hf_cip_event_ts, { "Event Time Stamp", "cipm.evntimestamp", FT_UINT64, BASE_DEC, NULL, 0, "Event Channel: Time Stamp", HFILL}},
2032
2033       { &hf_cip_evnt_ctrl_reg1_pos, { "Reg 1 Pos Edge", "cipm.evnt.ctrl.reg1posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Event Checking Control: Reg 1 Pos Edge", HFILL}},
2034       { &hf_cip_evnt_ctrl_reg1_neg, { "Reg 1 Neg Edge", "cipm.evnt.ctrl.reg1negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Event Checking Control: Reg 1 Neg Edge", HFILL}},
2035       { &hf_cip_evnt_ctrl_reg2_pos, { "Reg 2 Pos Edge", "cipm.evnt.ctrl.reg2posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Event Checking Control: Reg 2 Pos Edge", HFILL}},
2036       { &hf_cip_evnt_ctrl_reg2_neg, { "Reg 2 Neg Edge", "cipm.evnt.ctrl.reg2negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Event Checking Control: Reg 2 Neg Edge", HFILL}},
2037       { &hf_cip_evnt_ctrl_reg1_posrearm, { "Reg 1 Pos Rearm", "cipm.evnt.ctrl.reg1posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Event Checking Control: Reg 1 Pos Rearm", HFILL}},
2038       { &hf_cip_evnt_ctrl_reg1_negrearm, { "Reg 1 Neg Rearm", "cipm.evnt.ctrl.reg1negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Event Checking Control: Reg 1 Neg Rearm", HFILL}},
2039       { &hf_cip_evnt_ctrl_reg2_posrearm, { "Reg 2 Pos Rearm", "cipm.evnt.ctrl.reg2posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Event Checking Control: Reg 2 Pos Rearm", HFILL}},
2040       { &hf_cip_evnt_ctrl_reg2_negrearm, { "Reg 2 Neg Rearm", "cipm.evnt.ctrl.reg2negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Event Checking Control: Reg 2 Neg Rearm", HFILL}},
2041       { &hf_cip_evnt_ctrl_marker_pos, { "Marker Pos Edge", "cipm.evnt.ctrl.mrkrpos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Event Checking Control: Marker Pos Edge", HFILL}},
2042       { &hf_cip_evnt_ctrl_marker_neg, { "Marker Neg Edge", "cipm.evnt.ctrl.mrkrneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Event Checking Control: Marker Neg Edge", HFILL}},
2043       { &hf_cip_evnt_ctrl_home_pos, { "Home Pos Edge", "cipm.evnt.ctrl.homepos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Event Checking Control: Home Pos Edge", HFILL}},
2044       { &hf_cip_evnt_ctrl_home_neg, { "Home Neg Edge", "cipm.evnt.ctrl.homeneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Event Checking Control: Home Neg Edge", HFILL}},
2045       { &hf_cip_evnt_ctrl_home_pp, { "Home-Switch-Marker Plus Plus", "cipm.evnt.ctrl.homepp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000, "Event Checking Control: Home-Switch-Marker Plus Plus", HFILL}},
2046       { &hf_cip_evnt_ctrl_home_pm, { "Home-Switch-Marker Plus Minus", "cipm.evnt.ctrl.homepm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000, "Event Checking Control: Home-Switch-Marker Plus Minus", HFILL}},
2047       { &hf_cip_evnt_ctrl_home_mp,{ "Home-Switch-Marker Minus Plus", "cipm.evnt.ctrl.homemp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000, "Event Checking Control: Home-Switch-Marker Minus Plus", HFILL}},
2048       { &hf_cip_evnt_ctrl_home_mm, { "Home-Switch-Marker Minus Minus", "cipm.evnt.ctrl.homemm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000, "Event Checking Control: Home-Switch-Marker Minus Minus", HFILL}},
2049       { &hf_cip_evnt_ctrl_acks, { "Event Acknowledge Blocks", "cipm.evnt.ctrl.acks", FT_UINT32, BASE_DEC, NULL, 0x70000000, "Event Checking Control: Event Acknowledge Blocks", HFILL}},
2050       { &hf_cip_evnt_extend_format, { "Extended Event Format", "cipm.evnt.extend", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x80000000, "Event Checking Control: Extended Event Format", HFILL}},
2051
2052       { &hf_cip_evnt_sts_reg1_pos,{ "Reg 1 Pos Edge", "cipm.evnt.sts.reg1posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Event Checking Status: Reg 1 Pos Edge", HFILL}},
2053       { &hf_cip_evnt_sts_reg1_neg, { "Reg 1 Neg Edge", "cipm.evnt.sts.reg1negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Event Checking Status: Reg 1 Neg Edge", HFILL }},
2054       { &hf_cip_evnt_sts_reg2_pos, { "Reg 2 Pos Edge", "cipm.evnt.sts.reg2posedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Event Checking Status: Reg 2 Pos Edge", HFILL}},
2055       { &hf_cip_evnt_sts_reg2_neg, { "Reg 2 Neg Edge", "cipm.evnt.sts.reg2negedge", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Event Checking Status: Reg 2 Neg Edge", HFILL}},
2056       { &hf_cip_evnt_sts_reg1_posrearm, { "Reg 1 Pos Rearm", "cipm.evnt.sts.reg1posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Event Checking Status: Reg 1 Pos Rearm", HFILL}},
2057       { &hf_cip_evnt_sts_reg1_negrearm, { "Reg 1 Neg Rearm", "cipm.evnt.sts.reg1negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Event Checking Status: Reg 1 Neg Rearm", HFILL}},
2058       { &hf_cip_evnt_sts_reg2_posrearm, { "Reg 2 Pos Rearm", "cipm.evnt.sts.reg2posrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Event Checking Status: Reg 2 Pos Rearm", HFILL}},
2059       { &hf_cip_evnt_sts_reg2_negrearm, { "Reg 2 Neg Rearm", "cipm.evnt.sts.reg2negrearm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Event Checking Status: Reg 2 Neg Rearm", HFILL}},
2060       { &hf_cip_evnt_sts_marker_pos, { "Marker Pos Edge", "cipm.evnt.sts.mrkrpos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Event Checking Status: Marker Pos Edge", HFILL}},
2061       { &hf_cip_evnt_sts_marker_neg, { "Marker Neg Edge", "cipm.evnt.sts.mrkrneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Event Checking Status: Marker Neg Edge", HFILL }},
2062       { &hf_cip_evnt_sts_home_pos, { "Home Pos Edge", "cipm.evnt.sts.homepos", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Event Checking Status: Home Pos Edge", HFILL}},
2063       { &hf_cip_evnt_sts_home_neg, { "Home Neg Edge", "cipm.evnt.sts.homeneg", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Event Checking Status: Home Neg Edge", HFILL }},
2064       { &hf_cip_evnt_sts_home_pp, { "Home-Switch-Marker Plus Plus", "cipm.evnt.sts.homepp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000, "Event Checking Status: Home-Switch-Marker Plus Plus", HFILL}},
2065       { &hf_cip_evnt_sts_home_pm, { "Home-Switch-Marker Plus Minus", "cipm.evnt.sts.homepm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000, "Event Checking Status: Home-Switch-Marker Plus Minus", HFILL}},
2066       { &hf_cip_evnt_sts_home_mp, { "Home-Switch-Marker Minus Plus", "cipm.evnt.sts.homemp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000, "Event Checking Status: Home-Switch-Marker Minus Plus", HFILL}},
2067       { &hf_cip_evnt_sts_home_mm, { "Home-Switch-Marker Minus Minus", "cipm.evnt.sts.homemm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000, "Event Checking Status: Home-Switch-Marker Minus Minus", HFILL}},
2068       { &hf_cip_evnt_sts_nfs, { "Event Notification Blocks", "cipm.evnt.sts.nfs", FT_UINT32, BASE_DEC, NULL, 0x70000000, "Event Checking Status: Event Notification Blocks", HFILL}},
2069
2070       { &hf_cip_evnt_sts_stat, { "Event Status", "cipm.evnt.stat", FT_UINT8,  BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Event Data Block: Event Status", HFILL }},
2071       { &hf_cip_evnt_type, { "Event Type", "cipm.evnt.type", FT_UINT8,  BASE_DEC, VALS(cip_event_type_vals), 0, "Event Data Block: Event Type", HFILL}},
2072       { &hf_cip_svc_code, { "Service Code", "cipm.svc.code", FT_UINT8, BASE_DEC, VALS(cip_sc_vals), 0, "Service Data Block: Service Code", HFILL}},
2073       { &hf_cip_svc_sts, { "General Status", "cipm.svc.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Data Block: General Status", HFILL }},
2074       { &hf_cip_svc_transction, { "Transaction Id", "cipm.svc.tranid", FT_UINT8, BASE_DEC, NULL, 0, "Service Data Block: Transaction Id", HFILL }},
2075       { &hf_cip_svc_ext_status, { "Extended Status", "cipm.svc.extstatus", FT_UINT8, BASE_DEC, NULL, 0, "Service Data Block: Extended Status", HFILL }},
2076       { &hf_cip_svc_data, { "Service Data", "cipm.svc.data", FT_BYTES, BASE_NONE, NULL, 0, "Service Data Block: Data", HFILL }},
2077       { &hf_cip_attribute_data, { "Attribute Data", "cipm.attrdata", FT_BYTES, BASE_NONE, NULL, 0, "Attribute Service: Data", HFILL }},
2078       { &hf_cip_ptp_grandmaster, { "Grandmaster", "cipm.grandmaster", FT_UINT64, BASE_HEX, NULL, 0, "Group Sync: Grandmaster Id", HFILL}},
2079
2080       { &hf_cip_svc_get_axis_attr_sts, { "Attribute Status", "cipm.getaxisattr.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Channel: Get Axis Attribute List Response Status", HFILL }},
2081       { &hf_get_axis_attr_list_attribute_cnt, { "Number of attributes", "cipm.getaxisattr.cnt", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Attribute Count", HFILL}},
2082       { &hf_get_axis_attr_list_attribute_id, { "Attribute ID", "cipm.getaxisattr.id", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Attribute ID", HFILL}},
2083       { &hf_get_axis_attr_list_dimension, { "Dimension", "cipm.getaxisattr.dimension", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Dimension", HFILL}},
2084       { &hf_get_axis_attr_list_element_size, { "Element size", "cipm.getaxisattr.element_size", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Element Size", HFILL}},
2085       { &hf_get_axis_attr_list_start_index, { "Start index", "cipm.getaxisattr.start_index", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Start index", HFILL}},
2086       { &hf_get_axis_attr_list_data_elements, { "Data elements", "cipm.getaxisattr.data_elements", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Get Axis Attribute List Data elements", HFILL}},
2087
2088       { &hf_cip_svc_set_axis_attr_sts, { "Attribute Status", "cipm.setaxisattr.sts", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &cip_gs_vals_ext, 0, "Service Channel: Set Axis Attribute List Response Status", HFILL }},
2089       { &hf_set_axis_attr_list_attribute_cnt, { "Number of attributes", "cipm.setaxisattr.cnt", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Attribute Count", HFILL}},
2090       { &hf_set_axis_attr_list_attribute_id, { "Attribute ID", "cipm.setaxisattr.id", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Attribute ID", HFILL}},
2091       { &hf_set_axis_attr_list_dimension, { "Dimension", "cipm.setaxisattr.dimension", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Dimension", HFILL}},
2092       { &hf_set_axis_attr_list_element_size, { "Element size", "cipm.setaxisattr.element_size", FT_UINT8, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Element Size", HFILL}},
2093       { &hf_set_axis_attr_list_start_index, { "Start index", "cipm.setaxisattr.start_index", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Start index", HFILL}},
2094       { &hf_set_axis_attr_list_data_elements, { "Data elements", "cipm.setaxisattr.data_elements", FT_UINT16, BASE_DEC, NULL, 0, "Service Channel: Set Axis Attribute List Data elements", HFILL}},
2095
2096       { &hf_var_devce_instance, { "Instance Number", "cipm.var_devce.header.instance", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Instance Number", HFILL}},
2097       { &hf_var_devce_instance_block_size, { "Instance Block Size", "cipm.var_devce.header.instance_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Instance Block Size", HFILL}},
2098       { &hf_var_devce_cyclic_block_size, { "Cyclic Block Size", "cipm.var_devce.header.cyclic_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Block Size", HFILL}},
2099       { &hf_var_devce_cyclic_data_block_size, { "Cyclic Data Block Size", "cipm.var_devce.header.cyclic_data_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Data Block Size", HFILL}},
2100       { &hf_var_devce_cyclic_rw_block_size, { "Cyclic Read/Write Block Size", "cipm.var_devce.header.cyclic_rw_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Cyclic Read/Write Block Size", HFILL}},
2101       { &hf_var_devce_event_block_size, { "Event Block Size", "cipm.var_devce.header.event_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Event Block Size", HFILL}},
2102       { &hf_var_devce_service_block_size, { "Service Block Size", "cipm.var_devce.header.service_block_size", FT_UINT8, BASE_DEC, NULL, 0, "Variable Device Header: Service Block Size", HFILL}},
2103
2104       { &hf_cip_axis_alarm, { "Axis Alarm Code", "cipm.alarm.code", FT_UINT8, BASE_DEC, NULL, 0, "Status Data Set: Alarm Code", HFILL }},
2105       { &hf_cip_axis_sts_local_ctrl, { "Local Control", "cipm.axis.local", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000001, "Axis Status Data Set: Local Contol", HFILL }},
2106       { &hf_cip_axis_sts_alarm, { "Alarm", "cipm.axis.alarm", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000002, "Axis Status Data Set: Alarm", HFILL }},
2107       { &hf_cip_axis_sts_dc_bus, { "DC Bus", "cipm.axis.bus", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000004, "Axis Status Data Set: DC Bus", HFILL }},
2108       { &hf_cip_axis_sts_pwr_struct, { "Power Struct", "cipm.axis.pwr", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000008, "Axis Status Data Set: Power Struct", HFILL }},
2109       { &hf_cip_axis_sts_tracking, { "Tracking", "cipm.axis.track", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000020, "Axis Status Data Set: Tracking", HFILL }},
2110       { &hf_cip_axis_sts_pos_lock, { "Pos Lock", "cipm.axis.poslock", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000040, "Axis Status Data Set: Pos Lock", HFILL }},
2111       { &hf_cip_axis_sts_vel_lock, { "Vel Lock", "cipm.axis.vellock", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000080, "Axis Status Data Set: Vel Lock", HFILL }},
2112       { &hf_cip_axis_sts_vel_standstill, { "Standstill", "cipm.axis.nomo", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000100, "Axis Status Data Set: Standstill", HFILL }},
2113       { &hf_cip_axis_sts_vel_threshold, { "Vel Threshold", "cipm.axis.vthresh", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000200, "Axis Status Data Set: Vel Threshold", HFILL }},
2114       { &hf_cip_axis_sts_vel_limit, { "Vel Limit", "cipm.axis.vlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000400, "Axis Status Data Set: Vel Limit", HFILL }},
2115       { &hf_cip_axis_sts_acc_limit, { "Acc Limit", "cipm.axis.alim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00000800, "Axis Status Data Set: Acc Limit", HFILL }},
2116       { &hf_cip_axis_sts_dec_limit, { "Dec Limit", "cipm.axis.dlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00001000, "Axis Status Data Set: Dec Limit", HFILL }},
2117       { &hf_cip_axis_sts_torque_threshold, { "Torque Threshold", "cipm.axis.tthresh", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00002000, "Axis Status Data Set: Torque Threshold", HFILL }},
2118       { &hf_cip_axis_sts_torque_limit, { "Torque Limit", "cipm.axis.tlim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00004000, "Axis Status Data Set: Torque Limit", HFILL }},
2119       { &hf_cip_axis_sts_cur_limit, { "Current Limit", "cipm.axis.ilim", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00008000, "Axis Status Data Set: Current Limit", HFILL }},
2120       { &hf_cip_axis_sts_therm_limit, { "Thermal Limit", "cipm.axis.hot", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000, "Axis Status Data Set: Thermal Limit", HFILL }},
2121       { &hf_cip_axis_sts_feedback_integ, { "Feedback Integrity", "cipm.axis.fgood", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000, "Axis Status Data Set: Feedback Integrity", HFILL }},
2122       { &hf_cip_axis_sts_shutdown, { "Shutdown", "cipm.axis.sdwn", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000, "Axis Status Data Set: Shutdown", HFILL }},
2123       { &hf_cip_axis_sts_in_process, { "In Process", "cipm.axis.inp", FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000, "Axis Status Data Set: In Process", HFILL }},
2124
2125       { &hf_cip_act_pos, { "Actual Position", "cipm.actpos", FT_INT32, BASE_DEC, NULL, 0, "Cyclic Data Set: Actual Position", HFILL }},
2126       { &hf_cip_act_vel, { "Actual Velocity", "cipm.actvel", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Velocity", HFILL }},
2127       { &hf_cip_act_accel, { "Actual Acceleration", "cipm.actaccel", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Acceleration", HFILL }},
2128       { &hf_cip_act_trq, { "Actual Torque", "cipm.acttrq", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Torque", HFILL }},
2129       { &hf_cip_act_crnt, { "Actual Current", "cipm.actcrnt", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Current", HFILL }},
2130       { &hf_cip_act_volts, { "Actual Volts", "cipm.actvolts", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Volts", HFILL }},
2131       { &hf_cip_act_freq, { "Actual Frequency", "cipm.actfreq", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Actual Frequency", HFILL }},
2132       { &hf_cip_pos_cmd,  { "Position Command", "cipm.posfcmd", FT_DOUBLE, BASE_NONE, NULL, 0, "Cyclic Data Set: Position Command (LREAL)", HFILL }},
2133       { &hf_cip_pos_cmd_int, { "Position Command", "cipm.posicmd", FT_INT32, BASE_DEC, NULL, 0, "Cyclic Data Set: Position Command (DINT)", HFILL }},
2134       { &hf_cip_vel_cmd, { "Velocity Command", "cipm.velcmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Velocity Command", HFILL }},
2135       { &hf_cip_accel_cmd, { "Acceleration Command", "cipm.accelcmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Acceleration Command", HFILL }},
2136       { &hf_cip_trq_cmd, { "Torque Command", "cipm.torquecmd", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Torque Command", HFILL }},
2137       { &hf_cip_pos_trim, { "Position Trim", "cipm.postrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Position Trim", HFILL }},
2138       { &hf_cip_vel_trim, { "Velocity Trim", "cipm.veltrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Velocity Trim", HFILL }},
2139       { &hf_cip_accel_trim, { "Acceleration Trim", "cipm.acceltrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Acceleration Trim", HFILL }},
2140       { &hf_cip_trq_trim, { "Torque Trim", "cipm.trqtrim", FT_FLOAT, BASE_NONE, NULL, 0, "Cyclic Data Set: Torque Trim", HFILL }}
2141    };
2142
2143    /* Setup protocol subtree array, these will help Wireshark remember
2144    * if the subtree should be expanded as the user moves through packets */
2145    static gint *cip_subtree[] = {
2146       &ett_cipmotion,
2147       &ett_cont_dev_header,
2148       &ett_node_control,
2149       &ett_node_status,
2150       &ett_time_data_set,
2151       &ett_inst_data_header,
2152       &ett_cyclic_data_block,
2153       &ett_control_mode,
2154       &ett_feedback_config,
2155       &ett_command_data_set,
2156       &ett_actual_data_set,
2157       &ett_status_data_set,
2158       &ett_interp_control,
2159       &ett_cyclic_rd_wt,
2160       &ett_event,
2161       &ett_event_check_ctrl,
2162       &ett_event_check_sts,
2163       &ett_service,
2164       &ett_get_axis_attribute,
2165       &ett_set_axis_attribute,
2166       &ett_get_axis_attr_list,
2167       &ett_set_axis_attr_list,
2168       &ett_group_sync,
2169       &ett_axis_status_set,
2170       &ett_command_control
2171    };
2172
2173    /* Create a CIP Motion protocol handle */
2174    proto_cipmotion = proto_register_protocol(
2175      "Common Industrial Protocol, Motion",  /* Full name of protocol        */
2176      "CIP Motion",           /* Short name of protocol       */
2177      "cipm");                /* Abbreviated name of protocol */
2178
2179    /* Register the header fields with the protocol */
2180    proto_register_field_array(proto_cipmotion, header_fields, array_length(header_fields));
2181
2182    /* Register the subtrees for the protocol dissection */
2183    proto_register_subtree_array(cip_subtree, array_length(cip_subtree));
2184 }
2185
2186 /*
2187  * Function name: proto_reg_handoff_cipmotion
2188  *
2189  * Purpose: This function will setup the automatic dissection of the CIP Motion datagram,
2190  * it is called by Wireshark when the protocol is registered
2191  *
2192  * Returns: void
2193  */
2194 void
2195 proto_reg_handoff_cipmotion(void)
2196 {
2197    heur_dissector_add("enip.cpf.conndata", dissect_cipmotion_heur, proto_cipmotion);
2198 }
2199
2200
2201 /*
2202 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2203 *
2204 * Local variables:
2205 * c-basic-offset: 3
2206 * tab-width: 8
2207 * indent-tabs-mode: nil
2208 * End:
2209 *
2210 * ex: set shiftwidth=3 tabstop=8 expandtab:
2211 * :indentSize=3:tabSize=8:noTabs=true:
2212 */