Coverage Report

Created: 2026-01-02 06:13

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