xref: /libbtbb/lib/src/bluetooth_le_packet.c (revision ab0dd8d729cbc0d223f4f5f073e2b3f873ee8069)
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 
33f25ef1fbSMike Ryan /* company identifier lookup */
34f25ef1fbSMike Ryan const char *bt_compidtostr(uint16_t compid);
35f25ef1fbSMike 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 
49*ab0dd8d7SMike Ryan /* flags */
50*ab0dd8d7SMike Ryan static const char *FLAGS[] = {
51*ab0dd8d7SMike Ryan 	"LE Limited Discoverable Mode", "LE General Discoverable Mode",
52*ab0dd8d7SMike Ryan 	"BR/EDR Not Supported",
53*ab0dd8d7SMike Ryan 	"Simultaneous LE and BR/EDR to Same Device Capable (Controller)",
54*ab0dd8d7SMike Ryan 	"Simultaneous LE and BR/EDR to Same Device Capable (Host)",
55*ab0dd8d7SMike Ryan 	"Reserved", "Reserved", "Reserved",
56*ab0dd8d7SMike Ryan };
57*ab0dd8d7SMike Ryan 
58e25b118aSDominic Spill // count of objects in an array, shamelessly stolen from Chrome
59e25b118aSDominic Spill #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
60e25b118aSDominic Spill 
count_bits(uint32_t n)610a637dcaSDominic Spill static uint8_t count_bits(uint32_t n)
620a637dcaSDominic Spill {
630a637dcaSDominic Spill 	uint8_t i = 0;
640a637dcaSDominic Spill 	for (i = 0; n != 0; i++)
650a637dcaSDominic Spill 		n &= n - 1;
660a637dcaSDominic Spill 	return i;
670a637dcaSDominic Spill }
680a637dcaSDominic Spill 
aa_access_channel_off_by_one(const uint32_t aa)698f3e7eeaSChristopher Kilgour static int aa_access_channel_off_by_one(const uint32_t aa) {
708f3e7eeaSChristopher Kilgour 	int retval = 0;
710a637dcaSDominic Spill 	if(count_bits(aa ^ LE_ADV_AA) == 1) {
728f3e7eeaSChristopher Kilgour 		retval = 1;
738f3e7eeaSChristopher Kilgour 	}
748f3e7eeaSChristopher Kilgour 	return retval;
758f3e7eeaSChristopher Kilgour }
76e25b118aSDominic Spill 
778f3e7eeaSChristopher Kilgour /*
788f3e7eeaSChristopher Kilgour  * A helper function for filtering bogus packets on data channels.
798f3e7eeaSChristopher Kilgour  *
808f3e7eeaSChristopher Kilgour  * If a candidate capture packet is random noise we would expect its
818f3e7eeaSChristopher Kilgour  * Access Address to be a randomly distributed 32-bit number.  An
828f3e7eeaSChristopher Kilgour  * exhaustive software analysis reveals that of 4294967296 possible
838f3e7eeaSChristopher Kilgour  * 32-bit Access Address values, 2900629660 (67.5%) are acceptable and
848f3e7eeaSChristopher Kilgour  * 1394337636 (32.5%) are invalid.  This function will identify which
858f3e7eeaSChristopher Kilgour  * category a candidate Access Address falls into by returning the
868f3e7eeaSChristopher Kilgour  * number of offenses contained.
878f3e7eeaSChristopher Kilgour  *
888f3e7eeaSChristopher Kilgour  * Refer to BT 4.x, Vol 6, Par B, Section 2.1.2.
898f3e7eeaSChristopher Kilgour  *
908f3e7eeaSChristopher Kilgour  * The Access Address in data channel packets meet the
918f3e7eeaSChristopher Kilgour  * following requirements:
928f3e7eeaSChristopher Kilgour  *  - It shall have no more than six consecutive zeros or ones.
938f3e7eeaSChristopher Kilgour  *  - It shall not be the advertising channel packets’ Access Address.
948f3e7eeaSChristopher Kilgour  *  - It shall not be a sequence that differs from the advertising channel packets’
958f3e7eeaSChristopher Kilgour  *    Access Address by only one bit.
968f3e7eeaSChristopher Kilgour  *  - It shall not have all four octets equal.
978f3e7eeaSChristopher Kilgour  *  - It shall have no more than 24 transitions.
988f3e7eeaSChristopher Kilgour  *  - It shall have a minimum of two transitions in the most significant six bits.
998f3e7eeaSChristopher Kilgour  */
aa_data_channel_offenses(const uint32_t aa)1008f3e7eeaSChristopher Kilgour static int aa_data_channel_offenses(const uint32_t aa) {
1018f3e7eeaSChristopher Kilgour 	int retval = 0, transitions = 0;
1028f3e7eeaSChristopher Kilgour 	unsigned shift, odd = (unsigned) (aa & 1);
1038f3e7eeaSChristopher Kilgour 	uint8_t aab3, aab2, aab1, aab0 = (uint8_t) (aa & 0xff);
104e25b118aSDominic Spill 
1058f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_EVEN[256] = {
1068f3e7eeaSChristopher Kilgour 		0, 2, 2, 2, 2, 4, 2, 2, 2, 4, 4, 4, 2, 4, 2, 2,
1078f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
1088f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1098f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
1108f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1118f3e7eeaSChristopher Kilgour 		4, 6, 6, 6, 6, 8, 6, 6, 4, 6, 6, 6, 4, 6, 4, 4,
1128f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
1138f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
1148f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1158f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1168f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 5, 7, 7, 7, 5, 7, 5, 5,
1178f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1188f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1198f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
1208f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
1218f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 1, 3, 3, 3, 1, 3, 1, 1
1228f3e7eeaSChristopher Kilgour 	};
123e25b118aSDominic Spill 
1248f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_ODD[256] = {
1258f3e7eeaSChristopher Kilgour 		1, 1, 3, 1, 3, 3, 3, 1, 3, 3, 5, 3, 3, 3, 3, 1,
1268f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1278f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1288f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1298f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1308f3e7eeaSChristopher Kilgour 		5, 5, 7, 5, 7, 7, 7, 5, 5, 5, 7, 5, 5, 5, 5, 3,
1318f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
1328f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
1338f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1348f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1358f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 6, 6, 8, 6, 6, 6, 6, 4,
1368f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1378f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1388f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
1398f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
1408f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 2, 2, 4, 2, 2, 2, 2, 0
1418f3e7eeaSChristopher Kilgour 	};
1428f3e7eeaSChristopher Kilgour 
1438f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab0] : EIGHT_BIT_TRANSITIONS_EVEN[aab0] );
1448f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab0 & 0x80);
1458f3e7eeaSChristopher Kilgour 	aab1 = (uint8_t) (aa >> 8);
1468f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab1] : EIGHT_BIT_TRANSITIONS_EVEN[aab1] );
1478f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab1 & 0x80);
1488f3e7eeaSChristopher Kilgour 	aab2 = (uint8_t) (aa >> 16);
1498f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab2] : EIGHT_BIT_TRANSITIONS_EVEN[aab2] );
1508f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab2 & 0x80);
1518f3e7eeaSChristopher Kilgour 	aab3 = (uint8_t) (aa >> 24);
1528f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab3] : EIGHT_BIT_TRANSITIONS_EVEN[aab3] );
1538f3e7eeaSChristopher Kilgour 
1548f3e7eeaSChristopher Kilgour 	/* consider excessive transitions as offenses */
1558f3e7eeaSChristopher Kilgour 	if (transitions > 24) {
1568f3e7eeaSChristopher Kilgour 		retval += (transitions - 24);
1578f3e7eeaSChristopher Kilgour 	}
1588f3e7eeaSChristopher Kilgour 
1598f3e7eeaSChristopher Kilgour 	const uint8_t AA_MSB6_ALLOWED[64] = {
1608f3e7eeaSChristopher Kilgour 		0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
1618f3e7eeaSChristopher Kilgour 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1628f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1638f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0
1648f3e7eeaSChristopher Kilgour 	};
1658f3e7eeaSChristopher Kilgour 
1668f3e7eeaSChristopher Kilgour 	/* consider excessive transitions in the 6 MSBs as an offense */
1678f3e7eeaSChristopher Kilgour 	retval += (1 - AA_MSB6_ALLOWED[aab3>>2]);
1688f3e7eeaSChristopher Kilgour 
1698f3e7eeaSChristopher Kilgour 	/* consider all bytes as being equal an offense */
1708f3e7eeaSChristopher Kilgour 	retval += (((aab0 == aab1) && (aab0 == aab2) && (aab0 == aab3)) ? 1 : 0);
1718f3e7eeaSChristopher Kilgour 
1728f3e7eeaSChristopher Kilgour 	/* access-channel address and off-by-ones are illegal */
1730a637dcaSDominic Spill 	retval += ((aa == LE_ADV_AA) ? 1 : 0);
1748f3e7eeaSChristopher Kilgour 	retval += aa_access_channel_off_by_one(aa);
1758f3e7eeaSChristopher Kilgour 
1768f3e7eeaSChristopher Kilgour 	/* inspect nibble triples for insufficient bit transitions */
1778f3e7eeaSChristopher Kilgour 	for(shift=0; shift<=20; shift+=4) {
1788f3e7eeaSChristopher Kilgour 		uint16_t twelvebits = (uint16_t) ((aa >> shift) & 0xfff);
1798f3e7eeaSChristopher Kilgour 		switch( twelvebits ) {
1808f3e7eeaSChristopher Kilgour 			/* seven consecutive zeroes */
1818f3e7eeaSChristopher Kilgour 		case 0x080: case 0x180: case 0x280: case 0x380: case 0x480:
1828f3e7eeaSChristopher Kilgour 		case 0x580: case 0x680: case 0x780: case 0x880: case 0x980:
1838f3e7eeaSChristopher Kilgour 		case 0xa80: case 0xb80: case 0xc80: case 0xd80: case 0xe80:
1848f3e7eeaSChristopher Kilgour 		case 0xf80: case 0x101: case 0x301: case 0x501: case 0x701:
1858f3e7eeaSChristopher Kilgour 		case 0x901: case 0xb01: case 0xd01: case 0xf01: case 0x202:
1868f3e7eeaSChristopher Kilgour 		case 0x602: case 0xa02: case 0xe02: case 0x203: case 0x603:
1878f3e7eeaSChristopher Kilgour 		case 0xa03: case 0xe03: case 0x404: case 0xc04: case 0x405:
1888f3e7eeaSChristopher Kilgour 		case 0xc05: case 0x406: case 0xc06: case 0x407: case 0xc07:
1898f3e7eeaSChristopher Kilgour 		case 0x808: case 0x809: case 0x80a: case 0x80b: case 0x80c:
1908f3e7eeaSChristopher Kilgour 		case 0x80d: case 0x80e: case 0x80f: case 0x010: case 0x011:
1918f3e7eeaSChristopher Kilgour 		case 0x012: case 0x013: case 0x014: case 0x015: case 0x016:
1928f3e7eeaSChristopher Kilgour 		case 0x017: case 0x018: case 0x019: case 0x01a: case 0x01b:
1938f3e7eeaSChristopher Kilgour 		case 0x01c: case 0x01d: case 0x01e: case 0x01f:
1948f3e7eeaSChristopher Kilgour 			/* eight consecutive zeroes */
1958f3e7eeaSChristopher Kilgour 		case 0x100: case 0x300: case 0x500: case 0x700: case 0x900:
1968f3e7eeaSChristopher Kilgour 		case 0xb00: case 0xd00: case 0xf00: case 0x201: case 0x601:
1978f3e7eeaSChristopher Kilgour 		case 0xa01: case 0xe01: case 0x402: case 0xc02: case 0x403:
1988f3e7eeaSChristopher Kilgour 		case 0xc03: case 0x804: case 0x805: case 0x806: case 0x807:
1998f3e7eeaSChristopher Kilgour 		case 0x008: case 0x009: case 0x00a: case 0x00b: case 0x00c:
2008f3e7eeaSChristopher Kilgour 		case 0x00d: case 0x00e: case 0x00f:
2018f3e7eeaSChristopher Kilgour 			/* nine consecutive zeroes */
2028f3e7eeaSChristopher Kilgour 		case 0xe00: case 0xc01: case 0x802: case 0x803: case 0x004:
2038f3e7eeaSChristopher Kilgour 		case 0x005: case 0x006: case 0x007:
2048f3e7eeaSChristopher Kilgour 			/* ten consecutive zeroes */
2058f3e7eeaSChristopher Kilgour 		case 0x400: case 0xc00: case 0x801: case 0x002: case 0x003:
2068f3e7eeaSChristopher Kilgour 			/* eleven consecutive zeroes */
2078f3e7eeaSChristopher Kilgour 		case 0x800: case 0x001:
2088f3e7eeaSChristopher Kilgour 			/* twelve consecutive zeroes */
2098f3e7eeaSChristopher Kilgour 		case 0x000:
2108f3e7eeaSChristopher Kilgour 			/* seven consecutive ones */
2118f3e7eeaSChristopher Kilgour 		case 0x07f: case 0x0fe: case 0x2fe: case 0x4fe: case 0x6fe:
2128f3e7eeaSChristopher Kilgour 		case 0x8fe: case 0xafe: case 0xcfe: case 0xefe: case 0x1fc:
2138f3e7eeaSChristopher Kilgour 		case 0x5fc: case 0x9fc: case 0xdfc: case 0x1fd: case 0x5fd:
2148f3e7eeaSChristopher Kilgour 		case 0x9fd: case 0xdfd: case 0x3f8: case 0xbf8: case 0x3f9:
2158f3e7eeaSChristopher Kilgour 		case 0xbf9: case 0x3fa: case 0xbfa: case 0x3fb: case 0xbfb:
2168f3e7eeaSChristopher Kilgour 		case 0x7f4: case 0x7f5: case 0x7f6: case 0x7f7: case 0xfe0:
2178f3e7eeaSChristopher Kilgour 			/* eight consecutive ones */
2188f3e7eeaSChristopher Kilgour 		case 0x0ff: case 0x2ff: case 0x4ff: case 0x6ff: case 0x8ff:
2198f3e7eeaSChristopher Kilgour 		case 0xaff: case 0xcff: case 0xeff: case 0x1fe: case 0x5fe:
2208f3e7eeaSChristopher Kilgour 		case 0x9fe: case 0xdfe: case 0x3fc: case 0xbfc: case 0x3fd:
2218f3e7eeaSChristopher Kilgour 		case 0xbfd: case 0x7f8: case 0x7f9: case 0x7fa: case 0x7fb:
2228f3e7eeaSChristopher Kilgour 		case 0xff0: case 0xff1: case 0xff2: case 0xff3: case 0xff4:
2238f3e7eeaSChristopher Kilgour 		case 0xff5: case 0xff6: case 0xff7:
2248f3e7eeaSChristopher Kilgour 			/* nine consecutive ones */
2258f3e7eeaSChristopher Kilgour 		case 0x1ff: case 0x5ff: case 0x9ff: case 0xdff: case 0x3fe:
2268f3e7eeaSChristopher Kilgour 		case 0xbfe: case 0x7fc: case 0x7fd: case 0xff8: case 0xff9:
2278f3e7eeaSChristopher Kilgour 		case 0xffa: case 0xffb:
2288f3e7eeaSChristopher Kilgour 			/* ten consecutive ones */
2298f3e7eeaSChristopher Kilgour 		case 0x3ff: case 0xbff: case 0x7fe: case 0xffc: case 0xffd:
2308f3e7eeaSChristopher Kilgour 			/* eleven consecutive ones */
2318f3e7eeaSChristopher Kilgour 		case 0x7ff: case 0xffe:
2328f3e7eeaSChristopher Kilgour 			/* all ones */
2338f3e7eeaSChristopher Kilgour 		case 0xfff:
2348f3e7eeaSChristopher Kilgour 			retval++;
2358f3e7eeaSChristopher Kilgour 			break;
2368f3e7eeaSChristopher Kilgour 		default:
2378f3e7eeaSChristopher Kilgour 			break;
238e25b118aSDominic Spill 		}
239e25b118aSDominic Spill 	}
240e25b118aSDominic Spill 
2418f3e7eeaSChristopher Kilgour 	return retval;
242e25b118aSDominic Spill }
243e25b118aSDominic Spill 
2448f3e7eeaSChristopher Kilgour lell_packet *
lell_packet_new(void)2458f3e7eeaSChristopher Kilgour lell_packet_new(void)
2468f3e7eeaSChristopher Kilgour {
2478f3e7eeaSChristopher Kilgour 	lell_packet *pkt = (lell_packet *)calloc(1, sizeof(lell_packet));
2488f3e7eeaSChristopher Kilgour 	pkt->refcount = 1;
2498f3e7eeaSChristopher Kilgour 	return pkt;
2508f3e7eeaSChristopher Kilgour }
2518f3e7eeaSChristopher Kilgour 
2528f3e7eeaSChristopher Kilgour void
lell_packet_ref(lell_packet * pkt)2538f3e7eeaSChristopher Kilgour lell_packet_ref(lell_packet *pkt)
2548f3e7eeaSChristopher Kilgour {
2558f3e7eeaSChristopher Kilgour 	pkt->refcount++;
2568f3e7eeaSChristopher Kilgour }
2578f3e7eeaSChristopher Kilgour 
2588f3e7eeaSChristopher Kilgour void
lell_packet_unref(lell_packet * pkt)2598f3e7eeaSChristopher Kilgour lell_packet_unref(lell_packet *pkt)
2608f3e7eeaSChristopher Kilgour {
2618f3e7eeaSChristopher Kilgour 	pkt->refcount--;
2628f3e7eeaSChristopher Kilgour 	if (pkt->refcount == 0)
2638f3e7eeaSChristopher Kilgour 		free(pkt);
2648f3e7eeaSChristopher Kilgour }
2658f3e7eeaSChristopher Kilgour 
le_channel_index(uint16_t phys_channel)2668f3e7eeaSChristopher Kilgour static uint8_t le_channel_index(uint16_t phys_channel) {
267e25b118aSDominic Spill 	uint8_t ret;
268e25b118aSDominic Spill 	if (phys_channel == 2402) {
269e25b118aSDominic Spill 		ret = 37;
270e25b118aSDominic Spill 	} else if (phys_channel < 2426) { // 0 - 10
271e25b118aSDominic Spill 		ret = (phys_channel - 2404) / 2;
272e25b118aSDominic Spill 	} else if (phys_channel == 2426) {
273e25b118aSDominic Spill 		ret = 38;
274e25b118aSDominic Spill 	} else if (phys_channel < 2480) { // 11 - 36
275e25b118aSDominic Spill 		ret = 11 + (phys_channel - 2428) / 2;
276e25b118aSDominic Spill 	} else {
277e25b118aSDominic Spill 		ret = 39;
278e25b118aSDominic Spill 	}
279e25b118aSDominic Spill 	return ret;
280e25b118aSDominic Spill }
281e25b118aSDominic Spill 
lell_allocate_and_decode(const uint8_t * stream,uint16_t phys_channel,uint32_t clk100ns,lell_packet ** pkt)2828f3e7eeaSChristopher Kilgour void lell_allocate_and_decode(const uint8_t *stream, uint16_t phys_channel, uint32_t clk100ns, lell_packet **pkt)
2838f3e7eeaSChristopher Kilgour {
2848f3e7eeaSChristopher Kilgour 	*pkt = lell_packet_new( );
2858f3e7eeaSChristopher Kilgour 	memcpy((*pkt)->symbols, stream, MAX_LE_SYMBOLS);
2868f3e7eeaSChristopher Kilgour 
2878f3e7eeaSChristopher Kilgour 	(*pkt)->channel_idx = le_channel_index(phys_channel);
2888f3e7eeaSChristopher Kilgour 	(*pkt)->channel_k = (phys_channel-2402)/2;
2898f3e7eeaSChristopher Kilgour 	(*pkt)->clk100ns = clk100ns;
2908f3e7eeaSChristopher Kilgour 
2918f3e7eeaSChristopher Kilgour 	(*pkt)->access_address = 0;
2928f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[0];
2938f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[1] << 8;
2948f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[2] << 16;
2958f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[3] << 24;
2968f3e7eeaSChristopher Kilgour 
2978f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(*pkt)) {
2988f3e7eeaSChristopher Kilgour 		// data PDU
2998f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x1f;
3008f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = aa_data_channel_offenses((*pkt)->access_address);
3018f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = (*pkt)->access_address_offenses ? 0 : 1;
3028f3e7eeaSChristopher Kilgour 	} else {
3038f3e7eeaSChristopher Kilgour 		// advertising PDU
3048f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x3f;
3058f3e7eeaSChristopher Kilgour 		(*pkt)->adv_type = (*pkt)->symbols[4] & 0xf;
3068f3e7eeaSChristopher Kilgour 		(*pkt)->adv_tx_add = (*pkt)->symbols[4] & 0x40 ? 1 : 0;
3078f3e7eeaSChristopher Kilgour 		(*pkt)->adv_rx_add = (*pkt)->symbols[4] & 0x80 ? 1 : 0;
3088f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = ((*pkt)->access_address == 0x8e89bed6);
3098f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = (*pkt)->flags.as_bits.access_address_ok ? 0 :
3108f3e7eeaSChristopher Kilgour 			(aa_access_channel_off_by_one((*pkt)->access_address) ? 1 : 32);
3118f3e7eeaSChristopher Kilgour 	}
3128f3e7eeaSChristopher Kilgour }
3138f3e7eeaSChristopher Kilgour 
lell_packet_is_data(const lell_packet * pkt)3148f3e7eeaSChristopher Kilgour unsigned lell_packet_is_data(const lell_packet *pkt)
3158f3e7eeaSChristopher Kilgour {
3168f3e7eeaSChristopher Kilgour 	return (unsigned) (pkt->channel_idx < 37);
3178f3e7eeaSChristopher Kilgour }
3188f3e7eeaSChristopher Kilgour 
lell_get_access_address(const lell_packet * pkt)3198f3e7eeaSChristopher Kilgour uint32_t lell_get_access_address(const lell_packet *pkt)
3208f3e7eeaSChristopher Kilgour {
3218f3e7eeaSChristopher Kilgour 	return pkt->access_address;
3228f3e7eeaSChristopher Kilgour }
3238f3e7eeaSChristopher Kilgour 
lell_get_access_address_offenses(const lell_packet * pkt)3248f3e7eeaSChristopher Kilgour unsigned lell_get_access_address_offenses(const lell_packet *pkt)
3258f3e7eeaSChristopher Kilgour {
3268f3e7eeaSChristopher Kilgour 	return pkt->access_address_offenses;
3278f3e7eeaSChristopher Kilgour }
3288f3e7eeaSChristopher Kilgour 
lell_get_channel_index(const lell_packet * pkt)3298f3e7eeaSChristopher Kilgour unsigned lell_get_channel_index(const lell_packet *pkt)
3308f3e7eeaSChristopher Kilgour {
3318f3e7eeaSChristopher Kilgour 	return pkt->channel_idx;
3328f3e7eeaSChristopher Kilgour }
3338f3e7eeaSChristopher Kilgour 
lell_get_channel_k(const lell_packet * pkt)3348f3e7eeaSChristopher Kilgour unsigned lell_get_channel_k(const lell_packet *pkt)
3358f3e7eeaSChristopher Kilgour {
3368f3e7eeaSChristopher Kilgour 	return pkt->channel_k;
3378f3e7eeaSChristopher Kilgour }
3388f3e7eeaSChristopher Kilgour 
lell_get_adv_type_str(const lell_packet * pkt)3398f3e7eeaSChristopher Kilgour const char * lell_get_adv_type_str(const lell_packet *pkt)
3408f3e7eeaSChristopher Kilgour {
3418f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt))
342e25b118aSDominic Spill 		return NULL;
3438f3e7eeaSChristopher Kilgour 	if (pkt->adv_type < COUNT_OF(ADV_TYPE_NAMES))
3448f3e7eeaSChristopher Kilgour 		return ADV_TYPE_NAMES[pkt->adv_type];
345e25b118aSDominic Spill 	return "UNKNOWN";
346e25b118aSDominic Spill }
347e25b118aSDominic Spill 
_dump_addr(const char * name,const uint8_t * buf,int offset,int random)3488f3e7eeaSChristopher Kilgour static void _dump_addr(const char *name, const uint8_t *buf, int offset, int random) {
349e25b118aSDominic Spill 	int i;
350e25b118aSDominic Spill 	printf("    %s%02x", name, buf[offset+5]);
351e25b118aSDominic Spill 	for (i = 4; i >= 0; --i)
352e25b118aSDominic Spill 		printf(":%02x", buf[offset+i]);
353e25b118aSDominic Spill 	printf(" (%s)\n", random ? "random" : "public");
354e25b118aSDominic Spill }
355e25b118aSDominic Spill 
_dump_8(const char * name,const uint8_t * buf,int offset)3568f3e7eeaSChristopher Kilgour static void _dump_8(const char *name, const uint8_t *buf, int offset) {
357e25b118aSDominic Spill 	printf("    %s%02x (%d)\n", name, buf[offset], buf[offset]);
358e25b118aSDominic Spill }
359e25b118aSDominic Spill 
_dump_16(const char * name,const uint8_t * buf,int offset)3608f3e7eeaSChristopher Kilgour static void _dump_16(const char *name, const uint8_t *buf, int offset) {
361e25b118aSDominic Spill 	uint16_t val = buf[offset+1] << 8 | buf[offset];
362e25b118aSDominic Spill 	printf("    %s%04x (%d)\n", name, val, val);
363e25b118aSDominic Spill }
364e25b118aSDominic Spill 
_dump_24(char * name,const uint8_t * buf,int offset)365c6bb3715SMike Ryan static void _dump_24(char *name, const uint8_t *buf, int offset) {
366c6bb3715SMike Ryan 	uint32_t val = buf[offset+2] << 16 | buf[offset+1] << 8 | buf[offset];
367e25b118aSDominic Spill 	printf("    %s%06x\n", name, val);
368e25b118aSDominic Spill }
369e25b118aSDominic Spill 
_dump_32(const char * name,const uint8_t * buf,int offset)3708f3e7eeaSChristopher Kilgour static void _dump_32(const char *name, const uint8_t *buf, int offset) {
371e25b118aSDominic Spill 	uint32_t val = buf[offset+3] << 24 |
372e25b118aSDominic Spill 				   buf[offset+2] << 16 |
373e25b118aSDominic Spill 				   buf[offset+1] << 8 |
374e25b118aSDominic Spill 				   buf[offset+0];
375e25b118aSDominic Spill 	printf("    %s%08x\n", name, val);
376e25b118aSDominic Spill }
377e25b118aSDominic Spill 
_dump_uuid(const uint8_t * uuid)3788f3e7eeaSChristopher Kilgour static void _dump_uuid(const uint8_t *uuid) {
379e25b118aSDominic Spill 	int i;
380e25b118aSDominic Spill 	for (i = 0; i < 4; ++i)
381e25b118aSDominic Spill 		printf("%02x", uuid[i]);
382e25b118aSDominic Spill 	printf("-");
383e25b118aSDominic Spill 	for (i = 4; i < 6; ++i)
384e25b118aSDominic Spill 		printf("%02x", uuid[i]);
385e25b118aSDominic Spill 	printf("-");
386e25b118aSDominic Spill 	for (i = 6; i < 8; ++i)
387e25b118aSDominic Spill 		printf("%02x", uuid[i]);
388e25b118aSDominic Spill 	printf("-");
389e25b118aSDominic Spill 	for (i = 8; i < 10; ++i)
390e25b118aSDominic Spill 		printf("%02x", uuid[i]);
391e25b118aSDominic Spill 	printf("-");
392e25b118aSDominic Spill 	for (i = 10; i < 16; ++i)
393e25b118aSDominic Spill 		printf("%02x", uuid[i]);
394e25b118aSDominic Spill }
395e25b118aSDominic Spill 
396e25b118aSDominic Spill // Refer to pg 1735 of Bluetooth Core Spec 4.0
_dump_scan_rsp_data(const uint8_t * buf,int len)3978f3e7eeaSChristopher Kilgour static void _dump_scan_rsp_data(const uint8_t *buf, int len) {
398e25b118aSDominic Spill 	int pos = 0;
399e25b118aSDominic Spill 	int sublen, i;
400e25b118aSDominic Spill 	uint8_t type;
401e25b118aSDominic Spill 	uint16_t val;
402e25b118aSDominic Spill 	char *cval;
403e25b118aSDominic Spill 
404e25b118aSDominic Spill 	while (pos < len) {
405e25b118aSDominic Spill 		sublen = buf[pos];
406e25b118aSDominic Spill 		++pos;
407e25b118aSDominic Spill 		if (pos + sublen > len) {
408e25b118aSDominic Spill 			printf("Error: attempt to read past end of buffer (%d + %d > %d)\n", pos, sublen, len);
409e25b118aSDominic Spill 			return;
410e25b118aSDominic Spill 		}
411e25b118aSDominic Spill 		if (sublen == 0) {
412e25b118aSDominic Spill 			printf("Early return due to 0 length\n");
413e25b118aSDominic Spill 			return;
414e25b118aSDominic Spill 		}
415e25b118aSDominic Spill 		type = buf[pos];
416e25b118aSDominic Spill 		printf("        Type %02x", type);
417e25b118aSDominic Spill 		switch (type) {
418e25b118aSDominic Spill 			case 0x01:
419e25b118aSDominic Spill 				printf(" (Flags)\n");
420e25b118aSDominic Spill 				printf("           ");
421e25b118aSDominic Spill 				for (i = 0; i < 8; ++i)
422e25b118aSDominic Spill 					printf("%d", buf[pos+1] & (1 << (7-i)) ? 1 : 0);
423e25b118aSDominic Spill 				printf("\n");
424*ab0dd8d7SMike Ryan 				for (i = 0; i < 8; ++i) {
425*ab0dd8d7SMike Ryan 					if (buf[pos+1] & (1 << i)) {
426*ab0dd8d7SMike Ryan 						printf("               ");
427*ab0dd8d7SMike Ryan 						printf("%s\n", FLAGS[i]);
428*ab0dd8d7SMike Ryan 					}
429*ab0dd8d7SMike Ryan 				}
430*ab0dd8d7SMike Ryan 				printf("\n");
431e25b118aSDominic Spill 				break;
43200c7e1b3SMike Ryan 			case 0x02:
43300c7e1b3SMike Ryan 				printf(" (16-bit Service UUIDs, more available)\n");
43400c7e1b3SMike Ryan 				goto print16;
43500c7e1b3SMike Ryan 			case 0x03:
43600c7e1b3SMike Ryan 				printf(" (16-bit Service UUIDs) \n");
43700c7e1b3SMike Ryan print16:
43800c7e1b3SMike Ryan 				if ((sublen - 1) % 2 == 0) {
43900c7e1b3SMike Ryan 					for (i = 0; i < sublen - 1; i += 2) {
44000c7e1b3SMike Ryan 						uint16_t *uuid = (uint16_t *)&buf[pos+1+i];
44100c7e1b3SMike Ryan 						printf("           %04x\n", *uuid);
44200c7e1b3SMike Ryan 					}
44300c7e1b3SMike Ryan 				}
44400c7e1b3SMike Ryan 				break;
44545000095SMike Ryan 			case 0x06:
44645000095SMike Ryan 				printf(" (128-bit Service UUIDs, more available)\n");
44745000095SMike Ryan 				goto print128;
448e25b118aSDominic Spill 			case 0x07:
449e25b118aSDominic Spill 				printf(" (128-bit Service UUIDs)\n");
45045000095SMike Ryan print128:
451e25b118aSDominic Spill 				if ((sublen - 1) % 16 == 0) {
452e25b118aSDominic Spill 					uint8_t uuid[16];
453e25b118aSDominic Spill 					for (i = 0; i < sublen - 1; ++i) {
454e25b118aSDominic Spill 						uuid[15 - (i % 16)] = buf[pos+1+i];
455e25b118aSDominic Spill 						if ((i & 15) == 15) {
456e25b118aSDominic Spill 							printf("           ");
457e25b118aSDominic Spill 							_dump_uuid(uuid);
458e25b118aSDominic Spill 							printf("\n");
459e25b118aSDominic Spill 						}
460e25b118aSDominic Spill 					}
461e25b118aSDominic Spill 				}
462e25b118aSDominic Spill 				else {
463e25b118aSDominic Spill 					printf("Wrong length (%d, must be divisible by 16)\n", sublen-1);
464e25b118aSDominic Spill 				}
465e25b118aSDominic Spill 				break;
466e25b118aSDominic Spill 			case 0x09:
467e25b118aSDominic Spill 				printf(" (Complete Local Name)\n");
468e25b118aSDominic Spill 				printf("           ");
469e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
470e25b118aSDominic Spill 					printf("%c", isprint(buf[pos+i]) ? buf[pos+i] : '.');
471e25b118aSDominic Spill 				printf("\n");
472e25b118aSDominic Spill 				break;
473e25b118aSDominic Spill 			case 0x0a:
474e25b118aSDominic Spill 				printf(" (Tx Power Level)\n");
475e25b118aSDominic Spill 				printf("           ");
476e25b118aSDominic Spill 				if (sublen-1 == 1) {
477e25b118aSDominic Spill 					cval = (char *)&buf[pos+1];
478e25b118aSDominic Spill 					printf("%d dBm\n", *cval);
479e25b118aSDominic Spill 				} else {
480e25b118aSDominic Spill 					printf("Wrong length (%d, should be 1)\n", sublen-1);
481e25b118aSDominic Spill 				}
482e25b118aSDominic Spill 				break;
483e25b118aSDominic Spill 			case 0x12:
484e25b118aSDominic Spill 				printf(" (Slave Connection Interval Range)\n");
485e25b118aSDominic Spill 				printf("           ");
486e25b118aSDominic Spill 				if (sublen-1 == 4) {
487e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
488e25b118aSDominic Spill 					printf("(%0.2f, ", val * 1.25);
489e25b118aSDominic Spill 					val = (buf[pos+4] << 8) | buf[pos+3];
490e25b118aSDominic Spill 					printf("%0.2f) ms\n", val * 1.25);
491e25b118aSDominic Spill 				}
492e25b118aSDominic Spill 				else {
493e25b118aSDominic Spill 					printf("Wrong length (%d, should be 4)\n", sublen-1);
494e25b118aSDominic Spill 				}
495e25b118aSDominic Spill 				break;
496e25b118aSDominic Spill 			case 0x16:
497e25b118aSDominic Spill 				printf(" (Service Data)\n");
498e25b118aSDominic Spill 				printf("           ");
499e25b118aSDominic Spill 				if (sublen-1 >= 2) {
500e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
501e25b118aSDominic Spill 					printf("UUID: %02x", val);
502e25b118aSDominic Spill 					if (sublen-1 > 2) {
503e25b118aSDominic Spill 						printf(", Additional:");
504e25b118aSDominic Spill 						for (i = 3; i < sublen; ++i)
505e25b118aSDominic Spill 							printf(" %02x", buf[pos+i]);
506e25b118aSDominic Spill 					}
507e25b118aSDominic Spill 					printf("\n");
508e25b118aSDominic Spill 				}
509e25b118aSDominic Spill 				else {
510e25b118aSDominic Spill 					printf("Wrong length (%d, should be >= 2)\n", sublen-1);
511e25b118aSDominic Spill 				}
512e25b118aSDominic Spill 				break;
513f25ef1fbSMike Ryan 			case 0xff:
514f25ef1fbSMike Ryan 				printf(" (Manufacturer Specific Data)\n");
515f25ef1fbSMike Ryan 				printf("           ");
516f25ef1fbSMike Ryan 				if (sublen - 1 >= 2) {
517f25ef1fbSMike Ryan 					uint16_t company = (buf[pos+2] << 8) | buf[pos+1];
518f25ef1fbSMike Ryan 					printf("Company: %s\n", bt_compidtostr(company));
519f25ef1fbSMike Ryan 					printf("           ");
520f25ef1fbSMike Ryan 					printf("Data:");
521f25ef1fbSMike Ryan 					for (i = 3; i < sublen; ++i)
522f25ef1fbSMike Ryan 						printf(" %02x", buf[pos+i]);
523f25ef1fbSMike Ryan 					printf("\n");
524f25ef1fbSMike Ryan 				}
525f25ef1fbSMike Ryan 				else {
526f25ef1fbSMike Ryan 					printf("Wrong length (%d, should be >= 2)\n", sublen-1);
527f25ef1fbSMike Ryan 				}
528f25ef1fbSMike Ryan 				break;
529e25b118aSDominic Spill 			default:
530e25b118aSDominic Spill 				printf("\n");
531e25b118aSDominic Spill 				printf("           ");
532e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
533e25b118aSDominic Spill 					printf(" %02x", buf[pos+i]);
534e25b118aSDominic Spill 				printf("\n");
535e25b118aSDominic Spill 		}
536e25b118aSDominic Spill 		pos += sublen;
537e25b118aSDominic Spill 	}
538e25b118aSDominic Spill }
539e25b118aSDominic Spill 
lell_print(const lell_packet * pkt)5408f3e7eeaSChristopher Kilgour void lell_print(const lell_packet *pkt)
5418f3e7eeaSChristopher Kilgour {
542387b6603SIvan Krasin 	int i, opcode;
5438f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt)) {
5448f3e7eeaSChristopher Kilgour 		int llid = pkt->symbols[4] & 0x3;
545e25b118aSDominic Spill 		static const char *llid_str[] = {
546e25b118aSDominic Spill 			"Reserved",
547e25b118aSDominic Spill 			"LL Data PDU / empty or L2CAP continuation",
548e25b118aSDominic Spill 			"LL Data PDU / L2CAP start",
549e25b118aSDominic Spill 			"LL Control PDU",
550e25b118aSDominic Spill 		};
551e25b118aSDominic Spill 
5528f3e7eeaSChristopher Kilgour 		printf("Data / AA %08x (%s) / %2d bytes\n", pkt->access_address,
5538f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
5548f3e7eeaSChristopher Kilgour 		       pkt->length);
5558f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
556e25b118aSDominic Spill 		printf("    LLID: %d / %s\n", llid, llid_str[llid]);
5578f3e7eeaSChristopher Kilgour 		printf("    NESN: %d  SN: %d  MD: %d\n", (pkt->symbols[4] >> 2) & 1,
5588f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 3) & 1,
5598f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 4) & 1);
560387b6603SIvan Krasin 		switch (llid) {
561387b6603SIvan Krasin 		case 3: // LL Control PDU
5628f3e7eeaSChristopher Kilgour 			opcode = pkt->symbols[6];
563387b6603SIvan Krasin 			static const char *opcode_str[] = {
564387b6603SIvan Krasin 				"LL_CONNECTION_UPDATE_REQ",
565387b6603SIvan Krasin 				"LL_CHANNEL_MAP_REQ",
566387b6603SIvan Krasin 				"LL_TERMINATE_IND",
567387b6603SIvan Krasin 				"LL_ENC_REQ",
568387b6603SIvan Krasin 				"LL_ENC_RSP",
569387b6603SIvan Krasin 				"LL_START_ENC_REQ",
570387b6603SIvan Krasin 				"LL_START_ENC_RSP",
571387b6603SIvan Krasin 				"LL_UNKNOWN_RSP",
572387b6603SIvan Krasin 				"LL_FEATURE_REQ",
573387b6603SIvan Krasin 				"LL_FEATURE_RSP",
574387b6603SIvan Krasin 				"LL_PAUSE_ENC_REQ",
575387b6603SIvan Krasin 				"LL_PAUSE_ENC_RSP",
576387b6603SIvan Krasin 				"LL_VERSION_IND",
577387b6603SIvan Krasin 				"LL_REJECT_IND",
578b3394904SMike Ryan 				"LL_SLAVE_FEATURE_REQ",
579b3394904SMike Ryan 				"LL_CONNECTION_PARAM_REQ",
580b3394904SMike Ryan 				"LL_CONNECTION_PARAM_RSP",
581b3394904SMike Ryan 				"LL_REJECT_IND_EXT",
582b3394904SMike Ryan 				"LL_PING_REQ",
583b3394904SMike Ryan 				"LL_PING_RSP",
584387b6603SIvan Krasin 				"Reserved for Future Use",
585387b6603SIvan Krasin 			};
586ba100f3dSMike Ryan 			printf("    Opcode: %d / %s\n", opcode, opcode_str[(opcode<0x14)?opcode:0x14]);
587387b6603SIvan Krasin 			break;
588387b6603SIvan Krasin 		default:
589387b6603SIvan Krasin 			break;
590387b6603SIvan Krasin 		}
591e25b118aSDominic Spill 	} else {
5928f3e7eeaSChristopher Kilgour 		printf("Advertising / AA %08x (%s)/ %2d bytes\n", pkt->access_address,
5938f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
5948f3e7eeaSChristopher Kilgour 		       pkt->length);
5958f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
5968f3e7eeaSChristopher Kilgour 		printf("    Type:  %s\n", lell_get_adv_type_str(pkt));
597e25b118aSDominic Spill 
5988f3e7eeaSChristopher Kilgour 		switch(pkt->adv_type) {
599e25b118aSDominic Spill 			case ADV_IND:
600ee0d4df0SMike Ryan 			case ADV_NONCONN_IND:
601ee0d4df0SMike Ryan 			case ADV_SCAN_IND:
6028f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
6038f3e7eeaSChristopher Kilgour 				if (pkt->length-6 > 0) {
604e25b118aSDominic Spill 					printf("    AdvData:");
6058f3e7eeaSChristopher Kilgour 					for (i = 0; i < pkt->length - 6; ++i)
6068f3e7eeaSChristopher Kilgour 						printf(" %02x", pkt->symbols[12+i]);
607e25b118aSDominic Spill 					printf("\n");
6088f3e7eeaSChristopher Kilgour 					_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
609e25b118aSDominic Spill 				}
610e25b118aSDominic Spill 				break;
611ee0d4df0SMike Ryan 			case ADV_DIRECT_IND:
612ee0d4df0SMike Ryan 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
613ee0d4df0SMike Ryan 				_dump_addr("InitA: ", pkt->symbols, 12, pkt->adv_rx_add);
614ee0d4df0SMike Ryan 				break;
615e25b118aSDominic Spill 			case SCAN_REQ:
6168f3e7eeaSChristopher Kilgour 				_dump_addr("ScanA: ", pkt->symbols, 6, pkt->adv_tx_add);
6178f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
618e25b118aSDominic Spill 				break;
619e25b118aSDominic Spill 			case SCAN_RSP:
6208f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
621e25b118aSDominic Spill 				printf("    ScanRspData:");
6228f3e7eeaSChristopher Kilgour 				for (i = 0; i < pkt->length - 6; ++i)
6238f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[12+i]);
624e25b118aSDominic Spill 				printf("\n");
6258f3e7eeaSChristopher Kilgour 				_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
626e25b118aSDominic Spill 				break;
627e25b118aSDominic Spill 			case CONNECT_REQ:
6288f3e7eeaSChristopher Kilgour 				_dump_addr("InitA: ", pkt->symbols, 6, pkt->adv_tx_add);
6298f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
6308f3e7eeaSChristopher Kilgour 				_dump_32("AA:    ", pkt->symbols, 18);
6318f3e7eeaSChristopher Kilgour 				_dump_24("CRCInit: ", pkt->symbols, 22);
6328f3e7eeaSChristopher Kilgour 				_dump_8("WinSize: ", pkt->symbols, 25);
6338f3e7eeaSChristopher Kilgour 				_dump_16("WinOffset: ", pkt->symbols, 26);
6348f3e7eeaSChristopher Kilgour 				_dump_16("Interval: ", pkt->symbols, 28);
6358f3e7eeaSChristopher Kilgour 				_dump_16("Latency: ", pkt->symbols, 30);
6368f3e7eeaSChristopher Kilgour 				_dump_16("Timeout: ", pkt->symbols, 32);
637e25b118aSDominic Spill 
638e25b118aSDominic Spill 				printf("    ChM:");
639e25b118aSDominic Spill 				for (i = 0; i < 5; ++i)
6408f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[34+i]);
641e25b118aSDominic Spill 				printf("\n");
642e25b118aSDominic Spill 
6438f3e7eeaSChristopher Kilgour 				printf("    Hop: %d\n", pkt->symbols[39] & 0x1f);
644e25b118aSDominic Spill 				printf("    SCA: %d, %s\n",
6458f3e7eeaSChristopher Kilgour 						pkt->symbols[39] >> 5,
6468f3e7eeaSChristopher Kilgour 						CONNECT_SCA[pkt->symbols[39] >> 5]);
647e25b118aSDominic Spill 				break;
648e25b118aSDominic Spill 		}
649e25b118aSDominic Spill 	}
650e25b118aSDominic Spill 
651e25b118aSDominic Spill 	printf("\n");
652e25b118aSDominic Spill 	printf("    Data: ");
6538f3e7eeaSChristopher Kilgour 	for (i = 6; i < 6 + pkt->length; ++i)
6548f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[i]);
655e25b118aSDominic Spill 	printf("\n");
656e25b118aSDominic Spill 
657e25b118aSDominic Spill 	printf("    CRC:  ");
658e25b118aSDominic Spill 	for (i = 0; i < 3; ++i)
6598f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[6 + pkt->length + i]);
660e25b118aSDominic Spill 	printf("\n");
661e25b118aSDominic Spill }
662