Remove all $Id$ from top of file
[metze/wireshark/wip.git] / epan / dissectors / packet-hdmi.c
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  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 /* this dissector handles I2C messages on the HDMI Display Data Channel (DDC)
25  *
26  * EDID (Extended Display Identification Data) messages are dissected here,
27  * HDCP messages are passed on to the HDCP dissector
28  */
29
30 #include "config.h"
31
32 #include <glib.h>
33 #include <epan/packet.h>
34 #include <epan/ptvcursor.h>
35 #include <epan/expert.h>
36 #include <epan/wmem/wmem.h>
37 #include "packet-hdmi.h"
38
39 void proto_register_hdmi(void);
40
41 static int proto_hdmi  = -1;
42
43 static dissector_handle_t hdcp_handle;
44
45 static gint ett_hdmi = -1;
46 static gint ett_hdmi_edid = -1;
47
48 static int hf_hdmi_addr = -1;
49 static int hf_hdmi_edid_offset = -1;
50 static int hf_hdmi_edid_hdr = -1;
51 static int hf_hdmi_edid_manf_id = -1;
52 static int hf_hdmi_edid_manf_prod_code = -1;
53 static int hf_hdmi_edid_manf_serial = -1;
54 static int hf_hdmi_edid_manf_week = -1;
55 static int hf_hdmi_edid_mod_year = -1;
56 static int hf_hdmi_edid_manf_year = -1;
57
58
59 /* also called Source and Sink in the HDMI spec */
60 #define ADDR_TRX "Transmitter"
61 #define ADDR_RCV "Receiver"
62
63 /* we use 8bit I2C addresses, including the direction bit */
64 #define ADDR8_HDCP_WRITE 0x74  /* transmitter->receiver */
65 #define ADDR8_HDCP_READ  0x75  /* r->t */
66 #define ADDR8_EDID_WRITE 0xA0  /* t->r */
67 #define ADDR8_EDID_READ  0xA1  /* r->t */
68
69 #define HDCP_ADDR8(x)   (x==ADDR8_HDCP_WRITE || x==ADDR8_HDCP_READ)
70
71 static const value_string hdmi_addr[] = {
72     { ADDR8_HDCP_WRITE, "transmitter writes HDCP data for receiver" },
73     { ADDR8_HDCP_READ,  "transmitter reads HDCP data from receiver" },
74
75     { ADDR8_EDID_WRITE, "EDID request" },
76     { ADDR8_EDID_READ,  "EDID read" },
77     { 0, NULL }
78 };
79
80 #define EDID_HDR_VALUE G_GUINT64_CONSTANT(0x00ffffffffffff00)
81
82 /* grab 5 bits, from bit n to n+4, from a big-endian number x
83    map those bits to a capital letter such that A==1, B==2, ... */
84 #define CAPITAL_LETTER(x, n) ('A'-1 + (((x) & (0x1F<<n)) >> n))
85
86
87 gboolean
88 sub_check_hdmi(packet_info *pinfo _U_)
89 {
90     /* by looking at the i2c_phdr only, we can't decide if this packet is HDMI
91        this function is called when the user explicitly selected HDMI
92        in the preferences
93        therefore, we always return TRUE and hand the data to the (new
94        style) dissector who sees the 8bit address and the packet content */
95
96    return TRUE;
97 }
98
99
100 /* dissect EDID data from the receiver
101    return the offset after the dissected data */
102 static gint
103 dissect_hdmi_edid(tvbuff_t *tvb, gint offset, packet_info *pinfo, proto_tree *tree)
104 {
105     proto_item *ti, *yi;
106     proto_tree *edid_tree;
107     guint64     edid_hdr;
108     guint16     manf_id;
109     gchar       manf_id_str[4]; /* 3 letters + 0-termination */
110     guint8      week, year;
111     int         year_hf;
112     guint8      edid_ver, edid_rev;
113
114
115     ti = proto_tree_add_text(tree, tvb,
116             offset, tvb_reported_length_remaining(tvb, offset),
117             "Extended Display Identification Data (EDID)");
118     edid_tree = proto_item_add_subtree(ti, ett_hdmi_edid);
119
120     edid_hdr = tvb_get_ntoh64(tvb, offset);
121     if (edid_hdr != EDID_HDR_VALUE)
122         return offset; /* XXX handle fragmented EDID messages */
123
124     col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "EDID");
125
126     proto_tree_add_item(edid_tree, hf_hdmi_edid_hdr,
127             tvb, offset, 8, ENC_LITTLE_ENDIAN);
128     offset += 8;
129
130     /* read as big endian for easier splitting */
131     manf_id = tvb_get_ntohs(tvb, offset);
132     /* XXX check that MSB is 0 */
133     manf_id_str[0] = CAPITAL_LETTER(manf_id, 10);
134     manf_id_str[1] = CAPITAL_LETTER(manf_id,  5);
135     manf_id_str[2] = CAPITAL_LETTER(manf_id,  0);
136     manf_id_str[3] = 0;
137     proto_tree_add_string(edid_tree, hf_hdmi_edid_manf_id,
138             tvb, offset, 2, manf_id_str);
139     offset += 2;
140
141     proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_prod_code,
142             tvb, offset, 2, ENC_LITTLE_ENDIAN);
143     offset += 2;
144
145     proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_serial,
146             tvb, offset, 4, ENC_LITTLE_ENDIAN);
147     offset += 4;
148
149     week = tvb_get_guint8(tvb, offset);
150     proto_tree_add_item(edid_tree, hf_hdmi_edid_manf_week,
151             tvb, offset, 1, ENC_LITTLE_ENDIAN);
152     offset++;
153
154     year_hf = week==255 ? hf_hdmi_edid_mod_year : hf_hdmi_edid_manf_year;
155     year = tvb_get_guint8(tvb, offset);
156     yi = proto_tree_add_item(edid_tree, year_hf,
157             tvb, offset, 1, ENC_LITTLE_ENDIAN);
158     proto_item_append_text(yi, " (year %d)", 1990+year);
159     offset++;
160
161     edid_ver = tvb_get_guint8(tvb, offset);
162     edid_rev = tvb_get_guint8(tvb, offset+1);
163
164     /* XXX make this filterable */
165     proto_tree_add_text(edid_tree, tvb, offset, 2,
166             "EDID Version %d.%d", edid_ver, edid_rev);
167
168     /* XXX dissect the parts following the EDID header */
169
170     return tvb_reported_length(tvb);
171 }
172
173
174 static int
175 dissect_hdmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
176 {
177     guint8      addr;
178     gint        offset=0;
179     proto_item *pi;
180     proto_tree *hdmi_tree;
181
182     /* the I2C address in the first byte is always handled by the HDMI
183        dissector, even if the packet contains HDCP data */
184     addr = tvb_get_guint8(tvb, 0);
185     if (!try_val_to_str(addr, hdmi_addr))
186         return 0; /* no HDMI packet */
187
188     col_set_str(pinfo->cinfo, COL_PROTOCOL, "HDMI");
189     col_clear(pinfo->cinfo, COL_INFO);
190
191     pi = proto_tree_add_protocol_format(tree, proto_hdmi,
192             tvb, 0, tvb_reported_length(tvb), "HDMI");
193     hdmi_tree = proto_item_add_subtree(pi, ett_hdmi);
194
195     if (addr&0x01) {
196         SET_ADDRESS(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
197         SET_ADDRESS(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
198         pinfo->p2p_dir = P2P_DIR_RECV;
199     }
200     else {
201         SET_ADDRESS(&pinfo->src, AT_STRINGZ, (int)strlen(ADDR_TRX)+1, ADDR_TRX);
202         SET_ADDRESS(&pinfo->dst, AT_STRINGZ, (int)strlen(ADDR_RCV)+1, ADDR_RCV);
203         pinfo->p2p_dir = P2P_DIR_SENT;
204     }
205
206     /* there's no explicit statement in the spec saying that the protocol is
207         big or little endian
208        there's three cases: one byte values, symmetrical values or values
209         that are explicitly marked as little endian
210        for the sake of simplicity, we use little endian everywhere */
211     proto_tree_add_item(hdmi_tree, hf_hdmi_addr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
212     offset++;
213
214     if (HDCP_ADDR8(addr)) {
215         gint      hdcp_len;
216         tvbuff_t *hdcp_tvb;
217
218         hdcp_len = tvb_reported_length_remaining(tvb, offset);
219         hdcp_tvb = tvb_new_subset(tvb, offset, hdcp_len, hdcp_len);
220
221         return call_dissector(hdcp_handle, hdcp_tvb, pinfo, hdmi_tree);
222     }
223
224     if (addr==ADDR8_EDID_WRITE) {
225         col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "EDID request");
226         proto_tree_add_item(hdmi_tree, hf_hdmi_edid_offset,
227             tvb, offset, 1, ENC_LITTLE_ENDIAN);
228         offset++;
229         return offset;
230     }
231
232     return dissect_hdmi_edid(tvb, offset, pinfo, hdmi_tree);
233 }
234
235
236 void
237 proto_register_hdmi(void)
238 {
239     static hf_register_info hf[] = {
240         { &hf_hdmi_addr,
241             { "8bit I2C address", "hdmi.addr", FT_UINT8, BASE_HEX,
242                 VALS(hdmi_addr), 0, NULL, HFILL } },
243         { &hf_hdmi_edid_offset,
244             { "Offset", "hdmi.edid.offset",
245                 FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
246         { &hf_hdmi_edid_hdr,
247             { "EDID header", "hdmi.edid.hdr",
248                 FT_UINT64, BASE_HEX, NULL, 0, NULL, HFILL } },
249         { &hf_hdmi_edid_manf_id,
250             { "Manufacturer ID", "hdmi.edid.manf_id",
251                 FT_STRING, STR_ASCII, NULL, 0, NULL, HFILL } },
252         { &hf_hdmi_edid_manf_prod_code,
253             { "Manufacturer product code", "hdmi.edid.manf_prod_code",
254                 FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
255         { &hf_hdmi_edid_manf_serial,
256             { "Serial number", "hdmi.edid.serial_num",
257                 FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
258         { &hf_hdmi_edid_manf_week,
259             { "Week of manufacture", "hdmi.edid.manf_week",
260                 FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
261         { &hf_hdmi_edid_mod_year,
262             { "Model year", "hdmi.edid.model_year",
263                 FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
264         { &hf_hdmi_edid_manf_year,
265             { "Year of manufacture", "hdmi.edid.manf_year",
266                 FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }
267     };
268
269     static gint *ett[] = {
270         &ett_hdmi,
271         &ett_hdmi_edid
272     };
273
274     proto_hdmi = proto_register_protocol(
275             "High-Definition Multimedia Interface", "HDMI", "hdmi");
276
277     proto_register_field_array(proto_hdmi, hf, array_length(hf));
278     proto_register_subtree_array(ett, array_length(ett));
279
280     new_register_dissector("hdmi", dissect_hdmi, proto_hdmi);
281 }
282
283
284 void
285 proto_reg_handoff_hdmi(void)
286 {
287     hdcp_handle = find_dissector("hdcp");
288 }
289
290 /*
291  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
292  *
293  * Local variables:
294  * c-basic-offset: 4
295  * tab-width: 8
296  * indent-tabs-mode: nil
297  * End:
298  *
299  * vi: set shiftwidth=4 tabstop=8 expandtab:
300  * :indentSize=4:tabSize=8:noTabs=true:
301  */