Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-dpaux.c
Line
Count
Source
1
/* packet-dpaux.c
2
 * Routines for DisplayPort AUX-Channel dissection
3
 * Copyright 2018, Dirk Eibach, Guntermann & Drunck GmbH <dirk.eibach@gdsys.cc>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
* SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include <config.h>
13
#include <conversation.h>
14
15
#include <epan/packet.h>
16
#include <epan/proto_data.h>
17
18
#include "packet-dpaux.h"
19
20
void proto_register_dpaux(void);
21
22
static int proto_dpaux;
23
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;
32
33
static int hf_dpaux_reg_addr;
34
35
static int hf_00000;
36
static int hf_00000_MINOR;
37
static int hf_00000_MAJOR;
38
static int * const reg00000_fields[] = {
39
    &hf_00000_MAJOR,
40
    &hf_00000_MINOR,
41
    NULL
42
};
43
44
static int hf_00001;
45
static int hf_00001_MAX_LINK_RATE;
46
static int * const reg00001_fields[] = {
47
    &hf_00001_MAX_LINK_RATE,
48
    NULL
49
};
50
51
static int hf_00002;
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,
61
    NULL
62
};
63
64
static int hf_00003;
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,
72
    NULL
73
};
74
75
static int hf_00004;
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[] = {
81
    &hf_00004_NORP,
82
    &hf_00004_5V_DP_PWR_CAP,
83
    &hf_00004_12V_DP_PWR_CAP,
84
    &hf_00004_18V_DP_PWR_CAP,
85
    NULL
86
};
87
88
/* Initialize the subtree pointers */
89
static int ett_dpaux;
90
static int ett_register;
91
92
struct dpaux_transaction {
93
    bool is_native;
94
    uint32_t addr;
95
};
96
97
enum {
98
    DPAUX_TRANSACTION_NATIVE,
99
    DPAUX_TRANSACTION_I2C_OVER_AUX,
100
    DPAUX_TRANSACTION_N_A,
101
};
102
103
enum {
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,
110
};
111
112
enum {
113
    DPAUX_REGISTER_TYPE_BITFIELD,
114
};
115
116
struct bitfield_data {
117
    int *hf;
118
    int * const *fields;
119
};
120
121
struct dpaux_register {
122
    uint32_t addr;
123
    uint8_t type;
124
    union {
125
        struct bitfield_data bitfield;
126
    } data;
127
};
128
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 } },
135
};
136
137
static int
138
dissect_dpaux_register(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
139
    unsigned int offset, unsigned int register_addr)
140
0
{
141
0
    unsigned int k;
142
0
    struct dpaux_register *reg = NULL;
143
144
0
    for (k = 0; k < G_N_ELEMENTS(registers); ++k) {
145
0
        if (registers[k].addr == register_addr) {
146
0
            reg = &registers[k];
147
0
            break;
148
0
        }
149
0
    }
150
151
0
    if (!reg)
152
0
        return -1;
153
154
0
    switch (reg->type) {
155
0
    case DPAUX_REGISTER_TYPE_BITFIELD:
156
0
        proto_tree_add_bitmask_with_flags(tree, tvb, offset,
157
0
                                          *reg->data.bitfield.hf, 0,
158
0
                                          reg->data.bitfield.fields,
159
0
                                          ENC_BIG_ENDIAN, BMT_NO_FLAGS);
160
0
        break;
161
0
    }
162
163
0
    return 1;
164
0
}
165
166
static int
167
dissect_dpaux_from_source(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
168
0
{
169
0
    uint8_t type = tvb_get_bits8(tvb, 0, 1);
170
0
    uint8_t mot = tvb_get_bits8(tvb, 1, 1);
171
0
    uint8_t cmd = tvb_get_bits8(tvb, 2, 2);
172
0
    uint32_t addr = tvb_get_bits32(tvb, 4, 20, ENC_BIG_ENDIAN);
173
0
    unsigned len = tvb_get_uint8(tvb, 3) + 1;
174
0
    bool is_read = cmd & 0x1;
175
176
    /* Remember the request for the reply, on the first pass only */
177
0
    if (!PINFO_FD_VISITED(pinfo)) {
178
0
        conversation_t *conversation;
179
0
        struct dpaux_transaction *transaction;
180
181
0
        conversation = conversation_new(pinfo->num,  &pinfo->src, &pinfo->dst,
182
0
            CONVERSATION_NONE, pinfo->srcport, pinfo->destport, 0);
183
184
0
        transaction = wmem_new(wmem_file_scope(), struct dpaux_transaction);
185
0
        transaction->is_native = type;
186
0
        transaction->addr = addr;
187
188
0
        conversation_add_proto_data(conversation, proto_dpaux, (void *)transaction);
189
0
    }
190
191
0
    proto_tree_add_uint(tree, hf_dpaux_transaction_type, tvb, 0, 0,
192
0
        type ? DPAUX_TRANSACTION_NATIVE : DPAUX_TRANSACTION_I2C_OVER_AUX);
193
194
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL,
195
0
                type ? "Native" : "I2C-over-AUX");
196
0
    col_set_str(pinfo->cinfo, COL_INFO, is_read ? "RD" : "WR");
197
0
    col_append_fstr(pinfo->cinfo, COL_INFO, " %u byte%s %s 0x%05x",
198
0
        len, len > 1 ? "s" : "", is_read ? "FROM" : "TO", addr);
199
200
0
    if (type) {
201
0
        proto_tree_add_uint(tree, hf_dpaux_native_req_cmd, tvb, 0, 1, cmd);
202
0
    } else {
203
0
        proto_tree_add_uint(tree, hf_dpaux_i2c_req_cmd, tvb, 0, 1, cmd);
204
0
        proto_tree_add_boolean(tree, hf_dpaux_mot, tvb, 0, 1, mot);
205
0
    }
206
0
    proto_tree_add_uint(tree, hf_dpaux_addr, tvb, 0, 3, addr);
207
0
    proto_tree_add_uint(tree, hf_dpaux_len, tvb, 3, 1, len);
208
209
210
0
    if (!is_read)
211
0
        proto_tree_add_item(tree, hf_dpaux_data, tvb, 4, len, ENC_NA);
212
213
0
    return 0;
214
0
}
215
216
static int
217
dissect_dpaux_from_sink(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
218
0
{
219
    /* Reply command: I2C-over-AUX reply in bits 7:6, AUX reply in bits 5:4 */
220
0
    uint8_t cmd = tvb_get_bits8(tvb, 0, 4);
221
0
    unsigned len = (tvb_reported_length(tvb) > 1) ? tvb_reported_length(tvb) -1 : 0;
222
0
    conversation_t *conversation = NULL;
223
0
    struct dpaux_transaction *transaction = NULL;
224
0
    proto_item *ti;
225
226
0
    conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst,
227
0
            CONVERSATION_NONE, pinfo->srcport, pinfo->destport, 0);
228
0
    if (conversation)
229
0
        transaction = (struct dpaux_transaction*)conversation_get_proto_data(
230
0
            conversation, proto_dpaux);
231
232
0
    if (transaction) {
233
0
        proto_tree_add_uint(tree, hf_dpaux_transaction_type, tvb, 0, 0,
234
0
                            transaction->is_native ? DPAUX_TRANSACTION_NATIVE :
235
0
                            DPAUX_TRANSACTION_I2C_OVER_AUX);
236
0
        col_set_str(pinfo->cinfo, COL_PROTOCOL,
237
0
                    transaction->is_native ? "Native" : "I2C-over-AUX");
238
0
    } else {
239
0
        proto_tree_add_uint(tree, hf_dpaux_transaction_type, tvb, 0, 0, DPAUX_TRANSACTION_N_A);
240
0
        col_set_str(pinfo->cinfo, COL_PROTOCOL, "N/A");
241
0
    }
242
243
0
    switch (cmd) {
244
0
    case DPAUX_REPLY_CODE_ACK:
245
0
        col_set_str(pinfo->cinfo, COL_INFO, "ACK");
246
0
        break;
247
0
    case DPAUX_REPLY_CODE_NACK:
248
0
    case DPAUX_REPLY_CODE_I2C_NACK:
249
0
        col_set_str(pinfo->cinfo, COL_INFO, "NACK");
250
0
        break;
251
0
    case DPAUX_REPLY_CODE_DEFER:
252
0
    case DPAUX_REPLY_CODE_I2C_DEFER:
253
0
        col_set_str(pinfo->cinfo, COL_INFO, "DEFER");
254
0
        break;
255
0
    };
256
257
0
    proto_tree_add_uint(tree, hf_dpaux_reply_cmd, tvb, 0, 1, cmd);
258
259
0
    if (len) {
260
0
        if (transaction) {
261
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " with %u byte%s FROM 0x%05x",
262
0
                            len, len > 1 ? "s" : "", transaction->addr);
263
0
            proto_tree_add_uint(tree, hf_dpaux_addr, tvb, 0, 3, transaction->addr);
264
0
        } else {
265
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " with %u byte%s", len,
266
0
                            len > 1 ? "s" : "");
267
0
        }
268
        /* A reply has no length field, the length is that of the data */
269
0
        ti = proto_tree_add_uint(tree, hf_dpaux_len, tvb, 0, 0, len);
270
0
        proto_item_set_generated(ti);
271
0
        proto_tree_add_item(tree, hf_dpaux_data, tvb, 1, len, ENC_NA);
272
273
0
        if (transaction && transaction->is_native) {
274
0
            unsigned int k;
275
276
0
            for (k = 0; k < len;) {
277
0
                proto_tree *register_tree;
278
0
                int res;
279
280
0
                ti = proto_tree_add_uint_format(tree, hf_dpaux_reg_addr,
281
0
                                                tvb, k + 1, 1,
282
0
                                                transaction->addr + k,
283
0
                                                "DPCD 0x%05x: 0x%02x",
284
0
                                                transaction->addr + k,
285
0
                                                tvb_get_uint8(tvb, k + 1));
286
0
                register_tree = proto_item_add_subtree(ti, ett_register);
287
288
0
                res = dissect_dpaux_register(tvb, pinfo, register_tree, k + 1,
289
0
                                             transaction->addr + k);
290
291
0
                k += (res > 0) ? res : 1;
292
0
            }
293
0
        }
294
0
    }
295
296
0
    return 0;
297
0
}
298
299
static int
300
dissect_dpaux(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
301
0
{
302
0
    proto_item *ti;
303
0
    proto_tree *dpaux_tree;
304
0
    bool from_source = false;
305
0
    struct dpaux_info *dpaux_info = (struct dpaux_info*)data;
306
307
0
    if (dpaux_info != NULL)
308
0
        from_source = dpaux_info->from_source;
309
310
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "dpaux");
311
0
    col_set_str(pinfo->cinfo, COL_INFO, "DisplayPort AUX channel");
312
0
    col_set_str(pinfo->cinfo, COL_RES_DL_DST, "N/A");
313
314
0
    if (from_source)
315
0
        col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "DP-Source");
316
0
    else
317
0
        col_set_str(pinfo->cinfo, COL_RES_DL_SRC, "DP-Sink");
318
319
    /* create display subtree for the protocol */
320
0
    ti = proto_tree_add_item(tree, proto_dpaux, tvb, 0, -1, ENC_NA);
321
0
    dpaux_tree = proto_item_add_subtree(ti, ett_dpaux);
322
323
0
    if (from_source)
324
0
        dissect_dpaux_from_source(tvb, pinfo, dpaux_tree);
325
0
    else
326
0
        dissect_dpaux_from_sink(tvb, pinfo, dpaux_tree);
327
328
0
    return tvb_captured_length(tvb);
329
0
}
330
331
/* Register the protocol with Wireshark.
332
 *
333
 * This format is required because a script is used to build the C function that
334
 * calls all the protocol registration.
335
 */
336
void
337
proto_register_dpaux(void)
338
16
{
339
16
    static const value_string convert_transaction_type[] = {
340
16
        { DPAUX_TRANSACTION_NATIVE, "Native" },
341
16
        { DPAUX_TRANSACTION_I2C_OVER_AUX, "I2C-over-AUX" },
342
16
        { DPAUX_TRANSACTION_N_A, "N/A," },
343
16
        { 0, NULL }
344
16
    };
345
346
16
    static const value_string convert_native_req_cmd[] = {
347
16
        { 0, "Write" },
348
16
        { 1, "Read" },
349
16
        { 0, NULL }
350
16
    };
351
352
16
    static const value_string convert_i2c_req_cmd[] = {
353
16
        { 0, "Write" },
354
16
        { 1, "Read" },
355
16
        { 2, "Write_Status_Update_Request" },
356
16
        { 0, NULL }
357
16
    };
358
359
16
    static const value_string convert_reply_cmd[] = {
360
16
        { 0, "AUX ACK" },
361
16
        { 1, "AUX NACK" },
362
16
        { 2, "AUX DEFER" },
363
16
        { 1 << 2, "I2C NACK" },
364
16
        { 2 << 2, "I2C DEFER" },
365
16
        { 0, NULL }
366
16
    };
367
368
16
    static const value_string convert_link_rate[] = {
369
16
        { 0x06, "1.62Gbps/lane" },
370
16
        { 0x0a, "2.7Gbps/lane" },
371
16
        { 0x14, "5.4Gbps/lane" },
372
16
        { 0x1e, "8.1Gbps/lane" },
373
16
        { 0, NULL }
374
16
    };
375
376
16
    static const value_string convert_downspread[] = {
377
16
        { 0x00, "none" },
378
16
        { 0x01, "up to 0.5%" },
379
16
        { 0, NULL }
380
16
    };
381
382
16
    static const value_string convert_norp[] = {
383
16
        { 0x00, "One receiver port" },
384
16
        { 0x01, "Two or more receiver ports" },
385
16
        { 0, NULL }
386
16
    };
387
388
    /* Setup protocol subtree array */
389
16
    static int *ett[] = {
390
16
        &ett_dpaux,
391
16
        &ett_register,
392
16
    };
393
394
16
    static hf_register_info hf[] = {
395
16
        { &hf_dpaux_transaction_type, { "Transaction type", "dpaux.transaction_type", FT_UINT8, BASE_DEC, VALS(convert_transaction_type), 0, NULL, HFILL } },
396
16
        { &hf_dpaux_native_req_cmd, { "Native Request Command", "dpaux.native_req_cmd", FT_UINT8, BASE_DEC, VALS(convert_native_req_cmd), 0, NULL, HFILL } },
397
16
        { &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 } },
398
16
        { &hf_dpaux_reply_cmd, { "Reply Command", "dpaux.reply_cmd", FT_UINT8, BASE_DEC, VALS(convert_reply_cmd), 0, NULL, HFILL } },
399
16
        { &hf_dpaux_mot, { "MOT (Middle-of-Transaction)", "dpaux.mot", FT_BOOLEAN, BASE_NONE, NULL, 0, NULL, HFILL } },
400
16
        { &hf_dpaux_addr, { "Address", "dpaux.addr", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL } },
401
16
        { &hf_dpaux_len, { "Data Length", "dpaux.len", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
402
16
        { &hf_dpaux_data, { "Data", "dpaux.data", FT_BYTES, SEP_SPACE, NULL, 0, NULL, HFILL } },
403
16
        { &hf_dpaux_reg_addr, { "DPCD", "dpaux.reg", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL } },
404
405
16
        { &hf_00000, { "DPCD_REV", "dpaux.00000", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
406
16
        { &hf_00000_MINOR, { "MINOR", "dpaux.00000_MINOR", FT_UINT8, BASE_HEX, NULL, 0x0f, NULL, HFILL } },
407
16
        { &hf_00000_MAJOR, { "MAJOR", "dpaux.00000_MAJOR", FT_UINT8, BASE_HEX, NULL, 0xf0, NULL, HFILL } },
408
409
16
        { &hf_00001, { "MAX_LINK_RATE", "dpaux.00001", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
410
16
        { &hf_00001_MAX_LINK_RATE, { "MAX_LINK_RATE", "dpaux.00001_MAX_LINK_RATE", FT_UINT8, BASE_HEX, VALS(convert_link_rate), 0xff, NULL, HFILL } },
411
412
16
        { &hf_00002, { "MAX_LANE_COUNT", "dpaux.00002", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
413
16
        { &hf_00002_MAX_LANE_COUNT, { "MAX_LANE_COUNT", "dpaux.00002_MAX_LANE_COUNT", FT_UINT8, BASE_DEC, NULL, 0x0f, NULL, HFILL } },
414
16
        { &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 } },
415
16
        { &hf_00002_TPS3_SUPPORTED, { "TPS3_SUPPORTED", "dpaux.00002_TPS3_SUPPORTED", FT_BOOLEAN, 8, NULL, 1<<6, NULL, HFILL } },
416
16
        { &hf_00002_ENHANCED_FRAME_CAP, { "ENHANCED_FRAME_CAP", "dpaux.00002_ENHANCED_FRAME_CAP", FT_BOOLEAN, 8, NULL, 1<<7, NULL, HFILL } },
417
418
16
        { &hf_00003, { "MAX_DOWNSPREAD", "dpaux.00003", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
419
16
        { &hf_00003_MAX_DOWNSPREAD, { "MAX_DOWNSPREAD", "dpaux.00003_MAX_DOWNSPREAD", FT_UINT8, BASE_DEC, VALS(convert_downspread), 0x01, NULL, HFILL } },
420
16
        { &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 } },
421
16
        { &hf_00003_TPS4_SUPPORTED, { "TPS4_SUPPORTED", "dpaux.00003_TPS4_SUPPORTED", FT_BOOLEAN, 8, NULL, 1<<7, NULL, HFILL } },
422
423
16
        { &hf_00004, { "NORP & DP_PWR_VOLTAGE_CAP", "dpaux.00004", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
424
16
        { &hf_00004_NORP, { "NORP", "dpaux.00004_NORP", FT_UINT8, BASE_DEC, VALS(convert_norp), 0x01, NULL, HFILL } },
425
16
        { &hf_00004_5V_DP_PWR_CAP, { "5V_DP_PWR_CAP", "dpaux.00004_5V_DP_PWR_CAP", FT_BOOLEAN, 8, NULL, 1<<5, NULL, HFILL } },
426
16
        { &hf_00004_12V_DP_PWR_CAP, { "12V_DP_PWR_CAP", "dpaux.00004_12V_DP_PWR_CAP", FT_BOOLEAN, 8, NULL, 1<<6, NULL, HFILL } },
427
16
        { &hf_00004_18V_DP_PWR_CAP, { "18V_DP_PWR_CAP", "dpaux.00004_18V_DP_PWR_CAP", FT_BOOLEAN, 8, NULL, 1<<7, NULL, HFILL } },
428
16
    };
429
430
    /* Register the protocol name and description */
431
16
    proto_dpaux = proto_register_protocol("DisplayPort AUX-Channel", "DPAUX", "dpaux");
432
16
    register_dissector("dpaux", dissect_dpaux, proto_dpaux);
433
434
16
    proto_register_field_array(proto_dpaux, hf, array_length(hf));
435
    proto_register_subtree_array(ett, array_length(ett));
436
16
}
437
438
439
/*
440
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
441
 *
442
 * Local variables:
443
 * c-basic-offset: 4
444
 * tab-width: 8
445
 * indent-tabs-mode: nil
446
 * End:
447
 *
448
 * vi: set shiftwidth=4 tabstop=8 expandtab:
449
 * :indentSize=4:tabSize=8:noTabs=true:
450
 */