2 * Routines for ENTTEC packet disassembly
6 * Copyright (c) 2003,2004 by Erwin Rol <erwin@erwinrol.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1999 Gerald Combs
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.
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.
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.
33 #include <epan/packet.h>
34 #include <epan/addr_resolv.h>
35 #include <epan/prefs.h>
36 #include <epan/wmem/wmem.h>
41 * http://www.enttec.com/docs/enttec_protocol.pdf
44 /* Define UDP/TCP ports for ENTTEC */
46 #define UDP_PORT_ENTTEC 0x0D05
47 #define TCP_PORT_ENTTEC 0x0D05
50 #define ENTTEC_HEAD_ESPR 0x45535052
51 #define ENTTEC_HEAD_ESPP 0x45535050
52 #define ENTTEC_HEAD_ESAP 0x45534150
53 #define ENTTEC_HEAD_ESDD 0x45534444
54 #define ENTTEC_HEAD_ESNC 0x45534E43
55 #define ENTTEC_HEAD_ESZZ 0x45535A5A
57 static const value_string enttec_head_vals
[] = {
58 { ENTTEC_HEAD_ESPR
, "Poll Reply" },
59 { ENTTEC_HEAD_ESPP
, "Poll" },
60 { ENTTEC_HEAD_ESAP
, "Ack/nAck" },
61 { ENTTEC_HEAD_ESDD
, "DMX Data" },
62 { ENTTEC_HEAD_ESNC
, "Config" },
63 { ENTTEC_HEAD_ESZZ
, "Reset" },
67 #define ENTTEC_DATA_TYPE_DMX 0x01
68 #define ENTTEC_DATA_TYPE_CHAN_VAL 0x02
69 #define ENTTEC_DATA_TYPE_RLE 0x04
71 static const value_string enttec_data_type_vals
[] = {
72 { ENTTEC_DATA_TYPE_DMX
, "Uncompressed DMX" },
73 { ENTTEC_DATA_TYPE_CHAN_VAL
, "Channel+Value" },
74 { ENTTEC_DATA_TYPE_RLE
, "RLE Compressed DMX" },
78 void proto_register_enttec(void);
79 void proto_reg_handoff_enttec(void);
81 /* Define the enttec proto */
82 static int proto_enttec
= -1;
85 static int hf_enttec_head
= -1;
88 static int hf_enttec_poll_type
= -1;
91 static int hf_enttec_poll_reply_mac
= -1;
92 static int hf_enttec_poll_reply_node_type
= -1;
93 static int hf_enttec_poll_reply_version
= -1;
94 static int hf_enttec_poll_reply_switch
= -1;
95 static int hf_enttec_poll_reply_name
= -1;
96 static int hf_enttec_poll_reply_option
= -1;
97 static int hf_enttec_poll_reply_tos
= -1;
98 static int hf_enttec_poll_reply_ttl
= -1;
101 static int hf_enttec_dmx_data_universe
= -1;
102 static int hf_enttec_dmx_data_start_code
= -1;
103 static int hf_enttec_dmx_data_type
= -1;
104 static int hf_enttec_dmx_data_size
= -1;
105 static int hf_enttec_dmx_data_data
= -1;
106 static int hf_enttec_dmx_data_data_filter
= -1;
107 static int hf_enttec_dmx_data_dmx_data
= -1;
109 /* Define the tree for enttec */
110 static int ett_enttec
= -1;
113 * Here are the global variables associated with the preferences
117 static guint global_udp_port_enttec
= UDP_PORT_ENTTEC
;
118 static guint global_tcp_port_enttec
= TCP_PORT_ENTTEC
;
120 static gint global_disp_chan_val_type
= 0;
121 static gint global_disp_col_count
= 16;
122 static gint global_disp_chan_nr_type
= 0;
125 dissect_enttec_poll_reply(tvbuff_t
*tvb
, guint offset
, proto_tree
*tree
)
127 proto_tree_add_item(tree
, hf_enttec_poll_reply_mac
, tvb
,
131 proto_tree_add_item(tree
, hf_enttec_poll_reply_node_type
, tvb
,
132 offset
, 2, ENC_BIG_ENDIAN
);
135 proto_tree_add_item(tree
, hf_enttec_poll_reply_version
, tvb
,
136 offset
, 1, ENC_BIG_ENDIAN
);
139 proto_tree_add_item(tree
, hf_enttec_poll_reply_switch
, tvb
,
140 offset
, 1, ENC_BIG_ENDIAN
);
143 proto_tree_add_item(tree
, hf_enttec_poll_reply_name
, tvb
,
144 offset
, 10, ENC_ASCII
|ENC_NA
);
147 proto_tree_add_item(tree
, hf_enttec_poll_reply_option
, tvb
,
148 offset
, 1, ENC_BIG_ENDIAN
);
151 proto_tree_add_item(tree
, hf_enttec_poll_reply_tos
, tvb
,
152 offset
, 1, ENC_BIG_ENDIAN
);
155 proto_tree_add_item(tree
, hf_enttec_poll_reply_ttl
, tvb
,
156 offset
, 1, ENC_BIG_ENDIAN
);
165 dissect_enttec_poll(tvbuff_t
*tvb
, guint offset
, proto_tree
*tree
)
167 proto_tree_add_item(tree
, hf_enttec_poll_type
, tvb
,
168 offset
, 1, ENC_BIG_ENDIAN
);
175 dissect_enttec_ack(tvbuff_t
*tvb _U_
, guint offset
, proto_tree
*tree _U_
)
182 dissect_enttec_dmx_data(tvbuff_t
*tvb
, guint offset
, proto_tree
*tree
)
184 static const char* chan_format
[] = {
189 static const char* string_format
[] = {
194 guint8
*dmx_data
= (guint8
*)wmem_alloc(wmem_packet_scope(), 512 * sizeof(guint8
));
195 guint16
*dmx_data_offset
= (guint16
*)wmem_alloc(wmem_packet_scope(), 513 * sizeof(guint16
)); /* 1 extra for last offset */
196 wmem_strbuf_t
*dmx_epstr
;
200 guint16 length
,r
,c
,row_count
;
202 guint16 ci
,ui
,i
,start_offset
,end_offset
;
204 proto_tree_add_item(tree
, hf_enttec_dmx_data_universe
, tvb
,
205 offset
, 1, ENC_BIG_ENDIAN
);
208 proto_tree_add_item(tree
, hf_enttec_dmx_data_start_code
, tvb
,
209 offset
, 1, ENC_BIG_ENDIAN
);
212 type
= tvb_get_guint8(tvb
, offset
);
213 proto_tree_add_item(tree
, hf_enttec_dmx_data_type
, tvb
,
214 offset
, 1, ENC_BIG_ENDIAN
);
217 length
= tvb_get_ntohs(tvb
, offset
);
218 proto_tree_add_item(tree
, hf_enttec_dmx_data_size
, tvb
,
219 offset
, 2, ENC_BIG_ENDIAN
);
223 * XXX - we should handle a too-long length better.
228 if (type
== ENTTEC_DATA_TYPE_RLE
) {
229 /* uncompress the DMX data */
232 while (ci
< length
&& ui
< 512) {
233 v
= tvb_get_guint8(tvb
, offset
+ci
);
236 count
= tvb_get_guint8(tvb
, offset
+ci
);
238 v
= tvb_get_guint8(tvb
, offset
+ci
);
240 for (i
=0;i
< count
&& ui
< 512;i
++) {
242 dmx_data_offset
[ui
] = ci
-3;
245 } else if (v
== 0xFD) {
247 v
= tvb_get_guint8(tvb
, offset
+ci
);
249 dmx_data_offset
[ui
] = ci
;
254 dmx_data_offset
[ui
] = ci
;
259 dmx_data_offset
[ui
] = ci
;
261 for (ui
=0; ui
< length
;ui
++) {
262 dmx_data
[ui
] = tvb_get_guint8(tvb
, offset
+ui
);
263 dmx_data_offset
[ui
] = ui
;
265 dmx_data_offset
[ui
] = ui
;
269 if ((type
== ENTTEC_DATA_TYPE_DMX
|| type
== ENTTEC_DATA_TYPE_RLE
) && global_disp_col_count
> 0) {
270 hi
= proto_tree_add_item(tree
,
271 hf_enttec_dmx_data_data
,
277 si
= proto_item_add_subtree(hi
, ett_enttec
);
279 row_count
= (ui
/global_disp_col_count
) + ((ui
%global_disp_col_count
) == 0 ? 0 : 1);
280 dmx_epstr
= wmem_strbuf_new_label(wmem_packet_scope());
281 for (r
=0; r
< row_count
;r
++) {
282 for (c
=0;(c
< global_disp_col_count
) && (((r
*global_disp_col_count
)+c
) < ui
);c
++) {
283 if ((global_disp_col_count
> 1) && (c
% (global_disp_col_count
/2)) == 0) {
284 wmem_strbuf_append_c(dmx_epstr
, ' ');
286 v
= dmx_data
[(r
*global_disp_col_count
)+c
];
287 if (global_disp_chan_val_type
== 0) {
290 wmem_strbuf_append(dmx_epstr
, "FL ");
292 wmem_strbuf_append_printf(dmx_epstr
, chan_format
[global_disp_chan_val_type
], v
);
295 wmem_strbuf_append_printf(dmx_epstr
, chan_format
[global_disp_chan_val_type
], v
);
299 start_offset
= dmx_data_offset
[(r
*global_disp_col_count
)];
300 end_offset
= dmx_data_offset
[(r
*global_disp_col_count
)+c
];
302 proto_tree_add_none_format(si
,hf_enttec_dmx_data_dmx_data
, tvb
,
304 end_offset
-start_offset
,
305 string_format
[global_disp_chan_nr_type
], (r
*global_disp_col_count
)+1,
306 wmem_strbuf_get_str(dmx_epstr
));
307 wmem_strbuf_truncate(dmx_epstr
, 0);
310 item
= proto_tree_add_item(si
, hf_enttec_dmx_data_data_filter
, tvb
,
311 offset
, length
, ENC_NA
);
312 PROTO_ITEM_SET_HIDDEN(item
);
315 } else if (type
== ENTTEC_DATA_TYPE_CHAN_VAL
) {
316 proto_tree_add_item(tree
, hf_enttec_dmx_data_data_filter
, tvb
,
317 offset
, length
, ENC_NA
);
320 proto_tree_add_item(tree
, hf_enttec_dmx_data_data_filter
, tvb
,
321 offset
, length
, ENC_NA
);
331 dissect_enttec_config(tvbuff_t
*tvb _U_
, guint offset
, proto_tree
*tree _U_
)
338 dissect_enttec_reset(tvbuff_t
*tvb _U_
, guint offset
, proto_tree
*tree _U_
)
345 dissect_enttec(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
349 proto_tree
*ti
,*enttec_tree
=NULL
;
351 /* Set the protocol column */
352 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ENTTEC");
354 head
= tvb_get_ntohl(tvb
, offset
);
356 /* Clear out stuff in the info column */
357 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s",
358 val_to_str(head
, enttec_head_vals
, "Unknown (0x%08x)"));
361 ti
= proto_tree_add_item(tree
, proto_enttec
, tvb
, offset
, -1, ENC_NA
);
362 enttec_tree
= proto_item_add_subtree(ti
, ett_enttec
);
366 proto_tree_add_item(enttec_tree
, hf_enttec_head
, tvb
,
367 offset
, 4, ENC_BIG_ENDIAN
);
371 case ENTTEC_HEAD_ESPR
:
372 offset
= dissect_enttec_poll_reply( tvb
, offset
, enttec_tree
);
375 case ENTTEC_HEAD_ESPP
:
376 offset
= dissect_enttec_poll( tvb
, offset
, enttec_tree
);
379 case ENTTEC_HEAD_ESAP
:
380 offset
= dissect_enttec_ack( tvb
, offset
, enttec_tree
);
383 case ENTTEC_HEAD_ESDD
:
384 offset
= dissect_enttec_dmx_data( tvb
, offset
, enttec_tree
);
387 case ENTTEC_HEAD_ESNC
:
388 offset
= dissect_enttec_config( tvb
, offset
, enttec_tree
);
391 case ENTTEC_HEAD_ESZZ
:
392 offset
= dissect_enttec_reset( tvb
, offset
, enttec_tree
);
401 proto_register_enttec(void)
403 static hf_register_info hf
[] = {
406 { "Head", "enttec.head",
407 FT_UINT32
, BASE_HEX
, VALS(enttec_head_vals
), 0x0,
409 { &hf_enttec_poll_reply_mac
,
410 { "MAC", "enttec.poll_reply.mac",
411 FT_ETHER
, BASE_NONE
, NULL
, 0x0,
413 { &hf_enttec_poll_reply_node_type
,
414 { "Node Type", "enttec.poll_reply.node_type",
415 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
417 { &hf_enttec_poll_reply_version
,
418 { "Version", "enttec.poll_reply.version",
419 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
421 { &hf_enttec_poll_reply_switch
,
422 { "Switch settings", "enttec.poll_reply.switch_settings",
423 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
425 { &hf_enttec_poll_reply_name
,
426 { "Name", "enttec.poll_reply.name",
427 FT_STRING
, BASE_NONE
, NULL
, 0x0,
429 { &hf_enttec_poll_reply_option
,
430 { "Option Field", "enttec.poll_reply.option_field",
431 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
433 { &hf_enttec_poll_reply_tos
,
434 { "TOS", "enttec.poll_reply.tos",
435 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
437 { &hf_enttec_poll_reply_ttl
,
438 { "TTL", "enttec.poll_reply.ttl",
439 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
441 { &hf_enttec_dmx_data_universe
,
442 { "Universe", "enttec.dmx_data.universe",
443 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
445 { &hf_enttec_dmx_data_start_code
,
446 { "Start Code", "enttec.dmx_data.start_code",
447 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
449 { &hf_enttec_dmx_data_type
,
450 { "Data Type", "enttec.dmx_data.type",
451 FT_UINT8
, BASE_HEX
, VALS(enttec_data_type_vals
), 0x0,
453 { &hf_enttec_dmx_data_size
,
454 { "Data Size", "enttec.dmx_data.size",
455 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
457 { &hf_enttec_dmx_data_data
,
458 { "DMX Data", "enttec.dmx_data.data",
459 FT_NONE
, BASE_NONE
, NULL
, 0x0,
461 { &hf_enttec_dmx_data_data_filter
,
462 { "DMX Data", "enttec.dmx_data.data_filter",
463 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
465 { &hf_enttec_dmx_data_dmx_data
,
466 { "DMX Data", "enttec.dmx_data.dmx_data",
467 FT_NONE
, BASE_NONE
, NULL
, 0x0,
469 { &hf_enttec_poll_type
,
470 { "Reply Type", "enttec.poll.reply_type",
471 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
475 static gint
*ett
[] = {
479 module_t
*enttec_module
;
481 static const enum_val_t disp_chan_val_types
[] = {
482 { "pro", "Percent", 0 },
483 { "hex", "Hexadecimal", 1 },
484 { "dec", "Decimal", 2 },
488 static const enum_val_t disp_chan_nr_types
[] = {
489 { "hex", "Hexadecimal", 0 },
490 { "dec", "Decimal", 1 },
494 static const enum_val_t col_count
[] = {
503 proto_enttec
= proto_register_protocol("ENTTEC", "ENTTEC","enttec");
504 proto_register_field_array(proto_enttec
,hf
,array_length(hf
));
505 proto_register_subtree_array(ett
,array_length(ett
));
507 enttec_module
= prefs_register_protocol(proto_enttec
,
508 proto_reg_handoff_enttec
);
509 prefs_register_uint_preference(enttec_module
, "udp_port",
511 "The UDP port on which ENTTEC packets will be sent",
512 10,&global_udp_port_enttec
);
514 prefs_register_uint_preference(enttec_module
, "tcp_port",
516 "The TCP port on which ENTTEC packets will be sent",
517 10,&global_tcp_port_enttec
);
519 prefs_register_enum_preference(enttec_module
, "dmx_disp_chan_val_type",
520 "DMX Display channel value type",
521 "The way DMX values are displayed",
522 &global_disp_chan_val_type
,
523 disp_chan_val_types
, FALSE
);
525 prefs_register_enum_preference(enttec_module
, "dmx_disp_chan_nr_type",
526 "DMX Display channel nr. type",
527 "The way DMX channel numbers are displayed",
528 &global_disp_chan_nr_type
,
529 disp_chan_nr_types
, FALSE
);
531 prefs_register_enum_preference(enttec_module
, "dmx_disp_col_count",
532 "DMX Display Column Count",
533 "The number of columns for the DMX display",
534 &global_disp_col_count
,
538 /* The registration hand-off routing */
540 proto_reg_handoff_enttec(void) {
541 static gboolean enttec_initialized
= FALSE
;
542 static dissector_handle_t enttec_handle
;
543 static guint udp_port_enttec
;
544 static guint tcp_port_enttec
;
546 if(!enttec_initialized
) {
547 enttec_handle
= new_create_dissector_handle(dissect_enttec
,proto_enttec
);
548 enttec_initialized
= TRUE
;
550 dissector_delete_uint("udp.port",udp_port_enttec
,enttec_handle
);
551 dissector_delete_uint("tcp.port",tcp_port_enttec
,enttec_handle
);
554 udp_port_enttec
= global_udp_port_enttec
;
555 tcp_port_enttec
= global_tcp_port_enttec
;
557 dissector_add_uint("udp.port",global_udp_port_enttec
,enttec_handle
);
558 dissector_add_uint("tcp.port",global_tcp_port_enttec
,enttec_handle
);