Add dissector for CP "Cooper" 2179 Protocol
[metze/wireshark/wip.git] / epan / dissectors / packet-cp2179.c
1 /* packet-cp2179.c
2  * Routines for Communication Protocol 2179 (CP2179) Dissection
3  * By Qiaoyin Yang (qiaoyin[DOT]yang[AT]gmail.com
4  * Copyright 2014-2015,Schweitzer Engineering Laboratories
5  *
6  *
7  ************************************************************************************************
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  *
26  ************************************************************************************************
27 CP2179 protocol is a serial based protocol. The 2179 protocol is implemented with minor variations between vendors.
28 The RTAC implemented the 2179 client supporting a limited function codes and command codes. The RTAC doesn't support
29 multiple function codes in a single request and the dissector also doesn't support decoding these or corresponding responses.
30  * Dissector Notes:
31  A brief explanation of how a request and response messages are formulated in 2179 protocol.
32  The CP2179 request messages will follow the pattern below:
33 AA AA BB CC DD DD XX XX .... XX EE EE
34
35 A = 16-bit address field. The Most significant 5 bit is the Client address, the 11 bits for RTU address.
36 B = 8-bit Function code
37 C = 8-bit Command code
38 D = 16-bit Number of characters in the data field.
39 X = data field
40 E = 16-bit CRC
41
42 AA AA BB CC DD EE EE XX XX ... XX FF FF
43
44 A = 16-bit address field. The Most significant 5 bit is the Client address, the 11 bits for RTU address.
45 B = 8-bit Function code
46 C = 8-bit Status
47 D = 8-bit Port Status
48 E = 16-bit Number of characters
49 X = data field
50 F = 16-bit CRC
51  ************************************************************************************************/
52
53 #include "config.h"
54 #include <epan/packet.h>
55 #include <epan/dissectors/packet-tcp.h>
56 #include <epan/to_str.h>
57 #include <epan/conversation.h>
58 #include <epan/wmem/wmem.h>
59
60 #include <epan/prefs.h>
61 #include <epan/reassemble.h>
62 #include <epan/expert.h>
63
64
65 /* CP2179 function codes */
66 #define BASIC_SCAN                          0x00
67 #define SCAN_INCLUSIVE                      0x01
68 #define SCAN_FOR_SPECIAL_CALC               0x03
69 #define RETRIEVE_TIME_TAGGED_INFOR          0x04   /* not supported */
70 #define SCAN_BY_TABLE                       0x0A
71 #define SUPERVISORY_CONTROL                 0x10
72 #define RTU_CONFIG                          0x20
73 #define RETURN_RTU_CONFIG                   0x25
74 #define REPORT_EXCEPTION_DATA               0x0D
75
76 /* Function Code 0x00 (Basic Scan) Command codes */
77 #define SIMPLE_STATUS_DATA                 0x01
78 #define ALWAYS_RESERVED                    0x02
79 #define TWO_BIT_STATUS                     0x04
80 #define ANALOG_16_BIT                      0x08
81 #define ACCUMULATOR_16_BIT                 0x40
82
83 /* Function Code 0x03 (Special Calc) Command Codes */
84 #define SPECIAL_CALC_RANGE                 0x00
85 #define SPECIAL_CALC_ALL                   0x80
86
87 /* Function Code 0x10 (Supervisory Control) Command Codes */
88 #define SBO_SELECT_OPEN                    0x10
89 #define SBO_SELECT_CLOSE                   0x11
90 #define SBO_OPERATE                        0x20
91
92 /* Function Code 0x20 (RTU Control) Command Codes */
93 #define INIT_RTU_CONFIGURATION             0x00
94 #define RESET_ACCUMULATOR                  0x11
95
96 /* packet type */
97 #define BASIC_SCAN_QUERY_PACKET            1
98 #define BASIC_SCAN_RESPONSE_PACKET         2
99 #define SPECIAL_CALC_REQUEST_ALL           3
100 #define SPECIAL_CALC_RESPONSE_ALL          4
101 #define SPECIAL_CALC_REQUEST_RANGE         5
102 #define SPECIAL_CALC_RESPONSE_RANGE        6
103 #define SCAN_INCLUSIVE_16_ANALOG_REQUEST   7
104 #define SCAN_INCLUSIVE_16_ANALOG_RESPONSE  8
105 #define SBO_SELECT_REQUEST                 9
106 #define SBO_SELECT_RESPONSE                10
107 #define SBO_OPERATE_REQUEST                11
108 #define SBO_OPERATE_RESPONSE               12
109 #define INIT_RTU_REQUEST                   13
110 #define INIT_RTU_RESPONSE                  14
111 #define RESET_ACC_REQUEST                  15
112 #define RESET_ACC_RESPONSE                 16
113 #define SPECIAL_CALC_RESPONSE              17
114
115 /* packet length */
116 #define CP2179_MIN_LENGTH                  7
117 #define RESPONSE_HEADER_SIZE               7  /*includes addr, addr, function, status, port status, number of characters */
118 #define BASIC_SCAN_REQ_LEN                 8
119 #define SPECIAL_CALC_REQ_ALL_LEN           8
120 #define SBO_OPERATE_REQ_LEN                8
121 #define SPECIAL_CALC_REQ_RANGE_LEN         9
122 #define SBO_SELECT_REQ_LEN                 9
123 #define SBO_OPERATE_REPLY_LEN              9
124 #define SBO_SELECT_REPLY_LEN               10
125
126 #define PORT_CP2179    0
127 static gboolean cp2179_telnet_clean = TRUE;
128
129
130 /* Packet type Lookup */
131 static const value_string cp2179_packettype_vals[] = {
132 {BASIC_SCAN_QUERY_PACKET,               "Basic Scan Request"},
133 {BASIC_SCAN_RESPONSE_PACKET,            "Basic Scan Response"},
134 {SPECIAL_CALC_REQUEST_ALL,              "Special Calc Request All"},
135 {SPECIAL_CALC_RESPONSE_ALL,             "Special Calc Response All"},
136 {SPECIAL_CALC_REQUEST_RANGE,            "Special Calc Request a Range"},
137 {SPECIAL_CALC_RESPONSE_RANGE,           "Special Calc Response a Range"},
138 {SPECIAL_CALC_RESPONSE,                 "Special Calc Response"},
139 {SCAN_INCLUSIVE_16_ANALOG_REQUEST,      "Scan Inclusive Request"},
140 {SCAN_INCLUSIVE_16_ANALOG_RESPONSE,     "Scan Inclusive Response"},
141 {SBO_SELECT_REQUEST,                    "SBO Select Request"},
142 {SBO_SELECT_RESPONSE,                   "SBO Select Response"},
143 {SBO_OPERATE_REQUEST,                   "SBO Operate Request"},
144 {SBO_OPERATE_RESPONSE,                  "SBO Operate Response"},
145 {INIT_RTU_REQUEST,                      "INIT RTU Request"},
146 {INIT_RTU_RESPONSE,                     "INIT RTU Response"},
147 {RESET_ACC_REQUEST,                     "RESET Accumulator Request"},
148 {RESET_ACC_RESPONSE,                    "RESET Accumulator Response"},
149 {-99,                                   "Unknown Function Code"},
150 { 0,                                    NULL }
151
152 };
153
154 static value_string_ext cp2179_packettype_vals_ext = VALUE_STRING_EXT_INIT(cp2179_packettype_vals);
155
156 /* List contains request data  */
157 typedef struct {
158     wmem_list_t *bs_request_frame_data;
159 } cp2179_conversation;
160
161
162 /* Function code Lookup */
163 static const value_string FunctionCodenames[] = {
164 { BASIC_SCAN,                     "Basic Scan" },
165 { SCAN_INCLUSIVE,                 "Scan Inclusive"},
166 { SCAN_FOR_SPECIAL_CALC,          "Scan Floating Points" },
167 { SCAN_BY_TABLE,                  "Scan by Table" },
168 { SUPERVISORY_CONTROL,            "Supervisory Control" },
169 { RTU_CONFIG,                     "RTU Internal Control" },
170 { RETURN_RTU_CONFIG,              "Return RTU Config"},
171 { REPORT_EXCEPTION_DATA,          "Report Exception data"},
172 { 0,                              NULL }
173 };
174
175 /* Command code Lookup (FC00, FC03, FC10) */
176 static const value_string cp2179_CommandCodeNames [] = {
177 { SIMPLE_STATUS_DATA,             "Simple Status" },
178 { ALWAYS_RESERVED,                "Reserved" },
179 { TWO_BIT_STATUS,                 "2 Bit Data Status" },
180 { ANALOG_16_BIT,                  "16 Bit Analog" },
181 { ACCUMULATOR_16_BIT,             "16 Bit Pulsed Accumulator" },
182 { SBO_SELECT_OPEN,                "SBO Open" },
183 { SBO_SELECT_CLOSE,               "SBO Close" },
184 { SBO_OPERATE,                    "SBO Operate" },
185 { SPECIAL_CALC_ALL,               "Request All Special Calc Data"},
186 { SPECIAL_CALC_RANGE,             "Request a Range of Special Calc"},
187 { 0,                              NULL }
188 };
189
190 static value_string_ext cp2179_CommandCodeNames_ext = VALUE_STRING_EXT_INIT(cp2179_CommandCodeNames);
191
192 /* Function Code 0x20 Command Code Lookup */
193 static const value_string cp2179_FC20_CommandCodeNames [] = {
194 { INIT_RTU_CONFIGURATION,         "Initialize RTU Config" },
195 { RESET_ACCUMULATOR,              "Accumulator Reset" },
196 { 0,                              NULL }
197 };
198
199 /* Holds Request information required to later decode a response  */
200 typedef struct {
201    guint32  fnum;  /* frame number */
202    guint16  address_word;
203    guint8   function_code;
204    guint8   commmand_code;
205    guint16  numberofcharacters;
206    guint8   *requested_points;
207 } bs_request_frame;
208
209
210 static int proto_cp2179 = -1;
211
212 static guint global_cp2179_tcp_port = PORT_CP2179; /* Port 0 (by default), adjustable by user prefs */
213
214 /* Initialize the subtree pointers */
215 static gint ett_cp2179 = -1;
216 static gint ett_cp2179_header = -1;
217 static gint ett_cp2179_addr = -1;
218 static gint ett_cp2179_fc = -1;
219 static gint ett_cp2179_data = -1;
220 static gint ett_cp2179_subdata = -1;
221
222 /* Initialize the protocol and registered fields */
223 static int hf_cp2179_request_frame = -1;
224 static int hf_cp2179_rtu_address = -1;
225 static int hf_cp2179_master_address = -1;
226 static int hf_cp2179_function_code = -1;
227 static int hf_cp2179_nop_flag = -1;
228 static int hf_cp2179_rst_flag = -1;
229 static int hf_cp2179_reserved = -1;
230 static int hf_cp2179_command_code = -1;
231 static int hf_cp2179_command_code_fc20 = -1;
232 static int hf_cp2179_sbo_request_point = -1;
233 static int hf_cp2179_resetacc_request_point = -1;
234 static int hf_cp2179_speccalc_request_point = -1;
235 static int hf_cp2179_scaninc_startreq_point = -1;
236 static int hf_cp2179_scaninc_stopreq_point = -1;
237 static int hf_cp2179_number_characters = -1;
238 static int hf_cp2179_analog_16bit = -1;
239 static int hf_cp2179_accumulator = -1;
240 static int hf_cp2179_crc = -1;
241 static int hf_cp2179_data_field = -1;
242 static int hf_cp2179_status_byte = -1;
243 static int hf_cp2179_port_status_byte = -1;
244 static int hf_cp2179_simplestatusbit  = -1;
245 static int hf_cp2179_simplestatusbit0 = -1;
246 static int hf_cp2179_simplestatusbit1 = -1;
247 static int hf_cp2179_simplestatusbit2 = -1;
248 static int hf_cp2179_simplestatusbit3 = -1;
249 static int hf_cp2179_simplestatusbit4 = -1;
250 static int hf_cp2179_simplestatusbit5 = -1;
251 static int hf_cp2179_simplestatusbit6 = -1;
252 static int hf_cp2179_simplestatusbit7 = -1;
253 static int hf_cp2179_simplestatusbit8 = -1;
254 static int hf_cp2179_simplestatusbit9 = -1;
255 static int hf_cp2179_simplestatusbit10 = -1;
256 static int hf_cp2179_simplestatusbit11 = -1;
257 static int hf_cp2179_simplestatusbit12 = -1;
258 static int hf_cp2179_simplestatusbit13 = -1;
259 static int hf_cp2179_simplestatusbit14 = -1;
260 static int hf_cp2179_simplestatusbit15 = -1;
261 static int hf_cp2179_specialcalc       = -1;
262 static int hf_cp2179_2bitstatus        = -1;
263 static int hf_cp2179_2bitstatuschg0    = -1;
264 static int hf_cp2179_2bitstatuschg1    = -1;
265 static int hf_cp2179_2bitstatuschg2    = -1;
266 static int hf_cp2179_2bitstatuschg3    = -1;
267 static int hf_cp2179_2bitstatuschg4    = -1;
268 static int hf_cp2179_2bitstatuschg5    = -1;
269 static int hf_cp2179_2bitstatuschg6    = -1;
270 static int hf_cp2179_2bitstatuschg7    = -1;
271 static int hf_cp2179_2bitstatusstatus0 = -1;
272 static int hf_cp2179_2bitstatusstatus1 = -1;
273 static int hf_cp2179_2bitstatusstatus2 = -1;
274 static int hf_cp2179_2bitstatusstatus3 = -1;
275 static int hf_cp2179_2bitstatusstatus4 = -1;
276 static int hf_cp2179_2bitstatusstatus5 = -1;
277 static int hf_cp2179_2bitstatusstatus6 = -1;
278 static int hf_cp2179_2bitstatusstatus7 = -1;
279
280 static const int *cp2179_simplestatus_bits[] = {
281   &hf_cp2179_simplestatusbit0,
282   &hf_cp2179_simplestatusbit1,
283   &hf_cp2179_simplestatusbit2,
284   &hf_cp2179_simplestatusbit3,
285   &hf_cp2179_simplestatusbit4,
286   &hf_cp2179_simplestatusbit5,
287   &hf_cp2179_simplestatusbit6,
288   &hf_cp2179_simplestatusbit7,
289   &hf_cp2179_simplestatusbit8,
290   &hf_cp2179_simplestatusbit9,
291   &hf_cp2179_simplestatusbit10,
292   &hf_cp2179_simplestatusbit11,
293   &hf_cp2179_simplestatusbit12,
294   &hf_cp2179_simplestatusbit13,
295   &hf_cp2179_simplestatusbit14,
296   &hf_cp2179_simplestatusbit15,
297   NULL
298 };
299
300 static const int *cp2179_2bitstatus_bits[] = {
301   &hf_cp2179_2bitstatuschg0,
302   &hf_cp2179_2bitstatuschg1,
303   &hf_cp2179_2bitstatuschg2,
304   &hf_cp2179_2bitstatuschg3,
305   &hf_cp2179_2bitstatuschg4,
306   &hf_cp2179_2bitstatuschg5,
307   &hf_cp2179_2bitstatuschg6,
308   &hf_cp2179_2bitstatuschg7,
309   &hf_cp2179_2bitstatusstatus0,
310   &hf_cp2179_2bitstatusstatus1,
311   &hf_cp2179_2bitstatusstatus2,
312   &hf_cp2179_2bitstatusstatus3,
313   &hf_cp2179_2bitstatusstatus4,
314   &hf_cp2179_2bitstatusstatus5,
315   &hf_cp2179_2bitstatusstatus6,
316   &hf_cp2179_2bitstatusstatus7,
317   NULL
318 };
319
320
321 /**********************************************************************************************************/
322 /* Clean all instances of 0xFFFF from Telnet payload to compensate for IAC control code (replace w/ 0xFF) */
323 /* Function Duplicated from packet-telnet.c (unescape_and_tvbuffify_telnet_option)                        */
324 /**********************************************************************************************************/
325 static tvbuff_t *
326 clean_telnet_iac(packet_info *pinfo, tvbuff_t *tvb, int offset, int len)
327 {
328   tvbuff_t     *telnet_tvb;
329   guint8       *buf;
330   const guint8 *spos;
331   guint8       *dpos;
332   int           skip_byte, len_remaining;
333
334   spos=tvb_get_ptr(tvb, offset, len);
335   buf=(guint8 *)g_malloc(len);
336   dpos=buf;
337   skip_byte = 0;
338   len_remaining = len;
339   while(len_remaining > 0){
340
341     /* Only analyze two sequential bytes of source tvb if we have at least two bytes left */
342     if (len_remaining > 1) {
343         /* If two sequential 0xFF's exist, increment skip_byte counter, decrement  */
344         /* len_remaining by 2 and copy a single 0xFF to dest tvb. */
345         if((spos[0]==0xff) && (spos[1]==0xff)){
346             skip_byte++;
347             len_remaining -= 2;
348             *(dpos++)=0xff;
349             spos+=2;
350             continue;
351         }
352     }
353     /* If we only have a single byte left, or there were no sequential 0xFF's, copy byte from src tvb to dest tvb */
354     *(dpos++)=*(spos++);
355     len_remaining--;
356   }
357   telnet_tvb = tvb_new_child_real_data(tvb, buf, len-skip_byte, len-skip_byte);
358   tvb_set_free_cb(telnet_tvb, g_free);
359   add_new_data_source(pinfo, telnet_tvb, "Processed Telnet Data");
360
361   return telnet_tvb;
362 }
363
364 /******************************************************************************************************/
365 /* Code to Dissect Request frames */
366 /******************************************************************************************************/
367 static int
368 dissect_request_frame(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, int offset, guint16 packet_type )
369 {
370 /* Set up structures needed to add the protocol subtree and manage it */
371     proto_tree *cp2179_proto_tree = NULL;
372     proto_tree *cp2179_addr_tree = NULL;
373     proto_tree *cp2179_fc_tree = NULL;
374
375     proto_item *cp2179_proto_item = NULL;
376
377     guint8 req_command_code = 0;
378     guint8 function_code = 0;
379
380     guint16 address_word = -1;
381     guint16 requestnumberofcharacters = 0;
382
383     cp2179_proto_item = proto_tree_add_item(tree, proto_cp2179, tvb, 0, -1, ENC_NA);
384     cp2179_proto_tree = proto_item_add_subtree(cp2179_proto_item, ett_cp2179_header);
385
386     /* RTU & Master Address are encoded into a 16-bit word */
387     address_word = tvb_get_letohs(tvb, offset);
388     cp2179_addr_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 2, ett_cp2179_addr, NULL,
389                "RTU Address: %d, Master Address: %d", (address_word & 0x7FF), ((address_word & 0xF800) >> 11) );
390
391     proto_tree_add_item(cp2179_addr_tree, hf_cp2179_rtu_address, tvb, offset, 2, ENC_LITTLE_ENDIAN);
392     proto_tree_add_item(cp2179_addr_tree, hf_cp2179_master_address, tvb, offset, 2, ENC_LITTLE_ENDIAN);
393     offset += 2;
394
395     /* Report the function code */
396     function_code = tvb_get_guint8(tvb, offset) & 0x3f;
397     cp2179_fc_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 1, ett_cp2179_fc, NULL,
398                "Function Code: %s (0x%02x)", val_to_str_const(function_code, FunctionCodenames, "Unknown Function Code"), function_code);
399
400     proto_tree_add_item(cp2179_fc_tree, hf_cp2179_function_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
401     proto_tree_add_item(cp2179_fc_tree, hf_cp2179_reserved, tvb, offset, 1, ENC_LITTLE_ENDIAN);
402     offset += 1;
403
404     /* Because the function code basic scan for simple status and function code Internal Control for INIT RTU have the same
405        command code. If the packet type is a INIT RTU request, interpret the command code as INIT RTU, or else report it as
406        basic scan simple status command code.*/
407     switch(packet_type)
408     {
409     case INIT_RTU_REQUEST:
410     case RESET_ACC_REQUEST:
411         proto_tree_add_item(cp2179_proto_tree,  hf_cp2179_command_code_fc20 , tvb, offset, 1, ENC_LITTLE_ENDIAN);
412         break;
413
414     case BASIC_SCAN_QUERY_PACKET:
415     case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
416         req_command_code = tvb_get_guint8(tvb, offset);
417         /* Update Info column with useful information of Command Code Type */
418         col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
419         proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
420         break;
421
422     default:
423         proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
424         break;
425     }
426     offset += 1;
427
428     requestnumberofcharacters = tvb_get_letohs(tvb, 4);
429     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_number_characters, tvb, offset, 2, ENC_LITTLE_ENDIAN);
430     offset += 2;
431
432     /*If request number is greater than 0, data field in the request is not empty, we need to report the data field*/
433     if ( requestnumberofcharacters > 0 ){
434         /*Depends on the packet type, the data field should be dissect differently*/
435         switch (packet_type)
436         {
437             case SBO_SELECT_REQUEST:
438                 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_sbo_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
439                 offset += 1;
440                 break;
441
442             /*Reset Accumulator request data field will always only has 1 byte. The Sequence ID of the Accumulator that it wants to reset*/
443             case RESET_ACC_REQUEST:
444                 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_resetacc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
445                 offset += 1;
446                 break;
447
448             /* A special calculation that requests for a range of points will have a list of sequence ID of the Special Calculation points */
449             case SPECIAL_CALC_REQUEST_RANGE:
450                 do{
451                     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_speccalc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
452                     offset += 1;
453                 }while(tvb_reported_length_remaining(tvb, offset) > 2);
454                 break;
455
456             /*Scan Inclusive will have a starting sequence ID and a ending sequence ID in the data field.*/
457             case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
458                 do{
459                     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_scaninc_startreq_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
460                     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_scaninc_stopreq_point, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
461                     offset += 2;
462                 }while(tvb_reported_length_remaining(tvb, offset) > 2);
463                 break;
464         }
465     }
466         /*report the last two bytes as CRC in the request*/
467     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_crc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
468
469     return tvb_reported_length(tvb);
470 }
471
472
473 /******************************************************************************************************/
474 /* Code to dissect Response frames  */
475 /******************************************************************************************************/
476 static int
477 dissect_bs_response_frame(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, int offset, guint16 packet_type)
478 {
479     /* Set up structures needed to add the protocol subtree and manage it */
480     proto_item *bs_response_item = NULL;
481     proto_item *cp2179_proto_item = NULL;
482     proto_item *cp2179_subdata_item = NULL;
483
484     proto_tree *cp2179_proto_tree = NULL;
485     proto_tree *cp2179_addr_tree = NULL;
486     proto_tree *cp2179_fc_tree = NULL;
487     proto_tree *cp2179_data_tree = NULL;
488
489     cp2179_conversation  *conv;
490     guint32 req_frame_num;
491     guint16 req_address_word;
492     guint8  req_command_code;
493     gboolean request_found = FALSE;
494     bs_request_frame *request_data;
495
496     gint analogtestvalue = 0;
497     gint analog16_num = 0;
498     gint point_num = 0;
499
500     guint function_code;
501     guint simplestatusseq = 0x30;
502
503     guint16 address_word = 0;
504     guint16 numberofcharacters = -1;
505
506     gfloat specialcalvalue = 0;
507
508     cp2179_proto_item = proto_tree_add_item(tree, proto_cp2179, tvb, 0, -1, ENC_NA);
509     cp2179_proto_tree = proto_item_add_subtree(cp2179_proto_item, ett_cp2179_header);
510
511     /* RTU & Master Address are encoded into a 16-bit word */
512     address_word = tvb_get_letohs(tvb, offset);
513
514     cp2179_addr_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 2, ett_cp2179_addr, NULL,
515                    "RTU Address: %d, Master Address: %d", (address_word & 0x7FF), ((address_word & 0xF800) >> 11) );
516
517     proto_tree_add_item(cp2179_addr_tree, hf_cp2179_rtu_address, tvb, 0, 2, ENC_LITTLE_ENDIAN);
518     proto_tree_add_item(cp2179_addr_tree, hf_cp2179_master_address, tvb, 0, 2, ENC_LITTLE_ENDIAN);
519     offset += 2;
520
521     /*The response always echos the function code in request, except when the RTU can't perform the required function.
522     It may set the NOP or RST bit. Bit 0 to bit 5 is the field for function codes. Bit 6 is NOP bit. Bit 7 is RST bit. */
523     function_code = tvb_get_guint8(tvb, offset) & 0x3f;
524
525     cp2179_fc_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 1, ett_cp2179_fc, NULL,
526                "Function Code: %s (0x%02x)", val_to_str_const(function_code, FunctionCodenames, "Unknown Function Code"), function_code);
527
528     proto_tree_add_item(cp2179_fc_tree, hf_cp2179_function_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
529     proto_tree_add_item(cp2179_fc_tree, hf_cp2179_nop_flag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
530     proto_tree_add_item(cp2179_fc_tree, hf_cp2179_rst_flag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
531
532     offset += 1;
533
534     /* Status Byte & Port Status */
535     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_status_byte, tvb, offset, 1, ENC_LITTLE_ENDIAN);
536     offset += 1;
537     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_port_status_byte, tvb, offset, 1, ENC_LITTLE_ENDIAN);
538     offset += 1;
539
540     /* Number of characters */
541     numberofcharacters = tvb_get_letohs(tvb, 5);
542     proto_tree_add_item(cp2179_proto_tree, hf_cp2179_number_characters, tvb, offset, 2, ENC_LITTLE_ENDIAN);
543     offset += 2;
544
545     /* get the converstation data */
546     conv = (cp2179_conversation *)p_get_proto_data(wmem_file_scope(), pinfo, proto_cp2179, 0);
547
548     if (conv) {
549         wmem_list_frame_t *frame = wmem_list_head(conv->bs_request_frame_data);
550         /* Cycle through all logged instances of request frames, looking for request frame number that occurred immediately
551            prior to current frame number that has a matching address word */
552         while (frame && !request_found) {
553             request_data = (bs_request_frame *)wmem_list_frame_data(frame);
554             req_frame_num = request_data->fnum;
555             req_command_code = request_data->commmand_code;
556             req_address_word = request_data->address_word;
557                 if ((pinfo->fd->num > req_frame_num) && (req_address_word == address_word)) {
558                     bs_response_item = proto_tree_add_uint(cp2179_proto_tree, hf_cp2179_request_frame, tvb, 0, 0, req_frame_num);
559                     PROTO_ITEM_SET_GENERATED(bs_response_item);
560                     request_found = TRUE;
561                 }
562                 frame = wmem_list_frame_next(frame);
563         }
564
565         if (request_found)
566         {
567             switch (packet_type)
568             {
569                 case SBO_SELECT_RESPONSE:
570                 case SBO_OPERATE_RESPONSE:
571                 case RESET_ACC_RESPONSE:
572                 case INIT_RTU_RESPONSE:
573
574                     if ( numberofcharacters > 0 ){
575                         /*Based on the packet type, change the displayed messages*/
576                         if ( packet_type == SBO_SELECT_RESPONSE ){
577                             proto_tree_add_item(cp2179_proto_tree, hf_cp2179_sbo_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
578                             offset += 1;
579                         }
580                         if ( packet_type == RESET_ACC_RESPONSE ){
581                             proto_tree_add_item(cp2179_proto_tree, hf_cp2179_resetacc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
582                             offset += 1;
583                         }
584                     }
585                     break;
586
587                 case SPECIAL_CALC_RESPONSE:
588                     /* Based on the command code from the corresponding request, dissect the data field differently.
589                        If required a range of Special calculation, display the requested sequence ID and corresponding
590                        values. The requested sequence number is obtained from the previous request frame.  */
591                     cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
592
593                     if (req_command_code == SPECIAL_CALC_ALL ){
594                         do{
595                             specialcalvalue = tvb_get_letohieee_float(tvb, offset );
596                             proto_tree_add_float_format(cp2179_data_tree, hf_cp2179_specialcalc, tvb, offset, 4, specialcalvalue,
597                                 "Special Calculation %u : %f", point_num, specialcalvalue);
598                             point_num += 1;
599                             offset += 4;
600                         }while(tvb_reported_length_remaining(tvb, offset) > 2);
601                     }
602                     /*If it request all the special calculation data, dissect all of them and associated a sequence ID with it.*/
603                     else if (req_command_code == SPECIAL_CALC_RANGE ){
604                         do{
605                             specialcalvalue = tvb_get_letohieee_float(tvb, offset );
606                             proto_tree_add_float_format(cp2179_data_tree, hf_cp2179_specialcalc, tvb, offset, 4, specialcalvalue,
607                                 "Special Calculation %u : %f",  request_data->requested_points[point_num], specialcalvalue);
608                             point_num += 1;
609                             offset += 4;
610                         }while(tvb_reported_length_remaining(tvb, offset) > 2);
611                     }
612                     break;
613
614                 case SCAN_INCLUSIVE_16_ANALOG_RESPONSE:
615
616                     cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
617
618                     /* Update Info column with useful information of Command Code Type */
619                     col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
620
621                     /*Report the values of the requested SCAN inclusive data. To figure out which sequence ID the values in the response associated with,
622                     we read the bs_request_frame information and show the corresponding sequence ID of the data in response frame.*/
623                     do{
624                         analogtestvalue = (gint16)tvb_get_letohs(tvb, offset);
625                         proto_tree_add_uint_format(cp2179_data_tree, hf_cp2179_analog_16bit, tvb, offset, 2, request_data->requested_points[point_num],
626                                                    "Analog (16 bit) %u : %i",  request_data->requested_points[point_num], analogtestvalue);
627                         point_num += 1;
628                         offset += 2;
629                     }while(tvb_reported_length_remaining(tvb, offset) > 2);
630
631                     break;
632
633                 case BASIC_SCAN_RESPONSE_PACKET:
634                 {
635                     cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
636
637                     /* Update Info column with useful information of Command Code Type */
638                     col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
639
640                     switch (req_command_code)
641                     {
642                         /* Based the command code from the request frame, we dissect the response data differently.
643                            For example, if the request packet has command byte as ANALOG_16_BIT, the
644                            the data field in the response should be dissected as 16-bit signed integer(s). */
645                         case ACCUMULATOR_16_BIT:
646                             do{
647                                 analogtestvalue = tvb_get_letohs(tvb, offset);
648                                 proto_tree_add_uint_format(cp2179_data_tree, hf_cp2179_accumulator, tvb, offset, 2, analog16_num,
649                                                            "Accumulator %u : %u", analog16_num, analogtestvalue);
650                                 analog16_num += 1;
651                                 offset += 2;
652                             }while(tvb_reported_length_remaining(tvb, offset) > 2);
653
654                             break;
655
656                         case ANALOG_16_BIT:
657                             do{
658                                 analogtestvalue =(gint16)tvb_get_letohs(tvb, offset);
659                                 proto_tree_add_uint_format(cp2179_data_tree, hf_cp2179_analog_16bit, tvb, offset, 2, analog16_num,
660                                                            "Analog (16 bit) %u : %i", analog16_num, analogtestvalue);
661                                 analog16_num += 1;
662                                 offset += 2;
663                             }while(tvb_reported_length_remaining(tvb, offset) > 2);
664
665                             break;
666
667                         case SIMPLE_STATUS_DATA:
668                             do{
669                                 cp2179_subdata_item = proto_tree_add_bitmask(cp2179_data_tree, tvb, offset, hf_cp2179_simplestatusbit,
670                                                        ett_cp2179_subdata, cp2179_simplestatus_bits, ENC_LITTLE_ENDIAN);
671                                 proto_item_set_text(cp2179_subdata_item, "Simple Status Point 0x%x", simplestatusseq);
672
673                                 simplestatusseq += 1;
674                                 offset += 2;
675                             }while(tvb_reported_length_remaining(tvb, offset) > 2);
676
677                             break;
678
679                         case TWO_BIT_STATUS:
680                             do{
681                                 cp2179_subdata_item = proto_tree_add_bitmask(cp2179_data_tree, tvb, offset, hf_cp2179_2bitstatus,
682                                                        ett_cp2179_subdata, cp2179_2bitstatus_bits, ENC_LITTLE_ENDIAN);
683                                 proto_item_set_text(cp2179_subdata_item, "2 Bit Status Point 0x%x", simplestatusseq);
684
685                                 simplestatusseq += 1;
686                                 offset += 2;
687                             }while(tvb_reported_length_remaining(tvb, offset) > 2);
688
689                             break;
690                     } /* end of command code switch */
691
692                 } /* end of basic scan response switch */
693
694                 break;
695
696             } /* end of packet type switch */
697
698             proto_tree_add_item(cp2179_proto_tree, hf_cp2179_crc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
699
700         } /* request found */
701
702     } /* conversation data found */
703
704     if (!request_found) {
705         proto_item_append_text(bs_response_item, ", No Request found");
706         return 0;
707     }
708
709     return tvb_reported_length(tvb);
710 }
711
712 /******************************************************************************************************/
713 /* Load Request information into bs request struct */
714 /******************************************************************************************************/
715 static bs_request_frame* copy_bs_request_frame(tvbuff_t *tvb  )
716 {
717  /* Set up structures needed to add the protocol request and use it for dissecting response packet */
718     guint offset = 0;
719     guint8 index=0 ;
720     bs_request_frame *frame;
721     guint16 num_objects=0;
722
723     /* get a new frame and initialize it */
724     frame = wmem_new(wmem_file_scope(), bs_request_frame);
725
726     /* update the data within the structure frame */
727     frame->address_word = tvb_get_letohs(tvb, offset); offset +=2;
728     frame->function_code = tvb_get_guint8(tvb, offset); offset +=1;
729     frame->commmand_code = tvb_get_guint8(tvb, offset); offset +=1;
730     frame->numberofcharacters = tvb_get_letohs(tvb, offset);offset +=2;
731
732     /*Keep track of the request data field in a request.
733       Such as SCAN INCLUSIVE request contains a Start Sequence Number and an Ending Sequence Number. */
734     if (frame->function_code == SCAN_INCLUSIVE) {
735         guint8 startpt, endpt;
736         startpt = tvb_get_guint8(tvb, offset);
737         endpt = tvb_get_guint8(tvb, offset+1);
738         num_objects = (endpt - startpt) + 1;
739         frame->requested_points = (guint8 *)wmem_alloc(wmem_file_scope(), num_objects * sizeof(guint8));
740
741         /* We have a range of 'request' points */
742         for (index = 0; index < num_objects; index++) {
743             frame->requested_points[index] = startpt;
744             startpt++;
745         }
746         /* offset += 2; */
747     }
748     /* Get Details for all Requested Points */
749     else {
750         num_objects = frame->numberofcharacters;
751         frame->requested_points = (guint8 *)wmem_alloc(wmem_file_scope(), num_objects * sizeof(guint8));
752         for (index = 0; index < num_objects; index++) {
753             frame->requested_points[index] = tvb_get_guint8(tvb, offset);
754             offset += 1;
755         }
756
757     }
758
759     return frame;
760 }
761
762
763
764 /******************************************************************************************************/
765 /* Classify the different packet type  */
766 /******************************************************************************************************/
767 static int
768 classify_packet_type(tvbuff_t *tvb)
769 {
770     int packet_type = -1;
771     guint8 function_code;
772     guint8 command_code;
773     guint16 requestnumberofcharacters = 0;
774     guint16 responsenumberofcharacters = 0;
775     guint16 packet_length = 0;
776
777
778     packet_length = tvb_reported_length(tvb);
779     /*Get function code*/
780     function_code = tvb_get_guint8(tvb, 2);
781     /*Get command codes */
782     command_code = tvb_get_guint8(tvb, 3);
783     /* Get number of characters */
784     requestnumberofcharacters = tvb_get_letohs(tvb, 4);
785     responsenumberofcharacters = tvb_get_letohs(tvb, 5);
786
787     /*The response always echos the function code in request, except when the RTU can't perform the required function.
788     It may set the NOP or RST bit. Bit 0 to bit 5 is the field for function codes. Bit 6 is NOP bit. Bit 7 is RST bit. */
789
790     /*Remove NOP and RST bit*/
791     function_code = function_code & 0x3f ;
792
793     /*2179 protocol frames doesn't have data tells you whether it is a request or a response. We will decide what packet type is based on
794     multiple factors, function code, command code, the length of the packet*/
795     switch (function_code ){
796         case BASIC_SCAN:
797             /*Basic scan request message, the number of characters is always 0 and length is fixed*/
798             if ( (requestnumberofcharacters == 0) && (packet_length == BASIC_SCAN_REQ_LEN) ) {
799                 packet_type = BASIC_SCAN_QUERY_PACKET ; /* supported */
800             }
801             else if ( (responsenumberofcharacters > 0) && (packet_length > BASIC_SCAN_REQ_LEN) ) {
802                 packet_type = BASIC_SCAN_RESPONSE_PACKET; /* supported */
803             }
804
805             break;
806
807         case SUPERVISORY_CONTROL:
808             /*SBO select request messages always has number of characters equals to 1 and SBO length is fixed*/
809             if ( (requestnumberofcharacters == 1) && (packet_length == SBO_SELECT_REQ_LEN) ) {
810                 packet_type = SBO_SELECT_REQUEST; /* supported */
811             }
812             /*SBO select response always has number of characters equals to 1 and SBO length is fixed. */
813             else if ( (responsenumberofcharacters == 1) && (packet_length == SBO_SELECT_REPLY_LEN) ) {
814                 packet_type = SBO_SELECT_RESPONSE; /* supported */
815             }
816             /*SBO operate request always has number of characters as 0 */
817             else if (requestnumberofcharacters == 0) {
818                 if ( (packet_length == SBO_OPERATE_REQ_LEN) && (command_code == SBO_OPERATE) ) {
819                     packet_type = SBO_OPERATE_REQUEST; /* supported */
820                 }
821             }
822             /*SBO operate response always has number of characters as 0 */
823             else if (responsenumberofcharacters == 0) {
824                 if (packet_length == SBO_OPERATE_REPLY_LEN) {
825                     packet_type = SBO_OPERATE_RESPONSE; /* supported */
826                 }
827             }
828
829             break;
830
831         case SCAN_FOR_SPECIAL_CALC:
832             /*Scan for special cal has to command code associated with it, requests all special calculation data or a range of it */
833             if ( (requestnumberofcharacters == 0) && (command_code == SPECIAL_CALC_ALL ) ) {
834                 packet_type = SPECIAL_CALC_REQUEST_ALL; /* supported */
835             }
836             else if ( (requestnumberofcharacters > 0) && (command_code == SPECIAL_CALC_RANGE ) ) {
837                 packet_type = SPECIAL_CALC_REQUEST_RANGE; /* supported */
838             }
839             /*If a packet has SCAN_FOR_SPECIAL_CAL as function code and it is not a request, then it is a response */
840             else if ( (responsenumberofcharacters > 0) && (packet_length == (responsenumberofcharacters + 9) ) ) {
841                 packet_type = SPECIAL_CALC_RESPONSE; /* supported */
842             }
843
844             break;
845         /*Scan Inclusive request always has request number of characters equals to 2 and a fixed command code */
846         case SCAN_INCLUSIVE:
847             /*If a packet has SCAN Inclusive function code and it is not a request, then it is a SCAN Inclusive response*/
848             if ( (responsenumberofcharacters > 0) ) {
849                 packet_type = SCAN_INCLUSIVE_16_ANALOG_RESPONSE; /* supported */
850             }
851
852             if( (command_code == ANALOG_16_BIT) && (requestnumberofcharacters == 2) ) {
853                 packet_type = SCAN_INCLUSIVE_16_ANALOG_REQUEST; /* supported */
854             }
855
856             break;
857
858         case RTU_CONFIG:
859             if (responsenumberofcharacters == 0) {
860                 packet_type = INIT_RTU_RESPONSE;
861             }
862             if ( (requestnumberofcharacters == 0) && (command_code == INIT_RTU_CONFIGURATION) ) {
863                 packet_type = INIT_RTU_REQUEST;
864             }
865
866             if (responsenumberofcharacters == 1) {
867                 packet_type = RESET_ACC_RESPONSE;
868             }
869             if ( (requestnumberofcharacters == 1) && (command_code == RESET_ACCUMULATOR) ) {
870                 packet_type = RESET_ACC_REQUEST;
871             }
872             break;
873         default :
874             packet_type = -99;
875             break;
876     }
877
878     return packet_type;
879 }
880
881
882
883 /******************************************************************************************************/
884 /* Code to dissect CP2179 protocol packets */
885 /******************************************************************************************************/
886 static int
887 dissect_cp2179_pdu(tvbuff_t *cp2179_tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
888 {
889     int offset = 0;
890     gint16 packet_type;
891     col_set_str(pinfo->cinfo, COL_PROTOCOL, "CP2179");
892     col_clear(pinfo->cinfo,COL_INFO);
893
894     packet_type = classify_packet_type(cp2179_tvb);
895     /* set information for Information column for CP2179 */
896     col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str_ext_const(packet_type, &cp2179_packettype_vals_ext, "Unknown Packet Type"));
897
898     if (!pinfo->fd->flags.visited){
899         conversation_t       *conversation = NULL;
900         cp2179_conversation      *bs_conv_data = NULL;
901
902         /* Find a conversation, create a new if no one exists */
903         conversation = find_or_create_conversation(pinfo);
904         bs_conv_data = (cp2179_conversation *)conversation_get_proto_data(conversation, proto_cp2179);
905
906         if (bs_conv_data == NULL){
907            bs_conv_data = wmem_new(wmem_file_scope(), cp2179_conversation);
908            bs_conv_data->bs_request_frame_data = wmem_list_new(wmem_file_scope());
909            conversation_add_proto_data(conversation, proto_cp2179, (void *)bs_conv_data);
910         }
911
912         p_add_proto_data(wmem_file_scope(), pinfo, proto_cp2179, 0, bs_conv_data);
913
914         if ((packet_type == BASIC_SCAN_QUERY_PACKET) || (packet_type == SBO_SELECT_REQUEST)
915            ||(packet_type == SPECIAL_CALC_REQUEST_ALL)||(packet_type == SBO_OPERATE_REQUEST)
916            ||(packet_type == SPECIAL_CALC_REQUEST_RANGE)||(packet_type == INIT_RTU_REQUEST)
917            ||(packet_type == RESET_ACC_REQUEST)||(packet_type == SCAN_INCLUSIVE_16_ANALOG_REQUEST)) {
918
919             /*fill the bs request frame. It holds the request information.*/
920             bs_request_frame    *frame_ptr = NULL;
921             frame_ptr = copy_bs_request_frame(cp2179_tvb);
922
923             /*also hold the current frame number*/
924             frame_ptr->fnum = pinfo->fd->num;
925             wmem_list_prepend(bs_conv_data->bs_request_frame_data, frame_ptr);
926         }
927     } /* !visited */
928
929     if (tvb_reported_length_remaining(cp2179_tvb, offset) > 0){
930         switch (packet_type){
931             case BASIC_SCAN_QUERY_PACKET:
932             case SBO_SELECT_REQUEST:
933             case SBO_OPERATE_REQUEST:
934             case SPECIAL_CALC_REQUEST_ALL:
935             case SPECIAL_CALC_REQUEST_RANGE:
936             case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
937             case RESET_ACC_REQUEST:
938             case INIT_RTU_REQUEST:
939                 dissect_request_frame(cp2179_tvb, tree, pinfo, offset, packet_type);
940                 break;
941
942             case BASIC_SCAN_RESPONSE_PACKET:
943             case SBO_SELECT_RESPONSE:
944             case SBO_OPERATE_RESPONSE:
945             case SPECIAL_CALC_RESPONSE:
946             case SCAN_INCLUSIVE_16_ANALOG_RESPONSE:
947             case INIT_RTU_RESPONSE:
948             case RESET_ACC_RESPONSE:
949                 dissect_bs_response_frame(cp2179_tvb, tree, pinfo, offset, packet_type);
950                 break;
951             default:
952                 break;
953         } /* packet type */
954     } /* length remaining */
955
956     return tvb_reported_length(cp2179_tvb);
957 }
958
959
960 /******************************************************************************************************/
961 /* Dissect (and possibly Re-assemble) CP2179 protocol payload data */
962 /******************************************************************************************************/
963 static int
964 dissect_cp2179(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
965 {
966     tvbuff_t *cp2179_tvb;
967     gint length = tvb_reported_length(tvb);
968
969    /* Check for the packet length, a 2179 Message is at least 7 byte long*/
970     if(length < CP2179_MIN_LENGTH){
971         return 0;
972     }
973
974     if((pinfo->srcport) && cp2179_telnet_clean){
975         cp2179_tvb = clean_telnet_iac(pinfo, tvb, 0, length);
976     }
977     else{
978         /* cp2179_tvb = tvb_new_subset( tvb, 0, length, length); */
979         cp2179_tvb = tvb_new_subset_length( tvb, 0, length);
980     }
981
982     dissect_cp2179_pdu(cp2179_tvb, pinfo, tree, data);
983
984     return length;
985 }
986
987 void proto_reg_handoff_cp2179(void);
988
989 void
990 proto_register_cp2179(void)
991 {
992     static hf_register_info hf[] =
993     {
994         { &hf_cp2179_request_frame,
995             { "Request Frame", "cp2179.request_frame",
996             FT_FRAMENUM, BASE_NONE,
997             NULL, 0x0,
998             NULL, HFILL }
999         },
1000
1001         { &hf_cp2179_rtu_address,
1002             { "RTU Address", "cp2179.RTUAddress",
1003             FT_UINT16, BASE_DEC,
1004             NULL, 0x7FF,
1005             NULL, HFILL }
1006         },
1007
1008        { &hf_cp2179_master_address,
1009             { "Master Address", "cp2179.MasterAddress",
1010             FT_UINT16, BASE_DEC,
1011             NULL, 0xF800,
1012             NULL, HFILL }
1013         },
1014         { &hf_cp2179_function_code,
1015             { "Function Code", "cp2179.functioncode",
1016             FT_UINT8, BASE_HEX,
1017             VALS(FunctionCodenames), 0x3F,
1018             NULL, HFILL }
1019         },
1020
1021         { &hf_cp2179_nop_flag,
1022             { "NOP Flag", "cp2179.nop_flag",
1023             FT_UINT8, BASE_DEC,
1024             NULL, 0x40,
1025             NULL, HFILL }
1026         },
1027
1028         { &hf_cp2179_rst_flag,
1029             { "RST Flag", "cp2179.rst_flag",
1030             FT_UINT8, BASE_DEC,
1031             NULL, 0x80,
1032             NULL, HFILL }
1033         },
1034
1035         { &hf_cp2179_reserved,
1036             { "Reserved Bits", "cp2179.Reserved",
1037             FT_UINT8, BASE_DEC,
1038             NULL, 0xC0,
1039             NULL, HFILL }
1040         },
1041
1042         { &hf_cp2179_command_code,
1043             { "Command Code", "cp2179.commandcode",
1044             FT_UINT8, BASE_HEX,
1045             VALS(cp2179_CommandCodeNames), 0x0,
1046             NULL, HFILL }
1047         },
1048
1049         { &hf_cp2179_command_code_fc20,
1050             { "Command Code (FC 0x20)", "cp2179.commandcodeinitrtu",
1051             FT_UINT8, BASE_HEX,
1052             VALS(cp2179_FC20_CommandCodeNames), 0x0,
1053             NULL, HFILL }
1054         },
1055        { &hf_cp2179_status_byte,
1056             { "RTU Status", "cp2179.rtustatus",
1057             FT_UINT8, BASE_DEC,
1058             0x0, 0x0,
1059             NULL, HFILL }
1060         },
1061        { &hf_cp2179_port_status_byte,
1062             { "Port Status", "cp2179.portstatus",
1063             FT_UINT8, BASE_DEC,
1064             0x0, 0x0,
1065             NULL, HFILL }
1066         },
1067        { &hf_cp2179_sbo_request_point,
1068             { "SBO Request Point", "cp2179.sbo_requestpoint",
1069             FT_UINT8, BASE_DEC,
1070             0x0, 0x0,
1071             NULL, HFILL }
1072         },
1073        { &hf_cp2179_resetacc_request_point,
1074             { "Reset Accumulator Request Point", "cp2179.resetacc_requestpoint",
1075             FT_UINT8, BASE_DEC,
1076             0x0, 0x0,
1077             NULL, HFILL }
1078         },
1079        { &hf_cp2179_speccalc_request_point,
1080             { "Special Calc Request Point", "cp2179.speccalc_requestpoint",
1081             FT_UINT8, BASE_DEC,
1082             0x0, 0x0,
1083             NULL, HFILL }
1084         },
1085        { &hf_cp2179_scaninc_startreq_point,
1086             { "Start Request Point", "cp2179.scaninc_startreq_point",
1087             FT_UINT8, BASE_DEC,
1088             0x0, 0x0,
1089             NULL, HFILL }
1090         },
1091        { &hf_cp2179_scaninc_stopreq_point,
1092             { "Stop Request Point", "cp2179.scaninc_stopreq_point",
1093             FT_UINT8, BASE_DEC,
1094             0x0, 0x0,
1095             NULL, HFILL }
1096         },
1097       { &hf_cp2179_number_characters,
1098             { "Number of Characters", "cp2179.numberofcharacters",
1099             FT_UINT16, BASE_DEC,
1100             0x0, 0x0,
1101             NULL, HFILL }
1102         },
1103       { &hf_cp2179_crc,
1104             { "CRC", "cp2179.crc",
1105             FT_UINT16, BASE_HEX,
1106             0x0, 0x0,
1107             NULL, HFILL }
1108         },
1109       { &hf_cp2179_data_field,
1110             { "Data Field", "cp2179.datafield",
1111             FT_UINT8, BASE_DEC,
1112             0x0, 0x0,
1113             NULL, HFILL }
1114         },
1115
1116       { &hf_cp2179_accumulator,
1117             { "Accumulator", "cp2179.accumulator",
1118             FT_UINT16, BASE_DEC,
1119             0x0, 0x0,
1120             NULL, HFILL }
1121         },
1122
1123       { &hf_cp2179_specialcalc,
1124             { "Special Calc", "cp2179.specialcalc",
1125             FT_FLOAT, BASE_NONE,
1126             0x0, 0x0,
1127             NULL, HFILL }
1128         },
1129
1130       { &hf_cp2179_analog_16bit,
1131          { "Analog 16-bit", "cp2179.analogdata",
1132          FT_UINT16, BASE_DEC,
1133          0x0, 0x0,
1134          NULL, HFILL }
1135         },
1136       { &hf_cp2179_simplestatusbit,
1137          { "Simple Status Bit", "cp2179.simplestatusbit",
1138          FT_UINT16, BASE_HEX,
1139          NULL, 0x0,
1140          NULL, HFILL }
1141       },
1142       { &hf_cp2179_simplestatusbit0,
1143          { "Simple Status bit 0", "cp2179.simplestatusbit0",
1144          FT_BOOLEAN, 16,
1145          NULL, 0x0001,
1146          NULL, HFILL }
1147       },
1148       { &hf_cp2179_simplestatusbit1,
1149          { "Simple Status bit 1", "cp2179.simplestatusbit1",
1150          FT_BOOLEAN, 16,
1151          NULL, 0x0002,
1152          NULL, HFILL }
1153       },
1154       { &hf_cp2179_simplestatusbit2,
1155          { "Simple Status bit 2", "cp2179.simplestatusbit2",
1156          FT_BOOLEAN, 16,
1157          NULL, 0x0004,
1158          NULL, HFILL }
1159       },
1160       { &hf_cp2179_simplestatusbit3,
1161          { "Simple Status bit 3", "cp2179.simplestatusbit3",
1162          FT_BOOLEAN, 16,
1163          NULL, 0x0008,
1164          NULL, HFILL }
1165       },
1166       { &hf_cp2179_simplestatusbit4,
1167          { "Simple Status bit 4", "cp2179.simplestatusbit4",
1168          FT_BOOLEAN, 16,
1169          NULL, 0x0010,
1170          NULL, HFILL }
1171       },
1172
1173       { &hf_cp2179_simplestatusbit5,
1174          { "Simple Status bit 5", "cp2179.simplestatusbit5",
1175          FT_BOOLEAN, 16,
1176          NULL, 0x0020,
1177          NULL, HFILL }
1178       },
1179
1180       { &hf_cp2179_simplestatusbit6,
1181          { "Simple Status bit 6", "cp2179.simplestatusbit6",
1182          FT_BOOLEAN, 16,
1183          NULL, 0x0040,
1184          NULL, HFILL }
1185       },
1186       { &hf_cp2179_simplestatusbit7,
1187          { "Simple Status bit 7", "cp2179.simplestatusbit7",
1188          FT_BOOLEAN, 16,
1189          NULL, 0x0080,
1190          NULL, HFILL }
1191       },
1192       { &hf_cp2179_simplestatusbit8,
1193          { "Simple Status bit 8", "cp2179.simplestatusbit8",
1194          FT_BOOLEAN, 16,
1195          NULL, 0x0100,
1196          NULL, HFILL }
1197       },
1198       { &hf_cp2179_simplestatusbit9,
1199          { "Simple Status bit 9", "cp2179.simplestatusbit9",
1200          FT_BOOLEAN, 16,
1201          NULL, 0x0200,
1202          NULL, HFILL }
1203       },
1204       { &hf_cp2179_simplestatusbit10,
1205          { "Simple Status bit 10", "cp2179.simplestatusbit10",
1206          FT_BOOLEAN, 16,
1207          NULL, 0x0400,
1208          NULL, HFILL }
1209       },
1210       { &hf_cp2179_simplestatusbit11,
1211          { "Simple Status bit 11", "cp2179.simplestatusbit11",
1212          FT_BOOLEAN, 16,
1213          NULL, 0x0800,
1214          NULL, HFILL }
1215       },
1216       { &hf_cp2179_simplestatusbit12,
1217          { "Simple Status bit 12", "cp2179.simplestatusbit12",
1218          FT_BOOLEAN, 16,
1219          NULL, 0x1000,
1220          NULL, HFILL }
1221       },
1222       { &hf_cp2179_simplestatusbit13,
1223          { "Simple Status bit 13", "cp2179.simplestatusbit13",
1224          FT_BOOLEAN, 16,
1225          NULL, 0x2000,
1226          NULL, HFILL }
1227       },
1228
1229       { &hf_cp2179_simplestatusbit14,
1230          { "Simple Status bit 14", "cp2179.simplestatusbit14",
1231          FT_BOOLEAN, 16,
1232          NULL, 0x4000,
1233          NULL, HFILL }
1234       },
1235
1236       { &hf_cp2179_simplestatusbit15,
1237          { "Simple Status bit 15", "cp2179.simplestatusbit15",
1238          FT_BOOLEAN, 16,
1239          NULL, 0x8000,
1240          NULL, HFILL }
1241       },
1242       { &hf_cp2179_2bitstatus,
1243          { "2 Bit Status", "cp2179.twobitstatus",
1244          FT_UINT16, BASE_HEX,
1245          NULL, 0x0,
1246          NULL, HFILL }
1247       },
1248       { &hf_cp2179_2bitstatuschg0,
1249          { "2 Bit Status Change 0", "cp2179.twobitstatuschg0",
1250          FT_BOOLEAN, 16,
1251          NULL, 0x0001,
1252          NULL, HFILL }
1253       },
1254       { &hf_cp2179_2bitstatuschg1,
1255          { "2 Bit Status Change 1", "cp2179.twobitstatuschg1",
1256          FT_BOOLEAN, 16,
1257          NULL, 0x0002,
1258          NULL, HFILL }
1259       },
1260       { &hf_cp2179_2bitstatuschg2,
1261          { "2 Bit Status Change 2", "cp2179.twobitstatuschg2",
1262          FT_BOOLEAN, 16,
1263          NULL, 0x0004,
1264          NULL, HFILL }
1265       },
1266       { &hf_cp2179_2bitstatuschg3,
1267          { "2 Bit Status Change 3", "cp2179.twobitstatuschg3",
1268          FT_BOOLEAN, 16,
1269          NULL, 0x0008,
1270          NULL, HFILL }
1271       },
1272       { &hf_cp2179_2bitstatuschg4,
1273          { "2 Bit Status Change 4", "cp2179.twobitstatuschg4",
1274          FT_BOOLEAN, 16,
1275          NULL, 0x0010,
1276          NULL, HFILL }
1277       },
1278       { &hf_cp2179_2bitstatuschg5,
1279          { "2 Bit Status Change 5", "cp2179.twobitstatuschg5",
1280          FT_BOOLEAN, 16,
1281          NULL, 0x0020,
1282          NULL, HFILL }
1283       },
1284
1285       { &hf_cp2179_2bitstatuschg6,
1286          { "2 Bit Status Change 6", "cp2179.twobitstatuschg6",
1287          FT_BOOLEAN, 16,
1288          NULL, 0x0040,
1289          NULL, HFILL }
1290       },
1291
1292       { &hf_cp2179_2bitstatuschg7,
1293          {  "2 Bit Status Change 7", "cp2179.twobitstatuschg7",
1294          FT_BOOLEAN, 16,
1295          NULL, 0x0080,
1296          NULL, HFILL }
1297       },
1298
1299       { &hf_cp2179_2bitstatusstatus0,
1300          { "2 Bit Status bit 0", "cp2179.twobitstatusbit0",
1301          FT_BOOLEAN, 16,
1302          NULL, 0x0100,
1303          NULL, HFILL }
1304       },
1305       { &hf_cp2179_2bitstatusstatus1,
1306          { "2 Bit Status bit 1", "cp2179.twobitstatusbit1",
1307          FT_BOOLEAN, 16,
1308          NULL, 0x0200,
1309          NULL, HFILL }
1310       },
1311       { &hf_cp2179_2bitstatusstatus2,
1312          { "2 Bit Status bit 2", "cp2179.twobitstatusbit2",
1313          FT_BOOLEAN, 16,
1314          NULL, 0x0400,
1315          NULL, HFILL }
1316       },
1317       { &hf_cp2179_2bitstatusstatus3,
1318          { "2 Bit Status bit 3", "cp2179.twobitstatusbit3",
1319          FT_BOOLEAN, 16,
1320          NULL, 0x0800,
1321          NULL, HFILL }
1322       },
1323       { &hf_cp2179_2bitstatusstatus4,
1324          { "2 Bit Status bit 4", "cp2179.twobitstatusbit4",
1325          FT_BOOLEAN, 16,
1326          NULL, 0x1000,
1327          NULL, HFILL }
1328       },
1329       { &hf_cp2179_2bitstatusstatus5,
1330          { "2 Bit Status bit 5", "cp2179.twobitstatusbit5",
1331          FT_BOOLEAN, 16,
1332          NULL, 0x2000,
1333          NULL, HFILL }
1334       },
1335       { &hf_cp2179_2bitstatusstatus6,
1336          { "2 Bit Status bit 6", "cp2179.twobitstatusbit6",
1337          FT_BOOLEAN, 16,
1338          NULL, 0x4000,
1339          NULL, HFILL }
1340       },
1341       { &hf_cp2179_2bitstatusstatus7,
1342          { "2 Bit Status bit 7", "cp2179.twobitstatusbit7",
1343          FT_BOOLEAN, 16,
1344          NULL, 0x8000,
1345          NULL, HFILL }
1346 }
1347     };
1348
1349     /* Setup protocol subtree array */
1350     static gint *ett[] = {
1351       &ett_cp2179,
1352       &ett_cp2179_header,
1353       &ett_cp2179_addr,
1354       &ett_cp2179_fc,
1355       &ett_cp2179_data,
1356       &ett_cp2179_subdata
1357
1358     };
1359
1360     module_t *cp2179_module;
1361
1362     proto_cp2179 = proto_register_protocol ("CP2179 Protocol", "CP2179", "cp2179");
1363     new_register_dissector("cp2179", dissect_cp2179, proto_cp2179);
1364     proto_register_field_array(proto_cp2179, hf, array_length(hf));
1365     proto_register_subtree_array(ett, array_length(ett));
1366
1367     /* Register required preferences for CP2179 Encapsulated-over-TCP decoding */
1368     cp2179_module = prefs_register_protocol(proto_cp2179, proto_reg_handoff_cp2179);
1369
1370     /* Default TCP Port, allows for "user" port either than 0. */
1371     prefs_register_uint_preference(cp2179_module, "tcp.port", "CP 2179 Protocol Port",
1372                        "Set the TCP port for CP 2179 Protocol packets (if other"
1373                        " than the default of 0)",
1374                        10, &global_cp2179_tcp_port);
1375
1376     /* Telnet protocol IAC (0xFF) processing; defaults to TRUE to allow Telnet Encapsulated Data */
1377     prefs_register_bool_preference(cp2179_module, "telnetclean",
1378                                   "Enable Automatic pre-processing of Telnet-encapsulated data to remove extra 0xFF (IAC) bytes",
1379                                   "Whether the SEL Protocol dissector should automatically pre-process Telnet data to remove IAC bytes",
1380                                   &cp2179_telnet_clean);
1381
1382
1383 }
1384
1385
1386 void
1387 proto_reg_handoff_cp2179(void)
1388 {
1389    static int cp2179_prefs_initialized = FALSE;
1390    static dissector_handle_t cp2179_handle;
1391    static unsigned int cp2179_port;
1392
1393     if (!cp2179_prefs_initialized){
1394         cp2179_handle = new_create_dissector_handle(dissect_cp2179, proto_cp2179);
1395         cp2179_prefs_initialized = TRUE;
1396     }
1397      else {
1398         dissector_delete_uint("tcp.port", cp2179_port, cp2179_handle);
1399     }
1400
1401     cp2179_port = global_cp2179_tcp_port;
1402
1403     dissector_add_uint("tcp.port", cp2179_port, cp2179_handle);
1404 }
1405
1406 /*
1407  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1408  *
1409  * Local variables:
1410  * c-basic-offset: 4
1411  * tab-width: 8
1412  * indent-tabs-mode: nil
1413  * End:
1414  *
1415  * vi: set shiftwidth=4 tabstop=8 expandtab:
1416  * :indentSize=4:tabSize=8:noTabs=true:
1417  */