3 * Routines for MDB dissection
4 * Copyright 2023 Martin Kaiser for PayTec AG (www.paytec.ch)
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * The MDB (Multi-Drop Bus) protocol is used inside a vending machine. MDB
15 * defines the communication between the main control board (VMC = Vending
16 * Machine Controller) and peripheral components, e.g. a payment terminal
17 * or a bill validator.
19 * The VMC acts as bus master and sends a request to one peripheral at a time.
20 * A peripheral may send data only in response to such a request.
22 * The MDB specification is maintained by the National Automatic Merchandising
23 * Association (NAMA). As of August 2023, the current version of the MDB
24 * specification is 4.3. It is available from
25 * https://namanow.org/nama-releases-mdb-version-4-3/
27 * The pcap input format for this dissector is documented at
28 * https://www.kaiser.cx/pcap-mdb.html
32 #include <epan/expert.h>
33 #include <epan/packet.h>
35 #include <wsutil/array.h>
36 #include <wiretap/wtap.h>
38 void proto_register_mdb(void);
40 static dissector_handle_t mdb_handle
;
45 static int ett_mdb_hdr
;
46 static int ett_mdb_cl
;
47 static int ett_mdb_cgw
;
49 static int hf_mdb_hdr_ver
;
50 static int hf_mdb_event
;
51 static int hf_mdb_addr
;
52 static int hf_mdb_cmd
;
53 static int hf_mdb_cl_setup_sub
;
54 static int hf_mdb_cl_feat_lvl
;
55 static int hf_mdb_cl_cols
;
56 static int hf_mdb_cl_rows
;
57 static int hf_mdb_cl_disp_info
;
58 static int hf_mdb_cl_max_price
;
59 static int hf_mdb_cl_min_price
;
60 static int hf_mdb_cl_vend_sub
;
61 static int hf_mdb_cl_item_price
;
62 static int hf_mdb_cl_item_num
;
63 static int hf_mdb_cl_reader_sub
;
64 static int hf_mdb_cl_resp
;
65 static int hf_mdb_cl_scale
;
66 static int hf_mdb_cl_dec_pl
;
67 static int hf_mdb_cl_max_rsp_time
;
68 static int hf_mdb_cl_vend_amt
;
69 static int hf_mdb_cl_expns_sub
;
70 static int hf_mdb_cl_manuf_code
;
71 static int hf_mdb_cl_ser_num
;
72 static int hf_mdb_cl_mod_num
;
73 static int hf_mdb_cl_opt_feat
;
74 static int hf_mdb_cgw_feat_lvl
;
75 static int hf_mdb_cgw_scale
;
76 static int hf_mdb_cgw_dec_pl
;
77 static int hf_mdb_cgw_resp
;
78 static int hf_mdb_cgw_max_rsp_time
;
79 static int hf_mdb_cgw_report_sub
;
80 static int hf_mdb_cgw_dts_evt_code
;
81 static int hf_mdb_cgw_duration
;
82 static int hf_mdb_cgw_activity
;
83 static int hf_mdb_cgw_expns_sub
;
84 static int hf_mdb_cgw_opt_feat
;
85 static int hf_mdb_cgw_manuf_code
;
86 static int hf_mdb_cgw_ser_num
;
87 static int hf_mdb_cgw_mod_num
;
88 static int hf_mdb_ack
;
89 static int hf_mdb_data
;
90 static int hf_mdb_chk
;
92 static expert_field ei_mdb_short_packet
;
94 #define MDB_EVT_DATA_MST_PER 0xFF
95 #define MDB_EVT_DATA_PER_MST 0xFE
96 #define MDB_EVT_BUS_RESET 0xFD
98 static const value_string mdb_event
[] = {
99 { MDB_EVT_DATA_MST_PER
, "Data transfer Master -> Peripheral" },
100 { MDB_EVT_DATA_PER_MST
, "Data transfer Peripheral -> Master" },
101 { MDB_EVT_BUS_RESET
, "Bus reset" },
105 #define ADDR_VMC "VMC"
107 #define ADDR_CASHLESS1 0x10
108 #define ADDR_COMMS_GW 0x18
110 static const value_string mdb_addr
[] = {
112 { ADDR_CASHLESS1
, "Cashless #1" },
113 { ADDR_COMMS_GW
, "Communications Gateway" },
114 { 0x30, "Bill Validator" },
115 { 0x60, "Cashless #2" },
116 { 0x68, "Age Verification Device" },
120 static const value_string mdb_ack
[] = {
128 * These are just the command bits in the address + command byte. MDB supports
129 * two Cashless peripherals (Cashless #1 and #2) with different addresses,
130 * both use the same commands.
132 #define MDB_CL_CMD_SETUP 0x01
133 #define MDB_CL_CMD_VEND 0x03
134 #define MDB_CL_CMD_READER 0x04
135 #define MDB_CL_CMD_EXPNS 0x07
137 static const value_string mdb_cl_cmd
[] = {
139 { MDB_CL_CMD_SETUP
, "Setup" },
141 { MDB_CL_CMD_VEND
, "Vend" },
142 { MDB_CL_CMD_READER
, "Reader" },
143 { MDB_CL_CMD_EXPNS
, "Expansion" },
147 #define MDB_CL_SETUP_CFG_DATA 0x00
148 #define MDB_CL_SETUP_MAX_MIN 0x01
150 static const value_string mdb_cl_setup_sub_cmd
[] = {
151 { MDB_CL_SETUP_CFG_DATA
, "Config Data" },
152 { MDB_CL_SETUP_MAX_MIN
, "Max/Min Prices" },
156 #define MDB_CL_VEND_REQ 0x00
157 #define MDB_CL_VEND_SUC 0x02
159 static const value_string mdb_cl_vend_sub_cmd
[] = {
160 { MDB_CL_VEND_REQ
, "Vend Request" },
161 { MDB_CL_VEND_SUC
, "Vend Success" },
162 { 0x04, "Session Complete" },
166 static const value_string mdb_cl_reader_sub_cmd
[] = {
167 { 0x00, "Reader Disable" },
168 { 0x01, "Reader Enable" },
172 #define MDB_CL_EXPNS_REQ_ID 0x00
173 #define MDB_CL_EXPNS_OPT_ENA 0x04
175 static const value_string mdb_cl_expns_sub_cmd
[] = {
176 { MDB_CL_EXPNS_REQ_ID
, "Request ID" },
177 { MDB_CL_EXPNS_OPT_ENA
, "Optional Feature Enabled" },
181 #define MDB_CL_RESP_RD_CFG_DATA 0x01
182 #define MDB_CL_RESP_VEND_APRV 0x05
183 #define MDB_CL_RESP_PER_ID 0x09
185 static const value_string mdb_cl_resp
[] = {
186 { 0x00, "Just Reset" },
187 { MDB_CL_RESP_RD_CFG_DATA
, "Reader Config Data" },
188 { 0x03, "Begin Session" },
189 { MDB_CL_RESP_VEND_APRV
, "Vend Approved" },
190 { 0x06, "Vend Denied" },
191 { 0x07, "End Session" },
192 { MDB_CL_RESP_PER_ID
, "Peripheral ID" },
193 { 0x0b, "Cmd Out Of Sequence" },
198 * For the Communications Gateway, we use the complete address + command byte
199 * as value for the value string. The values here match those in the MDB
202 * There's only one Communications Gateway, the address bits are always the
203 * same. (This is different from the Cashless peripherals, see above.)
205 #define MDB_CGW_ADDR_CMD_SETUP 0x19
206 #define MDB_CGW_ADDR_CMD_REPORT 0x1B
207 #define MDB_CGW_ADDR_CMD_EXPNS 0x1F
209 static const value_string mdb_cgw_addr_cmd
[] = {
211 { MDB_CGW_ADDR_CMD_SETUP
, "Setup" },
213 { MDB_CGW_ADDR_CMD_REPORT
, "Report" },
214 { MDB_CGW_ADDR_CMD_EXPNS
, "Expansion" },
218 #define MDB_CGW_REPORT_DTS_EVT 0x02
220 static const value_string mdb_cgw_report_sub_cmd
[] = {
221 { 0x01, "Transaction" },
222 { MDB_CGW_REPORT_DTS_EVT
, "DTS Event" },
226 #define MDB_CGW_EXPNS_FEAT_ENA 0x01
228 static const value_string mdb_cgw_expns_sub_cmd
[] = {
229 { 0x00, "Identification" },
230 { MDB_CGW_EXPNS_FEAT_ENA
, "Feature enable" },
231 { 0x02, "Time/Date Request" },
235 #define MDB_CGW_RESP_CFG 0x01
236 #define MDB_CGW_RESP_PER_ID 0x06
238 static const value_string mdb_cgw_resp
[] = {
239 { 0x00, "Just Reset" },
240 { MDB_CGW_RESP_CFG
, "Comms Gateway Config" },
241 { 0x05, "DTS Event Acknowledge" },
242 { MDB_CGW_RESP_PER_ID
, "Peripheral ID" },
246 static void dissect_mdb_ack(tvbuff_t
*tvb
, int offset
,
247 packet_info
*pinfo
, proto_tree
*tree
)
251 proto_tree_add_item_ret_uint(tree
, hf_mdb_ack
, tvb
, offset
, 1,
252 ENC_BIG_ENDIAN
, &ack
);
253 col_set_str(pinfo
->cinfo
, COL_INFO
,
254 val_to_str_const(ack
, mdb_ack
, "Invalid ack byte"));
257 static void mdb_set_addrs(uint8_t event
, uint8_t addr
, packet_info
*pinfo
)
259 const char *periph
= val_to_str(addr
, mdb_addr
, "Unknown (0x%02x)");
261 /* pinfo->p2p_dir is from the perspective of the master (VMC) */
263 if (event
== MDB_EVT_DATA_MST_PER
) {
264 set_address(&pinfo
->src
, AT_STRINGZ
, (int)strlen(ADDR_VMC
)+1, ADDR_VMC
);
265 set_address(&pinfo
->dst
, AT_STRINGZ
, (int)strlen(periph
)+1, periph
);
266 pinfo
->p2p_dir
= P2P_DIR_SENT
;
268 else if (event
== MDB_EVT_DATA_PER_MST
) {
269 set_address(&pinfo
->src
, AT_STRINGZ
, (int)strlen(periph
)+1, periph
);
270 set_address(&pinfo
->dst
, AT_STRINGZ
, (int)strlen(ADDR_VMC
)+1, ADDR_VMC
);
271 pinfo
->p2p_dir
= P2P_DIR_RECV
;
275 static void dissect_mdb_cl_setup(tvbuff_t
*tvb
, int offset
,
276 packet_info
*pinfo
, proto_tree
*tree
)
278 uint32_t sub_cmd
, price
;
282 proto_tree_add_item_ret_uint(tree
, hf_mdb_cl_setup_sub
,
283 tvb
, offset
, 1, ENC_BIG_ENDIAN
, &sub_cmd
);
284 s
= try_val_to_str(sub_cmd
, mdb_cl_setup_sub_cmd
);
286 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
291 case MDB_CL_SETUP_CFG_DATA
:
292 proto_tree_add_item(tree
, hf_mdb_cl_feat_lvl
, tvb
, offset
, 1,
295 proto_tree_add_item(tree
, hf_mdb_cl_cols
, tvb
, offset
, 1,
298 proto_tree_add_item(tree
, hf_mdb_cl_rows
, tvb
, offset
, 1,
301 proto_tree_add_item(tree
, hf_mdb_cl_disp_info
, tvb
, offset
, 1,
305 case MDB_CL_SETUP_MAX_MIN
:
306 if (tvb_reported_length_remaining(tvb
, offset
) == 5) {
307 /* This is the "default version" of Max/Min Prices. */
309 /* XXX - convert the scaled prices into actual amounts */
310 price
= tvb_get_ntohs(tvb
, offset
);
311 pi
= proto_tree_add_uint_format(tree
, hf_mdb_cl_max_price
,
312 tvb
, offset
, 2, price
, "Maximum price: 0x%04x", price
);
313 if (price
== 0xFFFF) {
314 proto_item_append_text(pi
, " (unknown)");
318 price
= tvb_get_ntohs(tvb
, offset
);
319 pi
= proto_tree_add_uint_format(tree
, hf_mdb_cl_min_price
,
320 tvb
, offset
, 2, price
, "Minimum price: 0x%04x", price
);
321 if (price
== 0x0000) {
322 proto_item_append_text(pi
, " (unknown)");
325 else if (tvb_reported_length_remaining(tvb
, offset
) == 11) {
326 /* This is the "expanded currency version" of Max/Min Prices. */
328 proto_tree_add_item(tree
, hf_mdb_cl_max_price
, tvb
, offset
, 4,
331 proto_tree_add_item(tree
, hf_mdb_cl_min_price
, tvb
, offset
, 4,
334 /* XXX - expert info for other lengths */
339 static void dissect_mdb_cl_vend(tvbuff_t
*tvb
, int offset
,
340 packet_info
*pinfo
, proto_tree
*tree
)
342 uint32_t sub_cmd
, price
, item
;
345 proto_tree_add_item_ret_uint(tree
, hf_mdb_cl_vend_sub
, tvb
, offset
, 1,
346 ENC_BIG_ENDIAN
, &sub_cmd
);
347 s
= try_val_to_str(sub_cmd
, mdb_cl_vend_sub_cmd
);
349 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
354 case MDB_CL_VEND_REQ
:
355 if (tvb_reported_length_remaining(tvb
, offset
) == 5) {
356 proto_tree_add_item_ret_uint(tree
, hf_mdb_cl_item_price
, tvb
,
357 offset
, 2, ENC_BIG_ENDIAN
, &price
);
359 proto_tree_add_item_ret_uint(tree
, hf_mdb_cl_item_num
, tvb
,
360 offset
, 2, ENC_BIG_ENDIAN
, &item
);
361 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " (item %d, price %d)",
364 /* XXX - dissect the longer request in Expanded Currency Mode */
366 case MDB_CL_VEND_SUC
:
367 proto_tree_add_item(tree
, hf_mdb_cl_item_num
, tvb
, offset
, 2,
374 dissect_mdb_cl_id_fields(tvbuff_t
*tvb
, int offset
, proto_tree
*tree
)
376 proto_tree_add_item(tree
, hf_mdb_cl_manuf_code
, tvb
, offset
, 3, ENC_ASCII
);
378 proto_tree_add_item(tree
, hf_mdb_cl_ser_num
, tvb
, offset
, 12, ENC_ASCII
);
380 proto_tree_add_item(tree
, hf_mdb_cl_mod_num
, tvb
, offset
, 12, ENC_ASCII
);
382 /* XXX - dissect the Software Version bytes */
388 static void dissect_mdb_cl_expns(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
394 proto_tree_add_item_ret_uint(tree
, hf_mdb_cl_expns_sub
,
395 tvb
, offset
, 1, ENC_BIG_ENDIAN
, &sub_cmd
);
396 s
= try_val_to_str(sub_cmd
, mdb_cl_expns_sub_cmd
);
398 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
403 case MDB_CL_EXPNS_REQ_ID
:
404 dissect_mdb_cl_id_fields(tvb
, offset
, tree
);
406 case MDB_CL_EXPNS_OPT_ENA
:
407 /* XXX - add a bitmask for the Optional Feature Bits */
408 proto_tree_add_item(tree
, hf_mdb_cl_opt_feat
, tvb
, offset
, 4,
414 static void dissect_mdb_cl_rd_cfg_data(tvbuff_t
*tvb
, int offset
,
415 packet_info
*pinfo _U_
, proto_tree
*tree
)
417 proto_tree_add_item(tree
, hf_mdb_cl_feat_lvl
, tvb
, offset
, 1,
420 /* XXX - dissect Country/Currency Code */
422 proto_tree_add_item(tree
, hf_mdb_cl_scale
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
424 proto_tree_add_item(tree
, hf_mdb_cl_dec_pl
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
426 proto_tree_add_item(tree
, hf_mdb_cl_max_rsp_time
, tvb
, offset
, 1,
427 ENC_TIME_SECS
| ENC_BIG_ENDIAN
);
430 static void dissect_mdb_mst_per_cl( tvbuff_t
*tvb
, int offset
, int len _U_
,
431 packet_info
*pinfo
, proto_tree
*tree
, proto_item
*cmd_it
,
434 uint8_t cmd
= addr_byte
& 0x07; /* the 3-bit command */
439 s
= val_to_str_const(cmd
, mdb_cl_cmd
, "Unknown");
440 proto_item_append_text(cmd_it
, " (%s)", s
);
441 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
443 cl_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, len
, ett_mdb_cl
,
448 case MDB_CL_CMD_SETUP
:
449 dissect_mdb_cl_setup(tvb
, offset
, pinfo
, cl_tree
);
451 case MDB_CL_CMD_VEND
:
452 dissect_mdb_cl_vend(tvb
, offset
, pinfo
, cl_tree
);
454 case MDB_CL_CMD_READER
:
455 proto_tree_add_item_ret_uint(cl_tree
, hf_mdb_cl_reader_sub
,
456 tvb
, offset
, 1, ENC_BIG_ENDIAN
, &sub_cmd
);
457 s
= try_val_to_str(sub_cmd
, mdb_cl_reader_sub_cmd
);
459 case MDB_CL_CMD_EXPNS
:
460 dissect_mdb_cl_expns(tvb
, offset
, pinfo
, cl_tree
);
464 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
467 static void dissect_mdb_per_mst_cl( tvbuff_t
*tvb
, int offset
,
468 int len _U_
, packet_info
*pinfo
, proto_tree
*tree
)
473 cl_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, len
, ett_mdb_cl
,
476 proto_tree_add_item_ret_uint(cl_tree
, hf_mdb_cl_resp
, tvb
, offset
, 1,
477 ENC_BIG_ENDIAN
, &cl_resp
);
478 col_set_str(pinfo
->cinfo
,
479 COL_INFO
, val_to_str_const(cl_resp
, mdb_cl_resp
, "Unknown"));
483 case MDB_CL_RESP_RD_CFG_DATA
:
484 dissect_mdb_cl_rd_cfg_data(tvb
, offset
, pinfo
, cl_tree
);
486 case MDB_CL_RESP_VEND_APRV
:
487 if (tvb_reported_length_remaining(tvb
, offset
) == 3) {
488 proto_tree_add_item(cl_tree
, hf_mdb_cl_vend_amt
, tvb
, offset
,
491 /* XXX - dissect the longer response in Expanded Currency Mode */
493 case MDB_CL_RESP_PER_ID
:
494 dissect_mdb_cl_id_fields(tvb
, offset
, tree
);
495 /* XXX - check if we have Optional Feature Bits */
500 static void dissect_mdb_cgw_report(tvbuff_t
*tvb
, int offset
,
501 packet_info
*pinfo
, proto_tree
*tree
)
506 proto_tree_add_item_ret_uint(tree
, hf_mdb_cgw_report_sub
,
507 tvb
, offset
, 1, ENC_BIG_ENDIAN
, &sub_cmd
);
508 s
= try_val_to_str(sub_cmd
, mdb_cgw_report_sub_cmd
);
510 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
515 case MDB_CGW_REPORT_DTS_EVT
:
516 proto_tree_add_item(tree
, hf_mdb_cgw_dts_evt_code
, tvb
, offset
, 10,
519 /* XXX - dissect Date */
521 /* XXX - dissect Time */
523 proto_tree_add_item(tree
, hf_mdb_cgw_duration
, tvb
, offset
, 4,
526 proto_tree_add_item(tree
, hf_mdb_cgw_activity
, tvb
, offset
, 1,
532 static void dissect_mdb_cgw_expns(tvbuff_t
*tvb
, int offset
,
533 packet_info
*pinfo
, proto_tree
*tree
)
538 proto_tree_add_item_ret_uint(tree
, hf_mdb_cgw_expns_sub
,
539 tvb
, offset
, 1, ENC_BIG_ENDIAN
, &sub_cmd
);
540 s
= try_val_to_str(sub_cmd
, mdb_cgw_expns_sub_cmd
);
542 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
547 case MDB_CGW_EXPNS_FEAT_ENA
:
548 proto_tree_add_item(tree
, hf_mdb_cgw_opt_feat
, tvb
, offset
, 4,
554 static void dissect_mdb_mst_per_cgw( tvbuff_t
*tvb
, int offset
, int len
,
555 packet_info
*pinfo
, proto_tree
*tree
, proto_item
*cmd_it
,
556 uint8_t addr_cmd_byte
)
558 proto_tree
*cgw_tree
;
561 s
= val_to_str_const(addr_cmd_byte
, mdb_cgw_addr_cmd
, "Unknown");
562 proto_item_append_text(cmd_it
, " (%s)", s
);
563 col_set_str(pinfo
->cinfo
, COL_INFO
, s
);
565 cgw_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, len
, ett_mdb_cgw
,
566 NULL
, "Communications Gateway");
568 switch (addr_cmd_byte
) {
569 case MDB_CGW_ADDR_CMD_SETUP
:
570 proto_tree_add_item(cgw_tree
, hf_mdb_cgw_feat_lvl
, tvb
, offset
, 1,
573 proto_tree_add_item(cgw_tree
, hf_mdb_cgw_scale
, tvb
, offset
, 1,
576 proto_tree_add_item(cgw_tree
, hf_mdb_cgw_dec_pl
, tvb
, offset
, 1,
579 case MDB_CGW_ADDR_CMD_REPORT
:
580 dissect_mdb_cgw_report(tvb
, offset
, pinfo
, cgw_tree
);
582 case MDB_CGW_ADDR_CMD_EXPNS
:
583 dissect_mdb_cgw_expns(tvb
, offset
, pinfo
, cgw_tree
);
588 static void dissect_mdb_per_mst_cgw( tvbuff_t
*tvb
, int offset
,
589 int len
, packet_info
*pinfo _U_
, proto_tree
*tree
)
591 proto_tree
*cgw_tree
;
594 cgw_tree
= proto_tree_add_subtree(tree
, tvb
, offset
, len
, ett_mdb_cgw
,
595 NULL
, "Communications Gateway");
597 proto_tree_add_item_ret_uint(cgw_tree
, hf_mdb_cgw_resp
, tvb
, offset
, 1,
598 ENC_BIG_ENDIAN
, &cgw_resp
);
599 col_set_str(pinfo
->cinfo
,
600 COL_INFO
, val_to_str_const(cgw_resp
, mdb_cgw_resp
, "Unknown"));
604 case MDB_CGW_RESP_CFG
:
605 proto_tree_add_item(cgw_tree
, hf_mdb_cgw_feat_lvl
, tvb
, offset
, 1,
608 proto_tree_add_item(cgw_tree
, hf_mdb_cgw_max_rsp_time
, tvb
, offset
,
609 2, ENC_TIME_SECS
| ENC_BIG_ENDIAN
);
611 case MDB_CGW_RESP_PER_ID
:
612 proto_tree_add_item(tree
, hf_mdb_cgw_manuf_code
, tvb
, offset
, 3,
615 proto_tree_add_item(tree
, hf_mdb_cgw_ser_num
, tvb
, offset
, 12,
618 proto_tree_add_item(tree
, hf_mdb_cgw_mod_num
, tvb
, offset
, 12,
621 /* XXX - dissect the Software Version bytes */
623 proto_tree_add_item(tree
, hf_mdb_cgw_opt_feat
, tvb
, offset
, 4,
629 static void dissect_mdb_mst_per(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
632 uint8_t addr_byte
, addr
;
637 mst_per_len
= tvb_reported_length_remaining(tvb
, offset
);
638 if (mst_per_len
<= 0) {
639 expert_add_info(pinfo
, tree
, &ei_mdb_short_packet
);
643 if (mst_per_len
== 1) {
644 dissect_mdb_ack(tvb
, offset
, pinfo
, tree
);
649 * Our packet has one address byte, an optional data block and one
653 data_len
= mst_per_len
- 2;
656 * The address byte is 5-bit address | 3-bit command.
658 * The specification uses 8-bit addresses which are the address byte
659 * with the three lowest bits set to 0.
661 * The commands are defined as the complete address byte (i.e. they
662 * include the address part). This does not make much sense: Cashless #1
663 * and #2 have different addresses but exactly the same 3-bit commands.
665 * In this dissector, we try to use the same values as the specification.
667 addr_byte
= tvb_get_uint8(tvb
, offset
);
668 addr
= addr_byte
& 0xF8;
669 proto_tree_add_uint_bits_format_value(tree
, hf_mdb_addr
,
670 tvb
, 8*offset
, 5, addr
, ENC_BIG_ENDIAN
, "0x%02x", addr
);
671 cmd_it
= proto_tree_add_uint(tree
, hf_mdb_cmd
, tvb
, offset
, 1, addr_byte
);
672 mdb_set_addrs(MDB_EVT_DATA_MST_PER
, addr
, pinfo
);
676 * We call the peripheral functions even if data_len == 0 so they can fix
677 * up the command with peripheral-specific info.
681 dissect_mdb_mst_per_cl(tvb
, offset
, data_len
, pinfo
, tree
,
685 dissect_mdb_mst_per_cgw(tvb
, offset
, data_len
, pinfo
, tree
,
690 proto_tree_add_item(tree
, hf_mdb_data
,
691 tvb
, offset
, data_len
, ENC_NA
);
697 /* XXX - verify the checksum */
698 proto_tree_add_item(tree
, hf_mdb_chk
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
701 static void dissect_mdb_per_mst(tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
702 proto_tree
*tree
, uint8_t addr
)
708 * A packet from peripheral to master is either a single ACK/NAK byte or
709 * a non-empty data block followed by one checksum byte.
712 per_mst_len
= tvb_reported_length_remaining(tvb
, offset
);
713 if (per_mst_len
<= 0) {
714 expert_add_info(pinfo
, tree
, &ei_mdb_short_packet
);
718 if (per_mst_len
== 1) {
719 dissect_mdb_ack(tvb
, offset
, pinfo
, tree
);
723 data_len
= per_mst_len
- 1;
726 dissect_mdb_per_mst_cl(tvb
, offset
, data_len
, pinfo
, tree
);
729 dissect_mdb_per_mst_cgw(tvb
, offset
, data_len
, pinfo
, tree
);
732 proto_tree_add_item(tree
, hf_mdb_data
, tvb
, offset
, data_len
, ENC_NA
);
737 /* XXX - verify the checksum */
738 proto_tree_add_item(tree
, hf_mdb_chk
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
741 static int dissect_mdb(tvbuff_t
*tvb
,
742 packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
744 int offset
= 0, offset_ver
, offset_evt
;
745 uint8_t version
, event
, addr
;
746 proto_tree
*mdb_tree
, *hdr_tree
;
747 proto_item
*tree_ti
, *hdr_ti
;
749 /* We need at least the shortest possible pseudo header. */
750 if (tvb_captured_length(tvb
) < 3)
754 version
= tvb_get_uint8(tvb
, offset
++);
759 event
= tvb_get_uint8(tvb
, offset
++);
760 if (!try_val_to_str(event
, mdb_event
))
763 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "MDB");
764 col_clear(pinfo
->cinfo
, COL_INFO
);
766 tree_ti
= proto_tree_add_protocol_format(tree
, proto_mdb
,
767 tvb
, 0, tvb_reported_length(tvb
), "MDB");
768 mdb_tree
= proto_item_add_subtree(tree_ti
, ett_mdb
);
770 hdr_tree
= proto_tree_add_subtree(mdb_tree
, tvb
, 0, -1, ett_mdb_hdr
,
771 &hdr_ti
, "Pseudo header");
773 proto_tree_add_item(hdr_tree
, hf_mdb_hdr_ver
,
774 tvb
, offset_ver
, 1, ENC_BIG_ENDIAN
);
775 proto_tree_add_item(hdr_tree
, hf_mdb_event
,
776 tvb
, offset_evt
, 1, ENC_BIG_ENDIAN
);
778 /* Packets from peripheral to master always have an address byte in their
780 if (event
== MDB_EVT_DATA_PER_MST
) {
781 /* See the comment in dissect_mdb_mst_per about MDB addresses. */
782 addr
= tvb_get_uint8(tvb
, offset
) & 0xF8;
783 proto_tree_add_uint_bits_format_value(hdr_tree
, hf_mdb_addr
,
784 tvb
, 8*offset
, 5, addr
, ENC_BIG_ENDIAN
, "0x%02x", addr
);
786 mdb_set_addrs(event
, addr
, pinfo
);
789 /* We're now at the end of the pseudo header. */
790 proto_item_set_len(hdr_ti
, offset
);
792 if (event
== MDB_EVT_BUS_RESET
)
795 if (event
== MDB_EVT_DATA_MST_PER
)
796 dissect_mdb_mst_per(tvb
, offset
, pinfo
, mdb_tree
);
797 else if (event
== MDB_EVT_DATA_PER_MST
)
798 dissect_mdb_per_mst(tvb
, offset
, pinfo
, mdb_tree
, addr
);
800 return tvb_reported_length(tvb
);
803 void proto_register_mdb(void)
805 expert_module_t
* expert_mdb
;
807 static int *ett
[] = {
814 static hf_register_info hf
[] = {
816 { "Version", "mdb.hdr_ver",
817 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
820 { "Event", "mdb.event",
821 FT_UINT8
, BASE_HEX
, VALS(mdb_event
), 0, NULL
, HFILL
}
824 { "Address", "mdb.addr",
825 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
828 { "Command", "mdb.cmd",
829 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
831 { &hf_mdb_cl_setup_sub
,
832 { "Sub-command", "mdb.cashless.setup_sub_cmd",
833 FT_UINT8
, BASE_HEX
, VALS(mdb_cl_setup_sub_cmd
), 0, NULL
, HFILL
}
835 { &hf_mdb_cl_feat_lvl
,
836 { "Feature level", "mdb.cashless.feature_level",
837 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
840 { "Columns on display", "mdb.cashless.columns",
841 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
844 { "Rows on display", "mdb.cashless.rows",
845 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
847 { &hf_mdb_cl_disp_info
,
848 { "Display information", "mdb.cashless.disp_info",
849 FT_UINT8
, BASE_HEX
, NULL
, 0x07, NULL
, HFILL
}
851 { &hf_mdb_cl_max_price
,
852 { "Maximum price", "mdb.cashless.max_price",
853 FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
855 { &hf_mdb_cl_min_price
,
856 { "Minimum price", "mdb.cashless.min_price",
857 FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
859 { &hf_mdb_cl_vend_sub
,
860 { "Sub-command", "mdb.cashless.vend_sub_cmd",
861 FT_UINT8
, BASE_HEX
, VALS(mdb_cl_vend_sub_cmd
), 0, NULL
, HFILL
}
863 { &hf_mdb_cl_item_price
,
864 { "Item Price", "mdb.cashless.item_price",
865 FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
867 { &hf_mdb_cl_item_num
,
868 { "Item Number", "mdb.cashless.item_number",
869 FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
871 { &hf_mdb_cl_reader_sub
,
872 { "Sub-command", "mdb.cashless.reader_sub_cmd",
873 FT_UINT8
, BASE_HEX
, VALS(mdb_cl_reader_sub_cmd
), 0, NULL
, HFILL
}
876 { "Response", "mdb.cashless.resp",
877 FT_UINT8
, BASE_HEX
, VALS(mdb_cl_resp
), 0, NULL
, HFILL
}
880 { "Scale factor", "mdb.cashless.scale_factor",
881 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
884 { "Decimal places", "mdb.cashless.decimal_places",
885 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
887 { &hf_mdb_cl_max_rsp_time
,
888 { "Application maximum response time", "mdb.cashless.max_rsp_time",
889 FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
891 { &hf_mdb_cl_vend_amt
,
892 { "Vend Amount", "mdb.cashless.vend_amount",
893 FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
895 { &hf_mdb_cl_expns_sub
,
896 { "Sub-command", "mdb.cashless.expansion_sub_cmd",
897 FT_UINT8
, BASE_HEX
, VALS(mdb_cl_expns_sub_cmd
), 0, NULL
, HFILL
}
899 { &hf_mdb_cl_manuf_code
,
900 { "Manufacturer Code", "mdb.cashless.manuf_code",
901 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
903 { &hf_mdb_cl_ser_num
,
904 { "Serial Number", "mdb.cashless.serial_number",
905 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
907 { &hf_mdb_cl_mod_num
,
908 { "Model Number", "mdb.cashless.model_number",
909 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
911 { &hf_mdb_cl_opt_feat
,
912 { "Optional Feature Bits", "mdb.cashless.opt_feature_bits",
913 FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
915 { &hf_mdb_cgw_feat_lvl
,
916 { "Feature level", "mdb.comms_gw.feature_level",
917 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
920 { "Scale factor", "mdb.comms_gw.scale_factor",
921 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
923 { &hf_mdb_cgw_dec_pl
,
924 { "Decimal places", "mdb.comms_gw.decimal_places",
925 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
928 { "Response", "mdb.comms_gw.resp",
929 FT_UINT8
, BASE_HEX
, VALS(mdb_cgw_resp
), 0, NULL
, HFILL
}
931 { &hf_mdb_cgw_max_rsp_time
,
932 { "Application maximum response time", "mdb.comms_gw.max_rsp_time",
933 FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
935 { &hf_mdb_cgw_report_sub
,
936 { "Sub-command", "mdb.comms_gw.report_sub_cmd", FT_UINT8
,
937 BASE_HEX
, VALS(mdb_cgw_report_sub_cmd
), 0, NULL
, HFILL
}
939 { &hf_mdb_cgw_dts_evt_code
,
940 { "DTS Event Code", "mdb.comms_gw.dts_event_code",
941 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
943 { &hf_mdb_cgw_duration
,
944 { "Duration", "mdb.comms_gw.duration",
945 FT_UINT32
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
947 { &hf_mdb_cgw_activity
,
948 { "Activity", "mdb.comms_gw.activity",
949 FT_BOOLEAN
, 8, TFS(&tfs_active_inactive
), 0x1, NULL
, HFILL
}
951 { &hf_mdb_cgw_expns_sub
,
952 { "Sub-command", "mdb.comms_gw.expansion_sub_cmd", FT_UINT8
,
953 BASE_HEX
, VALS(mdb_cgw_expns_sub_cmd
), 0, NULL
, HFILL
}
955 { &hf_mdb_cgw_opt_feat
,
956 { "Optional Feature Bits", "mdb.comms_gw.opt_feature_bits",
957 FT_UINT32
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
959 { &hf_mdb_cgw_manuf_code
,
960 { "Manufacturer Code", "mdb.comms_gw.manuf_code",
961 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
963 { &hf_mdb_cgw_ser_num
,
964 { "Serial Number", "mdb.comms_gw.serial_number",
965 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
967 { &hf_mdb_cgw_mod_num
,
968 { "Model Number", "mdb.comms_gw.model_number",
969 FT_STRING
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
972 { "Ack byte", "mdb.ack",
973 FT_UINT8
, BASE_HEX
, VALS(mdb_ack
), 0, NULL
, HFILL
}
976 { "Data", "mdb.data",
977 FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
980 { "Checksum", "mdb.chk",
981 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
985 static ei_register_info ei
[] = {
986 { &ei_mdb_short_packet
,
987 { "mdb.short_packet", PI_PROTOCOL
, PI_ERROR
,
988 "MDB packet without payload", EXPFILL
}}
991 proto_mdb
= proto_register_protocol("Multi-Drop Bus", "MDB", "mdb");
992 proto_register_subtree_array(ett
, array_length(ett
));
993 proto_register_field_array(proto_mdb
, hf
, array_length(hf
));
994 expert_mdb
= expert_register_protocol(proto_mdb
);
995 expert_register_field_array(expert_mdb
, ei
, array_length(ei
));
996 mdb_handle
= register_dissector("mdb", dissect_mdb
, proto_mdb
);
999 void proto_reg_handoff_mdb(void)
1001 dissector_add_uint("wtap_encap", WTAP_ENCAP_MDB
, mdb_handle
);
1005 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1010 * indent-tabs-mode: nil
1013 * vi: set shiftwidth=4 tabstop=8 expandtab:
1014 * :indentSize=4:tabSize=8:noTabs=true: