xref: /libbtbb/lib/src/bluetooth_le_packet.c (revision f25ef1fb45c64c5530b5b87a548b3f183d843785)
1e25b118aSDominic Spill /* -*- c -*- */
2e25b118aSDominic Spill /*
3e25b118aSDominic Spill  * Copyright 2007 - 2012 Mike Ryan, Dominic Spill, Michael Ossmann
4e25b118aSDominic Spill  * Copyright 2005, 2006 Free Software Foundation, Inc.
5e25b118aSDominic Spill  *
6e25b118aSDominic Spill  * This file is part of libbtbb
7e25b118aSDominic Spill  *
8e25b118aSDominic Spill  * This program is free software; you can redistribute it and/or modify
9e25b118aSDominic Spill  * it under the terms of the GNU General Public License as published by
10e25b118aSDominic Spill  * the Free Software Foundation; either version 2, or (at your option)
11e25b118aSDominic Spill  * any later version.
12e25b118aSDominic Spill  *
13e25b118aSDominic Spill  * This program is distributed in the hope that it will be useful,
14e25b118aSDominic Spill  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15e25b118aSDominic Spill  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16e25b118aSDominic Spill  * GNU General Public License for more details.
17e25b118aSDominic Spill  *
18e25b118aSDominic Spill  * You should have received a copy of the GNU General Public License
19e25b118aSDominic Spill  * along with libbtbb; see the file COPYING.  If not, write to
20e25b118aSDominic Spill  * the Free Software Foundation, Inc., 51 Franklin Street,
21e25b118aSDominic Spill  * Boston, MA 02110-1301, USA.
22e25b118aSDominic Spill  */
23e25b118aSDominic Spill 
24e25b118aSDominic Spill #ifdef HAVE_CONFIG_H
25e25b118aSDominic Spill #include "config.h"
26e25b118aSDominic Spill #endif
27e25b118aSDominic Spill 
288f3e7eeaSChristopher Kilgour #include "btbb.h"
29e25b118aSDominic Spill #include "bluetooth_le_packet.h"
30e25b118aSDominic Spill #include <ctype.h>
318f3e7eeaSChristopher Kilgour #include <string.h>
32e25b118aSDominic Spill 
33*f25ef1fbSMike Ryan /* company identifier lookup */
34*f25ef1fbSMike Ryan const char *bt_compidtostr(uint16_t compid);
35*f25ef1fbSMike Ryan 
36e25b118aSDominic Spill /* string representations of advertising packet type */
37e25b118aSDominic Spill static const char *ADV_TYPE_NAMES[] = {
38e25b118aSDominic Spill 	"ADV_IND", "ADV_DIRECT_IND", "ADV_NONCONN_IND", "SCAN_REQ",
39e25b118aSDominic Spill 	"SCAN_RSP", "CONNECT_REQ", "ADV_SCAN_IND",
40e25b118aSDominic Spill };
41e25b118aSDominic Spill 
42e25b118aSDominic Spill /* source clock accuracy in a connect packet */
43e25b118aSDominic Spill static const char *CONNECT_SCA[] = {
44e25b118aSDominic Spill 	"251 ppm to 500 ppm", "151 ppm to 250 ppm", "101 ppm to 150 ppm",
45e25b118aSDominic Spill 	"76 ppm to 100 ppm", "51 ppm to 75 ppm", "31 ppm to 50 ppm",
46e25b118aSDominic Spill 	"21 ppm to 30 ppm", "0 ppm to 20 ppm",
47e25b118aSDominic Spill };
48e25b118aSDominic Spill 
49e25b118aSDominic Spill // count of objects in an array, shamelessly stolen from Chrome
50e25b118aSDominic Spill #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
51e25b118aSDominic Spill 
520a637dcaSDominic Spill static uint8_t count_bits(uint32_t n)
530a637dcaSDominic Spill {
540a637dcaSDominic Spill 	uint8_t i = 0;
550a637dcaSDominic Spill 	for (i = 0; n != 0; i++)
560a637dcaSDominic Spill 		n &= n - 1;
570a637dcaSDominic Spill 	return i;
580a637dcaSDominic Spill }
590a637dcaSDominic Spill 
608f3e7eeaSChristopher Kilgour static int aa_access_channel_off_by_one(const uint32_t aa) {
618f3e7eeaSChristopher Kilgour 	int retval = 0;
620a637dcaSDominic Spill 	if(count_bits(aa ^ LE_ADV_AA) == 1) {
638f3e7eeaSChristopher Kilgour 		retval = 1;
648f3e7eeaSChristopher Kilgour 	}
658f3e7eeaSChristopher Kilgour 	return retval;
668f3e7eeaSChristopher Kilgour }
67e25b118aSDominic Spill 
688f3e7eeaSChristopher Kilgour /*
698f3e7eeaSChristopher Kilgour  * A helper function for filtering bogus packets on data channels.
708f3e7eeaSChristopher Kilgour  *
718f3e7eeaSChristopher Kilgour  * If a candidate capture packet is random noise we would expect its
728f3e7eeaSChristopher Kilgour  * Access Address to be a randomly distributed 32-bit number.  An
738f3e7eeaSChristopher Kilgour  * exhaustive software analysis reveals that of 4294967296 possible
748f3e7eeaSChristopher Kilgour  * 32-bit Access Address values, 2900629660 (67.5%) are acceptable and
758f3e7eeaSChristopher Kilgour  * 1394337636 (32.5%) are invalid.  This function will identify which
768f3e7eeaSChristopher Kilgour  * category a candidate Access Address falls into by returning the
778f3e7eeaSChristopher Kilgour  * number of offenses contained.
788f3e7eeaSChristopher Kilgour  *
798f3e7eeaSChristopher Kilgour  * Refer to BT 4.x, Vol 6, Par B, Section 2.1.2.
808f3e7eeaSChristopher Kilgour  *
818f3e7eeaSChristopher Kilgour  * The Access Address in data channel packets meet the
828f3e7eeaSChristopher Kilgour  * following requirements:
838f3e7eeaSChristopher Kilgour  *  - It shall have no more than six consecutive zeros or ones.
848f3e7eeaSChristopher Kilgour  *  - It shall not be the advertising channel packets’ Access Address.
858f3e7eeaSChristopher Kilgour  *  - It shall not be a sequence that differs from the advertising channel packets’
868f3e7eeaSChristopher Kilgour  *    Access Address by only one bit.
878f3e7eeaSChristopher Kilgour  *  - It shall not have all four octets equal.
888f3e7eeaSChristopher Kilgour  *  - It shall have no more than 24 transitions.
898f3e7eeaSChristopher Kilgour  *  - It shall have a minimum of two transitions in the most significant six bits.
908f3e7eeaSChristopher Kilgour  */
918f3e7eeaSChristopher Kilgour static int aa_data_channel_offenses(const uint32_t aa) {
928f3e7eeaSChristopher Kilgour 	int retval = 0, transitions = 0;
938f3e7eeaSChristopher Kilgour 	unsigned shift, odd = (unsigned) (aa & 1);
948f3e7eeaSChristopher Kilgour 	uint8_t aab3, aab2, aab1, aab0 = (uint8_t) (aa & 0xff);
95e25b118aSDominic Spill 
968f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_EVEN[256] = {
978f3e7eeaSChristopher Kilgour 		0, 2, 2, 2, 2, 4, 2, 2, 2, 4, 4, 4, 2, 4, 2, 2,
988f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
998f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1008f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
1018f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1028f3e7eeaSChristopher Kilgour 		4, 6, 6, 6, 6, 8, 6, 6, 4, 6, 6, 6, 4, 6, 4, 4,
1038f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1048f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
1058f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1068f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1078f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 5, 7, 7, 7, 5, 7, 5, 5,
1088f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1098f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1108f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1118f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1128f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 1, 3, 3, 3, 1, 3, 1, 1
1138f3e7eeaSChristopher Kilgour 	};
114e25b118aSDominic Spill 
1158f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_ODD[256] = {
1168f3e7eeaSChristopher Kilgour 		1, 1, 3, 1, 3, 3, 3, 1, 3, 3, 5, 3, 3, 3, 3, 1,
1178f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1188f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1198f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1208f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1218f3e7eeaSChristopher Kilgour 		5, 5, 7, 5, 7, 7, 7, 5, 5, 5, 7, 5, 5, 5, 5, 3,
1228f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1238f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1248f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1258f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1268f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 6, 6, 8, 6, 6, 6, 6, 4,
1278f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1288f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1298f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1308f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1318f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 2, 2, 4, 2, 2, 2, 2, 0
1328f3e7eeaSChristopher Kilgour 	};
1338f3e7eeaSChristopher Kilgour 
1348f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab0] : EIGHT_BIT_TRANSITIONS_EVEN[aab0] );
1358f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab0 & 0x80);
1368f3e7eeaSChristopher Kilgour 	aab1 = (uint8_t) (aa >> 8);
1378f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab1] : EIGHT_BIT_TRANSITIONS_EVEN[aab1] );
1388f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab1 & 0x80);
1398f3e7eeaSChristopher Kilgour 	aab2 = (uint8_t) (aa >> 16);
1408f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab2] : EIGHT_BIT_TRANSITIONS_EVEN[aab2] );
1418f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab2 & 0x80);
1428f3e7eeaSChristopher Kilgour 	aab3 = (uint8_t) (aa >> 24);
1438f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab3] : EIGHT_BIT_TRANSITIONS_EVEN[aab3] );
1448f3e7eeaSChristopher Kilgour 
1458f3e7eeaSChristopher Kilgour 	/* consider excessive transitions as offenses */
1468f3e7eeaSChristopher Kilgour 	if (transitions > 24) {
1478f3e7eeaSChristopher Kilgour 		retval += (transitions - 24);
1488f3e7eeaSChristopher Kilgour 	}
1498f3e7eeaSChristopher Kilgour 
1508f3e7eeaSChristopher Kilgour 	const uint8_t AA_MSB6_ALLOWED[64] = {
1518f3e7eeaSChristopher Kilgour 		0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
1528f3e7eeaSChristopher Kilgour 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1538f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1548f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0
1558f3e7eeaSChristopher Kilgour 	};
1568f3e7eeaSChristopher Kilgour 
1578f3e7eeaSChristopher Kilgour 	/* consider excessive transitions in the 6 MSBs as an offense */
1588f3e7eeaSChristopher Kilgour 	retval += (1 - AA_MSB6_ALLOWED[aab3>>2]);
1598f3e7eeaSChristopher Kilgour 
1608f3e7eeaSChristopher Kilgour 	/* consider all bytes as being equal an offense */
1618f3e7eeaSChristopher Kilgour 	retval += (((aab0 == aab1) && (aab0 == aab2) && (aab0 == aab3)) ? 1 : 0);
1628f3e7eeaSChristopher Kilgour 
1638f3e7eeaSChristopher Kilgour 	/* access-channel address and off-by-ones are illegal */
1640a637dcaSDominic Spill 	retval += ((aa == LE_ADV_AA) ? 1 : 0);
1658f3e7eeaSChristopher Kilgour 	retval += aa_access_channel_off_by_one(aa);
1668f3e7eeaSChristopher Kilgour 
1678f3e7eeaSChristopher Kilgour 	/* inspect nibble triples for insufficient bit transitions */
1688f3e7eeaSChristopher Kilgour 	for(shift=0; shift<=20; shift+=4) {
1698f3e7eeaSChristopher Kilgour 		uint16_t twelvebits = (uint16_t) ((aa >> shift) & 0xfff);
1708f3e7eeaSChristopher Kilgour 		switch( twelvebits ) {
1718f3e7eeaSChristopher Kilgour 			/* seven consecutive zeroes */
1728f3e7eeaSChristopher Kilgour 		case 0x080: case 0x180: case 0x280: case 0x380: case 0x480:
1738f3e7eeaSChristopher Kilgour 		case 0x580: case 0x680: case 0x780: case 0x880: case 0x980:
1748f3e7eeaSChristopher Kilgour 		case 0xa80: case 0xb80: case 0xc80: case 0xd80: case 0xe80:
1758f3e7eeaSChristopher Kilgour 		case 0xf80: case 0x101: case 0x301: case 0x501: case 0x701:
1768f3e7eeaSChristopher Kilgour 		case 0x901: case 0xb01: case 0xd01: case 0xf01: case 0x202:
1778f3e7eeaSChristopher Kilgour 		case 0x602: case 0xa02: case 0xe02: case 0x203: case 0x603:
1788f3e7eeaSChristopher Kilgour 		case 0xa03: case 0xe03: case 0x404: case 0xc04: case 0x405:
1798f3e7eeaSChristopher Kilgour 		case 0xc05: case 0x406: case 0xc06: case 0x407: case 0xc07:
1808f3e7eeaSChristopher Kilgour 		case 0x808: case 0x809: case 0x80a: case 0x80b: case 0x80c:
1818f3e7eeaSChristopher Kilgour 		case 0x80d: case 0x80e: case 0x80f: case 0x010: case 0x011:
1828f3e7eeaSChristopher Kilgour 		case 0x012: case 0x013: case 0x014: case 0x015: case 0x016:
1838f3e7eeaSChristopher Kilgour 		case 0x017: case 0x018: case 0x019: case 0x01a: case 0x01b:
1848f3e7eeaSChristopher Kilgour 		case 0x01c: case 0x01d: case 0x01e: case 0x01f:
1858f3e7eeaSChristopher Kilgour 			/* eight consecutive zeroes */
1868f3e7eeaSChristopher Kilgour 		case 0x100: case 0x300: case 0x500: case 0x700: case 0x900:
1878f3e7eeaSChristopher Kilgour 		case 0xb00: case 0xd00: case 0xf00: case 0x201: case 0x601:
1888f3e7eeaSChristopher Kilgour 		case 0xa01: case 0xe01: case 0x402: case 0xc02: case 0x403:
1898f3e7eeaSChristopher Kilgour 		case 0xc03: case 0x804: case 0x805: case 0x806: case 0x807:
1908f3e7eeaSChristopher Kilgour 		case 0x008: case 0x009: case 0x00a: case 0x00b: case 0x00c:
1918f3e7eeaSChristopher Kilgour 		case 0x00d: case 0x00e: case 0x00f:
1928f3e7eeaSChristopher Kilgour 			/* nine consecutive zeroes */
1938f3e7eeaSChristopher Kilgour 		case 0xe00: case 0xc01: case 0x802: case 0x803: case 0x004:
1948f3e7eeaSChristopher Kilgour 		case 0x005: case 0x006: case 0x007:
1958f3e7eeaSChristopher Kilgour 			/* ten consecutive zeroes */
1968f3e7eeaSChristopher Kilgour 		case 0x400: case 0xc00: case 0x801: case 0x002: case 0x003:
1978f3e7eeaSChristopher Kilgour 			/* eleven consecutive zeroes */
1988f3e7eeaSChristopher Kilgour 		case 0x800: case 0x001:
1998f3e7eeaSChristopher Kilgour 			/* twelve consecutive zeroes */
2008f3e7eeaSChristopher Kilgour 		case 0x000:
2018f3e7eeaSChristopher Kilgour 			/* seven consecutive ones */
2028f3e7eeaSChristopher Kilgour 		case 0x07f: case 0x0fe: case 0x2fe: case 0x4fe: case 0x6fe:
2038f3e7eeaSChristopher Kilgour 		case 0x8fe: case 0xafe: case 0xcfe: case 0xefe: case 0x1fc:
2048f3e7eeaSChristopher Kilgour 		case 0x5fc: case 0x9fc: case 0xdfc: case 0x1fd: case 0x5fd:
2058f3e7eeaSChristopher Kilgour 		case 0x9fd: case 0xdfd: case 0x3f8: case 0xbf8: case 0x3f9:
2068f3e7eeaSChristopher Kilgour 		case 0xbf9: case 0x3fa: case 0xbfa: case 0x3fb: case 0xbfb:
2078f3e7eeaSChristopher Kilgour 		case 0x7f4: case 0x7f5: case 0x7f6: case 0x7f7: case 0xfe0:
2088f3e7eeaSChristopher Kilgour 			/* eight consecutive ones */
2098f3e7eeaSChristopher Kilgour 		case 0x0ff: case 0x2ff: case 0x4ff: case 0x6ff: case 0x8ff:
2108f3e7eeaSChristopher Kilgour 		case 0xaff: case 0xcff: case 0xeff: case 0x1fe: case 0x5fe:
2118f3e7eeaSChristopher Kilgour 		case 0x9fe: case 0xdfe: case 0x3fc: case 0xbfc: case 0x3fd:
2128f3e7eeaSChristopher Kilgour 		case 0xbfd: case 0x7f8: case 0x7f9: case 0x7fa: case 0x7fb:
2138f3e7eeaSChristopher Kilgour 		case 0xff0: case 0xff1: case 0xff2: case 0xff3: case 0xff4:
2148f3e7eeaSChristopher Kilgour 		case 0xff5: case 0xff6: case 0xff7:
2158f3e7eeaSChristopher Kilgour 			/* nine consecutive ones */
2168f3e7eeaSChristopher Kilgour 		case 0x1ff: case 0x5ff: case 0x9ff: case 0xdff: case 0x3fe:
2178f3e7eeaSChristopher Kilgour 		case 0xbfe: case 0x7fc: case 0x7fd: case 0xff8: case 0xff9:
2188f3e7eeaSChristopher Kilgour 		case 0xffa: case 0xffb:
2198f3e7eeaSChristopher Kilgour 			/* ten consecutive ones */
2208f3e7eeaSChristopher Kilgour 		case 0x3ff: case 0xbff: case 0x7fe: case 0xffc: case 0xffd:
2218f3e7eeaSChristopher Kilgour 			/* eleven consecutive ones */
2228f3e7eeaSChristopher Kilgour 		case 0x7ff: case 0xffe:
2238f3e7eeaSChristopher Kilgour 			/* all ones */
2248f3e7eeaSChristopher Kilgour 		case 0xfff:
2258f3e7eeaSChristopher Kilgour 			retval++;
2268f3e7eeaSChristopher Kilgour 			break;
2278f3e7eeaSChristopher Kilgour 		default:
2288f3e7eeaSChristopher Kilgour 			break;
229e25b118aSDominic Spill 		}
230e25b118aSDominic Spill 	}
231e25b118aSDominic Spill 
2328f3e7eeaSChristopher Kilgour 	return retval;
233e25b118aSDominic Spill }
234e25b118aSDominic Spill 
2358f3e7eeaSChristopher Kilgour lell_packet *
2368f3e7eeaSChristopher Kilgour lell_packet_new(void)
2378f3e7eeaSChristopher Kilgour {
2388f3e7eeaSChristopher Kilgour 	lell_packet *pkt = (lell_packet *)calloc(1, sizeof(lell_packet));
2398f3e7eeaSChristopher Kilgour 	pkt->refcount = 1;
2408f3e7eeaSChristopher Kilgour 	return pkt;
2418f3e7eeaSChristopher Kilgour }
2428f3e7eeaSChristopher Kilgour 
2438f3e7eeaSChristopher Kilgour void
2448f3e7eeaSChristopher Kilgour lell_packet_ref(lell_packet *pkt)
2458f3e7eeaSChristopher Kilgour {
2468f3e7eeaSChristopher Kilgour 	pkt->refcount++;
2478f3e7eeaSChristopher Kilgour }
2488f3e7eeaSChristopher Kilgour 
2498f3e7eeaSChristopher Kilgour void
2508f3e7eeaSChristopher Kilgour lell_packet_unref(lell_packet *pkt)
2518f3e7eeaSChristopher Kilgour {
2528f3e7eeaSChristopher Kilgour 	pkt->refcount--;
2538f3e7eeaSChristopher Kilgour 	if (pkt->refcount == 0)
2548f3e7eeaSChristopher Kilgour 		free(pkt);
2558f3e7eeaSChristopher Kilgour }
2568f3e7eeaSChristopher Kilgour 
2578f3e7eeaSChristopher Kilgour static uint8_t le_channel_index(uint16_t phys_channel) {
258e25b118aSDominic Spill 	uint8_t ret;
259e25b118aSDominic Spill 	if (phys_channel == 2402) {
260e25b118aSDominic Spill 		ret = 37;
261e25b118aSDominic Spill 	} else if (phys_channel < 2426) { // 0 - 10
262e25b118aSDominic Spill 		ret = (phys_channel - 2404) / 2;
263e25b118aSDominic Spill 	} else if (phys_channel == 2426) {
264e25b118aSDominic Spill 		ret = 38;
265e25b118aSDominic Spill 	} else if (phys_channel < 2480) { // 11 - 36
266e25b118aSDominic Spill 		ret = 11 + (phys_channel - 2428) / 2;
267e25b118aSDominic Spill 	} else {
268e25b118aSDominic Spill 		ret = 39;
269e25b118aSDominic Spill 	}
270e25b118aSDominic Spill 	return ret;
271e25b118aSDominic Spill }
272e25b118aSDominic Spill 
2738f3e7eeaSChristopher Kilgour void lell_allocate_and_decode(const uint8_t *stream, uint16_t phys_channel, uint32_t clk100ns, lell_packet **pkt)
2748f3e7eeaSChristopher Kilgour {
2758f3e7eeaSChristopher Kilgour 	*pkt = lell_packet_new( );
2768f3e7eeaSChristopher Kilgour 	memcpy((*pkt)->symbols, stream, MAX_LE_SYMBOLS);
2778f3e7eeaSChristopher Kilgour 
2788f3e7eeaSChristopher Kilgour 	(*pkt)->channel_idx = le_channel_index(phys_channel);
2798f3e7eeaSChristopher Kilgour 	(*pkt)->channel_k = (phys_channel-2402)/2;
2808f3e7eeaSChristopher Kilgour 	(*pkt)->clk100ns = clk100ns;
2818f3e7eeaSChristopher Kilgour 
2828f3e7eeaSChristopher Kilgour 	(*pkt)->access_address = 0;
2838f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[0];
2848f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[1] << 8;
2858f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[2] << 16;
2868f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[3] << 24;
2878f3e7eeaSChristopher Kilgour 
2888f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(*pkt)) {
2898f3e7eeaSChristopher Kilgour 		// data PDU
2908f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x1f;
2918f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = aa_data_channel_offenses((*pkt)->access_address);
2928f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = (*pkt)->access_address_offenses ? 0 : 1;
2938f3e7eeaSChristopher Kilgour 	} else {
2948f3e7eeaSChristopher Kilgour 		// advertising PDU
2958f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x3f;
2968f3e7eeaSChristopher Kilgour 		(*pkt)->adv_type = (*pkt)->symbols[4] & 0xf;
2978f3e7eeaSChristopher Kilgour 		(*pkt)->adv_tx_add = (*pkt)->symbols[4] & 0x40 ? 1 : 0;
2988f3e7eeaSChristopher Kilgour 		(*pkt)->adv_rx_add = (*pkt)->symbols[4] & 0x80 ? 1 : 0;
2998f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = ((*pkt)->access_address == 0x8e89bed6);
3008f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = (*pkt)->flags.as_bits.access_address_ok ? 0 :
3018f3e7eeaSChristopher Kilgour 			(aa_access_channel_off_by_one((*pkt)->access_address) ? 1 : 32);
3028f3e7eeaSChristopher Kilgour 	}
3038f3e7eeaSChristopher Kilgour }
3048f3e7eeaSChristopher Kilgour 
3058f3e7eeaSChristopher Kilgour unsigned lell_packet_is_data(const lell_packet *pkt)
3068f3e7eeaSChristopher Kilgour {
3078f3e7eeaSChristopher Kilgour 	return (unsigned) (pkt->channel_idx < 37);
3088f3e7eeaSChristopher Kilgour }
3098f3e7eeaSChristopher Kilgour 
3108f3e7eeaSChristopher Kilgour uint32_t lell_get_access_address(const lell_packet *pkt)
3118f3e7eeaSChristopher Kilgour {
3128f3e7eeaSChristopher Kilgour 	return pkt->access_address;
3138f3e7eeaSChristopher Kilgour }
3148f3e7eeaSChristopher Kilgour 
3158f3e7eeaSChristopher Kilgour unsigned lell_get_access_address_offenses(const lell_packet *pkt)
3168f3e7eeaSChristopher Kilgour {
3178f3e7eeaSChristopher Kilgour 	return pkt->access_address_offenses;
3188f3e7eeaSChristopher Kilgour }
3198f3e7eeaSChristopher Kilgour 
3208f3e7eeaSChristopher Kilgour unsigned lell_get_channel_index(const lell_packet *pkt)
3218f3e7eeaSChristopher Kilgour {
3228f3e7eeaSChristopher Kilgour 	return pkt->channel_idx;
3238f3e7eeaSChristopher Kilgour }
3248f3e7eeaSChristopher Kilgour 
3258f3e7eeaSChristopher Kilgour unsigned lell_get_channel_k(const lell_packet *pkt)
3268f3e7eeaSChristopher Kilgour {
3278f3e7eeaSChristopher Kilgour 	return pkt->channel_k;
3288f3e7eeaSChristopher Kilgour }
3298f3e7eeaSChristopher Kilgour 
3308f3e7eeaSChristopher Kilgour const char * lell_get_adv_type_str(const lell_packet *pkt)
3318f3e7eeaSChristopher Kilgour {
3328f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt))
333e25b118aSDominic Spill 		return NULL;
3348f3e7eeaSChristopher Kilgour 	if (pkt->adv_type < COUNT_OF(ADV_TYPE_NAMES))
3358f3e7eeaSChristopher Kilgour 		return ADV_TYPE_NAMES[pkt->adv_type];
336e25b118aSDominic Spill 	return "UNKNOWN";
337e25b118aSDominic Spill }
338e25b118aSDominic Spill 
3398f3e7eeaSChristopher Kilgour static void _dump_addr(const char *name, const uint8_t *buf, int offset, int random) {
340e25b118aSDominic Spill 	int i;
341e25b118aSDominic Spill 	printf("    %s%02x", name, buf[offset+5]);
342e25b118aSDominic Spill 	for (i = 4; i >= 0; --i)
343e25b118aSDominic Spill 		printf(":%02x", buf[offset+i]);
344e25b118aSDominic Spill 	printf(" (%s)\n", random ? "random" : "public");
345e25b118aSDominic Spill }
346e25b118aSDominic Spill 
3478f3e7eeaSChristopher Kilgour static void _dump_8(const char *name, const uint8_t *buf, int offset) {
348e25b118aSDominic Spill 	printf("    %s%02x (%d)\n", name, buf[offset], buf[offset]);
349e25b118aSDominic Spill }
350e25b118aSDominic Spill 
3518f3e7eeaSChristopher Kilgour static void _dump_16(const char *name, const uint8_t *buf, int offset) {
352e25b118aSDominic Spill 	uint16_t val = buf[offset+1] << 8 | buf[offset];
353e25b118aSDominic Spill 	printf("    %s%04x (%d)\n", name, val, val);
354e25b118aSDominic Spill }
355e25b118aSDominic Spill 
356c6bb3715SMike Ryan static void _dump_24(char *name, const uint8_t *buf, int offset) {
357c6bb3715SMike Ryan 	uint32_t val = buf[offset+2] << 16 | buf[offset+1] << 8 | buf[offset];
358e25b118aSDominic Spill 	printf("    %s%06x\n", name, val);
359e25b118aSDominic Spill }
360e25b118aSDominic Spill 
3618f3e7eeaSChristopher Kilgour static void _dump_32(const char *name, const uint8_t *buf, int offset) {
362e25b118aSDominic Spill 	uint32_t val = buf[offset+3] << 24 |
363e25b118aSDominic Spill 				   buf[offset+2] << 16 |
364e25b118aSDominic Spill 				   buf[offset+1] << 8 |
365e25b118aSDominic Spill 				   buf[offset+0];
366e25b118aSDominic Spill 	printf("    %s%08x\n", name, val);
367e25b118aSDominic Spill }
368e25b118aSDominic Spill 
3698f3e7eeaSChristopher Kilgour static void _dump_uuid(const uint8_t *uuid) {
370e25b118aSDominic Spill 	int i;
371e25b118aSDominic Spill 	for (i = 0; i < 4; ++i)
372e25b118aSDominic Spill 		printf("%02x", uuid[i]);
373e25b118aSDominic Spill 	printf("-");
374e25b118aSDominic Spill 	for (i = 4; i < 6; ++i)
375e25b118aSDominic Spill 		printf("%02x", uuid[i]);
376e25b118aSDominic Spill 	printf("-");
377e25b118aSDominic Spill 	for (i = 6; i < 8; ++i)
378e25b118aSDominic Spill 		printf("%02x", uuid[i]);
379e25b118aSDominic Spill 	printf("-");
380e25b118aSDominic Spill 	for (i = 8; i < 10; ++i)
381e25b118aSDominic Spill 		printf("%02x", uuid[i]);
382e25b118aSDominic Spill 	printf("-");
383e25b118aSDominic Spill 	for (i = 10; i < 16; ++i)
384e25b118aSDominic Spill 		printf("%02x", uuid[i]);
385e25b118aSDominic Spill }
386e25b118aSDominic Spill 
387e25b118aSDominic Spill // Refer to pg 1735 of Bluetooth Core Spec 4.0
3888f3e7eeaSChristopher Kilgour static void _dump_scan_rsp_data(const uint8_t *buf, int len) {
389e25b118aSDominic Spill 	int pos = 0;
390e25b118aSDominic Spill 	int sublen, i;
391e25b118aSDominic Spill 	uint8_t type;
392e25b118aSDominic Spill 	uint16_t val;
393e25b118aSDominic Spill 	char *cval;
394e25b118aSDominic Spill 
395e25b118aSDominic Spill 	while (pos < len) {
396e25b118aSDominic Spill 		sublen = buf[pos];
397e25b118aSDominic Spill 		++pos;
398e25b118aSDominic Spill 		if (pos + sublen > len) {
399e25b118aSDominic Spill 			printf("Error: attempt to read past end of buffer (%d + %d > %d)\n", pos, sublen, len);
400e25b118aSDominic Spill 			return;
401e25b118aSDominic Spill 		}
402e25b118aSDominic Spill 		if (sublen == 0) {
403e25b118aSDominic Spill 			printf("Early return due to 0 length\n");
404e25b118aSDominic Spill 			return;
405e25b118aSDominic Spill 		}
406e25b118aSDominic Spill 		type = buf[pos];
407e25b118aSDominic Spill 		printf("        Type %02x", type);
408e25b118aSDominic Spill 		switch (type) {
409e25b118aSDominic Spill 			case 0x01:
410e25b118aSDominic Spill 				printf(" (Flags)\n");
411e25b118aSDominic Spill 				printf("           ");
412e25b118aSDominic Spill 				for (i = 0; i < 8; ++i)
413e25b118aSDominic Spill 					printf("%d", buf[pos+1] & (1 << (7-i)) ? 1 : 0);
414e25b118aSDominic Spill 				printf("\n");
415e25b118aSDominic Spill 				break;
41600c7e1b3SMike Ryan 			case 0x02:
41700c7e1b3SMike Ryan 				printf(" (16-bit Service UUIDs, more available)\n");
41800c7e1b3SMike Ryan 				goto print16;
41900c7e1b3SMike Ryan 			case 0x03:
42000c7e1b3SMike Ryan 				printf(" (16-bit Service UUIDs) \n");
42100c7e1b3SMike Ryan print16:
42200c7e1b3SMike Ryan 				if ((sublen - 1) % 2 == 0) {
42300c7e1b3SMike Ryan 					for (i = 0; i < sublen - 1; i += 2) {
42400c7e1b3SMike Ryan 						uint16_t *uuid = (uint16_t *)&buf[pos+1+i];
42500c7e1b3SMike Ryan 						printf("           %04x\n", *uuid);
42600c7e1b3SMike Ryan 					}
42700c7e1b3SMike Ryan 				}
42800c7e1b3SMike Ryan 				break;
42945000095SMike Ryan 			case 0x06:
43045000095SMike Ryan 				printf(" (128-bit Service UUIDs, more available)\n");
43145000095SMike Ryan 				goto print128;
432e25b118aSDominic Spill 			case 0x07:
433e25b118aSDominic Spill 				printf(" (128-bit Service UUIDs)\n");
43445000095SMike Ryan print128:
435e25b118aSDominic Spill 				if ((sublen - 1) % 16 == 0) {
436e25b118aSDominic Spill 					uint8_t uuid[16];
437e25b118aSDominic Spill 					for (i = 0; i < sublen - 1; ++i) {
438e25b118aSDominic Spill 						uuid[15 - (i % 16)] = buf[pos+1+i];
439e25b118aSDominic Spill 						if ((i & 15) == 15) {
440e25b118aSDominic Spill 							printf("           ");
441e25b118aSDominic Spill 							_dump_uuid(uuid);
442e25b118aSDominic Spill 							printf("\n");
443e25b118aSDominic Spill 						}
444e25b118aSDominic Spill 					}
445e25b118aSDominic Spill 				}
446e25b118aSDominic Spill 				else {
447e25b118aSDominic Spill 					printf("Wrong length (%d, must be divisible by 16)\n", sublen-1);
448e25b118aSDominic Spill 				}
449e25b118aSDominic Spill 				break;
450e25b118aSDominic Spill 			case 0x09:
451e25b118aSDominic Spill 				printf(" (Complete Local Name)\n");
452e25b118aSDominic Spill 				printf("           ");
453e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
454e25b118aSDominic Spill 					printf("%c", isprint(buf[pos+i]) ? buf[pos+i] : '.');
455e25b118aSDominic Spill 				printf("\n");
456e25b118aSDominic Spill 				break;
457e25b118aSDominic Spill 			case 0x0a:
458e25b118aSDominic Spill 				printf(" (Tx Power Level)\n");
459e25b118aSDominic Spill 				printf("           ");
460e25b118aSDominic Spill 				if (sublen-1 == 1) {
461e25b118aSDominic Spill 					cval = (char *)&buf[pos+1];
462e25b118aSDominic Spill 					printf("%d dBm\n", *cval);
463e25b118aSDominic Spill 				} else {
464e25b118aSDominic Spill 					printf("Wrong length (%d, should be 1)\n", sublen-1);
465e25b118aSDominic Spill 				}
466e25b118aSDominic Spill 				break;
467e25b118aSDominic Spill 			case 0x12:
468e25b118aSDominic Spill 				printf(" (Slave Connection Interval Range)\n");
469e25b118aSDominic Spill 				printf("           ");
470e25b118aSDominic Spill 				if (sublen-1 == 4) {
471e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
472e25b118aSDominic Spill 					printf("(%0.2f, ", val * 1.25);
473e25b118aSDominic Spill 					val = (buf[pos+4] << 8) | buf[pos+3];
474e25b118aSDominic Spill 					printf("%0.2f) ms\n", val * 1.25);
475e25b118aSDominic Spill 				}
476e25b118aSDominic Spill 				else {
477e25b118aSDominic Spill 					printf("Wrong length (%d, should be 4)\n", sublen-1);
478e25b118aSDominic Spill 				}
479e25b118aSDominic Spill 				break;
480e25b118aSDominic Spill 			case 0x16:
481e25b118aSDominic Spill 				printf(" (Service Data)\n");
482e25b118aSDominic Spill 				printf("           ");
483e25b118aSDominic Spill 				if (sublen-1 >= 2) {
484e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
485e25b118aSDominic Spill 					printf("UUID: %02x", val);
486e25b118aSDominic Spill 					if (sublen-1 > 2) {
487e25b118aSDominic Spill 						printf(", Additional:");
488e25b118aSDominic Spill 						for (i = 3; i < sublen; ++i)
489e25b118aSDominic Spill 							printf(" %02x", buf[pos+i]);
490e25b118aSDominic Spill 					}
491e25b118aSDominic Spill 					printf("\n");
492e25b118aSDominic Spill 				}
493e25b118aSDominic Spill 				else {
494e25b118aSDominic Spill 					printf("Wrong length (%d, should be >= 2)\n", sublen-1);
495e25b118aSDominic Spill 				}
496e25b118aSDominic Spill 				break;
497*f25ef1fbSMike Ryan 			case 0xff:
498*f25ef1fbSMike Ryan 				printf(" (Manufacturer Specific Data)\n");
499*f25ef1fbSMike Ryan 				printf("           ");
500*f25ef1fbSMike Ryan 				if (sublen - 1 >= 2) {
501*f25ef1fbSMike Ryan 					uint16_t company = (buf[pos+2] << 8) | buf[pos+1];
502*f25ef1fbSMike Ryan 					printf("Company: %s\n", bt_compidtostr(company));
503*f25ef1fbSMike Ryan 					printf("           ");
504*f25ef1fbSMike Ryan 					printf("Data:");
505*f25ef1fbSMike Ryan 					for (i = 3; i < sublen; ++i)
506*f25ef1fbSMike Ryan 						printf(" %02x", buf[pos+i]);
507*f25ef1fbSMike Ryan 					printf("\n");
508*f25ef1fbSMike Ryan 				}
509*f25ef1fbSMike Ryan 				else {
510*f25ef1fbSMike Ryan 					printf("Wrong length (%d, should be >= 2)\n", sublen-1);
511*f25ef1fbSMike Ryan 				}
512*f25ef1fbSMike Ryan 				break;
513e25b118aSDominic Spill 			default:
514e25b118aSDominic Spill 				printf("\n");
515e25b118aSDominic Spill 				printf("           ");
516e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
517e25b118aSDominic Spill 					printf(" %02x", buf[pos+i]);
518e25b118aSDominic Spill 				printf("\n");
519e25b118aSDominic Spill 		}
520e25b118aSDominic Spill 		pos += sublen;
521e25b118aSDominic Spill 	}
522e25b118aSDominic Spill }
523e25b118aSDominic Spill 
5248f3e7eeaSChristopher Kilgour void lell_print(const lell_packet *pkt)
5258f3e7eeaSChristopher Kilgour {
526387b6603SIvan Krasin 	int i, opcode;
5278f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt)) {
5288f3e7eeaSChristopher Kilgour 		int llid = pkt->symbols[4] & 0x3;
529e25b118aSDominic Spill 		static const char *llid_str[] = {
530e25b118aSDominic Spill 			"Reserved",
531e25b118aSDominic Spill 			"LL Data PDU / empty or L2CAP continuation",
532e25b118aSDominic Spill 			"LL Data PDU / L2CAP start",
533e25b118aSDominic Spill 			"LL Control PDU",
534e25b118aSDominic Spill 		};
535e25b118aSDominic Spill 
5368f3e7eeaSChristopher Kilgour 		printf("Data / AA %08x (%s) / %2d bytes\n", pkt->access_address,
5378f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
5388f3e7eeaSChristopher Kilgour 		       pkt->length);
5398f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
540e25b118aSDominic Spill 		printf("    LLID: %d / %s\n", llid, llid_str[llid]);
5418f3e7eeaSChristopher Kilgour 		printf("    NESN: %d  SN: %d  MD: %d\n", (pkt->symbols[4] >> 2) & 1,
5428f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 3) & 1,
5438f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 4) & 1);
544387b6603SIvan Krasin 		switch (llid) {
545387b6603SIvan Krasin 		case 3: // LL Control PDU
5468f3e7eeaSChristopher Kilgour 			opcode = pkt->symbols[6];
547387b6603SIvan Krasin 			static const char *opcode_str[] = {
548387b6603SIvan Krasin 				"LL_CONNECTION_UPDATE_REQ",
549387b6603SIvan Krasin 				"LL_CHANNEL_MAP_REQ",
550387b6603SIvan Krasin 				"LL_TERMINATE_IND",
551387b6603SIvan Krasin 				"LL_ENC_REQ",
552387b6603SIvan Krasin 				"LL_ENC_RSP",
553387b6603SIvan Krasin 				"LL_START_ENC_REQ",
554387b6603SIvan Krasin 				"LL_START_ENC_RSP",
555387b6603SIvan Krasin 				"LL_UNKNOWN_RSP",
556387b6603SIvan Krasin 				"LL_FEATURE_REQ",
557387b6603SIvan Krasin 				"LL_FEATURE_RSP",
558387b6603SIvan Krasin 				"LL_PAUSE_ENC_REQ",
559387b6603SIvan Krasin 				"LL_PAUSE_ENC_RSP",
560387b6603SIvan Krasin 				"LL_VERSION_IND",
561387b6603SIvan Krasin 				"LL_REJECT_IND",
562b3394904SMike Ryan 				"LL_SLAVE_FEATURE_REQ",
563b3394904SMike Ryan 				"LL_CONNECTION_PARAM_REQ",
564b3394904SMike Ryan 				"LL_CONNECTION_PARAM_RSP",
565b3394904SMike Ryan 				"LL_REJECT_IND_EXT",
566b3394904SMike Ryan 				"LL_PING_REQ",
567b3394904SMike Ryan 				"LL_PING_RSP",
568387b6603SIvan Krasin 				"Reserved for Future Use",
569387b6603SIvan Krasin 			};
570ba100f3dSMike Ryan 			printf("    Opcode: %d / %s\n", opcode, opcode_str[(opcode<0x14)?opcode:0x14]);
571387b6603SIvan Krasin 			break;
572387b6603SIvan Krasin 		default:
573387b6603SIvan Krasin 			break;
574387b6603SIvan Krasin 		}
575e25b118aSDominic Spill 	} else {
5768f3e7eeaSChristopher Kilgour 		printf("Advertising / AA %08x (%s)/ %2d bytes\n", pkt->access_address,
5778f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
5788f3e7eeaSChristopher Kilgour 		       pkt->length);
5798f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
5808f3e7eeaSChristopher Kilgour 		printf("    Type:  %s\n", lell_get_adv_type_str(pkt));
581e25b118aSDominic Spill 
5828f3e7eeaSChristopher Kilgour 		switch(pkt->adv_type) {
583e25b118aSDominic Spill 			case ADV_IND:
584ee0d4df0SMike Ryan 			case ADV_NONCONN_IND:
585ee0d4df0SMike Ryan 			case ADV_SCAN_IND:
5868f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
5878f3e7eeaSChristopher Kilgour 				if (pkt->length-6 > 0) {
588e25b118aSDominic Spill 					printf("    AdvData:");
5898f3e7eeaSChristopher Kilgour 					for (i = 0; i < pkt->length - 6; ++i)
5908f3e7eeaSChristopher Kilgour 						printf(" %02x", pkt->symbols[12+i]);
591e25b118aSDominic Spill 					printf("\n");
5928f3e7eeaSChristopher Kilgour 					_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
593e25b118aSDominic Spill 				}
594e25b118aSDominic Spill 				break;
595ee0d4df0SMike Ryan 			case ADV_DIRECT_IND:
596ee0d4df0SMike Ryan 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
597ee0d4df0SMike Ryan 				_dump_addr("InitA: ", pkt->symbols, 12, pkt->adv_rx_add);
598ee0d4df0SMike Ryan 				break;
599e25b118aSDominic Spill 			case SCAN_REQ:
6008f3e7eeaSChristopher Kilgour 				_dump_addr("ScanA: ", pkt->symbols, 6, pkt->adv_tx_add);
6018f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
602e25b118aSDominic Spill 				break;
603e25b118aSDominic Spill 			case SCAN_RSP:
6048f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
605e25b118aSDominic Spill 				printf("    ScanRspData:");
6068f3e7eeaSChristopher Kilgour 				for (i = 0; i < pkt->length - 6; ++i)
6078f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[12+i]);
608e25b118aSDominic Spill 				printf("\n");
6098f3e7eeaSChristopher Kilgour 				_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
610e25b118aSDominic Spill 				break;
611e25b118aSDominic Spill 			case CONNECT_REQ:
6128f3e7eeaSChristopher Kilgour 				_dump_addr("InitA: ", pkt->symbols, 6, pkt->adv_tx_add);
6138f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
6148f3e7eeaSChristopher Kilgour 				_dump_32("AA:    ", pkt->symbols, 18);
6158f3e7eeaSChristopher Kilgour 				_dump_24("CRCInit: ", pkt->symbols, 22);
6168f3e7eeaSChristopher Kilgour 				_dump_8("WinSize: ", pkt->symbols, 25);
6178f3e7eeaSChristopher Kilgour 				_dump_16("WinOffset: ", pkt->symbols, 26);
6188f3e7eeaSChristopher Kilgour 				_dump_16("Interval: ", pkt->symbols, 28);
6198f3e7eeaSChristopher Kilgour 				_dump_16("Latency: ", pkt->symbols, 30);
6208f3e7eeaSChristopher Kilgour 				_dump_16("Timeout: ", pkt->symbols, 32);
621e25b118aSDominic Spill 
622e25b118aSDominic Spill 				printf("    ChM:");
623e25b118aSDominic Spill 				for (i = 0; i < 5; ++i)
6248f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[34+i]);
625e25b118aSDominic Spill 				printf("\n");
626e25b118aSDominic Spill 
6278f3e7eeaSChristopher Kilgour 				printf("    Hop: %d\n", pkt->symbols[39] & 0x1f);
628e25b118aSDominic Spill 				printf("    SCA: %d, %s\n",
6298f3e7eeaSChristopher Kilgour 						pkt->symbols[39] >> 5,
6308f3e7eeaSChristopher Kilgour 						CONNECT_SCA[pkt->symbols[39] >> 5]);
631e25b118aSDominic Spill 				break;
632e25b118aSDominic Spill 		}
633e25b118aSDominic Spill 	}
634e25b118aSDominic Spill 
635e25b118aSDominic Spill 	printf("\n");
636e25b118aSDominic Spill 	printf("    Data: ");
6378f3e7eeaSChristopher Kilgour 	for (i = 6; i < 6 + pkt->length; ++i)
6388f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[i]);
639e25b118aSDominic Spill 	printf("\n");
640e25b118aSDominic Spill 
641e25b118aSDominic Spill 	printf("    CRC:  ");
642e25b118aSDominic Spill 	for (i = 0; i < 3; ++i)
6438f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[6 + pkt->length + i]);
644e25b118aSDominic Spill 	printf("\n");
645e25b118aSDominic Spill }
646