2 * Routines for DisplayPort AUX-Channel dissection
3 * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <conversation.h>
15 #include <epan/packet.h>
16 #include <epan/proto_data.h>
18 #include "packet-dpaux.h"
20 void proto_register_dpaux(void);
22 static int proto_dpaux
;
24 static int hf_dpaux_transaction_type
;
25 static int hf_dpaux_native_req_cmd
;
26 static int hf_dpaux_i2c_req_cmd
;
27 static int hf_dpaux_reply_cmd
;
28 static int hf_dpaux_mot
;
29 static int hf_dpaux_addr
;
30 static int hf_dpaux_len
;
31 static int hf_dpaux_data
;
33 static int hf_dpaux_reg_addr
;
36 static int hf_00000_MINOR
;
37 static int hf_00000_MAJOR
;
38 static int * const reg00000_fields
[] = {
45 static int hf_00001_MAX_LINK_RATE
;
46 static int * const reg00001_fields
[] = {
47 &hf_00001_MAX_LINK_RATE
,
52 static int hf_00002_MAX_LANE_COUNT
;
53 static int hf_00002_POST_LT_ADJ_REQ_SUPPORTED
;
54 static int hf_00002_TPS3_SUPPORTED
;
55 static int hf_00002_ENHANCED_FRAME_CAP
;
56 static int * const reg00002_fields
[] = {
57 &hf_00002_MAX_LANE_COUNT
,
58 &hf_00002_POST_LT_ADJ_REQ_SUPPORTED
,
59 &hf_00002_TPS3_SUPPORTED
,
60 &hf_00002_ENHANCED_FRAME_CAP
,
65 static int hf_00003_MAX_DOWNSPREAD
;
66 static int hf_00003_NO_AUX_TRANSACTION_LINK_TRAINING
;
67 static int hf_00003_TPS4_SUPPORTED
;
68 static int * const reg00003_fields
[] = {
69 &hf_00003_MAX_DOWNSPREAD
,
70 &hf_00003_NO_AUX_TRANSACTION_LINK_TRAINING
,
71 &hf_00003_TPS4_SUPPORTED
,
76 static int hf_00004_NORP
;
77 static int hf_00004_5V_DP_PWR_CAP
;
78 static int hf_00004_12V_DP_PWR_CAP
;
79 static int hf_00004_18V_DP_PWR_CAP
;
80 static int * const reg00004_fields
[] = {
82 &hf_00004_5V_DP_PWR_CAP
,
83 &hf_00004_12V_DP_PWR_CAP
,
84 &hf_00004_18V_DP_PWR_CAP
,
88 /* Initialize the subtree pointers */
90 static int ett_register
;
92 struct dpaux_transaction
{
98 DPAUX_TRANSACTION_NATIVE
,
99 DPAUX_TRANSACTION_I2C_OVER_AUX
,
100 DPAUX_TRANSACTION_N_A
,
104 DPAUX_REPLY_CODE_ACK
= 0x0,
105 DPAUX_REPLY_CODE_I2C_ACK
= 0x0,
106 DPAUX_REPLY_CODE_NACK
= 0x1,
107 DPAUX_REPLY_CODE_DEFER
= 0x2,
108 DPAUX_REPLY_CODE_I2C_NACK
= 0x4,
109 DPAUX_REPLY_CODE_I2C_DEFER
= 0x8,
113 DPAUX_REGISTER_TYPE_BITFIELD
,
116 struct bitfield_data
{
121 struct dpaux_register
{
125 struct bitfield_data bitfield
;
129 static struct dpaux_register registers
[] = {
130 { 0x0, DPAUX_REGISTER_TYPE_BITFIELD
, .data
.bitfield
= { &hf_00000
, reg00000_fields
} },
131 { 0x1, DPAUX_REGISTER_TYPE_BITFIELD
, .data
.bitfield
= { &hf_00001
, reg00001_fields
} },
132 { 0x2, DPAUX_REGISTER_TYPE_BITFIELD
, .data
.bitfield
= { &hf_00002
, reg00002_fields
} },
133 { 0x3, DPAUX_REGISTER_TYPE_BITFIELD
, .data
.bitfield
= { &hf_00003
, reg00003_fields
} },
134 { 0x4, DPAUX_REGISTER_TYPE_BITFIELD
, .data
.bitfield
= { &hf_00004
, reg00004_fields
} },
138 dissect_dpaux_register(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
,
139 unsigned int offset
, unsigned int register_addr
)
142 struct dpaux_register
*reg
= NULL
;
144 for (k
= 0; k
< G_N_ELEMENTS(registers
); ++k
) {
145 if (registers
[k
].addr
== register_addr
) {
155 case DPAUX_REGISTER_TYPE_BITFIELD
:
156 proto_tree_add_bitmask_with_flags(tree
, tvb
, offset
,
157 *reg
->data
.bitfield
.hf
, 0,
158 reg
->data
.bitfield
.fields
,
159 ENC_BIG_ENDIAN
, BMT_NO_FLAGS
);
167 dissect_dpaux_from_source(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
169 uint8_t type
= tvb_get_bits8(tvb
, 0, 1);
170 uint8_t mot
= tvb_get_bits8(tvb
, 1, 1);
171 uint8_t cmd
= tvb_get_bits8(tvb
, 2, 2);
172 uint32_t addr
= tvb_get_bits32(tvb
, 4, 20, ENC_BIG_ENDIAN
);
173 uint8_t len
= tvb_get_uint8(tvb
, 3) + 1;
174 bool is_read
= cmd
& 0x1;
176 conversation_t
*conversation
= NULL
;
177 struct dpaux_transaction
*transaction
= NULL
;
179 conversation
= conversation_new(pinfo
->num
, &pinfo
->src
, &pinfo
->dst
,
180 CONVERSATION_NONE
, pinfo
->srcport
, pinfo
->destport
, 0);
182 transaction
= wmem_new(wmem_file_scope(), struct dpaux_transaction
);
183 transaction
->is_native
= type
;
184 transaction
->addr
= addr
;
186 conversation_add_proto_data(conversation
, proto_dpaux
, (void *)transaction
);
188 proto_tree_add_uint(tree
, hf_dpaux_transaction_type
, tvb
, 0, 0,
189 type
? DPAUX_TRANSACTION_NATIVE
: DPAUX_TRANSACTION_I2C_OVER_AUX
);
191 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
,
192 transaction
->is_native
? "Native" : "I2C-over-AUX");
193 col_set_str(pinfo
->cinfo
, COL_INFO
, is_read
? "RD" : "WR");
194 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %u byte%s %s 0x%05x",
195 len
, len
> 1 ? "s" : "", is_read
? "FROM" : "TO", addr
);
197 if (transaction
->is_native
) {
198 proto_tree_add_uint(tree
, hf_dpaux_native_req_cmd
, tvb
, 0, 1, cmd
);
200 proto_tree_add_uint(tree
, hf_dpaux_i2c_req_cmd
, tvb
, 0, 1, cmd
);
201 proto_tree_add_boolean(tree
, hf_dpaux_mot
, tvb
, 0, 1, mot
);
203 proto_tree_add_uint(tree
, hf_dpaux_addr
, tvb
, 0, 3, addr
);
204 proto_tree_add_uint(tree
, hf_dpaux_len
, tvb
, 3, 1, len
);
208 proto_tree_add_item(tree
, hf_dpaux_data
, tvb
, 4, len
, ENC_NA
);
214 dissect_dpaux_from_sink(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
216 uint8_t cmd
= tvb_get_bits8(tvb
, 2, 2);
217 uint8_t len
= (tvb_reported_length(tvb
) > 1) ? tvb_reported_length(tvb
) -1 : 0;
218 conversation_t
*conversation
= NULL
;
219 struct dpaux_transaction
*transaction
= NULL
;
222 conversation
= find_conversation(pinfo
->num
, &pinfo
->src
, &pinfo
->dst
,
223 CONVERSATION_NONE
, pinfo
->srcport
, pinfo
->destport
, 0);
225 transaction
= (struct dpaux_transaction
*)conversation_get_proto_data(
226 conversation
, proto_dpaux
);
229 proto_tree_add_uint(tree
, hf_dpaux_transaction_type
, tvb
, 0, 0,
230 transaction
->is_native
? DPAUX_TRANSACTION_NATIVE
:
231 DPAUX_TRANSACTION_I2C_OVER_AUX
);
232 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
,
233 transaction
->is_native
? "Native" : "I2C-over-AUX");
235 proto_tree_add_uint(tree
, hf_dpaux_transaction_type
, tvb
, 0, 0, DPAUX_TRANSACTION_N_A
);
236 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "N/A");
240 case DPAUX_REPLY_CODE_ACK
:
241 col_set_str(pinfo
->cinfo
, COL_INFO
, "ACK");
243 case DPAUX_REPLY_CODE_NACK
:
244 case DPAUX_REPLY_CODE_I2C_NACK
:
245 col_set_str(pinfo
->cinfo
, COL_INFO
, "NACK");
247 case DPAUX_REPLY_CODE_DEFER
:
248 case DPAUX_REPLY_CODE_I2C_DEFER
:
249 col_set_str(pinfo
->cinfo
, COL_INFO
, "DEFER");
253 proto_tree_add_uint(tree
, hf_dpaux_reply_cmd
, tvb
, 0, 1, cmd
);
257 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " with %u byte%s FROM 0x%05x",
258 len
, len
> 1 ? "s" : "", transaction
->addr
);
259 proto_tree_add_uint(tree
, hf_dpaux_addr
, tvb
, 0, 3, transaction
->addr
);
261 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " with %u byte%s", len
,
264 proto_tree_add_uint(tree
, hf_dpaux_len
, tvb
, 3, 1, len
);
265 proto_tree_add_item(tree
, hf_dpaux_data
, tvb
, 1, len
, ENC_NA
);
267 if (transaction
&& transaction
->is_native
) {
270 for (k
= 0; k
< len
;) {
271 proto_tree
*register_tree
;
274 ti
= proto_tree_add_uint_format(tree
, hf_dpaux_reg_addr
,
276 transaction
->addr
+ k
,
277 "DPCD 0x%05x: 0x%02x",
278 transaction
->addr
+ k
,
279 tvb_get_uint8(tvb
, k
+ 1));
280 register_tree
= proto_item_add_subtree(ti
, ett_register
);
282 res
= dissect_dpaux_register(tvb
, pinfo
, register_tree
, k
+ 1,
283 transaction
->addr
+ k
);
285 k
+= (res
> 0) ? res
: 1;
294 dissect_dpaux(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
297 proto_tree
*dpaux_tree
;
298 bool from_source
= false;
299 struct dpaux_info
*dpaux_info
= (struct dpaux_info
*)data
;
301 if (dpaux_info
!= NULL
)
302 from_source
= dpaux_info
->from_source
;
304 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "dpaux");
305 col_set_str(pinfo
->cinfo
, COL_INFO
, "DisplayPort AUX channel");
306 col_set_str(pinfo
->cinfo
, COL_RES_DL_DST
, "N/A");
309 col_set_str(pinfo
->cinfo
, COL_RES_DL_SRC
, "DP-Source");
311 col_set_str(pinfo
->cinfo
, COL_RES_DL_SRC
, "DP-Sink");
313 /* create display subtree for the protocol */
314 ti
= proto_tree_add_item(tree
, proto_dpaux
, tvb
, 0, -1, ENC_NA
);
315 dpaux_tree
= proto_item_add_subtree(ti
, ett_dpaux
);
318 dissect_dpaux_from_source(tvb
, pinfo
, dpaux_tree
);
320 dissect_dpaux_from_sink(tvb
, pinfo
, dpaux_tree
);
322 return tvb_captured_length(tvb
);
325 /* Register the protocol with Wireshark.
327 * This format is required because a script is used to build the C function that
328 * calls all the protocol registration.
331 proto_register_dpaux(void)
333 static const value_string convert_transaction_type
[] = {
334 { DPAUX_TRANSACTION_NATIVE
, "Native" },
335 { DPAUX_TRANSACTION_I2C_OVER_AUX
, "I2C-over-AUX" },
336 { DPAUX_TRANSACTION_N_A
, "N/A," },
340 static const value_string convert_native_req_cmd
[] = {
346 static const value_string convert_i2c_req_cmd
[] = {
349 { 2, "Write_Status_Update_Request" },
353 static const value_string convert_reply_cmd
[] = {
357 { 1 << 2, "I2C NACK" },
358 { 2 << 2, "I2C DEFER" },
362 static const value_string convert_link_rate
[] = {
363 { 0x06, "1.62Gbps/lane" },
364 { 0x0a, "2.7Gbps/lane" },
365 { 0x14, "5.4Gbps/lane" },
366 { 0x1e, "8.1Gbps/lane" },
370 static const value_string convert_downspread
[] = {
372 { 0x01, "up to 0.5%" },
376 static const value_string convert_norp
[] = {
377 { 0x00, "One receiver port" },
378 { 0x01, "Two or more receiver ports" },
382 /* Setup protocol subtree array */
383 static int *ett
[] = {
388 static hf_register_info hf
[] = {
389 { &hf_dpaux_transaction_type
, { "Transaction type", "dpaux.transaction_type", FT_UINT8
, BASE_DEC
, VALS(convert_transaction_type
), 0, NULL
, HFILL
} },
390 { &hf_dpaux_native_req_cmd
, { "Native Request Command", "dpaux.native_req_cmd", FT_UINT8
, BASE_DEC
, VALS(convert_native_req_cmd
), 0, NULL
, HFILL
} },
391 { &hf_dpaux_i2c_req_cmd
, { "I2C over AUX Request Command", "dpaux.native_i2c_req_cmd", FT_UINT8
, BASE_DEC
, VALS(convert_i2c_req_cmd
), 0, NULL
, HFILL
} },
392 { &hf_dpaux_reply_cmd
, { "Reply Command", "dpaux.reply_cmd", FT_UINT8
, BASE_DEC
, VALS(convert_reply_cmd
), 0, NULL
, HFILL
} },
393 { &hf_dpaux_mot
, { "MOT (Middle-of-Transaction)", "dpaux.mot", FT_BOOLEAN
, BASE_NONE
, NULL
, 0, NULL
, HFILL
} },
394 { &hf_dpaux_addr
, { "Address", "dpaux.addr", FT_UINT24
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
395 { &hf_dpaux_len
, { "Data Length", "dpaux.len", FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
} },
396 { &hf_dpaux_data
, { "Data", "dpaux.data", FT_BYTES
, SEP_SPACE
, NULL
, 0, NULL
, HFILL
} },
397 { &hf_dpaux_reg_addr
, { "DPCD", "dpaux.reg", FT_UINT24
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
399 { &hf_00000
, { "DPCD_REV", "dpaux." "00000", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
400 { &hf_00000_MINOR
, { "MINOR", "dpaux." "00000" "_" "MINOR", FT_UINT8
, BASE_HEX
, NULL
, 0x0f, NULL
, HFILL
} },
401 { &hf_00000_MAJOR
, { "MAJOR", "dpaux." "00000" "_" "MAJOR", FT_UINT8
, BASE_HEX
, NULL
, 0xf0, NULL
, HFILL
} },
403 { &hf_00001
, { "MAX_LINK_RATE", "dpaux." "00001", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
404 { &hf_00001_MAX_LINK_RATE
, { "MAX_LINK_RATE", "dpaux." "00001" "_" "MAX_LINK_RATE", FT_UINT8
, BASE_HEX
, VALS(convert_link_rate
), 0xff, NULL
, HFILL
} },
406 { &hf_00002
, { "MAX_LANE_COUNT", "dpaux." "00002", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
407 { &hf_00002_MAX_LANE_COUNT
, { "MAX_LANE_COUNT", "dpaux." "00002" "_" "MAX_LANE_COUNT", FT_UINT8
, BASE_DEC
, NULL
, 0x0f, NULL
, HFILL
} },
408 { &hf_00002_POST_LT_ADJ_REQ_SUPPORTED
, { "POST_LT_ADJ_REQ_SUPPORTED", "dpaux." "00002" "_" "POST_LT_ADJ_REQ_SUPPORTED", FT_BOOLEAN
, 8, NULL
, 1<<5, NULL
, HFILL
} },
409 { &hf_00002_TPS3_SUPPORTED
, { "TPS3_SUPPORTED", "dpaux." "00002" "_" "TPS3_SUPPORTED", FT_BOOLEAN
, 8, NULL
, 1<<6, NULL
, HFILL
} },
410 { &hf_00002_ENHANCED_FRAME_CAP
, { "ENHANCED_FRAME_CAP", "dpaux." "00002" "_" "ENHANCED_FRAME_CAP", FT_BOOLEAN
, 8, NULL
, 1<<7, NULL
, HFILL
} },
412 { &hf_00003
, { "MAX_DOWNSPREAD", "dpaux." "00003", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
413 { &hf_00003_MAX_DOWNSPREAD
, { "MAX_DOWNSPREAD", "dpaux." "00003" "_" "MAX_DOWNSPREAD", FT_UINT8
, BASE_DEC
, VALS(convert_downspread
), 0x01, NULL
, HFILL
} },
414 { &hf_00003_NO_AUX_TRANSACTION_LINK_TRAINING
, { "NO_AUX_TRANSACTION_LINK_TRAINING", "dpaux." "00003" "_" "NO_AUX_TRANSACTION_LINK_TRAINING", FT_BOOLEAN
, 8, NULL
, 1<<6, NULL
, HFILL
} },
415 { &hf_00003_TPS4_SUPPORTED
, { "TPS4_SUPPORTED", "dpaux." "00003" "_" "TPS4_SUPPORTED", FT_BOOLEAN
, 8, NULL
, 1<<7, NULL
, HFILL
} },
417 { &hf_00004
, { "NORP & DP_PWR_VOLTAGE_CAP", "dpaux." "00004", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
} },
418 { &hf_00004_NORP
, { "NORP", "dpaux." "00004" "_" "NORP", FT_UINT8
, BASE_DEC
, convert_norp
, 0x01, NULL
, HFILL
} },
419 { &hf_00004_5V_DP_PWR_CAP
, { "5V_DP_PWR_CAP", "dpaux." "00004" "_" "5V_DP_PWR_CAP", FT_BOOLEAN
, 8, NULL
, 1<<5, NULL
, HFILL
} },
420 { &hf_00004_12V_DP_PWR_CAP
, { "12V_DP_PWR_CAP", "dpaux." "00004" "_" "12V_DP_PWR_CAP", FT_BOOLEAN
, 8, NULL
, 1<<6, NULL
, HFILL
} },
421 { &hf_00004_18V_DP_PWR_CAP
, { "18V_DP_PWR_CAP", "dpaux." "00004" "_" "18V_DP_PWR_CAP", FT_BOOLEAN
, 8, NULL
, 1<<7, NULL
, HFILL
} },
424 /* Register the protocol name and description */
425 proto_dpaux
= proto_register_protocol("DisplayPort AUX-Channel", "DPAUX", "dpaux");
426 register_dissector("dpaux", dissect_dpaux
, proto_dpaux
);
428 proto_register_field_array(proto_dpaux
, hf
, array_length(hf
));
429 proto_register_subtree_array(ett
, array_length(ett
));
434 * Editor modelines - https://www.wireshark.org/tools/modelines.html
439 * indent-tabs-mode: nil
442 * vi: set shiftwidth=4 tabstop=8 expandtab:
443 * :indentSize=4:tabSize=8:noTabs=true: