2 * Routines for the Bluetooth ISO dissection
3 * Copyright 2020, Jakub Pawlowski <jpawlowski@google.com>
4 * Copyright 2020, Allan M. Madsen <almomadk@gmail.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include <epan/packet.h>
16 #include <epan/prefs.h>
17 #include <epan/addr_resolv.h>
18 #include <epan/expert.h>
19 #include <epan/unit_strings.h>
21 #include "packet-bluetooth.h"
22 #include "packet-bthci_iso.h"
24 /* Initialize the protocol and registered fields */
25 static int proto_bthci_iso
;
26 static int hf_bthci_iso_chandle
;
27 static int hf_bthci_iso_pb_flag
;
28 static int hf_bthci_iso_ts_flag
;
29 static int hf_bthci_iso_reserved
;
30 static int hf_bthci_iso_data_length
;
31 static int hf_bthci_iso_data
;
32 static int hf_bthci_iso_continuation_to
;
33 static int hf_bthci_iso_reassembled_in
;
34 static int hf_bthci_iso_connect_in
;
35 static int hf_bthci_iso_disconnect_in
;
37 static int proto_bthci_iso_data
;
38 static int hf_bthci_iso_data_timestamp
;
39 static int hf_bthci_iso_data_packet_seq_num
;
40 static int hf_bthci_iso_data_sdu_length
;
41 static int hf_bthci_iso_data_status_flag
;
42 static int hf_bthci_iso_data_sdu
;
44 /* Initialize the subtree pointers */
45 static int ett_bthci_iso
;
46 static int ett_bthci_iso_data
;
48 static expert_field ei_length_bad
;
50 static dissector_handle_t bthci_iso_handle
;
51 static dissector_handle_t bthci_iso_data_handle
;
53 static bool iso_reassembly
= true;
55 typedef struct _multi_fragment_pdu_t
{
60 int cur_off
; /* counter used by reassembly */
61 } multi_fragment_pdu_t
;
63 typedef struct _chandle_data_t
{
64 wmem_tree_t
*start_fragments
; /* indexed by pinfo->num */
67 static wmem_tree_t
*chandle_tree
;
69 static const value_string iso_pb_flag_vals
[] = {
70 { 0x00, "First SDU Fragment"},
71 { 0x01, "Continuation SDU Fragment"},
72 { 0x02, "Complete SDU"},
73 { 0x03, "Last SDU Fragment"},
77 static const value_string iso_data_status_vals
[] = {
79 { 0x01, "Possibly Invalid"},
84 void proto_register_bthci_iso(void);
85 void proto_reg_handoff_bthci_iso(void);
86 void proto_register_iso_data(void);
88 typedef struct _iso_data_info_t
{
90 bool timestamp_present
;
93 /* Code to actually dissect the packets */
95 dissect_bthci_iso(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
97 proto_item
*bthci_iso_item
;
98 proto_tree
*bthci_iso_tree
;
101 uint16_t pb_flag
= false;
103 bool fragmented
= false;
106 chandle_data_t
*chandle_data
;
107 bluetooth_data_t
*bluetooth_data
;
108 wmem_tree_key_t key
[6];
109 uint32_t k_connection_handle
= 0;
110 uint32_t k_stream_handle
;
111 uint32_t k_frame_number
;
112 uint32_t k_interface_id
;
113 uint32_t k_adapter_id
;
115 remote_bdaddr_t
*remote_bdaddr
;
116 const char *localhost_name
;
117 uint8_t *localhost_bdaddr
;
118 const char *localhost_ether_addr
;
119 char *localhost_addr_name
;
120 int localhost_length
;
121 localhost_bdaddr_entry_t
*localhost_bdaddr_entry
;
122 localhost_name_entry_t
*localhost_name_entry
;
123 chandle_session_t
*chandle_session
;
124 wmem_tree_t
*subtree
;
125 stream_connection_handle_pair_t
*handle_pairs
;
126 iso_data_info_t iso_data_info
;
128 /* Reject the packet if data is NULL */
132 bthci_iso_item
= proto_tree_add_item(tree
, proto_bthci_iso
, tvb
, offset
, -1, ENC_NA
);
133 bthci_iso_tree
= proto_item_add_subtree(bthci_iso_item
, ett_bthci_iso
);
135 switch (pinfo
->p2p_dir
) {
137 col_set_str(pinfo
->cinfo
, COL_INFO
, "Sent ");
140 col_set_str(pinfo
->cinfo
, COL_INFO
, "Rcvd ");
143 col_set_str(pinfo
->cinfo
, COL_INFO
, "UnknownDirection ");
147 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "HCI_ISO");
149 flags
= tvb_get_letohs(tvb
, offset
);
150 pb_flag
= (flags
>> 12) & 0x3;
151 iso_data_info
.timestamp_present
= (flags
>> 14) & 0x1;
152 iso_data_info
.handle
= flags
& 0xfff;
153 proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_chandle
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
154 proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_pb_flag
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
155 proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_ts_flag
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
156 proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_reserved
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
159 length
= tvb_get_letohs(tvb
, offset
);
160 sub_item
= proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_data_length
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
163 /* determine if packet is fragmented */
164 if (pb_flag
!= 0x2) {
168 bluetooth_data
= (bluetooth_data_t
*) data
;
169 DISSECTOR_ASSERT(bluetooth_data
);
171 k_interface_id
= bluetooth_data
->interface_id
;
172 k_adapter_id
= bluetooth_data
->adapter_id
;
173 k_stream_handle
= flags
& 0x0fff;
174 direction
= pinfo
->p2p_dir
;
175 k_frame_number
= pinfo
->num
;
178 key
[0].key
= &k_interface_id
;
180 key
[1].key
= &k_adapter_id
;
182 key
[2].key
= &k_stream_handle
;
186 subtree
= (wmem_tree_t
*) wmem_tree_lookup32_array(bluetooth_data
->chandle_sessions
, key
);
187 chandle_session
= (subtree
) ? (chandle_session_t
*) wmem_tree_lookup32_le(subtree
, pinfo
->num
) : NULL
;
188 if (!(chandle_session
&&
189 chandle_session
->connect_in_frame
< pinfo
->num
&&
190 chandle_session
->disconnect_in_frame
> pinfo
->num
)){
191 chandle_session
= NULL
;
194 /* replace stream (CIS/BIS) handle with connection (ACL) handle */
195 subtree
= (wmem_tree_t
*) wmem_tree_lookup32_array(bluetooth_data
->shandle_to_chandle
, key
);
196 handle_pairs
= (subtree
) ? (stream_connection_handle_pair_t
*) wmem_tree_lookup32_le(subtree
, pinfo
->num
) : NULL
;
198 k_connection_handle
= handle_pairs
->chandle
;
199 key
[2].key
= &k_connection_handle
;
203 key
[3].key
= &k_frame_number
;
207 /* remote bdaddr and name */
208 remote_bdaddr
= (remote_bdaddr_t
*)wmem_tree_lookup32_array_le(bluetooth_data
->chandle_to_bdaddr
, key
);
210 * XXX - do this only if we found a handle pair, so that we have
211 * a connection handle?
213 if (remote_bdaddr
&& remote_bdaddr
->interface_id
== bluetooth_data
->interface_id
&&
214 remote_bdaddr
->adapter_id
== bluetooth_data
->adapter_id
&&
215 remote_bdaddr
->chandle
== k_connection_handle
) {
216 uint32_t k_bd_addr_oui
;
217 uint32_t k_bd_addr_id
;
218 uint32_t bd_addr_oui
;
220 device_name_t
*device_name
;
221 const char *remote_name
;
222 const char *remote_ether_addr
;
223 char *remote_addr_name
;
226 bd_addr_oui
= remote_bdaddr
->bd_addr
[0] << 16 | remote_bdaddr
->bd_addr
[1] << 8 | remote_bdaddr
->bd_addr
[2];
227 bd_addr_id
= remote_bdaddr
->bd_addr
[3] << 16 | remote_bdaddr
->bd_addr
[4] << 8 | remote_bdaddr
->bd_addr
[5];
229 k_bd_addr_oui
= bd_addr_oui
;
230 k_bd_addr_id
= bd_addr_id
;
231 k_frame_number
= pinfo
->num
;
234 key
[0].key
= &k_interface_id
;
236 key
[1].key
= &k_adapter_id
;
238 key
[2].key
= &k_bd_addr_id
;
240 key
[3].key
= &k_bd_addr_oui
;
242 key
[4].key
= &k_frame_number
;
246 device_name
= (device_name_t
*)wmem_tree_lookup32_array_le(bluetooth_data
->bdaddr_to_name
, key
);
247 if (device_name
&& device_name
->bd_addr_oui
== bd_addr_oui
&& device_name
->bd_addr_id
== bd_addr_id
)
248 remote_name
= device_name
->name
;
252 remote_ether_addr
= get_ether_name(remote_bdaddr
->bd_addr
);
253 remote_length
= (int)(strlen(remote_ether_addr
) + 3 + strlen(remote_name
) + 1);
254 remote_addr_name
= (char *)wmem_alloc(pinfo
->pool
, remote_length
);
256 snprintf(remote_addr_name
, remote_length
, "%s (%s)", remote_ether_addr
, remote_name
);
258 if (pinfo
->p2p_dir
== P2P_DIR_RECV
) {
259 set_address(&pinfo
->net_src
, AT_STRINGZ
, (int)strlen(remote_name
) + 1, remote_name
);
260 set_address(&pinfo
->dl_src
, AT_ETHER
, 6, remote_bdaddr
->bd_addr
);
261 set_address(&pinfo
->src
, AT_STRINGZ
, (int)strlen(remote_addr_name
) + 1, remote_addr_name
);
262 } else if (pinfo
->p2p_dir
== P2P_DIR_SENT
) {
263 set_address(&pinfo
->net_dst
, AT_STRINGZ
, (int)strlen(remote_name
) + 1, remote_name
);
264 set_address(&pinfo
->dl_dst
, AT_ETHER
, 6, remote_bdaddr
->bd_addr
);
265 set_address(&pinfo
->dst
, AT_STRINGZ
, (int)strlen(remote_addr_name
) + 1, remote_addr_name
);
268 if (pinfo
->p2p_dir
== P2P_DIR_RECV
) {
269 set_address(&pinfo
->net_src
, AT_STRINGZ
, 1, "");
270 set_address(&pinfo
->dl_src
, AT_STRINGZ
, 1, "");
271 set_address(&pinfo
->src
, AT_STRINGZ
, 10, "remote ()");
272 } else if (pinfo
->p2p_dir
== P2P_DIR_SENT
) {
273 set_address(&pinfo
->net_dst
, AT_STRINGZ
, 1, "");
274 set_address(&pinfo
->dl_dst
, AT_STRINGZ
, 1, "");
275 set_address(&pinfo
->dst
, AT_STRINGZ
, 10, "remote ()");
279 /* localhost bdaddr and name */
281 key
[0].key
= &k_interface_id
;
283 key
[1].key
= &k_adapter_id
;
285 key
[2].key
= &k_frame_number
;
290 localhost_bdaddr_entry
= (localhost_bdaddr_entry_t
*)wmem_tree_lookup32_array_le(bluetooth_data
->localhost_bdaddr
, key
);
291 localhost_bdaddr
= (uint8_t *) wmem_alloc(pinfo
->pool
, 6);
292 if (localhost_bdaddr_entry
&& localhost_bdaddr_entry
->interface_id
== bluetooth_data
->interface_id
&&
293 localhost_bdaddr_entry
->adapter_id
== bluetooth_data
->adapter_id
) {
295 localhost_ether_addr
= get_ether_name(localhost_bdaddr_entry
->bd_addr
);
296 memcpy(localhost_bdaddr
, localhost_bdaddr_entry
->bd_addr
, 6);
298 localhost_ether_addr
= "localhost";
299 /* XXX - is this the right value to use? */
300 memset(localhost_bdaddr
, 0, 6);
303 localhost_name_entry
= (localhost_name_entry_t
*)wmem_tree_lookup32_array_le(bluetooth_data
->localhost_name
, key
);
304 if (localhost_name_entry
&& localhost_name_entry
->interface_id
== bluetooth_data
->interface_id
&&
305 localhost_name_entry
->adapter_id
== bluetooth_data
->adapter_id
)
306 localhost_name
= localhost_name_entry
->name
;
310 localhost_length
= (int)(strlen(localhost_ether_addr
) + 3 + strlen(localhost_name
) + 1);
311 localhost_addr_name
= (char *)wmem_alloc(pinfo
->pool
, localhost_length
);
313 snprintf(localhost_addr_name
, localhost_length
, "%s (%s)", localhost_ether_addr
, localhost_name
);
315 if (pinfo
->p2p_dir
== P2P_DIR_RECV
) {
316 set_address(&pinfo
->net_dst
, AT_STRINGZ
, (int)strlen(localhost_name
) + 1, localhost_name
);
317 set_address(&pinfo
->dl_dst
, AT_ETHER
, 6, localhost_bdaddr
);
318 set_address(&pinfo
->dst
, AT_STRINGZ
, (int)strlen(localhost_addr_name
) + 1, localhost_addr_name
);
319 } else if (pinfo
->p2p_dir
== P2P_DIR_SENT
) {
320 set_address(&pinfo
->net_src
, AT_STRINGZ
, (int)strlen(localhost_name
) + 1, localhost_name
);
321 set_address(&pinfo
->dl_src
, AT_ETHER
, 6, localhost_bdaddr
);
322 set_address(&pinfo
->src
, AT_STRINGZ
, (int)strlen(localhost_addr_name
) + 1, localhost_addr_name
);
325 /* find the chandle_data structure associated with this chandle */
327 key
[0].key
= &k_interface_id
;
329 key
[1].key
= &k_adapter_id
;
331 key
[2].key
= &k_stream_handle
;
333 key
[3].key
= &direction
;
337 subtree
= (wmem_tree_t
*) wmem_tree_lookup32_array(chandle_tree
, key
);
338 chandle_data
= (subtree
) ? (chandle_data_t
*) wmem_tree_lookup32_le(subtree
, pinfo
->num
) : NULL
;
339 if (!pinfo
->fd
->visited
&& !chandle_data
) {
341 key
[0].key
= &k_interface_id
;
343 key
[1].key
= &k_adapter_id
;
345 key
[2].key
= &k_stream_handle
;
347 key
[3].key
= &direction
;
349 key
[4].key
= &k_frame_number
;
353 chandle_data
= wmem_new(wmem_file_scope(), chandle_data_t
);
354 chandle_data
->start_fragments
= wmem_tree_new(wmem_file_scope());
356 wmem_tree_insert32_array(chandle_tree
, key
, chandle_data
);
357 } else if (pinfo
->fd
->visited
&& !chandle_data
) {
358 DISSECTOR_ASSERT_HINT(0, "Impossible: no previously session saved");
361 if (!fragmented
|| (!iso_reassembly
&& !pb_flag
)) {
362 /* call ISO data dissector for PDUs that are not fragmented
363 * also for the first fragment if reassembly is disabled
365 if (length
< tvb_captured_length_remaining(tvb
, offset
)) {
367 expert_add_info(pinfo
, sub_item
, &ei_length_bad
);
368 /* Try to dissect as much as possible */
369 length
= tvb_captured_length_remaining(tvb
, offset
);
372 next_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, tvb_captured_length_remaining(tvb
, offset
), length
);
373 call_dissector_with_data(bthci_iso_data_handle
, next_tvb
, pinfo
, tree
, &iso_data_info
);
374 } else if (fragmented
&& iso_reassembly
) {
375 multi_fragment_pdu_t
*mfp
= NULL
;
377 if (pb_flag
== 0x00) { /* first fragment */
378 if (!pinfo
->fd
->visited
) {
379 int timestamp_size
= 0;
380 mfp
= (multi_fragment_pdu_t
*) wmem_new(wmem_file_scope(), multi_fragment_pdu_t
);
381 mfp
->first_frame
= pinfo
->num
;
384 len
= tvb_captured_length_remaining(tvb
, offset
);
385 if (flags
& 0x4000) { /* 4 byte timestamp is present */
388 mfp
->tot_len
+= timestamp_size
+ (tvb_get_letohs(tvb
, offset
+ 2 + timestamp_size
) & 0xfff);
389 mfp
->reassembled
= (char *) wmem_alloc(wmem_file_scope(), mfp
->tot_len
);
390 if (len
<= mfp
->tot_len
) {
391 tvb_memcpy(tvb
, (uint8_t *) mfp
->reassembled
, offset
, len
);
393 wmem_tree_insert32(chandle_data
->start_fragments
, pinfo
->num
, mfp
);
396 mfp
= (multi_fragment_pdu_t
*)wmem_tree_lookup32(chandle_data
->start_fragments
, pinfo
->num
);
398 if (mfp
!= NULL
&& mfp
->last_frame
) {
401 item
= proto_tree_add_uint(bthci_iso_tree
, hf_bthci_iso_reassembled_in
, tvb
, 0, 0, mfp
->last_frame
);
402 proto_item_set_generated(item
);
403 col_append_frame_number(pinfo
, COL_INFO
, " [Reassembled in #%u]", mfp
->last_frame
);
406 else if (pb_flag
& 0x01) { /* continuation/last fragment */
407 mfp
= (multi_fragment_pdu_t
*)wmem_tree_lookup32_le(chandle_data
->start_fragments
, pinfo
->num
);
408 if (!pinfo
->fd
->visited
) {
409 len
= tvb_captured_length_remaining(tvb
, offset
);
410 if (mfp
!= NULL
&& !mfp
->last_frame
) {
411 int avail
= (int)mfp
->tot_len
- mfp
->cur_off
;
413 expert_add_info(pinfo
, sub_item
, &ei_length_bad
);
414 /* Try to reassemble as much as possible */
417 tvb_memcpy(tvb
, (uint8_t *) mfp
->reassembled
+ mfp
->cur_off
, offset
, len
);
420 mfp
->last_frame
= pinfo
->num
;
426 item
= proto_tree_add_uint(bthci_iso_tree
, hf_bthci_iso_continuation_to
, tvb
, 0, 0, mfp
->first_frame
);
427 proto_item_set_generated(item
);
428 col_append_frame_number(pinfo
, COL_INFO
, " [Continuation to #%u]", mfp
->first_frame
);
429 if (mfp
->last_frame
&& mfp
->last_frame
!= pinfo
->num
) {
430 item
= proto_tree_add_uint(bthci_iso_tree
, hf_bthci_iso_reassembled_in
, tvb
, 0, 0, mfp
->last_frame
);
431 proto_item_set_generated(item
);
432 col_append_frame_number(pinfo
, COL_INFO
, " [Reassembled in #%u]", mfp
->last_frame
);
435 if (pb_flag
== 0x03) { /* last fragment */
436 next_tvb
= tvb_new_child_real_data(tvb
, (uint8_t *) mfp
->reassembled
, mfp
->tot_len
, mfp
->tot_len
);
437 add_new_data_source(pinfo
, next_tvb
, "Reassembled BTHCI ISO");
439 call_dissector_with_data(bthci_iso_data_handle
, next_tvb
, pinfo
, tree
, &iso_data_info
);
445 if (tvb_captured_length_remaining(tvb
, offset
) > 0) {
446 sub_item
= proto_tree_add_item(bthci_iso_tree
, hf_bthci_iso_data
, tvb
, offset
, -1, ENC_NA
);
448 proto_item_append_text(sub_item
, " Fragment");
452 if (chandle_session
) {
453 sub_item
= proto_tree_add_uint(bthci_iso_tree
, hf_bthci_iso_connect_in
, tvb
, 0, 0, chandle_session
->connect_in_frame
);
454 proto_item_set_generated(sub_item
);
456 if (chandle_session
->disconnect_in_frame
< UINT32_MAX
) {
457 sub_item
= proto_tree_add_uint(bthci_iso_tree
, hf_bthci_iso_disconnect_in
, tvb
, 0, 0, chandle_session
->disconnect_in_frame
);
458 proto_item_set_generated(sub_item
);
462 return tvb_reported_length(tvb
);
467 proto_register_bthci_iso(void)
469 module_t
*bthci_iso_module
;
470 expert_module_t
*bthci_iso_expert_module
;
471 static hf_register_info hf
[] = {
472 { &hf_bthci_iso_chandle
,
473 { "Connection Handle", "bthci_iso.chandle",
474 FT_UINT16
, BASE_HEX
, NULL
, 0x0FFF,
477 { &hf_bthci_iso_pb_flag
,
478 { "PB Flag", "bthci_iso.pb_flag",
479 FT_UINT16
, BASE_HEX
, VALS(iso_pb_flag_vals
), 0x3000,
480 "Packet Boundary Flag", HFILL
}
482 { &hf_bthci_iso_ts_flag
,
483 { "Timestamp present", "bthci_iso.ts_flag",
484 FT_BOOLEAN
, 16, NULL
, 0x4000,
487 { &hf_bthci_iso_reserved
,
488 { "Reserved", "bthci_iso.reserved",
489 FT_UINT16
, BASE_HEX
, NULL
, 0x8000,
492 { &hf_bthci_iso_continuation_to
,
493 { "This is a continuation to the PDU in frame", "bthci_iso.continuation_to",
494 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
495 "This is a continuation to the PDU in frame #", HFILL
}
497 { &hf_bthci_iso_reassembled_in
,
498 { "This PDU is reassembled in frame", "bthci_iso.reassembled_in",
499 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
500 "This PDU is reassembled in frame #", HFILL
}
502 { &hf_bthci_iso_connect_in
,
503 { "Connect in frame", "bthci_iso.connect_in",
504 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
507 { &hf_bthci_iso_disconnect_in
,
508 { "Disconnect in frame", "bthci_iso.disconnect_in",
509 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
512 { &hf_bthci_iso_data_length
,
513 { "Data Length", "bthci_iso.data_length",
514 FT_UINT16
, BASE_DEC
, NULL
, 0x3FFF,
517 { &hf_bthci_iso_data
,
518 { "Data", "bthci_iso.data",
519 FT_NONE
, BASE_NONE
, NULL
, 0x0,
524 /* Setup protocol subtree array */
525 static int *ett
[] = {
529 static ei_register_info ei
[] = {
530 { &ei_length_bad
, { "bthci_iso.length.bad", PI_MALFORMED
, PI_WARN
, "Invalid length", EXPFILL
}},
533 /* Register the protocol name and description */
534 proto_bthci_iso
= proto_register_protocol("Bluetooth HCI ISO Packet", "HCI_ISO", "bthci_iso");
535 bthci_iso_handle
= register_dissector("bthci_iso", dissect_bthci_iso
, proto_bthci_iso
);
537 /* Required function calls to register the header fields and subtrees used */
538 proto_register_field_array(proto_bthci_iso
, hf
, array_length(hf
));
539 proto_register_subtree_array(ett
, array_length(ett
));
541 bthci_iso_expert_module
= expert_register_protocol(proto_bthci_iso
);
542 expert_register_field_array(bthci_iso_expert_module
, ei
, array_length(ei
));
544 /* Register configuration preferences */
545 bthci_iso_module
= prefs_register_protocol_subtree("Bluetooth", proto_bthci_iso
, NULL
);
546 prefs_register_bool_preference(bthci_iso_module
, "hci_iso_reassembly",
547 "Reassemble ISO Fragments",
548 "Whether the ISO dissector should reassemble fragmented PDUs",
551 chandle_tree
= wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
556 proto_reg_handoff_bthci_iso(void)
558 dissector_add_uint("hci_h4.type", HCI_H4_TYPE_ISO
, bthci_iso_handle
);
559 dissector_add_uint("hci_h1.type", BTHCI_CHANNEL_ISO
, bthci_iso_handle
);
563 dissect_iso_data(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
565 proto_item
*iso_data_load_item
;
566 proto_tree
*iso_data_load_tree
;
570 iso_data_info_t
*iso_data_info
= (iso_data_info_t
*) data
;
573 iso_data_load_item
= proto_tree_add_item(tree
, proto_bthci_iso_data
, tvb
, offset
, -1, ENC_NA
);
574 iso_data_load_tree
= proto_item_add_subtree(iso_data_load_item
, ett_bthci_iso_data
);
576 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ISO Data");
578 if (iso_data_info
->timestamp_present
) {
579 proto_tree_add_item(iso_data_load_tree
, hf_bthci_iso_data_timestamp
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
583 seq_no
= tvb_get_letohs(tvb
, offset
);
584 proto_tree_add_item(iso_data_load_tree
, hf_bthci_iso_data_packet_seq_num
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
587 proto_tree_add_item_ret_uint(iso_data_load_tree
, hf_bthci_iso_data_sdu_length
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
, &sdu_length
);
589 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Handle: 0x%x, SeqNo: %d, SDU length: %d", iso_data_info
->handle
, seq_no
, sdu_length
);
591 if (pinfo
->p2p_dir
== P2P_DIR_RECV
) {
592 uint16_t status
= tvb_get_letohs(tvb
, offset
) >> 14;
593 proto_tree_add_item(iso_data_load_tree
, hf_bthci_iso_data_status_flag
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
594 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", Status: %s", val_to_str_const(status
, VALS(iso_data_status_vals
), "RFU"));
598 remaining
= tvb_captured_length_remaining(tvb
, offset
);
601 item
= proto_tree_add_item(iso_data_load_tree
, hf_bthci_iso_data_sdu
, tvb
, offset
, -1, ENC_NA
);
602 if (remaining
< (uint16_t)sdu_length
)
603 proto_item_append_text(item
, " (Incomplete)");
611 proto_register_iso_data(void)
613 static hf_register_info hf
[] = {
614 { &hf_bthci_iso_data_timestamp
,
615 { "Timestamp", "bthci_iso_data.timestamp",
616 FT_UINT32
, BASE_DEC
|BASE_UNIT_STRING
, UNS(&units_microseconds
), 0x0,
619 { &hf_bthci_iso_data_packet_seq_num
,
620 { "Sequence Number", "bthci_iso_data.packet_seq_num",
621 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
624 { &hf_bthci_iso_data_sdu_length
,
625 { "SDU Length", "bthci_iso_data.sdu_length",
626 FT_UINT16
, BASE_DEC
, NULL
, 0x0FFF,
629 { &hf_bthci_iso_data_status_flag
,
630 { "Data Status Flag", "bthci_iso_data.status_flag",
631 FT_UINT16
, BASE_DEC
, VALS(iso_data_status_vals
), 0xC000,
634 { &hf_bthci_iso_data_sdu
,
635 { "SDU", "bthci_iso_data.sdu",
636 FT_NONE
, BASE_NONE
, NULL
, 0x0,
641 static int *ett
[] = {
645 proto_bthci_iso_data
= proto_register_protocol("Bluetooth ISO Data", "BT ISO Data", "bthci_iso_data");
647 proto_register_field_array(proto_bthci_iso_data
, hf
, array_length(hf
));
648 proto_register_subtree_array(ett
, array_length(ett
));
650 bthci_iso_data_handle
= register_dissector("bthci_iso_data", dissect_iso_data
, proto_bthci_iso_data
);
655 * Editor modelines - https://www.wireshark.org/tools/modelines.html
660 * indent-tabs-mode: nil
663 * vi: set shiftwidth=4 tabstop=8 expandtab:
664 * :indentSize=4:tabSize=8:noTabs=true: