/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 | | */ |