Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-hdmi.c
Line
Count
Source
1
/* packet-hdmi.c
2
 * Routines for HDMI dissection
3
 * Copyright 2014 Martin Kaiser <martin@kaiser.cx>
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
/* this dissector handles I2C messages on the HDMI Display Data Channel (DDC)
13
 *
14
 * EDID (Extended Display Identification Data) messages are dissected here,
15
 * HDCP messages are passed on to the HDCP dissector
16
 */
17
18
#include "config.h"
19
20
#include <epan/packet.h>
21
22
void proto_register_hdmi(void);
23
void proto_reg_handoff_hdmi(void);
24
25
static int proto_hdmi;
26
27
static dissector_handle_t hdmi_handle;
28
static dissector_handle_t hdcp_handle;
29
30
static int ett_hdmi;
31
static int ett_hdmi_edid;
32
33
static int hf_hdmi_addr;
34
static int hf_hdmi_edid_offset;
35
static int hf_hdmi_edid_hdr;
36
static int hf_hdmi_edid_manf_id;
37
static int hf_hdmi_edid_manf_prod_code;
38
static int hf_hdmi_edid_manf_serial;
39
static int hf_hdmi_edid_manf_week;
40
static int hf_hdmi_edid_mod_year;
41
static int hf_hdmi_edid_manf_year;
42
static int hf_hdmi_edid_version;
43
44
45
/* also called Source and Sink in the HDMI spec */
46
0
#define ADDR_TRX "Transmitter"
47
0
#define ADDR_RCV "Receiver"
48
49
/* we use 8bit I2C addresses, including the direction bit */
50
0
#define ADDR8_HDCP_WRITE 0x74  /* transmitter->receiver */
51
0
#define ADDR8_HDCP_READ  0x75  /* r->t */
52
0
#define ADDR8_EDID_WRITE 0xA0  /* t->r */
53
#define ADDR8_EDID_READ  0xA1  /* r->t */
54
55
0
#define HDCP_ADDR8(x)   (x == ADDR8_HDCP_WRITE || x == ADDR8_HDCP_READ)
56
57
static const value_string hdmi_addr[] = {
58
    { ADDR8_HDCP_WRITE, "transmitter writes HDCP data for receiver" },
59
    { ADDR8_HDCP_READ,  "transmitter reads HDCP data from receiver" },
60
61
    { ADDR8_EDID_WRITE, "EDID request" },
62
    { ADDR8_EDID_READ,  "EDID read" },
63
    { 0, NULL }
64
};
65
66
0
#define EDID_HDR_VALUE UINT64_C(0x00ffffffffffff00)
67
68
/* grab 5 bits, from bit n to n+4, from a big-endian number x
69
   map those bits to a capital letter such that A == 1, B == 2, ... */
70
0
#define CAPITAL_LETTER(x, n) ('A'-1 + (((x) & (0x1F<<n)) >> n))
71
72
73
/* dissect EDID data from the receiver
74
   return the offset after the dissected data */
75
static int
76
dissect_hdmi_edid(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
77
0
{
78
0
    proto_item *yi;
79
0
    proto_tree *edid_tree;
80
0
    uint64_t    edid_hdr;
81
0
    uint16_t    manf_id;
82
0
    char        manf_id_str[4]; /* 3 letters + 0-termination */
83
0
    uint8_t     week, year;
84
0
    int         year_hf;
85
86
0
    edid_tree = proto_tree_add_subtree(tree, tvb,
87
0
            offset, -1, ett_hdmi_edid, NULL,
88
0
            "Extended Display Identification Data (EDID)");
89
90
0
    edid_hdr = tvb_get_ntoh64(tvb, offset);
91
0
    if (edid_hdr != EDID_HDR_VALUE)
92
0
        return offset; /* XXX handle fragmented EDID messages */
93
94
0
    col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "EDID");
95
96
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_hdr,
97
0
            tvb, offset, 8, ENC_LITTLE_ENDIAN);
98
0
    offset += 8;
99
100
    /* read as big endian for easier splitting */
101
0
    manf_id = tvb_get_ntohs(tvb, offset);
102
    /* XXX check that MSB is 0 */
103
0
    manf_id_str[0] = CAPITAL_LETTER(manf_id, 10);
104
0
    manf_id_str[1] = CAPITAL_LETTER(manf_id,  5);
105
0
    manf_id_str[2] = CAPITAL_LETTER(manf_id,  0);
106
0
    manf_id_str[3] = 0;
107
0
    proto_tree_add_string(edid_tree, hf_hdmi_edid_manf_id,
108
0
            tvb, offset, 2, manf_id_str);
109
0
    offset += 2;
110
111
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_prod_code,
112
0
            tvb, offset, 2, ENC_LITTLE_ENDIAN);
113
0
    offset += 2;
114
115
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_serial,
116
0
            tvb, offset, 4, ENC_LITTLE_ENDIAN);
117
0
    offset += 4;
118
119
0
    proto_tree_add_item_ret_uint8(edid_tree, hf_hdmi_edid_manf_week,
120
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN, &week);
121
0
    offset += 1;
122
123
0
    year_hf = week == 255 ? hf_hdmi_edid_mod_year : hf_hdmi_edid_manf_year;
124
0
    yi = proto_tree_add_item_ret_uint8(edid_tree, year_hf,
125
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN, &year);
126
0
    proto_item_append_text(yi, " (year %d)", 1990+year);
127
0
    offset += 1;
128
129
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_version, tvb, offset, 2, ENC_BIG_ENDIAN);
130
131
    /* XXX dissect the parts following the EDID header */
132
133
0
    return tvb_reported_length(tvb);
134
0
}
135
136
137
static int
138
dissect_hdmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
139
0
{
140
0
    uint8_t     addr;
141
0
    int         offset=0;
142
0
    proto_item *pi;
143
0
    proto_tree *hdmi_tree;
144
145
    /* the I2C address in the first byte is always handled by the HDMI
146
       dissector, even if the packet contains HDCP data */
147
0
    addr = tvb_get_uint8(tvb, 0);
148
0
    if (!try_val_to_str(addr, hdmi_addr))
149
0
        return 0; /* no HDMI packet */
150
151
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "HDMI");
152
0
    col_clear(pinfo->cinfo, COL_INFO);
153
154
0
    pi = proto_tree_add_item(tree, proto_hdmi, tvb, 0, -1, ENC_NA);
155
0
    hdmi_tree = proto_item_add_subtree(pi, ett_hdmi);
156
157
0
    if (addr&0x01) {
158
0
        set_address(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
159
0
        set_address(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
160
0
        pinfo->p2p_dir = P2P_DIR_RECV;
161
0
    }
162
0
    else {
163
0
        set_address(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
164
0
        set_address(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
165
0
        pinfo->p2p_dir = P2P_DIR_SENT;
166
0
    }
167
168
    /* there's no explicit statement in the spec saying that the protocol is
169
        big or little endian
170
       there's three cases: one byte values, symmetrical values or values
171
        that are explicitly marked as little endian
172
       for the sake of simplicity, we use little endian everywhere */
173
0
    proto_tree_add_item(hdmi_tree, hf_hdmi_addr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
174
0
    offset += 1;
175
176
0
    if (HDCP_ADDR8(addr)) {
177
0
        tvbuff_t *hdcp_tvb;
178
179
0
        hdcp_tvb = tvb_new_subset_remaining(tvb, offset);
180
181
0
        return call_dissector(hdcp_handle, hdcp_tvb, pinfo, hdmi_tree);
182
0
    }
183
184
0
    if (addr == ADDR8_EDID_WRITE) {
185
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "EDID request");
186
0
        proto_tree_add_item(hdmi_tree, hf_hdmi_edid_offset,
187
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN);
188
0
        offset += 1;
189
0
        return offset;
190
0
    }
191
192
0
    return dissect_hdmi_edid(tvb, offset, pinfo, hdmi_tree);
193
0
}
194
195
static void
196
hdmi_fmt_edid_version( char *result, uint32_t revision )
197
0
{
198
0
   snprintf( result, ITEM_LABEL_LENGTH, "%d.%02d", (uint8_t)(( revision & 0xFF00 ) >> 8), (uint8_t)(revision & 0xFF) );
199
0
}
200
201
void
202
proto_register_hdmi(void)
203
16
{
204
16
    static hf_register_info hf[] = {
205
16
        { &hf_hdmi_addr,
206
16
            { "8bit I2C address", "hdmi.addr", FT_UINT8, BASE_HEX,
207
16
                VALS(hdmi_addr), 0, NULL, HFILL } },
208
16
        { &hf_hdmi_edid_offset,
209
16
            { "Offset", "hdmi.edid.offset",
210
16
                FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
211
16
        { &hf_hdmi_edid_hdr,
212
16
            { "EDID header", "hdmi.edid.hdr",
213
16
                FT_UINT64, BASE_HEX, NULL, 0, NULL, HFILL } },
214
16
        { &hf_hdmi_edid_manf_id,
215
16
            { "Manufacturer ID", "hdmi.edid.manf_id",
216
16
                FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
217
16
        { &hf_hdmi_edid_manf_prod_code,
218
16
            { "Manufacturer product code", "hdmi.edid.manf_prod_code",
219
16
                FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
220
16
        { &hf_hdmi_edid_manf_serial,
221
16
            { "Serial number", "hdmi.edid.serial_num",
222
16
                FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
223
16
        { &hf_hdmi_edid_manf_week,
224
16
            { "Week of manufacture", "hdmi.edid.manf_week",
225
16
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
226
16
        { &hf_hdmi_edid_mod_year,
227
16
            { "Model year", "hdmi.edid.model_year",
228
16
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
229
16
        { &hf_hdmi_edid_manf_year,
230
16
            { "Year of manufacture", "hdmi.edid.manf_year",
231
16
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
232
16
        { &hf_hdmi_edid_version,
233
16
            { "EDID Version", "hdmi.edid.version",
234
16
                FT_UINT16, BASE_CUSTOM, CF_FUNC(hdmi_fmt_edid_version), 0, NULL, HFILL } }
235
236
16
    };
237
238
16
    static int *ett[] = {
239
16
        &ett_hdmi,
240
16
        &ett_hdmi_edid
241
16
    };
242
243
16
    proto_hdmi = proto_register_protocol("High-Definition Multimedia Interface", "HDMI", "hdmi");
244
16
    hdmi_handle = register_dissector("hdmi",  dissect_hdmi, proto_hdmi );
245
246
16
    proto_register_field_array(proto_hdmi, hf, array_length(hf));
247
16
    proto_register_subtree_array(ett, array_length(ett));
248
16
}
249
250
251
void
252
proto_reg_handoff_hdmi(void)
253
16
{
254
16
    hdcp_handle = find_dissector_add_dependency("hdcp", proto_hdmi);
255
16
    dissector_add_for_decode_as("i2c.message", hdmi_handle );
256
16
}
257
258
/*
259
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
260
 *
261
 * Local variables:
262
 * c-basic-offset: 4
263
 * tab-width: 8
264
 * indent-tabs-mode: nil
265
 * End:
266
 *
267
 * vi: set shiftwidth=4 tabstop=8 expandtab:
268
 * :indentSize=4:tabSize=8:noTabs=true:
269
 */