1 /* packet-bthid.c
2  * Routines for Bluetooth HID dissection
3  *
4  * Copyright 2012, Michal Labedzki for Tieto Corporation
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "config.h"
14 
15 #include <epan/packet.h>
16 #include <epan/prefs.h>
17 #include <epan/expert.h>
18 
19 #include "packet-btl2cap.h"
20 #include "packet-btsdp.h"
21 
22 static int proto_bthid                                                     = -1;
23 static int hf_bthid_transaction_type                                       = -1;
24 static int hf_bthid_parameter_reserved                                     = -1;
25 static int hf_bthid_parameter_reserved_31                                  = -1;
26 static int hf_bthid_parameter_reserved_32                                  = -1;
27 static int hf_bthid_parameter_reserved_2                                   = -1;
28 static int hf_bthid_parameter_result_code                                  = -1;
29 static int hf_bthid_parameter_control_operation                            = -1;
30 static int hf_bthid_parameter_size                                         = -1;
31 static int hf_bthid_protocol                                               = -1;
32 static int hf_bthid_idle_rate                                              = -1;
33 static int hf_bthid_parameter_report_type                                  = -1;
34 static int hf_bthid_report_id                                              = -1;
35 static int hf_bthid_buffer_size                                            = -1;
36 static int hf_bthid_protocol_code                                          = -1;
37 static int hf_bthid_data                                                   = -1;
38 
39 static gint ett_bthid             = -1;
40 
41 static expert_field ei_bthid_parameter_control_operation_deprecated = EI_INIT;
42 static expert_field ei_bthid_transaction_type_deprecated = EI_INIT;
43 
44 static dissector_handle_t bthid_handle;
45 static dissector_handle_t usb_hid_boot_keyboard_input_report_handle;
46 static dissector_handle_t usb_hid_boot_keyboard_output_report_handle;
47 static dissector_handle_t usb_hid_boot_mouse_input_report_handle;
48 
49 static gboolean show_deprecated = FALSE;
50 
51 static const value_string transaction_type_vals[] = {
52     { 0x00,   "HANDSHAKE" },
53     { 0x01,   "HID_CONTROL" },
54     { 0x02,   "reserved" },
55     { 0x03,   "reserved" },
56     { 0x04,   "GET_REPORT" },
57     { 0x05,   "SET_REPORT" },
58     { 0x06,   "GET_PROTOCOL" },
59     { 0x07,   "SET_PROTOCOL" },
60     { 0x08,   "GET_IDLE" },
61     { 0x09,   "SET_IDLE" },
62     { 0x0A,   "DATA" },
63     { 0x0B,   "DATC" },
64     { 0x0C,   "reserved" },
65     { 0x0D,   "reserved" },
66     { 0x0E,   "reserved" },
67     { 0x0F,   "reserved" },
68     { 0, NULL }
69 };
70 
71 static const value_string report_type_vals[] = {
72     { 0x00,   "Other" },
73     { 0x01,   "Input" },
74     { 0x02,   "Output" },
75     { 0x03,   "Feature" },
76     { 0, NULL }
77 };
78 
79 static const value_string result_code_vals[] = {
80     { 0x00,   "Successful" },
81     { 0x01,   "Not Ready" },
82     { 0x02,   "Error, Invalid Report ID" },
83     { 0x03,   "Error, Unsupported Request" },
84     { 0x04,   "Error, Invalid Parameters" },
85     { 0x0E,   "Error, Unknown " },
86     { 0x0F,   "Error, Fatal " },
87     { 0, NULL }
88 };
89 
90 static const value_string control_operation_vals[] = {
91     { 0x00,   "NOP" },
92     { 0x01,   "Hard Reset" },
93     { 0x02,   "Soft Reset" },
94     { 0x03,   "Suspend" },
95     { 0x04,   "Exit Suspend" },
96     { 0x05,   "Virtual Cable Unplug" },
97     { 0, NULL }
98 };
99 
100 static const value_string size_vals[] = {
101     { 0x00,   "Buffer equal to report size" },
102     { 0x01,   "BufferSize field follows the Report ID" },
103     { 0, NULL }
104 };
105 
106 static const value_string protocol_vals[] = {
107     { 0x00,   "Report" },
108     { 0x01,   "Boot" },
109     { 0, NULL }
110 };
111 
112 static const value_string protocol_code_vals[] = {
113     { 0x00,   "None" },
114     { 0x01,   "Keyboard" },
115     { 0x02,   "Mouse" },
116     { 0, NULL }
117 };
118 
119 void proto_register_bthid(void);
120 void proto_reg_handoff_bthid(void);
121 
122 static gint
dissect_hid_data(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,gint offset,guint report_type)123 dissect_hid_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
124         gint offset, guint report_type)
125 {
126     unsigned int protocol_code;
127 
128     proto_tree_add_item(tree, hf_bthid_protocol_code, tvb, offset, 1, ENC_BIG_ENDIAN);
129     protocol_code = tvb_get_guint8(tvb, offset);
130     col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(protocol_code, protocol_code_vals, "unknown type"));
131     offset += 1;
132 
133     switch (protocol_code) {
134         case 0x01: /* Keyboard */
135             if (report_type == 0x02) { /* Output - LEDs */
136                 offset += call_dissector_with_data(usb_hid_boot_keyboard_output_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
137 
138                 break;
139             } else if (report_type != 0x01) {/* is not Input (Keys) */
140                 break;
141             }
142 
143             offset += call_dissector_with_data(usb_hid_boot_keyboard_input_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
144 
145             break;
146         case 0x02: /* Mouse */
147             offset += call_dissector_with_data(usb_hid_boot_mouse_input_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
148 
149             break;
150     }
151 
152     return offset;
153 }
154 
155 static int
dissect_bthid(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)156 dissect_bthid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
157 {
158     proto_item   *ti;
159     proto_tree   *bthid_tree;
160     gint          offset = 0;
161     guint         transaction_type;
162     guint         parameter;
163     guint         protocol;
164     guint         idle_rate;
165     guint8        control_operation;
166     proto_item   *pitem;
167 
168     ti = proto_tree_add_item(tree, proto_bthid, tvb, offset, -1, ENC_NA);
169     bthid_tree = proto_item_add_subtree(ti, ett_bthid);
170 
171     col_set_str(pinfo->cinfo, COL_PROTOCOL, "HID");
172     col_clear(pinfo->cinfo, COL_INFO);
173 
174     switch (pinfo->p2p_dir) {
175         case P2P_DIR_SENT:
176             col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
177             break;
178         case P2P_DIR_RECV:
179             col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
180             break;
181         default:
182             col_set_str(pinfo->cinfo, COL_INFO, "UnknownDirection ");
183             break;
184     }
185 
186     pitem = proto_tree_add_item(bthid_tree, hf_bthid_transaction_type, tvb, offset, 1, ENC_BIG_ENDIAN);
187     transaction_type = tvb_get_guint8(tvb, offset);
188     parameter = transaction_type & 0x0F;
189     transaction_type = transaction_type >> 4;
190 
191     col_append_str(pinfo->cinfo, COL_INFO, val_to_str_const(transaction_type, transaction_type_vals, "Unknown TransactionType"));
192 
193     switch(transaction_type) {
194         case 0x00: /* HANDSHAKE */
195             proto_tree_add_item(bthid_tree, hf_bthid_parameter_result_code, tvb, offset, 1, ENC_BIG_ENDIAN);
196             offset += 1;
197             col_append_fstr(pinfo->cinfo, COL_INFO, " - Result Code: %s", val_to_str_const(parameter, result_code_vals, "reserved"));
198             break;
199         case 0x01: /* HID_CONTROL */
200             pitem = proto_tree_add_item(bthid_tree, hf_bthid_parameter_control_operation, tvb, offset, 1, ENC_BIG_ENDIAN);
201             control_operation = tvb_get_guint8(tvb, offset);
202             col_append_fstr(pinfo->cinfo, COL_INFO, " - Control Operation: %s", val_to_str_const(parameter, control_operation_vals, "reserved"));
203             if (control_operation < 3 && show_deprecated)
204                 expert_add_info(pinfo, pitem, &ei_bthid_parameter_control_operation_deprecated);
205             offset += 1;
206             break;
207         case 0x04: /* GET_REPORT */
208             proto_tree_add_item(bthid_tree, hf_bthid_parameter_size, tvb, offset, 1, ENC_BIG_ENDIAN);
209             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_2, tvb, offset, 1, ENC_BIG_ENDIAN);
210             proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
211             offset += 1;
212             col_append_fstr(pinfo->cinfo, COL_INFO, " - Size: %s, Report Type: %s",
213                             val_to_str_const(parameter >> 3 , size_vals, "reserved"),
214                             val_to_str_const(parameter & 0x03, report_type_vals, "reserved"));
215 
216             /* XXX: This is workaround, this should come from SDP:
217                "This field is required in Report Protocol Mode when any Report ID
218                Global Items are declared in the report descriptor, and in
219                Boot Protocol Mode. Otherwise the field does not exist."
220             */
221             if (((parameter >> 3) && tvb_reported_length_remaining(tvb, offset) >= 3) ||
222                     (!(parameter >> 3) && tvb_reported_length_remaining(tvb, offset) >= 1)) {
223                 proto_tree_add_item(bthid_tree, hf_bthid_report_id, tvb, offset, 1, ENC_BIG_ENDIAN);
224                 offset += 1;
225             }
226 
227             if (parameter >> 3) {
228                 proto_tree_add_item(bthid_tree, hf_bthid_buffer_size, tvb, offset, 2, ENC_LITTLE_ENDIAN);
229                 offset += 2;
230             }
231             break;
232         case 0x05: /* SET_REPORT */
233             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_32, tvb, offset, 1, ENC_BIG_ENDIAN);
234             proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
235             offset += 1;
236 
237             col_append_fstr(pinfo->cinfo, COL_INFO, " - Report Type: %s",
238                             val_to_str_const(parameter & 0x03, report_type_vals, "reserved"));
239 
240             /* playload */
241             proto_tree_add_item(bthid_tree, hf_bthid_data, tvb, offset, -1, ENC_NA);
242             offset += tvb_captured_length_remaining(tvb, offset);
243             break;
244         case 0x06: /* GET_PROTOCOL */
245             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
246             offset += 1;
247 
248             proto_tree_add_item(bthid_tree, hf_bthid_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
249             protocol = tvb_get_guint8(tvb, offset) & 0x01;
250             offset += 1;
251 
252             col_append_fstr(pinfo->cinfo, COL_INFO, " - Protocol: %s",
253                             val_to_str_const(protocol, protocol_vals, "reserved"));
254 
255             break;
256         case 0x07: /* SET_PROTOCOL */
257             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_31, tvb, offset, 1, ENC_BIG_ENDIAN);
258             proto_tree_add_item(bthid_tree, hf_bthid_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
259             offset += 1;
260 
261             col_append_fstr(pinfo->cinfo, COL_INFO, " - Protocol: %s",
262                             val_to_str_const(parameter & 0x01, protocol_vals, "reserved"));
263             break;
264         case 0x08: /* GET_IDLE */
265         case 0x09: /* SET_IDLE */
266             if (show_deprecated)
267                 expert_add_info(pinfo, pitem, &ei_bthid_transaction_type_deprecated);
268 
269             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
270             offset += 1;
271 
272             pitem = proto_tree_add_item(bthid_tree, hf_bthid_idle_rate, tvb, offset, 1, ENC_BIG_ENDIAN);
273             idle_rate = tvb_get_guint8(tvb, offset);
274             proto_item_append_text(pitem, " (%u.%03u ms)", idle_rate * 4 / 1000, idle_rate * 4 % 1000);
275             col_append_fstr(pinfo->cinfo, COL_INFO, " - Idle Rate: %u.%03u ms", idle_rate*4/1000, idle_rate*4%1000);
276             offset += 1;
277             break;
278         case 0x0B: /* DATC */
279             if (show_deprecated)
280                 expert_add_info(pinfo, pitem, &ei_bthid_transaction_type_deprecated);
281             /* FALL THROUGH */
282         case 0x0A: /* DATA */
283             proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_32, tvb, offset, 1, ENC_BIG_ENDIAN);
284             proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
285             offset += 1;
286             col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(parameter, report_type_vals, "reserved"));
287 
288             /* playload */
289             offset = dissect_hid_data(tvb, pinfo,  bthid_tree, offset, parameter & 0x03);
290             break;
291     }
292 
293     return offset;
294 }
295 
296 
297 void
proto_register_bthid(void)298 proto_register_bthid(void)
299 {
300     module_t *module;
301     expert_module_t* expert_bthid;
302 
303     static hf_register_info hf[] = {
304         { &hf_bthid_transaction_type,
305             { "Transaction Type",                "bthid.transaction_type",
306             FT_UINT8, BASE_HEX, VALS(transaction_type_vals), 0xF0,
307             NULL, HFILL }
308         },
309         { &hf_bthid_parameter_reserved,
310             { "Parameter reserved",              "bthid.parameter.reserved",
311             FT_UINT8, BASE_HEX, NULL, 0x0F,
312             NULL, HFILL }
313         },
314         { &hf_bthid_parameter_reserved_32,
315             { "Parameter reserved",              "bthid.parameter.reserved_32",
316             FT_UINT8, BASE_HEX, NULL, 0x0C,
317             NULL, HFILL }
318         },
319         { &hf_bthid_parameter_reserved_31,
320             { "Parameter reserved",              "bthid.parameter.reserved_31",
321             FT_UINT8, BASE_HEX, NULL, 0x0E,
322             NULL, HFILL }
323         },
324         { &hf_bthid_parameter_reserved_2,
325             { "Parameter reserved",              "bthid.parameter.reserved_2",
326             FT_UINT8, BASE_HEX, NULL, 0x04,
327             NULL, HFILL }
328         },
329         { &hf_bthid_parameter_report_type,
330             { "Report Type",                     "bthid.parameter.report_type",
331             FT_UINT8, BASE_HEX, VALS(report_type_vals), 0x03,
332             NULL, HFILL }
333         },
334         { &hf_bthid_parameter_size,
335             { "Size",                            "bthid.parameter.size",
336             FT_UINT8, BASE_HEX, VALS(size_vals), 0x08,
337             NULL, HFILL }
338         },
339         { &hf_bthid_parameter_result_code,
340             { "Result Code",                     "bthid.result_code",
341             FT_UINT8, BASE_HEX, VALS(result_code_vals), 0x0F,
342             NULL, HFILL }
343         },
344         { &hf_bthid_parameter_control_operation,
345             { "Control Operation",               "bthid.control_operation",
346             FT_UINT8, BASE_HEX, VALS(control_operation_vals), 0x0F,
347             NULL, HFILL }
348         },
349         { &hf_bthid_protocol,
350             { "Protocol",                        "bthid.protocol",
351             FT_UINT8, BASE_HEX, VALS(protocol_vals), 0x01,
352             NULL, HFILL }
353         },
354         { &hf_bthid_idle_rate,
355             { "Idle Rate",                       "bthid.idle_rate",
356             FT_UINT8, BASE_DEC, NULL, 0x00,
357             NULL, HFILL }
358         },
359         { &hf_bthid_report_id,
360             { "Report Id",                       "bthid.report_id",
361             FT_UINT8, BASE_HEX, VALS(protocol_code_vals), 0x00,
362             NULL, HFILL }
363         },
364         { &hf_bthid_buffer_size,
365             { "Buffer Size",                     "bthid.buffer_size",
366             FT_UINT16, BASE_HEX, NULL, 0x00,
367             NULL, HFILL }
368         },
369         { &hf_bthid_protocol_code,
370             { "Protocol Code",                   "bthid.data.protocol_code",
371             FT_UINT8, BASE_HEX, VALS(protocol_code_vals), 0x00,
372             NULL, HFILL }
373         },
374         { &hf_bthid_data,
375             { "Data",                            "bthid.data",
376             FT_NONE, BASE_NONE, NULL, 0x00,
377             NULL, HFILL }
378         },
379 
380     };
381 
382     static gint *ett[] = {
383         &ett_bthid
384     };
385 
386     static ei_register_info ei[] = {
387         { &ei_bthid_parameter_control_operation_deprecated, { "bthid.control_operation.deprecated", PI_PROTOCOL, PI_WARN, "This value of Control Operation is deprecated by HID 1.1", EXPFILL }},
388         { &ei_bthid_transaction_type_deprecated, { "bthid.transaction_type.deprecated", PI_PROTOCOL, PI_WARN, "This Transaction Type is deprecated by HID 1.1", EXPFILL }},
389     };
390 
391     proto_bthid = proto_register_protocol("Bluetooth HID Profile", "BT HID", "bthid");
392     bthid_handle = register_dissector("bthid", dissect_bthid, proto_bthid);
393 
394     proto_register_field_array(proto_bthid, hf, array_length(hf));
395     proto_register_subtree_array(ett, array_length(ett));
396     expert_bthid = expert_register_protocol(proto_bthid);
397     expert_register_field_array(expert_bthid, ei, array_length(ei));
398 
399     module = prefs_register_protocol_subtree("Bluetooth", proto_bthid, NULL);
400     prefs_register_static_text_preference(module, "hid.version",
401             "Bluetooth Profile HID version: 1.1",
402             "Version of profile supported by this dissector.");
403 
404     prefs_register_bool_preference(module, "hid.deprecated",
405             "Show what is deprecated in HID 1.1",
406             "Show what is deprecated in HID 1.1", &show_deprecated);
407 }
408 
409 
410 void
proto_reg_handoff_bthid(void)411 proto_reg_handoff_bthid(void)
412 {
413     usb_hid_boot_keyboard_input_report_handle  = find_dissector_add_dependency("usbhid.boot_report.keyboard.input", proto_bthid);
414     usb_hid_boot_keyboard_output_report_handle = find_dissector_add_dependency("usbhid.boot_report.keyboard.output", proto_bthid);
415     usb_hid_boot_mouse_input_report_handle     = find_dissector_add_dependency("usbhid.boot_report.mouse.input", proto_bthid);
416 
417     dissector_add_string("bluetooth.uuid", "11", bthid_handle);
418     dissector_add_string("bluetooth.uuid", "1124", bthid_handle);
419 
420     dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_HID_CTRL, bthid_handle);
421     dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_HID_INTR, bthid_handle);
422     dissector_add_for_decode_as("btl2cap.cid", bthid_handle);
423 }
424 
425 /*
426  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
427  *
428  * Local variables:
429  * c-basic-offset: 4
430  * tab-width: 8
431  * indent-tabs-mode: nil
432  * End:
433  *
434  * vi: set shiftwidth=4 tabstop=8 expandtab:
435  * :indentSize=4:tabSize=8:noTabs=true:
436  */
437