/* packet-lg8979.c * Routines for Landis & Gyr (Telegyr) 8979 Protocol (lg8979) Dissection * By Chris Bontje (cbontje[AT]gmail.com * Copyright 2013-2016 * ************************************************************************************************ * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include "packet-tcp.h" #include void proto_register_lg8979(void); /* Initialize the protocol and registered fields */ static int proto_lg8979 = -1; static int hf_lg8979_header = -1; static int hf_lg8979_flags = -1; static int hf_lg8979_shr = -1; static int hf_lg8979_mfc = -1; static int hf_lg8979_ack = -1; static int hf_lg8979_con = -1; static int hf_lg8979_frz = -1; static int hf_lg8979_ind = -1; static int hf_lg8979_sch = -1; static int hf_lg8979_slg = -1; static int hf_lg8979_address = -1; static int hf_lg8979_lastblock = -1; static int hf_lg8979_funccode = -1; static int hf_lg8979_length = -1; static int hf_lg8979_start_ptnum16 = -1; static int hf_lg8979_start_ptnum8 = -1; static int hf_lg8979_stop_ptnum16 = -1; static int hf_lg8979_stop_ptnum8 = -1; static int hf_lg8979_ang_point = -1; static int hf_lg8979_adc_ref_zero = -1; static int hf_lg8979_adc_ref_neg90 = -1; static int hf_lg8979_adc_ref_pos90 = -1; static int hf_lg8979_ind_chgrpt_ptnum = -1; static int hf_lg8979_ind_chgrpt_status = -1; static int hf_lg8979_ind_chgrpt_change = -1; static int hf_lg8979_ind_frcrpt_status_b0 = -1; static int hf_lg8979_ind_frcrpt_status_b1 = -1; static int hf_lg8979_ind_frcrpt_status_b2 = -1; static int hf_lg8979_ind_frcrpt_status_b3 = -1; static int hf_lg8979_ind_frcrpt_status_b4 = -1; static int hf_lg8979_ind_frcrpt_status_b5 = -1; static int hf_lg8979_ind_frcrpt_status_b6 = -1; static int hf_lg8979_ind_frcrpt_status_b7 = -1; static int hf_lg8979_ind_frcrpt_change_b0 = -1; static int hf_lg8979_ind_frcrpt_change_b1 = -1; static int hf_lg8979_ind_frcrpt_change_b2 = -1; static int hf_lg8979_ind_frcrpt_change_b3 = -1; static int hf_lg8979_ind_frcrpt_change_b4 = -1; static int hf_lg8979_ind_frcrpt_change_b5 = -1; static int hf_lg8979_ind_frcrpt_change_b6 = -1; static int hf_lg8979_ind_frcrpt_change_b7 = -1; static int hf_lg8979_soe_chgrpt_ptnum = -1; static int hf_lg8979_soe_chgrpt_status = -1; static int hf_lg8979_soe_chgrpt_change = -1; static int hf_lg8979_soe_frcrpt_status_b0 = -1; static int hf_lg8979_soe_frcrpt_status_b1 = -1; static int hf_lg8979_soe_frcrpt_status_b2 = -1; static int hf_lg8979_soe_frcrpt_status_b3 = -1; static int hf_lg8979_soe_frcrpt_status_b4 = -1; static int hf_lg8979_soe_frcrpt_status_b5 = -1; static int hf_lg8979_soe_frcrpt_status_b6 = -1; static int hf_lg8979_soe_frcrpt_status_b7 = -1; static int hf_lg8979_soe_frcrpt_change_b0 = -1; static int hf_lg8979_soe_frcrpt_change_b1 = -1; static int hf_lg8979_soe_frcrpt_change_b2 = -1; static int hf_lg8979_soe_frcrpt_change_b3 = -1; static int hf_lg8979_soe_frcrpt_change_b4 = -1; static int hf_lg8979_soe_frcrpt_change_b5 = -1; static int hf_lg8979_soe_frcrpt_change_b6 = -1; static int hf_lg8979_soe_frcrpt_change_b7 = -1; static int hf_lg8979_digin_b0 = -1; static int hf_lg8979_digin_b1 = -1; static int hf_lg8979_digin_b2 = -1; static int hf_lg8979_digin_b3 = -1; static int hf_lg8979_digin_b4 = -1; static int hf_lg8979_digin_b5 = -1; static int hf_lg8979_digin_b6 = -1; static int hf_lg8979_digin_b7 = -1; static int hf_lg8979_digin_b8 = -1; static int hf_lg8979_digin_b9 = -1; static int hf_lg8979_digin_b10 = -1; static int hf_lg8979_digin_b11 = -1; static int hf_lg8979_digin_b12 = -1; static int hf_lg8979_digin_b13 = -1; static int hf_lg8979_digin_b14 = -1; static int hf_lg8979_digin_b15 = -1; static int hf_lg8979_acc_point = -1; static int hf_lg8979_soe_logchg_ptnum = -1; static int hf_lg8979_soe_logchg_newstat = -1; static int hf_lg8979_soe_logchg_mon = -1; static int hf_lg8979_soe_logchg_day = -1; static int hf_lg8979_soe_logchg_hour = -1; static int hf_lg8979_soe_logchg_min = -1; static int hf_lg8979_soe_logchg_sec = -1; static int hf_lg8979_soe_logchg_msec = -1; static int hf_lg8979_ang_output_val = -1; static int hf_lg8979_sbo_tripclose = -1; static int hf_lg8979_sbo_timercnt = -1; static int hf_lg8979_digout_data = -1; static int hf_lg8979_pul_output_base = -1; static int hf_lg8979_pul_output_dur = -1; static int hf_lg8979_pul_output_rl = -1; static int hf_lg8979_ang_deadband = -1; static int hf_lg8979_ang_group = -1; static int hf_lg8979_ang_group_pts = -1; static int hf_lg8979_acc_preset = -1; static int hf_lg8979_rtucfg_num_chassis = -1; static int hf_lg8979_rtucfg_chassis_num = -1; static int hf_lg8979_rtucfg_card_slot = -1; static int hf_lg8979_timesync_mon = -1; static int hf_lg8979_timesync_day = -1; static int hf_lg8979_timesync_hour = -1; static int hf_lg8979_timesync_min = -1; static int hf_lg8979_timesync_sec = -1; static int hf_lg8979_timesync_msec = -1; static int hf_lg8979_timebias_value = -1; static int hf_lg8979_timebias_proctime = -1; static int hf_lg8979_firmware_ver = -1; static int hf_lg8979_exprpt_code = -1; static int hf_lg8979_exprpt_parm = -1; static int hf_lg8979_disallowed_func = -1; static int hf_lg8979_crc16 = -1; /* Initialize the subtree pointers */ static gint ett_lg8979 = -1; static gint ett_lg8979_flags = -1; static gint ett_lg8979_funccode = -1; static gint ett_lg8979_point = -1; static gint ett_lg8979_ts = -1; /* Globals for L&G 8979 Protocol Preferences */ static gboolean lg8979_desegment = TRUE; #define LG8979_HEADER 0xFF #define LG8979_DIR_INDETERMINATE 0 #define LG8979_DIR_MASTER_TO_RTU 1 #define LG8979_DIR_RTU_TO_MASTER 2 /* Function Codes */ #define LG8979_FC_ANG_CHGRPT 0 #define LG8979_FC_ANG_FRCRPT 1 #define LG8979_FC_ANGGRP_CHGRPT 2 #define LG8979_FC_ANGGRP_FRCRPT 3 #define LG8979_FC_ADC_FRCRPT 5 #define LG8979_FC_IND_CHGRPT 6 #define LG8979_FC_IND_FRCRPT 7 #define LG8979_FC_SOE_CHGRPT 8 #define LG8979_FC_SOE_FRCRPT 9 #define LG8979_FC_DIG_FRCRPT 11 #define LG8979_FC_ACC_CHGRPT 12 #define LG8979_FC_ACC_FRCRPT 13 #define LG8979_FC_SOELOG_CHGRPT 14 #define LG8979_FC_ANG_OUTPUT 20 #define LG8979_FC_SBO_SELECT 21 #define LG8979_FC_SBO_OPERATE 22 #define LG8979_FC_DIG_OUTPUT 23 #define LG8979_FC_ACC_FREEZE 24 #define LG8979_FC_PUL_OUTPUT 25 #define LG8979_FC_PULTR_OUTPUT 26 #define LG8979_FC_SBO_IMEXECUTE 28 #define LG8979_FC_RTU_RESTART 30 #define LG8979_FC_RTU_CONFIG 31 #define LG8979_FC_TIME_SYNC 32 #define LG8979_FC_TIME_BIAS 33 #define LG8979_FC_ANG_DEADBAND 34 #define LG8979_FC_ANGGRP_DEFINE 35 #define LG8979_FC_ACC_PRESET 36 #define LG8979_FC_CONT_REQUEST 37 #define LG8979_FC_REPEAT_MSG 38 #define LG8979_FC_FIRMWARE_CFG 39 #define LG8979_FC_TABLE_READ 47 #define LG8979_FC_TABLE_WRITE 48 #define LG8979_FC_SPRPT_INT 50 #define LG8979_FC_SPRPT_SEQNUM 51 #define LG8979_FC_EXP_RPT 63 static const value_string lg8979_funccode_vals[] = { { LG8979_FC_ANG_CHGRPT, "Analog Change Report" }, { LG8979_FC_ANG_FRCRPT, "Analog Force Report" }, { LG8979_FC_ANGGRP_CHGRPT, "Analog Group Change Report" }, { LG8979_FC_ANGGRP_FRCRPT, "Analog Group Force Report" }, { 4, "Unknown/Invalid Function" }, { LG8979_FC_ADC_FRCRPT, "ADC Reference Force Report" }, { LG8979_FC_IND_CHGRPT, "Indication Change Report" }, { LG8979_FC_IND_FRCRPT, "Indication Force Report" }, { LG8979_FC_SOE_CHGRPT, "SOE Change Report" }, { LG8979_FC_SOE_FRCRPT, "SOE Force Report" }, { 10, "Unknown/Invalid Function" }, { LG8979_FC_DIG_FRCRPT, "Digital Input Force Report" }, { LG8979_FC_ACC_CHGRPT, "Accumulator Change Report" }, { LG8979_FC_ACC_FRCRPT, "Accumulator Force Report" }, { LG8979_FC_SOELOG_CHGRPT, "SOE Log Change Report" }, { 15, "Unknown/Invalid Function" }, { 16, "Unknown/Invalid Function" }, { 17, "Unknown/Invalid Function" }, { 18, "Unknown/Invalid Function" }, { 19, "Unknown/Invalid Function" }, { LG8979_FC_ANG_OUTPUT, "Analog Output" }, { LG8979_FC_SBO_SELECT, "SBO Select" }, { LG8979_FC_SBO_OPERATE, "SBO Operate" }, { LG8979_FC_DIG_OUTPUT, "Digital Output" }, { LG8979_FC_ACC_FREEZE, "Accumulator Freeze" }, { LG8979_FC_PUL_OUTPUT, "Pulse Output" }, { LG8979_FC_PULTR_OUTPUT, "Pulse Train Output" }, { 27, "Unknown/Invalid Function" }, { LG8979_FC_SBO_IMEXECUTE, "SBO Immediate Execute" }, { 29, "Unknown/Invalid Function" }, { LG8979_FC_RTU_RESTART, "Restart RTU" }, { LG8979_FC_RTU_CONFIG, "RTU Configuration" }, { LG8979_FC_TIME_SYNC, "Time Synchronization" }, { LG8979_FC_TIME_BIAS, "Time Bias" }, { LG8979_FC_ANG_DEADBAND, "Analog Deadbands" }, { LG8979_FC_ANGGRP_DEFINE, "Analog Group Define" }, { LG8979_FC_ACC_PRESET, "Accumulator Preset" }, { LG8979_FC_CONT_REQUEST, "Continuation Request" }, { LG8979_FC_REPEAT_MSG, "Repeat Last Message" }, { LG8979_FC_FIRMWARE_CFG, "Firmware Configuration" }, { 40, "Unknown/Invalid Function" }, { 41, "Unknown/Invalid Function" }, { 42, "Unknown/Invalid Function" }, { 43, "Unknown/Invalid Function" }, { 44, "Unknown/Invalid Function" }, { 45, "Unknown/Invalid Function" }, { 46, "Unknown/Invalid Function" }, { LG8979_FC_TABLE_READ, "Table Read" }, { LG8979_FC_TABLE_WRITE, "Table Write" }, { 49, "Unknown/Invalid Function" }, { LG8979_FC_SPRPT_INT, "Spontaneous Report Interval" }, { LG8979_FC_SPRPT_SEQNUM, "Spontaneous Report Sequence Number" }, { 52, "Unknown/Invalid Function" }, { 53, "Unknown/Invalid Function" }, { 54, "Unknown/Invalid Function" }, { 55, "Unknown/Invalid Function" }, { 56, "Unknown/Invalid Function" }, { 57, "Unknown/Invalid Function" }, { 58, "Unknown/Invalid Function" }, { 59, "Unknown/Invalid Function" }, { 60, "Unknown/Invalid Function" }, { 61, "Unknown/Invalid Function" }, { 62, "Unknown/Invalid Function" }, { LG8979_FC_EXP_RPT, "Exception Report" }, { 0, NULL } }; static value_string_ext lg8979_funccode_vals_ext = VALUE_STRING_EXT_INIT(lg8979_funccode_vals); static const value_string lg8979_cardcode_vals[] = { { 0, "Non-Existent Slot" }, { 1, "Analog Input" }, { 2, "A/D Converter" }, { 3, "Analog Output" }, { 4, "Indication Input" }, { 5, "24-Bit Digital Output" }, { 7, "SBO Control Output" }, { 8, "Accumulator, Form A" }, { 11, "32-Bit Digital Output" }, { 12, "Accumulator, Form C" }, { 15, "Pulse Output" }, { 28, "SOE Input" }, { 29, "KWH Input" }, { 30, "Serial Data Collector" }, { 31, "Empty Slot" }, { 0, NULL } }; static const value_string lg8979_exprpt_code_vals[] = { { 0x00, "Warm Restart" }, { 0x01, "Cold Start" }, { 0x02, "Insufficient Ram" }, { 0x03, "Bus Failure" }, { 0x04, "SBO Failure" }, { 0x05, "Analog Failure" }, { 0x06, "Indication/SOE Failure" }, { 0x07, "Card Placement Error" }, { 0x08, "Not Used" }, { 0x09, "Invalid Function Code" }, { 0x0A, "Invalid Block Length" }, { 0x0B, "Non-Existent Point" }, { 0x0C, "Invalid Parameter" }, { 0x0D, "Select/Execute Mismatch" }, { 0x0E, "Function Not Allowed" }, { 0x0F, "Not Used" }, { 0x10, "Database Setup has Changed" }, { 0x11, "Indication Change Sequence" }, { 0, NULL } }; static const value_string lg8979_exprpt_parm_vals[] = { { 0x00, "N/A" }, { 0x01, "1=Requested CLDSTRT" }, { 0x02, "N/A" }, { 0x03, "Unit" }, { 0x04, "Unit/Slot" }, { 0x05, "Unit/Slot" }, { 0x06, "Unit/Slot" }, { 0x07, "Unit/Slot" }, { 0x08, "N/A" }, { 0x09, "Function Code" }, { 0x0A, "Block Length" }, { 0x0B, "Point" }, { 0x0C, "Parameter" }, { 0x0D, "Execute Point" }, { 0x0E, "Function Code" }, { 0x0F, "N/A" }, { 0x10, "N/A" }, { 0x11, "1=Time Order (0=Not T/O)" }, { 0, NULL } }; static const value_string lg8979_sbo_tripclose_vals[] = { { 0x00, "Trip" }, { 0x01, "Close" }, { 0, NULL } }; static const value_string lg8979_pul_output_base_vals[] = { { 0x00, "10 msec" }, { 0x01, "100 msec" }, { 0x02, "1 sec" }, { 0x03, "10 sec" }, { 0, NULL } }; static const value_string lg8979_pul_output_rl_vals[] = { { 0x00, "Lower" }, { 0x01, "Raise" }, { 0, NULL } }; /*************************************************************/ /* Try to determine "direction" of message. */ /* Check the data length within the packet and compare */ /* vs. the function code. Master->RTU messages will have a */ /* fixed length that can be used to determine the direction */ /* of the message */ /*************************************************************/ static int classify_lg8979_packet(tvbuff_t *tvb) { guint8 func, len, data_len, flags; len = tvb_reported_length(tvb); /* If TVB length is equal to 5, this is classifed as a 'short response message' */ /* and is guaranteed to be RTU->Master only */ if (len == 5) { return LG8979_DIR_RTU_TO_MASTER; } /* If TVB length is greater than 5, let's dig deeper */ if (len > 5) { flags = tvb_get_guint8(tvb, 1); /* Flags vary between message types, so let's try those first to determine the message direction */ /* If both bit 3 and bit 4 are set, this is almost certainly a RTU->Master message */ if ( (flags & 0x04) && (flags & 0x08) ){ return LG8979_DIR_RTU_TO_MASTER; } /* If anything is in bits 3-6 without bit 7, this is a RTU->Master message */ else if ( (flags & 0x78) && !(flags & 0x80) ){ return LG8979_DIR_RTU_TO_MASTER; } func = tvb_get_guint8(tvb, 3) & 0x7F; data_len = tvb_get_guint8(tvb, 4); /* If we have more data in the tvb then should be there, this is a stacked RTU->Master response */ if (len > (data_len + 5 + 2)) { return LG8979_DIR_RTU_TO_MASTER; } switch (func) { case LG8979_FC_ANG_CHGRPT: case LG8979_FC_ADC_FRCRPT: case LG8979_FC_IND_CHGRPT: case LG8979_FC_SOE_CHGRPT: case LG8979_FC_ACC_CHGRPT: case LG8979_FC_SOELOG_CHGRPT: case LG8979_FC_REPEAT_MSG: case LG8979_FC_RTU_CONFIG: case LG8979_FC_FIRMWARE_CFG: if (data_len == 0) { return LG8979_DIR_MASTER_TO_RTU; } else { return LG8979_DIR_RTU_TO_MASTER; } break; case LG8979_FC_ANGGRP_CHGRPT: case LG8979_FC_ANGGRP_FRCRPT: if (data_len == 1) { return LG8979_DIR_MASTER_TO_RTU; } else { return LG8979_DIR_RTU_TO_MASTER; } break; case LG8979_FC_DIG_FRCRPT: case LG8979_FC_ACC_FRCRPT: case LG8979_FC_TIME_BIAS: if (data_len == 2) { return LG8979_DIR_MASTER_TO_RTU; } else { return LG8979_DIR_RTU_TO_MASTER; } break; case LG8979_FC_ANG_FRCRPT: case LG8979_FC_IND_FRCRPT: case LG8979_FC_SOE_FRCRPT: if (data_len == 4) { return LG8979_DIR_MASTER_TO_RTU; } else { return LG8979_DIR_RTU_TO_MASTER; } break; /* These are either totally or mostly master->RTU operations */ case LG8979_FC_ANG_OUTPUT: case LG8979_FC_SBO_SELECT: case LG8979_FC_SBO_OPERATE: case LG8979_FC_DIG_OUTPUT: case LG8979_FC_ACC_FREEZE: case LG8979_FC_PUL_OUTPUT: case LG8979_FC_PULTR_OUTPUT: case LG8979_FC_SBO_IMEXECUTE: case LG8979_FC_TIME_SYNC: case LG8979_FC_ANG_DEADBAND: case LG8979_FC_ANGGRP_DEFINE: case LG8979_FC_ACC_PRESET: case LG8979_FC_CONT_REQUEST: return LG8979_DIR_MASTER_TO_RTU; break; case LG8979_FC_EXP_RPT: return LG8979_DIR_RTU_TO_MASTER; break; default: return LG8979_DIR_INDETERMINATE; break; } } /* else, cannot classify */ return LG8979_DIR_INDETERMINATE; } /******************************************************************************************************/ /* Code to dissect L&G 8979 Protocol packets */ /******************************************************************************************************/ static int dissect_lg8979(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { /* Set up structures needed to add the protocol subtree and manage it */ proto_item *lg8979_item, *lg8979_point_item = NULL; proto_item *lg8979_slot_item = NULL, *lg8979_ang_group_pts_item = NULL; proto_tree *lg8979_tree, *lg8979_fc_tree = NULL; proto_tree *lg8979_point_tree = NULL, *lg8979_ts_tree = NULL; int offset = 0; guint8 rtu_addr, func, packet_type, data_len, ptnum8, tripclose, rl, exp_code, num_chassis; guint8 ts_mon, ts_day, ts_hr, ts_min, ts_sec; guint16 ptnum, ptval, ana12_val; guint16 ts_ms; gint num_points = 0, cnt = 0, cnt1 = 0; gboolean shr, new_status, change; /* Make entries in Protocol column on summary display */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "L&G 8979"); col_clear(pinfo->cinfo, COL_INFO); lg8979_item = proto_tree_add_item(tree, proto_lg8979, tvb, 0, -1, ENC_NA); lg8979_tree = proto_item_add_subtree(lg8979_item, ett_lg8979); /* Add 0xFF Header to Protocol Tree */ proto_tree_add_item(lg8979_tree, hf_lg8979_header, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; /* "Request" or "Response" */ packet_type = classify_lg8979_packet(tvb); /* This packet type is classified as a "Request" and is deemed in the direction of "master -> RTU" */ if (packet_type == LG8979_DIR_MASTER_TO_RTU) { static const int * request_flags[] = { &hf_lg8979_shr, &hf_lg8979_mfc, &hf_lg8979_ack, NULL }; col_add_str(pinfo->cinfo, COL_INFO, "Master -> RTU"); /* Add Flags to Protocol Tree */ shr = tvb_get_guint8(tvb, offset) & 0x80; proto_tree_add_bitmask(lg8979_tree, tvb, offset, hf_lg8979_flags, ett_lg8979_flags, request_flags, ENC_LITTLE_ENDIAN); offset += 1; /* Add RTU Address to Protocol Tree */ rtu_addr = tvb_get_guint8(tvb, offset); col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Address: %d", rtu_addr); proto_tree_add_item(lg8979_tree, hf_lg8979_address, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; if (!shr) { /* Add Function Code & last Mark Block to Protocol Tree */ /* Function code is 7 lower bits of byte , LMB is 8th bit*/ func = tvb_get_guint8(tvb, offset) & 0x7f; col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str_const(func, lg8979_funccode_vals, "Unknown Function Code")); lg8979_fc_tree = proto_tree_add_subtree_format( lg8979_tree, tvb, offset, 1, ett_lg8979_funccode, NULL, "Function Code: %s (%d)", val_to_str_const(func, lg8979_funccode_vals, "Unknown Function Code"), func); proto_tree_add_item(lg8979_fc_tree, hf_lg8979_lastblock, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_fc_tree, hf_lg8979_funccode, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; data_len = tvb_get_guint8(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_length, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; switch (func) { /* Function Code 0 Analog Change Report */ /* Function Code 7 Indication Force Report */ /* Function Code 9 SOE Force Report */ case LG8979_FC_ANG_FRCRPT: case LG8979_FC_IND_FRCRPT: case LG8979_FC_SOE_FRCRPT: proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_tree, hf_lg8979_stop_ptnum16, tvb, offset+2, 2, ENC_LITTLE_ENDIAN); offset += 4; break; /* Function Code 2 Analog Group Change Report */ case LG8979_FC_ANGGRP_CHGRPT: proto_tree_add_item(lg8979_tree, hf_lg8979_ang_group, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; break; /* Function Code 11 Digital Input Force Report */ /* Function Code 13 Accumulator Force Report */ case LG8979_FC_DIG_FRCRPT: case LG8979_FC_ACC_FRCRPT: proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_tree, hf_lg8979_stop_ptnum8, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); offset += 2; break; /* Function Code 20 Analog Output */ case LG8979_FC_ANG_OUTPUT: proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_tree, hf_lg8979_ang_output_val, tvb, offset+1, 2, ENC_LITTLE_ENDIAN); offset += 3; break; /* Function Code 21 SBO Select */ case LG8979_FC_SBO_SELECT: /* Get 8-bit point number and trip/close command-code */ ptnum = tvb_get_guint8(tvb, offset); tripclose = (tvb_get_guint8(tvb, offset+1) & 0x80) >> 7; lg8979_point_tree = proto_tree_add_subtree_format( lg8979_tree, tvb, offset, 2, ett_lg8979_point, NULL, "SBO Command, Pt.Num: %u, Code: %s", ptnum, val_to_str_const(tripclose, lg8979_sbo_tripclose_vals, "Unknown Control Code")); /* Update the Information Column with Command Details */ col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Output: %u, Code: %s", ptnum, val_to_str_const(tripclose, lg8979_sbo_tripclose_vals, "Unknown Control Code")); /* Add SBO Select Details to tree */ proto_tree_add_item(lg8979_point_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_sbo_tripclose, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_sbo_timercnt, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); offset += 2; break; /* Function Code 22 SBO Operate */ case LG8979_FC_SBO_OPERATE: /* Get 8-bit point number */ ptnum = tvb_get_guint8(tvb, offset); /* Update the Information Column with Command Details */ col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Output: %u", ptnum); /* Add 8-bit point number to tree */ proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; break; /* Function Code 23 Digital Output */ case LG8979_FC_DIG_OUTPUT: /* Add Digital Output Details to tree */ proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_tree, hf_lg8979_digout_data, tvb, offset+1, 3, ENC_LITTLE_ENDIAN); offset += 4; break; /* Function Code 25 Pulse Output */ case LG8979_FC_PUL_OUTPUT: ptnum = tvb_get_guint8(tvb, offset); rl = (tvb_get_guint8(tvb, offset+1) & 0x80) >> 7; lg8979_point_tree = proto_tree_add_subtree_format(lg8979_tree, tvb, offset, 2, ett_lg8979_point, NULL, "Pulse Output, Pt.Num: %u, Code: %s", ptnum, val_to_str_const(rl, lg8979_pul_output_rl_vals, "Unknown Control Code")); /* Add Pulse Output Details to tree */ proto_tree_add_item(lg8979_point_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_pul_output_base, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_pul_output_dur, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_pul_output_rl, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); offset += 2; break; /* Function Code 32 Time Synchronization */ case LG8979_FC_TIME_SYNC: /* Add 7-byte time-sync value to tree */ ts_mon = tvb_get_guint8(tvb, offset); ts_day = tvb_get_guint8(tvb, offset+1); ts_hr = tvb_get_guint8(tvb, offset+2); ts_min = tvb_get_guint8(tvb, offset+3); ts_sec = tvb_get_guint8(tvb, offset+4); ts_ms = tvb_get_letohs(tvb, offset+5); lg8979_ts_tree = proto_tree_add_subtree_format(lg8979_tree, tvb, offset, 7, ett_lg8979_ts, NULL, "Time-Sync Value: %02d/%02d %02d:%02d:%02d.%03d", ts_mon, ts_day, ts_hr, ts_min, ts_sec, ts_ms); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_mon, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_day, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_hour, tvb, offset+2, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_min, tvb, offset+3, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_sec, tvb, offset+4, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_timesync_msec, tvb, offset+5, 2, ENC_LITTLE_ENDIAN); offset += 7; break; /* Function Code 33 Time Bias */ case LG8979_FC_TIME_BIAS: proto_tree_add_item(lg8979_tree, hf_lg8979_timebias_value, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; break; /* Function Code 34 Analog Deadband Write */ case LG8979_FC_ANG_DEADBAND: /* Get analog point number base and add to tree */ ptnum = tvb_get_letohs(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; num_points = (data_len-2); for (cnt=0; cnt master" */ else if (packet_type == LG8979_DIR_RTU_TO_MASTER) { static const int * response_flags[] = { &hf_lg8979_shr, &hf_lg8979_con, &hf_lg8979_frz, &hf_lg8979_ind, &hf_lg8979_sch, &hf_lg8979_slg, NULL }; col_add_str(pinfo->cinfo, COL_INFO, "RTU -> Master"); /* Retrieve and add Flags to Protocol Tree */ shr = tvb_get_guint8(tvb, offset) & 0x80; proto_tree_add_bitmask(lg8979_tree, tvb, offset, hf_lg8979_flags, ett_lg8979_flags, response_flags, ENC_LITTLE_ENDIAN); offset += 1; /* Add RTU Address to Protocol Tree */ rtu_addr = tvb_get_guint8(tvb, offset); col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Address: %d", rtu_addr); proto_tree_add_item(lg8979_tree, hf_lg8979_address, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; /* If this is not a short response, and there are at least 2 bytes remaining continue to process function codes */ while ((!shr) && (tvb_reported_length_remaining(tvb, offset) > 2)){ /* Add Function Code & last Mark Block to Protocol Tree */ /* Function code is 7 lower bits of byte , LMB is 8th bit*/ func = tvb_get_guint8(tvb, offset) & 0x7f; col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, val_to_str_const(func, lg8979_funccode_vals, "Unknown Function Code")); lg8979_fc_tree = proto_tree_add_subtree_format( lg8979_tree, tvb, offset, 1, ett_lg8979_funccode, NULL, "Function Code: %s (%d)", val_to_str_const(func, lg8979_funccode_vals, "Unknown Function Code"), func); proto_tree_add_item(lg8979_fc_tree, hf_lg8979_lastblock, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(lg8979_fc_tree, hf_lg8979_funccode, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; data_len = tvb_get_guint8(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_length, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; switch (func) { /* Function Code 0 Analog Change Report */ /* Function Code 2 Analog Group Change Report */ case LG8979_FC_ANG_CHGRPT: case LG8979_FC_ANGGRP_CHGRPT: num_points = (data_len / 3); for (cnt=0; cnt> 4) | (tvb_get_guint8(tvb, offset+2) << 4) ); proto_tree_add_uint_format(lg8979_tree, hf_lg8979_ang_point, tvb, offset, 3, ptnum, "Point Number %u: %u", ptnum, ptval); offset += 3; } break; /* Function Code 1 Analog Force Report */ case LG8979_FC_ANG_FRCRPT: ptnum = tvb_get_letohs(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; /* Decode 12-bit analog data following this 3-byte bit pattern. * Byte 1: PtVal 'N' LSB * Byte 2: PtVal 'N+1' LSB : PtVal 'N' MSB * Byte 3: PtVal 'N+1' MSB * To determine the number of points based on the data bytes, we need to know * if we have an even or odd number of data bytes. */ /* even number of data bytes */ if (((data_len-2) % 3) == 0) { num_points = (((data_len-2) / 3) * 2); } /* odd number of data bytes */ else { num_points = ((((data_len-2) / 3) * 2) + 1); } /* loop through the data bytes decoding 12-bit analogs. When on an even count, offset by 1 and on an odd, offset by 2. */ for (cnt=0; cnt < num_points; cnt++) { if (cnt%2 == 0) { ana12_val = ( tvb_get_guint8(tvb, offset) | ((tvb_get_guint8(tvb, offset+1) & 0x0F) << 8) ); proto_tree_add_uint_format(lg8979_tree, hf_lg8979_ang_point, tvb, offset, 2, ptnum, "Point Number %u: %u", ptnum, ana12_val); offset += 1; /* If we are in the last run through the for loop, increment the offset by 1 more byte than normal */ if (cnt == (num_points - 1)) { offset += 1; } } else { ana12_val = ( ((tvb_get_guint8(tvb, offset) & 0xF0) >> 4) | (tvb_get_guint8(tvb, offset+1) << 4) ); proto_tree_add_uint_format(lg8979_tree, hf_lg8979_ang_point, tvb, offset, 2, ptnum, "Point Number %u: %u", ptnum, ana12_val); offset += 2; } ptnum += 1; } break; /* Function Code 5 ADC Reference Force Report */ /* Same byte pattern as 3 sequential analogs in a Force Report would follow */ case LG8979_FC_ADC_FRCRPT: proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; /* Retrieve the 0 and -90% references */ ana12_val = ( tvb_get_guint8(tvb, offset) | ((tvb_get_guint8(tvb, offset+1) & 0x0F) << 8) ); proto_tree_add_uint(lg8979_tree, hf_lg8979_adc_ref_zero, tvb, offset, 2, ana12_val); ana12_val = ( ((tvb_get_guint8(tvb, offset+1) & 0xF0) >> 4) | (tvb_get_guint8(tvb, offset+2) << 4) ); proto_tree_add_uint(lg8979_tree, hf_lg8979_adc_ref_neg90, tvb, offset+1, 2, ana12_val); offset += 3; /* Retrieve the +90% reference */ ana12_val = ( tvb_get_guint8(tvb, offset) | ((tvb_get_guint8(tvb, offset+1) & 0x0F) << 8) ); proto_tree_add_uint(lg8979_tree, hf_lg8979_adc_ref_pos90, tvb, offset, 2, ana12_val); offset += 2; break; /* Function Code 6 Indication Change Report */ case LG8979_FC_IND_CHGRPT: num_points = (data_len / 2); for (cnt=0; cnt> 7; change = (tvb_get_guint8(tvb, offset+1) & 0x40) >> 6; lg8979_point_tree = proto_tree_add_subtree_format(lg8979_tree, tvb, offset, 2, ett_lg8979_point, NULL, "Indication Change Report, Point Number: %u, Status: %u, Change %u", ptnum, new_status, change); proto_tree_add_item(lg8979_point_tree, hf_lg8979_ind_chgrpt_ptnum, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_ind_chgrpt_status, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_ind_chgrpt_change, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; } break; /* Function Code 7 Indication Force Report */ case LG8979_FC_IND_FRCRPT: ptnum = tvb_get_letohs(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; num_points = ((data_len - 2) / 2); for (cnt=0; cnt> 7; change = (tvb_get_guint8(tvb, offset+1) & 0x40) >> 6; lg8979_point_tree = proto_tree_add_subtree_format(lg8979_tree, tvb, offset, 2, ett_lg8979_point, NULL, "SOE Change Report, Point Number: %u, Status: %u, Change %u", ptnum, new_status, change); proto_tree_add_item(lg8979_point_tree, hf_lg8979_soe_chgrpt_ptnum, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_soe_chgrpt_status, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_soe_chgrpt_change, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; } break; /* Function Code 9 SOE Force Report */ case LG8979_FC_SOE_FRCRPT: ptnum = tvb_get_letohs(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum16, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; num_points = ((data_len - 2) / 2); for (cnt=0; cnt> 7; lg8979_point_tree = proto_tree_add_subtree_format(lg8979_tree, tvb, offset, 9, ett_lg8979_point, NULL, "SOE Log Change Report, Point Number: %u, New Status: %u", ptnum, new_status); /* Add 12-bit point number and "new status" bit to tree */ proto_tree_add_item(lg8979_point_tree, hf_lg8979_soe_logchg_ptnum, tvb, offset, 2, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_soe_logchg_newstat, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; /* Add 7-byte time-stamp to tree */ ts_mon = tvb_get_guint8(tvb, offset); ts_day = tvb_get_guint8(tvb, offset+1); ts_hr = tvb_get_guint8(tvb, offset+2); ts_min = tvb_get_guint8(tvb, offset+3); ts_sec = tvb_get_guint8(tvb, offset+4); ts_ms = tvb_get_letohs(tvb, offset+5); lg8979_ts_tree = proto_tree_add_subtree_format(lg8979_point_tree, tvb, offset, 7, ett_lg8979_ts, NULL, "SOE Time Stamp: [%02d/%02d %02d:%02d:%02d.%03d]", ts_mon, ts_day, ts_hr, ts_min, ts_sec, ts_ms); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_mon, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_day, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_hour, tvb, offset+2, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_min, tvb, offset+3, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_sec, tvb, offset+4, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_ts_tree, hf_lg8979_soe_logchg_msec, tvb, offset+5, 2, ENC_LITTLE_ENDIAN); offset += 7; } break; /* Function Code 21 SBO Select - Echo of Master->RTU Message */ case LG8979_FC_SBO_SELECT: /* Get 8-bit point number and trip/close command-code */ ptnum = tvb_get_guint8(tvb, offset); tripclose = (tvb_get_guint8(tvb, offset+1) & 0x80) >> 7; lg8979_point_tree = proto_tree_add_subtree_format( lg8979_tree, tvb, offset, 2, ett_lg8979_point, NULL, "SBO Command, Pt.Num: %u, Code: %s", ptnum, val_to_str_const(tripclose, lg8979_sbo_tripclose_vals, "Unknown Control Code")); /* Update the Information Column with Command Details */ col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Output: %u, Code: %s", ptnum, val_to_str_const(tripclose, lg8979_sbo_tripclose_vals, "Unknown Control Code")); /* Add SBO Select Details to tree */ proto_tree_add_item(lg8979_point_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_sbo_tripclose, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(lg8979_point_tree, hf_lg8979_sbo_timercnt, tvb, offset+1, 1, ENC_LITTLE_ENDIAN); offset += 2; break; /* Function Code 22 SBO Operate - Echo of Master->RTU Message */ case LG8979_FC_SBO_OPERATE: /* Get 8-bit point number */ ptnum = tvb_get_guint8(tvb, offset); /* Update the Information Column with Command Details */ col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Output: %u", ptnum); /* Add 8-bit point number to tree */ proto_tree_add_item(lg8979_tree, hf_lg8979_start_ptnum8, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; break; /* Function Code 31 RTU Configuration */ case LG8979_FC_RTU_CONFIG: /* Number of IO Chassis */ num_chassis = tvb_get_guint8(tvb, offset); proto_tree_add_item(lg8979_tree, hf_lg8979_rtucfg_num_chassis, tvb, offset, 1, ENC_LITTLE_ENDIAN); offset += 1; for (cnt=0; cntcinfo, COL_INFO, NULL, "Short Response"); } /* Add CRC-16 */ proto_tree_add_item(lg8979_tree, hf_lg8979_crc16, tvb, offset, 2, ENC_BIG_ENDIAN); } /* packet type */ return tvb_reported_length(tvb); } /******************************************************************************************************/ /* Return length of L&G 8979 Protocol over TCP message (used for re-assembly) */ /******************************************************************************************************/ static guint get_lg8979_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_) { guint len; len = tvb_reported_length(tvb); /* XXX: should really be some minimum length ?? */ return len; } /******************************************************************************************************/ /* Dissect (and possibly Re-assemble) L&G 8979 protocol payload data */ /******************************************************************************************************/ static int dissect_lg8979_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { gint length = tvb_reported_length(tvb); /* Check for a L&G8979 packet. It should begin with 0xFF */ if(length < 2 || tvb_get_guint8(tvb, 0) != 0xFF) { /* Not a L&G 8979 Protocol packet, just happened to use the same port */ return 0; } tcp_dissect_pdus(tvb, pinfo, tree, lg8979_desegment, 1, get_lg8979_len, dissect_lg8979, data); return length; } /******************************************************************************************************/ /* Dissect "simple" L&G 8979 protocol payload (no TCP re-assembly) */ /******************************************************************************************************/ static int dissect_lg8979_simple(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { gint length = tvb_reported_length(tvb); /* Check for a L&G8979 packet. It should begin with 0xFF */ if(length < 2 || tvb_get_guint8(tvb, 0) != 0xFF) { /* Not a L&G 8979 Protocol packet ... */ return 0; } dissect_lg8979(tvb, pinfo, tree, data); return length; } /******************************************************************************************************/ /* Register the protocol with Wireshark */ /******************************************************************************************************/ void proto_reg_handoff_lg8979(void); void proto_register_lg8979(void) { /* L&G 8979 Protocol header fields */ static hf_register_info lg8979_hf[] = { { &hf_lg8979_header, { "Header", "lg8979.header", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_flags, { "Flags", "lg8979.flags", FT_UINT8, BASE_HEX, NULL, 0x00, NULL, HFILL }}, { &hf_lg8979_shr, { "SHR", "lg8979.shr", FT_UINT8, BASE_DEC, NULL, 0x80, "Short Response Flag", HFILL }}, { &hf_lg8979_mfc, { "MFC", "lg8979.mfc", FT_UINT8, BASE_DEC, NULL, 0x78, "Multi Function Code", HFILL }}, { &hf_lg8979_ack, { "ACK", "lg8979.ack", FT_UINT8, BASE_DEC, NULL, 0x04, "Acknowledge Flag", HFILL }}, { &hf_lg8979_con, { "CON", "lg8979.con", FT_UINT8, BASE_DEC, NULL, 0x40, "Continuation Flag", HFILL }}, { &hf_lg8979_frz, { "FRZ", "lg8979.frz", FT_UINT8, BASE_DEC, NULL, 0x20, "Accumulator Freeze Flag", HFILL }}, { &hf_lg8979_ind, { "IND", "lg8979.ind", FT_UINT8, BASE_DEC, NULL, 0x10, "Indication Change Flag", HFILL }}, { &hf_lg8979_sch, { "SCH", "lg8979.sch", FT_UINT8, BASE_DEC, NULL, 0x08, "SOE Change Flag", HFILL }}, { &hf_lg8979_slg, { "SLG", "lg8979.slg", FT_UINT8, BASE_DEC, NULL, 0x04, "SOE Log Flag", HFILL }}, { &hf_lg8979_address, { "RTU Address", "lg8979.address", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_lastblock, { "Last Block Mark", "lg8979.lastblock", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL }}, { &hf_lg8979_funccode, { "Function Code", "lg8979.funccode", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &lg8979_funccode_vals_ext, 0x7F, NULL, HFILL }}, { &hf_lg8979_length, { "Data Length", "lg8979.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_start_ptnum16, { "Start Point Number (16-bit)", "lg8979.start_ptnum16", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_start_ptnum8, { "Start Point Number (8-bit)", "lg8979.start_ptnum8", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_stop_ptnum16, { "Stop Point Number (16-bit)", "lg8979.stop_ptnum16", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_stop_ptnum8, { "Stop Point Number (8-bit)", "lg8979.stop_ptnum8", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_ang_point, { "Analog Point", "lg8979.ang_point", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_adc_ref_zero, { "ADC Reference (0%)", "lg8979.adc_ref_zero", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_adc_ref_neg90, { "ADC Reference (-90%)", "lg8979.adc_ref_neg90", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_adc_ref_pos90, { "ADC Reference (+90%)", "lg8979.adc_ref_pos90", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_ind_chgrpt_ptnum, { "Point Number (12-bit)", "lg8979.ind_chgrpt_ptnum", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, { &hf_lg8979_ind_chgrpt_status, { "Status Bit", "lg8979.ind_chgrpt_status", FT_UINT16, BASE_DEC, NULL, 0x8000, NULL, HFILL }}, { &hf_lg8979_ind_chgrpt_change, { "Change Bit", "lg8979.ind_chgrpt_change", FT_UINT16, BASE_DEC, NULL, 0x4000, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b0, { "Status Bit 0", "lg8979.ind.frcrpt.status_b0", FT_BOOLEAN, 8, NULL, 0x01, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b1, { "Status Bit 1", "lg8979.ind.frcrpt.status_b1", FT_BOOLEAN, 8, NULL, 0x02, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b2, { "Status Bit 2", "lg8979.ind.frcrpt.status_b2", FT_BOOLEAN, 8, NULL, 0x04, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b3, { "Status Bit 3", "lg8979.ind.frcrpt.status_b3", FT_BOOLEAN, 8, NULL, 0x08, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b4, { "Status Bit 4", "lg8979.ind.frcrpt.status_b4", FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b5, { "Status Bit 5", "lg8979.ind.frcrpt.status_b5", FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b6, { "Status Bit 6", "lg8979.ind.frcrpt.status_b6", FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_status_b7, { "Status Bit 7", "lg8979.ind.frcrpt.status_b7", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b0, { "Change Bit 0", "lg8979.ind.frcrpt.change_b0", FT_BOOLEAN, 8, NULL, 0x01, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b1, { "Change Bit 1", "lg8979.ind.frcrpt.change_b1", FT_BOOLEAN, 8, NULL, 0x02, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b2, { "Change Bit 2", "lg8979.ind.frcrpt.change_b2", FT_BOOLEAN, 8, NULL, 0x04, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b3, { "Change Bit 3", "lg8979.ind.frcrpt.change_b3", FT_BOOLEAN, 8, NULL, 0x08, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b4, { "Change Bit 4", "lg8979.ind.frcrpt.change_b4", FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b5, { "Change Bit 5", "lg8979.ind.frcrpt.change_b5", FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b6, { "Change Bit 6", "lg8979.ind.frcrpt.change_b6", FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL }}, { &hf_lg8979_ind_frcrpt_change_b7, { "Change Bit 7", "lg8979.ind.frcrpt.change_b7", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }}, { &hf_lg8979_soe_chgrpt_ptnum, { "Point Number (12-bit)", "lg8979.soe_chgrpt_ptnum", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, { &hf_lg8979_soe_chgrpt_status, { "Status Bit", "lg8979.soe_chgrpt_status", FT_UINT16, BASE_DEC, NULL, 0x8000, NULL, HFILL }}, { &hf_lg8979_soe_chgrpt_change, { "Change Bit", "lg8979.soe_chgrpt_change", FT_UINT16, BASE_DEC, NULL, 0x4000, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b0, { "Status Bit 0", "lg8979.soe.frcrpt.status_b0", FT_BOOLEAN, 8, NULL, 0x01, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b1, { "Status Bit 1", "lg8979.soe.frcrpt.status_b1", FT_BOOLEAN, 8, NULL, 0x02, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b2, { "Status Bit 2", "lg8979.soe.frcrpt.status_b2", FT_BOOLEAN, 8, NULL, 0x04, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b3, { "Status Bit 3", "lg8979.soe.frcrpt.status_b3", FT_BOOLEAN, 8, NULL, 0x08, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b4, { "Status Bit 4", "lg8979.soe.frcrpt.status_b4", FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b5, { "Status Bit 5", "lg8979.soe.frcrpt.status_b5", FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b6, { "Status Bit 6", "lg8979.soe.frcrpt.status_b6", FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_status_b7, { "Status Bit 7", "lg8979.soe.frcrpt.status_b7", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b0, { "Change Bit 0", "lg8979.soe.frcrpt.change_b0", FT_BOOLEAN, 8, NULL, 0x01, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b1, { "Change Bit 1", "lg8979.soe.frcrpt.change_b1", FT_BOOLEAN, 8, NULL, 0x02, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b2, { "Change Bit 2", "lg8979.soe.frcrpt.change_b2", FT_BOOLEAN, 8, NULL, 0x04, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b3, { "Change Bit 3", "lg8979.soe.frcrpt.change_b3", FT_BOOLEAN, 8, NULL, 0x08, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b4, { "Change Bit 4", "lg8979.soe.frcrpt.change_b4", FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b5, { "Change Bit 5", "lg8979.soe.frcrpt.change_b5", FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b6, { "Change Bit 6", "lg8979.soe.frcrpt.change_b6", FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL }}, { &hf_lg8979_soe_frcrpt_change_b7, { "Change Bit 7", "lg8979.soe.frcrpt.change_b7", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }}, { &hf_lg8979_digin_b0, { "Digital Input Bit 0", "lg8979.digin_b0", FT_BOOLEAN, 16, NULL, 0x0001, NULL, HFILL }}, { &hf_lg8979_digin_b1, { "Digital Input Bit 1", "lg8979.digin_b1", FT_BOOLEAN, 16, NULL, 0x0002, NULL, HFILL }}, { &hf_lg8979_digin_b2, { "Digital Input Bit 2", "lg8979.digin_b2", FT_BOOLEAN, 16, NULL, 0x0004, NULL, HFILL }}, { &hf_lg8979_digin_b3, { "Digital Input Bit 3", "lg8979.digin_b3", FT_BOOLEAN, 16, NULL, 0x0008, NULL, HFILL }}, { &hf_lg8979_digin_b4, { "Digital Input Bit 4", "lg8979.digin_b4", FT_BOOLEAN, 16, NULL, 0x0010, NULL, HFILL }}, { &hf_lg8979_digin_b5, { "Digital Input Bit 5", "lg8979.digin_b5", FT_BOOLEAN, 16, NULL, 0x0020, NULL, HFILL }}, { &hf_lg8979_digin_b6, { "Digital Input Bit 6", "lg8979.digin_b6", FT_BOOLEAN, 16, NULL, 0x0040, NULL, HFILL }}, { &hf_lg8979_digin_b7, { "Digital Input Bit 7", "lg8979.digin_b7", FT_BOOLEAN, 16, NULL, 0x0080, NULL, HFILL }}, { &hf_lg8979_digin_b8, { "Digital Input Bit 8", "lg8979.digin_b8", FT_BOOLEAN, 16, NULL, 0x0100, NULL, HFILL }}, { &hf_lg8979_digin_b9, { "Digital Input Bit 9", "lg8979.digin_b9", FT_BOOLEAN, 16, NULL, 0x0200, NULL, HFILL }}, { &hf_lg8979_digin_b10, { "Digital Input Bit 10", "lg8979.digin_b10", FT_BOOLEAN, 16, NULL, 0x0400, NULL, HFILL }}, { &hf_lg8979_digin_b11, { "Digital Input Bit 11", "lg8979.digin_b11", FT_BOOLEAN, 16, NULL, 0x0800, NULL, HFILL }}, { &hf_lg8979_digin_b12, { "Digital Input Bit 12", "lg8979.digin_b12", FT_BOOLEAN, 16, NULL, 0x1000, NULL, HFILL }}, { &hf_lg8979_digin_b13, { "Digital Input Bit 13", "lg8979.digin_b13", FT_BOOLEAN, 16, NULL, 0x2000, NULL, HFILL }}, { &hf_lg8979_digin_b14, { "Digital Input Bit 14", "lg8979.digin_b14", FT_BOOLEAN, 16, NULL, 0x4000, NULL, HFILL }}, { &hf_lg8979_digin_b15, { "Digital Input Bit 15", "lg8979.digin_b15", FT_BOOLEAN, 16, NULL, 0x8000, NULL, HFILL }}, { &hf_lg8979_acc_point, { "Value", "lg8979.acc_point", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_ptnum, { "Point Number", "lg8979.soe_logchg_ptnum", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, { &hf_lg8979_soe_logchg_newstat, { "New Status", "lg8979.soe_logchg_newstat", FT_UINT16, BASE_DEC, NULL, 0x8000, NULL, HFILL }}, { &hf_lg8979_soe_logchg_mon, { "Month", "lg8979.soe_logchg_mon", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_day, { "Day", "lg8979.soe_logchg_day", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_hour, { "Hours", "lg8979.soe_logchg_hour", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_min, { "Minute", "lg8979.soe_logchg_min", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_sec, { "Second", "lg8979.soe_logchg_sec", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_soe_logchg_msec, { "Milli-Second", "lg8979.soe_logchg_msec", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_ang_output_val, { "Point Value", "lg8979.ang_output_val", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL }}, { &hf_lg8979_sbo_tripclose, { "Trip/Close Control Code", "lg8979.sbo_tripclose", FT_UINT8, BASE_DEC, VALS(lg8979_sbo_tripclose_vals), 0x80, NULL, HFILL }}, { &hf_lg8979_sbo_timercnt, { "Timer Count", "lg8979.sbo_timercnt", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }}, { &hf_lg8979_digout_data, { "Data", "lg8979.digout_data", FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_pul_output_base, { "Base Time", "lg8979.pul_output_base", FT_UINT8, BASE_HEX, VALS(lg8979_pul_output_base_vals), 0x03, NULL, HFILL }}, { &hf_lg8979_pul_output_dur, { "Duration", "lg8979.pul_output_dur", FT_UINT8, BASE_HEX, NULL, 0x7C, NULL, HFILL }}, { &hf_lg8979_pul_output_rl, { "Raise/Lower", "lg8979.pul_output_rl", FT_UINT8, BASE_HEX, VALS(lg8979_pul_output_rl_vals), 0x80, NULL, HFILL }}, { &hf_lg8979_ang_deadband, { "Deadband", "lg8979.ang_deadband", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_ang_group, { "Analog Group", "lg8979.ang_group", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_ang_group_pts, { "Analog Group Points Mask", "lg8979.ang_group_pts", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_acc_preset, { "Preset Value", "lg8979.acc_preset", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_rtucfg_num_chassis, { "Number of I/O Chassis in RTU", "lg8979.rtucfg_num_chassis", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_rtucfg_chassis_num, { "Chassis Number", "lg8979.rtucfg_chassis_num", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_rtucfg_card_slot, { "Card Code", "lg8979.rtucfg_card_slot", FT_UINT8, BASE_DEC, VALS(lg8979_cardcode_vals), 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_mon, { "Month", "lg8979.timesync_mon", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_day, { "Day", "lg8979.timesync_day", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_hour, { "Hours", "lg8979.timesync_hour", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_min, { "Minute", "lg8979.timesync_min", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_sec, { "Second", "lg8979.timesync_sec", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timesync_msec, { "Milli-Second", "lg8979.timesync_msec", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timebias_value, { "Time Bias Value", "lg8979.timebias_value", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_firmware_ver, { "Firmware Version", "lg8979.firmware_ver", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_timebias_proctime, { "Time Bias Processing Time (ms)", "lg8979.timebias_proctime", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_lg8979_exprpt_code, { "Exception Report Code", "lg8979.exprpt_code", FT_UINT8, BASE_DEC, VALS(lg8979_exprpt_code_vals), 0x0, NULL, HFILL }}, { &hf_lg8979_exprpt_parm, { "Value", "lg8979.exprpt_parm", FT_UINT8, BASE_DEC, VALS(lg8979_exprpt_parm_vals), 0x0, NULL, HFILL }}, { &hf_lg8979_disallowed_func, { "Disallowed Function Code", "lg8979.disallowed_func", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &lg8979_funccode_vals_ext, 0x0, NULL, HFILL }}, { &hf_lg8979_crc16, { "CRC-16", "lg8979.crc16", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, }; /* Setup protocol subtree array */ static gint *ett[] = { &ett_lg8979, &ett_lg8979_flags, &ett_lg8979_funccode, &ett_lg8979_point, &ett_lg8979_ts, }; module_t *lg8979_module; /* Register the protocol name and description */ proto_lg8979 = proto_register_protocol("Landis & Gyr Telegyr 8979", "L&G 8979", "lg8979"); /* Registering protocol to be called by another dissector */ register_dissector("lg8979", dissect_lg8979_simple, proto_lg8979); /* Required function calls to register the header fields and subtrees used */ proto_register_field_array(proto_lg8979, lg8979_hf, array_length(lg8979_hf)); proto_register_subtree_array(ett, array_length(ett)); /* Register required preferences for L&G 8979 register decoding */ lg8979_module = prefs_register_protocol(proto_lg8979, NULL); /* L&G 8979 - Desegmentmentation; defaults to TRUE for TCP desegmentation*/ prefs_register_bool_preference(lg8979_module, "desegment", "Desegment all L&G 8979 Protocol packets spanning multiple TCP segments", "Whether the L&G 8979 dissector should desegment all messages spanning multiple TCP segments", &lg8979_desegment); } /******************************************************************************************************/ void proto_reg_handoff_lg8979(void) { dissector_handle_t lg8979_handle; /* Make sure to use L&G 8979 Protocol Preferences field to determine default TCP port */ lg8979_handle = create_dissector_handle(dissect_lg8979_tcp, proto_lg8979); dissector_add_for_decode_as_with_preference("tcp.port", lg8979_handle); dissector_add_for_decode_as("rtacser.data", lg8979_handle); } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */