Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-bthid.c
Line
Count
Source
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;
23
static int hf_bthid_transaction_type;
24
static int hf_bthid_parameter_reserved;
25
static int hf_bthid_parameter_reserved_31;
26
static int hf_bthid_parameter_reserved_32;
27
static int hf_bthid_parameter_reserved_2;
28
static int hf_bthid_parameter_result_code;
29
static int hf_bthid_parameter_control_operation;
30
static int hf_bthid_parameter_size;
31
static int hf_bthid_protocol;
32
static int hf_bthid_idle_rate;
33
static int hf_bthid_parameter_report_type;
34
static int hf_bthid_report_id;
35
static int hf_bthid_buffer_size;
36
static int hf_bthid_protocol_code;
37
static int hf_bthid_data;
38
39
static int ett_bthid;
40
41
static expert_field ei_bthid_parameter_control_operation_deprecated;
42
static expert_field ei_bthid_transaction_type_deprecated;
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 bool show_deprecated;
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 int
123
dissect_hid_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
124
        int offset, unsigned report_type)
125
3
{
126
3
    unsigned int protocol_code;
127
128
3
    proto_tree_add_item_ret_uint(tree, hf_bthid_protocol_code, tvb, offset, 1, ENC_BIG_ENDIAN, &protocol_code);
129
3
    col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(protocol_code, protocol_code_vals, "unknown type"));
130
3
    offset += 1;
131
132
3
    switch (protocol_code) {
133
0
        case 0x01: /* Keyboard */
134
0
            if (report_type == 0x02) { /* Output - LEDs */
135
0
                offset += call_dissector_with_data(usb_hid_boot_keyboard_output_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
136
137
0
                break;
138
0
            } else if (report_type != 0x01) {/* is not Input (Keys) */
139
0
                break;
140
0
            }
141
142
0
            offset += call_dissector_with_data(usb_hid_boot_keyboard_input_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
143
144
0
            break;
145
3
        case 0x02: /* Mouse */
146
3
            offset += call_dissector_with_data(usb_hid_boot_mouse_input_report_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
147
148
3
            break;
149
3
    }
150
151
3
    return offset;
152
3
}
153
154
static int
155
dissect_bthid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
156
3
{
157
3
    proto_item   *ti;
158
3
    proto_tree   *bthid_tree;
159
3
    int           offset = 0;
160
3
    unsigned      transaction_type;
161
3
    unsigned      parameter;
162
3
    unsigned      protocol;
163
3
    unsigned      idle_rate;
164
3
    uint8_t       control_operation;
165
3
    proto_item   *pitem;
166
167
3
    ti = proto_tree_add_item(tree, proto_bthid, tvb, offset, -1, ENC_NA);
168
3
    bthid_tree = proto_item_add_subtree(ti, ett_bthid);
169
170
3
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "HID");
171
3
    col_clear(pinfo->cinfo, COL_INFO);
172
173
3
    switch (pinfo->p2p_dir) {
174
0
        case P2P_DIR_SENT:
175
0
            col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
176
0
            break;
177
0
        case P2P_DIR_RECV:
178
0
            col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
179
0
            break;
180
3
        default:
181
3
            col_set_str(pinfo->cinfo, COL_INFO, "UnknownDirection ");
182
3
            break;
183
3
    }
184
185
3
    pitem = proto_tree_add_item_ret_uint(bthid_tree, hf_bthid_transaction_type, tvb, offset, 1, ENC_BIG_ENDIAN, &transaction_type);
186
3
    parameter = tvb_get_uint8(tvb, offset) & 0x0F;
187
188
3
    col_append_str(pinfo->cinfo, COL_INFO, val_to_str_const(transaction_type, transaction_type_vals, "Unknown TransactionType"));
189
190
3
    switch(transaction_type) {
191
0
        case 0x00: /* HANDSHAKE */
192
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_result_code, tvb, offset, 1, ENC_BIG_ENDIAN);
193
0
            offset += 1;
194
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Result Code: %s", val_to_str_const(parameter, result_code_vals, "reserved"));
195
0
            break;
196
0
        case 0x01: /* HID_CONTROL */
197
0
            pitem = proto_tree_add_item_ret_uint8(bthid_tree, hf_bthid_parameter_control_operation, tvb, offset, 1, ENC_BIG_ENDIAN, &control_operation);
198
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Control Operation: %s", val_to_str_const(parameter, control_operation_vals, "reserved"));
199
0
            if (control_operation < 3 && show_deprecated)
200
0
                expert_add_info(pinfo, pitem, &ei_bthid_parameter_control_operation_deprecated);
201
0
            offset += 1;
202
0
            break;
203
0
        case 0x04: /* GET_REPORT */
204
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_size, tvb, offset, 1, ENC_BIG_ENDIAN);
205
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_2, tvb, offset, 1, ENC_BIG_ENDIAN);
206
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
207
0
            offset += 1;
208
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Size: %s, Report Type: %s",
209
0
                            val_to_str_const(parameter >> 3 , size_vals, "reserved"),
210
0
                            val_to_str_const(parameter & 0x03, report_type_vals, "reserved"));
211
212
            /* XXX: This is workaround, this should come from SDP:
213
               "This field is required in Report Protocol Mode when any Report ID
214
               Global Items are declared in the report descriptor, and in
215
               Boot Protocol Mode. Otherwise the field does not exist."
216
            */
217
0
            if (((parameter >> 3) && tvb_reported_length_remaining(tvb, offset) >= 3) ||
218
0
                    (!(parameter >> 3) && tvb_reported_length_remaining(tvb, offset) >= 1)) {
219
0
                proto_tree_add_item(bthid_tree, hf_bthid_report_id, tvb, offset, 1, ENC_BIG_ENDIAN);
220
0
                offset += 1;
221
0
            }
222
223
0
            if (parameter >> 3) {
224
0
                proto_tree_add_item(bthid_tree, hf_bthid_buffer_size, tvb, offset, 2, ENC_LITTLE_ENDIAN);
225
0
                offset += 2;
226
0
            }
227
0
            break;
228
0
        case 0x05: /* SET_REPORT */
229
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_32, tvb, offset, 1, ENC_BIG_ENDIAN);
230
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
231
0
            offset += 1;
232
233
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Report Type: %s",
234
0
                            val_to_str_const(parameter & 0x03, report_type_vals, "reserved"));
235
236
            /* playload */
237
0
            proto_tree_add_item(bthid_tree, hf_bthid_data, tvb, offset, -1, ENC_NA);
238
0
            offset += tvb_captured_length_remaining(tvb, offset);
239
0
            break;
240
0
        case 0x06: /* GET_PROTOCOL */
241
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
242
0
            offset += 1;
243
244
0
            proto_tree_add_item_ret_uint(bthid_tree, hf_bthid_protocol, tvb, offset, 1, ENC_BIG_ENDIAN, &protocol);
245
0
            offset += 1;
246
247
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Protocol: %s",
248
0
                            val_to_str_const(protocol, protocol_vals, "reserved"));
249
250
0
            break;
251
0
        case 0x07: /* SET_PROTOCOL */
252
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_31, tvb, offset, 1, ENC_BIG_ENDIAN);
253
0
            proto_tree_add_item(bthid_tree, hf_bthid_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
254
0
            offset += 1;
255
256
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Protocol: %s",
257
0
                            val_to_str_const(parameter & 0x01, protocol_vals, "reserved"));
258
0
            break;
259
0
        case 0x08: /* GET_IDLE */
260
0
        case 0x09: /* SET_IDLE */
261
0
            if (show_deprecated)
262
0
                expert_add_info(pinfo, pitem, &ei_bthid_transaction_type_deprecated);
263
264
0
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved, tvb, offset, 1, ENC_BIG_ENDIAN);
265
0
            offset += 1;
266
267
0
            pitem = proto_tree_add_item_ret_uint(bthid_tree, hf_bthid_idle_rate, tvb, offset, 1, ENC_BIG_ENDIAN, &idle_rate);
268
0
            proto_item_append_text(pitem, " (%u.%03u ms)", idle_rate * 4 / 1000, idle_rate * 4 % 1000);
269
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " - Idle Rate: %u.%03u ms", idle_rate*4/1000, idle_rate*4%1000);
270
0
            offset += 1;
271
0
            break;
272
0
        case 0x0B: /* DATC */
273
0
            if (show_deprecated)
274
0
                expert_add_info(pinfo, pitem, &ei_bthid_transaction_type_deprecated);
275
            /* FALL THROUGH */
276
3
        case 0x0A: /* DATA */
277
3
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_reserved_32, tvb, offset, 1, ENC_BIG_ENDIAN);
278
3
            proto_tree_add_item(bthid_tree, hf_bthid_parameter_report_type, tvb, offset, 1, ENC_BIG_ENDIAN);
279
3
            offset += 1;
280
3
            col_append_fstr(pinfo->cinfo, COL_INFO, " - %s", val_to_str_const(parameter, report_type_vals, "reserved"));
281
282
            /* playload */
283
3
            offset = dissect_hid_data(tvb, pinfo,  bthid_tree, offset, parameter & 0x03);
284
3
            break;
285
3
    }
286
287
3
    return offset;
288
3
}
289
290
291
void
292
proto_register_bthid(void)
293
14
{
294
14
    module_t *module;
295
14
    expert_module_t* expert_bthid;
296
297
14
    static hf_register_info hf[] = {
298
14
        { &hf_bthid_transaction_type,
299
14
            { "Transaction Type",                "bthid.transaction_type",
300
14
            FT_UINT8, BASE_HEX, VALS(transaction_type_vals), 0xF0,
301
14
            NULL, HFILL }
302
14
        },
303
14
        { &hf_bthid_parameter_reserved,
304
14
            { "Parameter reserved",              "bthid.parameter.reserved",
305
14
            FT_UINT8, BASE_HEX, NULL, 0x0F,
306
14
            NULL, HFILL }
307
14
        },
308
14
        { &hf_bthid_parameter_reserved_32,
309
14
            { "Parameter reserved",              "bthid.parameter.reserved_32",
310
14
            FT_UINT8, BASE_HEX, NULL, 0x0C,
311
14
            NULL, HFILL }
312
14
        },
313
14
        { &hf_bthid_parameter_reserved_31,
314
14
            { "Parameter reserved",              "bthid.parameter.reserved_31",
315
14
            FT_UINT8, BASE_HEX, NULL, 0x0E,
316
14
            NULL, HFILL }
317
14
        },
318
14
        { &hf_bthid_parameter_reserved_2,
319
14
            { "Parameter reserved",              "bthid.parameter.reserved_2",
320
14
            FT_UINT8, BASE_HEX, NULL, 0x04,
321
14
            NULL, HFILL }
322
14
        },
323
14
        { &hf_bthid_parameter_report_type,
324
14
            { "Report Type",                     "bthid.parameter.report_type",
325
14
            FT_UINT8, BASE_HEX, VALS(report_type_vals), 0x03,
326
14
            NULL, HFILL }
327
14
        },
328
14
        { &hf_bthid_parameter_size,
329
14
            { "Size",                            "bthid.parameter.size",
330
14
            FT_UINT8, BASE_HEX, VALS(size_vals), 0x08,
331
14
            NULL, HFILL }
332
14
        },
333
14
        { &hf_bthid_parameter_result_code,
334
14
            { "Result Code",                     "bthid.result_code",
335
14
            FT_UINT8, BASE_HEX, VALS(result_code_vals), 0x0F,
336
14
            NULL, HFILL }
337
14
        },
338
14
        { &hf_bthid_parameter_control_operation,
339
14
            { "Control Operation",               "bthid.control_operation",
340
14
            FT_UINT8, BASE_HEX, VALS(control_operation_vals), 0x0F,
341
14
            NULL, HFILL }
342
14
        },
343
14
        { &hf_bthid_protocol,
344
14
            { "Protocol",                        "bthid.protocol",
345
14
            FT_UINT8, BASE_HEX, VALS(protocol_vals), 0x01,
346
14
            NULL, HFILL }
347
14
        },
348
14
        { &hf_bthid_idle_rate,
349
14
            { "Idle Rate",                       "bthid.idle_rate",
350
14
            FT_UINT8, BASE_DEC, NULL, 0x00,
351
14
            NULL, HFILL }
352
14
        },
353
14
        { &hf_bthid_report_id,
354
14
            { "Report Id",                       "bthid.report_id",
355
14
            FT_UINT8, BASE_HEX, VALS(protocol_code_vals), 0x00,
356
14
            NULL, HFILL }
357
14
        },
358
14
        { &hf_bthid_buffer_size,
359
14
            { "Buffer Size",                     "bthid.buffer_size",
360
14
            FT_UINT16, BASE_HEX, NULL, 0x00,
361
14
            NULL, HFILL }
362
14
        },
363
14
        { &hf_bthid_protocol_code,
364
14
            { "Protocol Code",                   "bthid.data.protocol_code",
365
14
            FT_UINT8, BASE_HEX, VALS(protocol_code_vals), 0x00,
366
14
            NULL, HFILL }
367
14
        },
368
14
        { &hf_bthid_data,
369
14
            { "Data",                            "bthid.data",
370
14
            FT_NONE, BASE_NONE, NULL, 0x00,
371
14
            NULL, HFILL }
372
14
        },
373
374
14
    };
375
376
14
    static int *ett[] = {
377
14
        &ett_bthid
378
14
    };
379
380
14
    static ei_register_info ei[] = {
381
14
        { &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 }},
382
14
        { &ei_bthid_transaction_type_deprecated, { "bthid.transaction_type.deprecated", PI_PROTOCOL, PI_WARN, "This Transaction Type is deprecated by HID 1.1", EXPFILL }},
383
14
    };
384
385
14
    proto_bthid = proto_register_protocol("Bluetooth HID Profile", "BT HID", "bthid");
386
14
    bthid_handle = register_dissector("bthid", dissect_bthid, proto_bthid);
387
388
14
    proto_register_field_array(proto_bthid, hf, array_length(hf));
389
14
    proto_register_subtree_array(ett, array_length(ett));
390
14
    expert_bthid = expert_register_protocol(proto_bthid);
391
14
    expert_register_field_array(expert_bthid, ei, array_length(ei));
392
393
14
    module = prefs_register_protocol_subtree("Bluetooth", proto_bthid, NULL);
394
14
    prefs_register_static_text_preference(module, "hid.version",
395
14
            "Bluetooth Profile HID version: 1.1",
396
14
            "Version of profile supported by this dissector.");
397
398
14
    prefs_register_bool_preference(module, "hid.deprecated",
399
14
            "Show what is deprecated in HID 1.1",
400
14
            "Show what is deprecated in HID 1.1", &show_deprecated);
401
14
}
402
403
404
void
405
proto_reg_handoff_bthid(void)
406
14
{
407
14
    usb_hid_boot_keyboard_input_report_handle  = find_dissector_add_dependency("usbhid.boot_report.keyboard.input", proto_bthid);
408
14
    usb_hid_boot_keyboard_output_report_handle = find_dissector_add_dependency("usbhid.boot_report.keyboard.output", proto_bthid);
409
14
    usb_hid_boot_mouse_input_report_handle     = find_dissector_add_dependency("usbhid.boot_report.mouse.input", proto_bthid);
410
411
14
    dissector_add_string("bluetooth.uuid", "11", bthid_handle);
412
14
    dissector_add_string("bluetooth.uuid", "1124", bthid_handle);
413
414
14
    dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_HID_CTRL, bthid_handle);
415
14
    dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_HID_INTR, bthid_handle);
416
14
    dissector_add_for_decode_as("btl2cap.cid", bthid_handle);
417
14
}
418
419
/*
420
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
421
 *
422
 * Local variables:
423
 * c-basic-offset: 4
424
 * tab-width: 8
425
 * indent-tabs-mode: nil
426
 * End:
427
 *
428
 * vi: set shiftwidth=4 tabstop=8 expandtab:
429
 * :indentSize=4:tabSize=8:noTabs=true:
430
 */