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