Coverage Report

Created: 2025-02-15 06:25

/src/wireshark/epan/dissectors/packet-hdmi.c
Line
Count
Source (jump to first uncovered line)
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
    week = tvb_get_uint8(tvb, offset);
120
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_week,
121
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN);
122
0
    offset += 1;
123
124
0
    year_hf = week == 255 ? hf_hdmi_edid_mod_year : hf_hdmi_edid_manf_year;
125
0
    year = tvb_get_uint8(tvb, offset);
126
0
    yi = proto_tree_add_item(edid_tree, year_hf,
127
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN);
128
0
    proto_item_append_text(yi, " (year %d)", 1990+year);
129
0
    offset += 1;
130
131
0
    proto_tree_add_item(edid_tree, hf_hdmi_edid_version, tvb, offset, 2, ENC_BIG_ENDIAN);
132
133
    /* XXX dissect the parts following the EDID header */
134
135
0
    return tvb_reported_length(tvb);
136
0
}
137
138
139
static int
140
dissect_hdmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
141
0
{
142
0
    uint8_t     addr;
143
0
    int         offset=0;
144
0
    proto_item *pi;
145
0
    proto_tree *hdmi_tree;
146
147
    /* the I2C address in the first byte is always handled by the HDMI
148
       dissector, even if the packet contains HDCP data */
149
0
    addr = tvb_get_uint8(tvb, 0);
150
0
    if (!try_val_to_str(addr, hdmi_addr))
151
0
        return 0; /* no HDMI packet */
152
153
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "HDMI");
154
0
    col_clear(pinfo->cinfo, COL_INFO);
155
156
0
    pi = proto_tree_add_item(tree, proto_hdmi, tvb, 0, -1, ENC_NA);
157
0
    hdmi_tree = proto_item_add_subtree(pi, ett_hdmi);
158
159
0
    if (addr&0x01) {
160
0
        set_address(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
161
0
        set_address(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
162
0
        pinfo->p2p_dir = P2P_DIR_RECV;
163
0
    }
164
0
    else {
165
0
        set_address(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
166
0
        set_address(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
167
0
        pinfo->p2p_dir = P2P_DIR_SENT;
168
0
    }
169
170
    /* there's no explicit statement in the spec saying that the protocol is
171
        big or little endian
172
       there's three cases: one byte values, symmetrical values or values
173
        that are explicitly marked as little endian
174
       for the sake of simplicity, we use little endian everywhere */
175
0
    proto_tree_add_item(hdmi_tree, hf_hdmi_addr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
176
0
    offset += 1;
177
178
0
    if (HDCP_ADDR8(addr)) {
179
0
        tvbuff_t *hdcp_tvb;
180
181
0
        hdcp_tvb = tvb_new_subset_remaining(tvb, offset);
182
183
0
        return call_dissector(hdcp_handle, hdcp_tvb, pinfo, hdmi_tree);
184
0
    }
185
186
0
    if (addr == ADDR8_EDID_WRITE) {
187
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "EDID request");
188
0
        proto_tree_add_item(hdmi_tree, hf_hdmi_edid_offset,
189
0
            tvb, offset, 1, ENC_LITTLE_ENDIAN);
190
0
        offset += 1;
191
0
        return offset;
192
0
    }
193
194
0
    return dissect_hdmi_edid(tvb, offset, pinfo, hdmi_tree);
195
0
}
196
197
static void
198
hdmi_fmt_edid_version( char *result, uint32_t revision )
199
0
{
200
0
   snprintf( result, ITEM_LABEL_LENGTH, "%d.%02d", (uint8_t)(( revision & 0xFF00 ) >> 8), (uint8_t)(revision & 0xFF) );
201
0
}
202
203
void
204
proto_register_hdmi(void)
205
14
{
206
14
    static hf_register_info hf[] = {
207
14
        { &hf_hdmi_addr,
208
14
            { "8bit I2C address", "hdmi.addr", FT_UINT8, BASE_HEX,
209
14
                VALS(hdmi_addr), 0, NULL, HFILL } },
210
14
        { &hf_hdmi_edid_offset,
211
14
            { "Offset", "hdmi.edid.offset",
212
14
                FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
213
14
        { &hf_hdmi_edid_hdr,
214
14
            { "EDID header", "hdmi.edid.hdr",
215
14
                FT_UINT64, BASE_HEX, NULL, 0, NULL, HFILL } },
216
14
        { &hf_hdmi_edid_manf_id,
217
14
            { "Manufacturer ID", "hdmi.edid.manf_id",
218
14
                FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
219
14
        { &hf_hdmi_edid_manf_prod_code,
220
14
            { "Manufacturer product code", "hdmi.edid.manf_prod_code",
221
14
                FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
222
14
        { &hf_hdmi_edid_manf_serial,
223
14
            { "Serial number", "hdmi.edid.serial_num",
224
14
                FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
225
14
        { &hf_hdmi_edid_manf_week,
226
14
            { "Week of manufacture", "hdmi.edid.manf_week",
227
14
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
228
14
        { &hf_hdmi_edid_mod_year,
229
14
            { "Model year", "hdmi.edid.model_year",
230
14
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
231
14
        { &hf_hdmi_edid_manf_year,
232
14
            { "Year of manufacture", "hdmi.edid.manf_year",
233
14
                FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
234
14
        { &hf_hdmi_edid_version,
235
14
            { "EDID Version", "hdmi.edid.version",
236
14
                FT_UINT16, BASE_CUSTOM, CF_FUNC(hdmi_fmt_edid_version), 0, NULL, HFILL } }
237
238
14
    };
239
240
14
    static int *ett[] = {
241
14
        &ett_hdmi,
242
14
        &ett_hdmi_edid
243
14
    };
244
245
14
    proto_hdmi = proto_register_protocol("High-Definition Multimedia Interface", "HDMI", "hdmi");
246
14
    hdmi_handle = register_dissector("hdmi",  dissect_hdmi, proto_hdmi );
247
248
14
    proto_register_field_array(proto_hdmi, hf, array_length(hf));
249
14
    proto_register_subtree_array(ett, array_length(ett));
250
14
}
251
252
253
void
254
proto_reg_handoff_hdmi(void)
255
14
{
256
14
    hdcp_handle = find_dissector_add_dependency("hdcp", proto_hdmi);
257
14
    dissector_add_for_decode_as("i2c.message", hdmi_handle );
258
14
}
259
260
/*
261
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
262
 *
263
 * Local variables:
264
 * c-basic-offset: 4
265
 * tab-width: 8
266
 * indent-tabs-mode: nil
267
 * End:
268
 *
269
 * vi: set shiftwidth=4 tabstop=8 expandtab:
270
 * :indentSize=4:tabSize=8:noTabs=true:
271
 */