xref: /libbtbb/lib/src/bluetooth_le_packet.c (revision 8f3e7eea1eae731e517c4fd7ac22f1ce3ad3cd5f)
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 
28*8f3e7eeaSChristopher Kilgour #include "btbb.h"
29e25b118aSDominic Spill #include "bluetooth_le_packet.h"
30e25b118aSDominic Spill #include <ctype.h>
31*8f3e7eeaSChristopher Kilgour #include <string.h>
32e25b118aSDominic Spill 
33e25b118aSDominic Spill /* string representations of advertising packet type */
34e25b118aSDominic Spill static const char *ADV_TYPE_NAMES[] = {
35e25b118aSDominic Spill 	"ADV_IND", "ADV_DIRECT_IND", "ADV_NONCONN_IND", "SCAN_REQ",
36e25b118aSDominic Spill 	"SCAN_RSP", "CONNECT_REQ", "ADV_SCAN_IND",
37e25b118aSDominic Spill };
38e25b118aSDominic Spill 
39e25b118aSDominic Spill /* source clock accuracy in a connect packet */
40e25b118aSDominic Spill static const char *CONNECT_SCA[] = {
41e25b118aSDominic Spill 	"251 ppm to 500 ppm", "151 ppm to 250 ppm", "101 ppm to 150 ppm",
42e25b118aSDominic Spill 	"76 ppm to 100 ppm", "51 ppm to 75 ppm", "31 ppm to 50 ppm",
43e25b118aSDominic Spill 	"21 ppm to 30 ppm", "0 ppm to 20 ppm",
44e25b118aSDominic Spill };
45e25b118aSDominic Spill 
46e25b118aSDominic Spill // count of objects in an array, shamelessly stolen from Chrome
47e25b118aSDominic Spill #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
48e25b118aSDominic Spill 
49*8f3e7eeaSChristopher Kilgour static int aa_access_channel_off_by_one(const uint32_t aa) {
50*8f3e7eeaSChristopher Kilgour 	int retval = 0;
51*8f3e7eeaSChristopher Kilgour 	switch(aa) {
52*8f3e7eeaSChristopher Kilgour 		/* single zero->one flips */
53*8f3e7eeaSChristopher Kilgour 	case 0x8e89bed7:
54*8f3e7eeaSChristopher Kilgour 	case 0x8e89bede:
55*8f3e7eeaSChristopher Kilgour 	case 0x8e89bef6:
56*8f3e7eeaSChristopher Kilgour 	case 0x8e89bfd6:
57*8f3e7eeaSChristopher Kilgour 	case 0x8e89fed6:
58*8f3e7eeaSChristopher Kilgour 	case 0x8e8bbed6:
59*8f3e7eeaSChristopher Kilgour 	case 0x8e8dbed6:
60*8f3e7eeaSChristopher Kilgour 	case 0x8e99bed6:
61*8f3e7eeaSChristopher Kilgour 	case 0x8ea9bed6:
62*8f3e7eeaSChristopher Kilgour 	case 0x8ec9bed6:
63*8f3e7eeaSChristopher Kilgour 	case 0x8f89bed6:
64*8f3e7eeaSChristopher Kilgour 	case 0x9e89bed6:
65*8f3e7eeaSChristopher Kilgour 	case 0xae89bed6:
66*8f3e7eeaSChristopher Kilgour 	case 0xce89bed6:
67*8f3e7eeaSChristopher Kilgour 		/* single one->zero flips */
68*8f3e7eeaSChristopher Kilgour 	case 0x8e89bed4:
69*8f3e7eeaSChristopher Kilgour 	case 0x8e89bed2:
70*8f3e7eeaSChristopher Kilgour 	case 0x8e89bec6:
71*8f3e7eeaSChristopher Kilgour 	case 0x8e89be96:
72*8f3e7eeaSChristopher Kilgour 	case 0x8e89be56:
73*8f3e7eeaSChristopher Kilgour 	case 0x8e89bcd6:
74*8f3e7eeaSChristopher Kilgour 	case 0x8e89bad6:
75*8f3e7eeaSChristopher Kilgour 	case 0x8e89b6d6:
76*8f3e7eeaSChristopher Kilgour 	case 0x8e89aed6:
77*8f3e7eeaSChristopher Kilgour 	case 0x8e899ed6:
78*8f3e7eeaSChristopher Kilgour 	case 0x8e893ed6:
79*8f3e7eeaSChristopher Kilgour 	case 0x8e88bed6:
80*8f3e7eeaSChristopher Kilgour 	case 0x8e81bed6:
81*8f3e7eeaSChristopher Kilgour 	case 0x8e09bed6:
82*8f3e7eeaSChristopher Kilgour 	case 0x8c89bed6:
83*8f3e7eeaSChristopher Kilgour 	case 0x8a89bed6:
84*8f3e7eeaSChristopher Kilgour 	case 0x8689bed6:
85*8f3e7eeaSChristopher Kilgour 	case 0x0e89bed6:
86*8f3e7eeaSChristopher Kilgour 		retval = 1;
87*8f3e7eeaSChristopher Kilgour 		break;
88*8f3e7eeaSChristopher Kilgour 	}
89*8f3e7eeaSChristopher Kilgour 	return retval;
90*8f3e7eeaSChristopher Kilgour }
91e25b118aSDominic Spill 
92*8f3e7eeaSChristopher Kilgour /*
93*8f3e7eeaSChristopher Kilgour  * A helper function for filtering bogus packets on data channels.
94*8f3e7eeaSChristopher Kilgour  *
95*8f3e7eeaSChristopher Kilgour  * If a candidate capture packet is random noise we would expect its
96*8f3e7eeaSChristopher Kilgour  * Access Address to be a randomly distributed 32-bit number.  An
97*8f3e7eeaSChristopher Kilgour  * exhaustive software analysis reveals that of 4294967296 possible
98*8f3e7eeaSChristopher Kilgour  * 32-bit Access Address values, 2900629660 (67.5%) are acceptable and
99*8f3e7eeaSChristopher Kilgour  * 1394337636 (32.5%) are invalid.  This function will identify which
100*8f3e7eeaSChristopher Kilgour  * category a candidate Access Address falls into by returning the
101*8f3e7eeaSChristopher Kilgour  * number of offenses contained.
102*8f3e7eeaSChristopher Kilgour  *
103*8f3e7eeaSChristopher Kilgour  * Refer to BT 4.x, Vol 6, Par B, Section 2.1.2.
104*8f3e7eeaSChristopher Kilgour  *
105*8f3e7eeaSChristopher Kilgour  * The Access Address in data channel packets meet the
106*8f3e7eeaSChristopher Kilgour  * following requirements:
107*8f3e7eeaSChristopher Kilgour  *  - It shall have no more than six consecutive zeros or ones.
108*8f3e7eeaSChristopher Kilgour  *  - It shall not be the advertising channel packets’ Access Address.
109*8f3e7eeaSChristopher Kilgour  *  - It shall not be a sequence that differs from the advertising channel packets’
110*8f3e7eeaSChristopher Kilgour  *    Access Address by only one bit.
111*8f3e7eeaSChristopher Kilgour  *  - It shall not have all four octets equal.
112*8f3e7eeaSChristopher Kilgour  *  - It shall have no more than 24 transitions.
113*8f3e7eeaSChristopher Kilgour  *  - It shall have a minimum of two transitions in the most significant six bits.
114*8f3e7eeaSChristopher Kilgour  */
115*8f3e7eeaSChristopher Kilgour static int aa_data_channel_offenses(const uint32_t aa) {
116*8f3e7eeaSChristopher Kilgour 	int retval = 0, transitions = 0;
117*8f3e7eeaSChristopher Kilgour 	unsigned shift, odd = (unsigned) (aa & 1);
118*8f3e7eeaSChristopher Kilgour 	uint8_t aab3, aab2, aab1, aab0 = (uint8_t) (aa & 0xff);
119e25b118aSDominic Spill 
120*8f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_EVEN[256] = {
121*8f3e7eeaSChristopher Kilgour 		0, 2, 2, 2, 2, 4, 2, 2, 2, 4, 4, 4, 2, 4, 2, 2,
122*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
123*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
124*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
125*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
126*8f3e7eeaSChristopher Kilgour 		4, 6, 6, 6, 6, 8, 6, 6, 4, 6, 6, 6, 4, 6, 4, 4,
127*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 4, 6, 6, 6, 4, 6, 4, 4,
128*8f3e7eeaSChristopher Kilgour 		2, 4, 4, 4, 4, 6, 4, 4, 2, 4, 4, 4, 2, 4, 2, 2,
129*8f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
130*8f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
131*8f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 5, 7, 7, 7, 5, 7, 5, 5,
132*8f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
133*8f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
134*8f3e7eeaSChristopher Kilgour 		3, 5, 5, 5, 5, 7, 5, 5, 3, 5, 5, 5, 3, 5, 3, 3,
135*8f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 5, 3, 5, 3, 3,
136*8f3e7eeaSChristopher Kilgour 		1, 3, 3, 3, 3, 5, 3, 3, 1, 3, 3, 3, 1, 3, 1, 1
137*8f3e7eeaSChristopher Kilgour 	};
138e25b118aSDominic Spill 
139*8f3e7eeaSChristopher Kilgour 	const uint8_t EIGHT_BIT_TRANSITIONS_ODD[256] = {
140*8f3e7eeaSChristopher Kilgour 		1, 1, 3, 1, 3, 3, 3, 1, 3, 3, 5, 3, 3, 3, 3, 1,
141*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
142*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
143*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
144*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
145*8f3e7eeaSChristopher Kilgour 		5, 5, 7, 5, 7, 7, 7, 5, 5, 5, 7, 5, 5, 5, 5, 3,
146*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 5, 5, 7, 5, 5, 5, 5, 3,
147*8f3e7eeaSChristopher Kilgour 		3, 3, 5, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 1,
148*8f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
149*8f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
150*8f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 6, 6, 8, 6, 6, 6, 6, 4,
151*8f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
152*8f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
153*8f3e7eeaSChristopher Kilgour 		4, 4, 6, 4, 6, 6, 6, 4, 4, 4, 6, 4, 4, 4, 4, 2,
154*8f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 4, 4, 6, 4, 4, 4, 4, 2,
155*8f3e7eeaSChristopher Kilgour 		2, 2, 4, 2, 4, 4, 4, 2, 2, 2, 4, 2, 2, 2, 2, 0
156*8f3e7eeaSChristopher Kilgour 	};
157*8f3e7eeaSChristopher Kilgour 
158*8f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab0] : EIGHT_BIT_TRANSITIONS_EVEN[aab0] );
159*8f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab0 & 0x80);
160*8f3e7eeaSChristopher Kilgour 	aab1 = (uint8_t) (aa >> 8);
161*8f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab1] : EIGHT_BIT_TRANSITIONS_EVEN[aab1] );
162*8f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab1 & 0x80);
163*8f3e7eeaSChristopher Kilgour 	aab2 = (uint8_t) (aa >> 16);
164*8f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab2] : EIGHT_BIT_TRANSITIONS_EVEN[aab2] );
165*8f3e7eeaSChristopher Kilgour 	odd = (unsigned) (aab2 & 0x80);
166*8f3e7eeaSChristopher Kilgour 	aab3 = (uint8_t) (aa >> 24);
167*8f3e7eeaSChristopher Kilgour 	transitions += (odd ? EIGHT_BIT_TRANSITIONS_ODD[aab3] : EIGHT_BIT_TRANSITIONS_EVEN[aab3] );
168*8f3e7eeaSChristopher Kilgour 
169*8f3e7eeaSChristopher Kilgour 	/* consider excessive transitions as offenses */
170*8f3e7eeaSChristopher Kilgour 	if (transitions > 24) {
171*8f3e7eeaSChristopher Kilgour 		retval += (transitions - 24);
172*8f3e7eeaSChristopher Kilgour 	}
173*8f3e7eeaSChristopher Kilgour 
174*8f3e7eeaSChristopher Kilgour 	const uint8_t AA_MSB6_ALLOWED[64] = {
175*8f3e7eeaSChristopher Kilgour 		0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0,
176*8f3e7eeaSChristopher Kilgour 		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
177*8f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
178*8f3e7eeaSChristopher Kilgour 		0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0
179*8f3e7eeaSChristopher Kilgour 	};
180*8f3e7eeaSChristopher Kilgour 
181*8f3e7eeaSChristopher Kilgour 	/* consider excessive transitions in the 6 MSBs as an offense */
182*8f3e7eeaSChristopher Kilgour 	retval += (1 - AA_MSB6_ALLOWED[aab3>>2]);
183*8f3e7eeaSChristopher Kilgour 
184*8f3e7eeaSChristopher Kilgour 	/* consider all bytes as being equal an offense */
185*8f3e7eeaSChristopher Kilgour 	retval += (((aab0 == aab1) && (aab0 == aab2) && (aab0 == aab3)) ? 1 : 0);
186*8f3e7eeaSChristopher Kilgour 
187*8f3e7eeaSChristopher Kilgour 	/* access-channel address and off-by-ones are illegal */
188*8f3e7eeaSChristopher Kilgour 	retval += ((aa == 0x8e89bed6) ? 1 : 0);
189*8f3e7eeaSChristopher Kilgour 	retval += aa_access_channel_off_by_one(aa);
190*8f3e7eeaSChristopher Kilgour 
191*8f3e7eeaSChristopher Kilgour 	/* inspect nibble triples for insufficient bit transitions */
192*8f3e7eeaSChristopher Kilgour 	for(shift=0; shift<=20; shift+=4) {
193*8f3e7eeaSChristopher Kilgour 		uint16_t twelvebits = (uint16_t) ((aa >> shift) & 0xfff);
194*8f3e7eeaSChristopher Kilgour 		switch( twelvebits ) {
195*8f3e7eeaSChristopher Kilgour 			/* seven consecutive zeroes */
196*8f3e7eeaSChristopher Kilgour 		case 0x080: case 0x180: case 0x280: case 0x380: case 0x480:
197*8f3e7eeaSChristopher Kilgour 		case 0x580: case 0x680: case 0x780: case 0x880: case 0x980:
198*8f3e7eeaSChristopher Kilgour 		case 0xa80: case 0xb80: case 0xc80: case 0xd80: case 0xe80:
199*8f3e7eeaSChristopher Kilgour 		case 0xf80: case 0x101: case 0x301: case 0x501: case 0x701:
200*8f3e7eeaSChristopher Kilgour 		case 0x901: case 0xb01: case 0xd01: case 0xf01: case 0x202:
201*8f3e7eeaSChristopher Kilgour 		case 0x602: case 0xa02: case 0xe02: case 0x203: case 0x603:
202*8f3e7eeaSChristopher Kilgour 		case 0xa03: case 0xe03: case 0x404: case 0xc04: case 0x405:
203*8f3e7eeaSChristopher Kilgour 		case 0xc05: case 0x406: case 0xc06: case 0x407: case 0xc07:
204*8f3e7eeaSChristopher Kilgour 		case 0x808: case 0x809: case 0x80a: case 0x80b: case 0x80c:
205*8f3e7eeaSChristopher Kilgour 		case 0x80d: case 0x80e: case 0x80f: case 0x010: case 0x011:
206*8f3e7eeaSChristopher Kilgour 		case 0x012: case 0x013: case 0x014: case 0x015: case 0x016:
207*8f3e7eeaSChristopher Kilgour 		case 0x017: case 0x018: case 0x019: case 0x01a: case 0x01b:
208*8f3e7eeaSChristopher Kilgour 		case 0x01c: case 0x01d: case 0x01e: case 0x01f:
209*8f3e7eeaSChristopher Kilgour 			/* eight consecutive zeroes */
210*8f3e7eeaSChristopher Kilgour 		case 0x100: case 0x300: case 0x500: case 0x700: case 0x900:
211*8f3e7eeaSChristopher Kilgour 		case 0xb00: case 0xd00: case 0xf00: case 0x201: case 0x601:
212*8f3e7eeaSChristopher Kilgour 		case 0xa01: case 0xe01: case 0x402: case 0xc02: case 0x403:
213*8f3e7eeaSChristopher Kilgour 		case 0xc03: case 0x804: case 0x805: case 0x806: case 0x807:
214*8f3e7eeaSChristopher Kilgour 		case 0x008: case 0x009: case 0x00a: case 0x00b: case 0x00c:
215*8f3e7eeaSChristopher Kilgour 		case 0x00d: case 0x00e: case 0x00f:
216*8f3e7eeaSChristopher Kilgour 			/* nine consecutive zeroes */
217*8f3e7eeaSChristopher Kilgour 		case 0xe00: case 0xc01: case 0x802: case 0x803: case 0x004:
218*8f3e7eeaSChristopher Kilgour 		case 0x005: case 0x006: case 0x007:
219*8f3e7eeaSChristopher Kilgour 			/* ten consecutive zeroes */
220*8f3e7eeaSChristopher Kilgour 		case 0x400: case 0xc00: case 0x801: case 0x002: case 0x003:
221*8f3e7eeaSChristopher Kilgour 			/* eleven consecutive zeroes */
222*8f3e7eeaSChristopher Kilgour 		case 0x800: case 0x001:
223*8f3e7eeaSChristopher Kilgour 			/* twelve consecutive zeroes */
224*8f3e7eeaSChristopher Kilgour 		case 0x000:
225*8f3e7eeaSChristopher Kilgour 			/* seven consecutive ones */
226*8f3e7eeaSChristopher Kilgour 		case 0x07f: case 0x0fe: case 0x2fe: case 0x4fe: case 0x6fe:
227*8f3e7eeaSChristopher Kilgour 		case 0x8fe: case 0xafe: case 0xcfe: case 0xefe: case 0x1fc:
228*8f3e7eeaSChristopher Kilgour 		case 0x5fc: case 0x9fc: case 0xdfc: case 0x1fd: case 0x5fd:
229*8f3e7eeaSChristopher Kilgour 		case 0x9fd: case 0xdfd: case 0x3f8: case 0xbf8: case 0x3f9:
230*8f3e7eeaSChristopher Kilgour 		case 0xbf9: case 0x3fa: case 0xbfa: case 0x3fb: case 0xbfb:
231*8f3e7eeaSChristopher Kilgour 		case 0x7f4: case 0x7f5: case 0x7f6: case 0x7f7: case 0xfe0:
232*8f3e7eeaSChristopher Kilgour 			/* eight consecutive ones */
233*8f3e7eeaSChristopher Kilgour 		case 0x0ff: case 0x2ff: case 0x4ff: case 0x6ff: case 0x8ff:
234*8f3e7eeaSChristopher Kilgour 		case 0xaff: case 0xcff: case 0xeff: case 0x1fe: case 0x5fe:
235*8f3e7eeaSChristopher Kilgour 		case 0x9fe: case 0xdfe: case 0x3fc: case 0xbfc: case 0x3fd:
236*8f3e7eeaSChristopher Kilgour 		case 0xbfd: case 0x7f8: case 0x7f9: case 0x7fa: case 0x7fb:
237*8f3e7eeaSChristopher Kilgour 		case 0xff0: case 0xff1: case 0xff2: case 0xff3: case 0xff4:
238*8f3e7eeaSChristopher Kilgour 		case 0xff5: case 0xff6: case 0xff7:
239*8f3e7eeaSChristopher Kilgour 			/* nine consecutive ones */
240*8f3e7eeaSChristopher Kilgour 		case 0x1ff: case 0x5ff: case 0x9ff: case 0xdff: case 0x3fe:
241*8f3e7eeaSChristopher Kilgour 		case 0xbfe: case 0x7fc: case 0x7fd: case 0xff8: case 0xff9:
242*8f3e7eeaSChristopher Kilgour 		case 0xffa: case 0xffb:
243*8f3e7eeaSChristopher Kilgour 			/* ten consecutive ones */
244*8f3e7eeaSChristopher Kilgour 		case 0x3ff: case 0xbff: case 0x7fe: case 0xffc: case 0xffd:
245*8f3e7eeaSChristopher Kilgour 			/* eleven consecutive ones */
246*8f3e7eeaSChristopher Kilgour 		case 0x7ff: case 0xffe:
247*8f3e7eeaSChristopher Kilgour 			/* all ones */
248*8f3e7eeaSChristopher Kilgour 		case 0xfff:
249*8f3e7eeaSChristopher Kilgour 			retval++;
250*8f3e7eeaSChristopher Kilgour 			break;
251*8f3e7eeaSChristopher Kilgour 		default:
252*8f3e7eeaSChristopher Kilgour 			break;
253e25b118aSDominic Spill 		}
254e25b118aSDominic Spill 	}
255e25b118aSDominic Spill 
256*8f3e7eeaSChristopher Kilgour 	return retval;
257e25b118aSDominic Spill }
258e25b118aSDominic Spill 
259*8f3e7eeaSChristopher Kilgour lell_packet *
260*8f3e7eeaSChristopher Kilgour lell_packet_new(void)
261*8f3e7eeaSChristopher Kilgour {
262*8f3e7eeaSChristopher Kilgour 	lell_packet *pkt = (lell_packet *)calloc(1, sizeof(lell_packet));
263*8f3e7eeaSChristopher Kilgour 	pkt->refcount = 1;
264*8f3e7eeaSChristopher Kilgour 	return pkt;
265*8f3e7eeaSChristopher Kilgour }
266*8f3e7eeaSChristopher Kilgour 
267*8f3e7eeaSChristopher Kilgour void
268*8f3e7eeaSChristopher Kilgour lell_packet_ref(lell_packet *pkt)
269*8f3e7eeaSChristopher Kilgour {
270*8f3e7eeaSChristopher Kilgour 	pkt->refcount++;
271*8f3e7eeaSChristopher Kilgour }
272*8f3e7eeaSChristopher Kilgour 
273*8f3e7eeaSChristopher Kilgour void
274*8f3e7eeaSChristopher Kilgour lell_packet_unref(lell_packet *pkt)
275*8f3e7eeaSChristopher Kilgour {
276*8f3e7eeaSChristopher Kilgour 	pkt->refcount--;
277*8f3e7eeaSChristopher Kilgour 	if (pkt->refcount == 0)
278*8f3e7eeaSChristopher Kilgour 		free(pkt);
279*8f3e7eeaSChristopher Kilgour }
280*8f3e7eeaSChristopher Kilgour 
281*8f3e7eeaSChristopher Kilgour static uint8_t le_channel_index(uint16_t phys_channel) {
282e25b118aSDominic Spill 	uint8_t ret;
283e25b118aSDominic Spill 	if (phys_channel == 2402) {
284e25b118aSDominic Spill 		ret = 37;
285e25b118aSDominic Spill 	} else if (phys_channel < 2426) { // 0 - 10
286e25b118aSDominic Spill 		ret = (phys_channel - 2404) / 2;
287e25b118aSDominic Spill 	} else if (phys_channel == 2426) {
288e25b118aSDominic Spill 		ret = 38;
289e25b118aSDominic Spill 	} else if (phys_channel < 2480) { // 11 - 36
290e25b118aSDominic Spill 		ret = 11 + (phys_channel - 2428) / 2;
291e25b118aSDominic Spill 	} else {
292e25b118aSDominic Spill 		ret = 39;
293e25b118aSDominic Spill 	}
294e25b118aSDominic Spill 	return ret;
295e25b118aSDominic Spill }
296e25b118aSDominic Spill 
297*8f3e7eeaSChristopher Kilgour void lell_allocate_and_decode(const uint8_t *stream, uint16_t phys_channel, uint32_t clk100ns, lell_packet **pkt)
298*8f3e7eeaSChristopher Kilgour {
299*8f3e7eeaSChristopher Kilgour 	*pkt = lell_packet_new( );
300*8f3e7eeaSChristopher Kilgour 	memcpy((*pkt)->symbols, stream, MAX_LE_SYMBOLS);
301*8f3e7eeaSChristopher Kilgour 
302*8f3e7eeaSChristopher Kilgour 	(*pkt)->channel_idx = le_channel_index(phys_channel);
303*8f3e7eeaSChristopher Kilgour 	(*pkt)->channel_k = (phys_channel-2402)/2;
304*8f3e7eeaSChristopher Kilgour 	(*pkt)->clk100ns = clk100ns;
305*8f3e7eeaSChristopher Kilgour 
306*8f3e7eeaSChristopher Kilgour 	(*pkt)->access_address = 0;
307*8f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[0];
308*8f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[1] << 8;
309*8f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[2] << 16;
310*8f3e7eeaSChristopher Kilgour 	(*pkt)->access_address |= (*pkt)->symbols[3] << 24;
311*8f3e7eeaSChristopher Kilgour 
312*8f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(*pkt)) {
313*8f3e7eeaSChristopher Kilgour 		// data PDU
314*8f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x1f;
315*8f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = aa_data_channel_offenses((*pkt)->access_address);
316*8f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = (*pkt)->access_address_offenses ? 0 : 1;
317*8f3e7eeaSChristopher Kilgour 	} else {
318*8f3e7eeaSChristopher Kilgour 		// advertising PDU
319*8f3e7eeaSChristopher Kilgour 		(*pkt)->length = (*pkt)->symbols[5] & 0x3f;
320*8f3e7eeaSChristopher Kilgour 		(*pkt)->adv_type = (*pkt)->symbols[4] & 0xf;
321*8f3e7eeaSChristopher Kilgour 		(*pkt)->adv_tx_add = (*pkt)->symbols[4] & 0x40 ? 1 : 0;
322*8f3e7eeaSChristopher Kilgour 		(*pkt)->adv_rx_add = (*pkt)->symbols[4] & 0x80 ? 1 : 0;
323*8f3e7eeaSChristopher Kilgour 		(*pkt)->flags.as_bits.access_address_ok = ((*pkt)->access_address == 0x8e89bed6);
324*8f3e7eeaSChristopher Kilgour 		(*pkt)->access_address_offenses = (*pkt)->flags.as_bits.access_address_ok ? 0 :
325*8f3e7eeaSChristopher Kilgour 			(aa_access_channel_off_by_one((*pkt)->access_address) ? 1 : 32);
326*8f3e7eeaSChristopher Kilgour 	}
327*8f3e7eeaSChristopher Kilgour }
328*8f3e7eeaSChristopher Kilgour 
329*8f3e7eeaSChristopher Kilgour unsigned lell_packet_is_data(const lell_packet *pkt)
330*8f3e7eeaSChristopher Kilgour {
331*8f3e7eeaSChristopher Kilgour 	return (unsigned) (pkt->channel_idx < 37);
332*8f3e7eeaSChristopher Kilgour }
333*8f3e7eeaSChristopher Kilgour 
334*8f3e7eeaSChristopher Kilgour uint32_t lell_get_access_address(const lell_packet *pkt)
335*8f3e7eeaSChristopher Kilgour {
336*8f3e7eeaSChristopher Kilgour 	return pkt->access_address;
337*8f3e7eeaSChristopher Kilgour }
338*8f3e7eeaSChristopher Kilgour 
339*8f3e7eeaSChristopher Kilgour unsigned lell_get_access_address_offenses(const lell_packet *pkt)
340*8f3e7eeaSChristopher Kilgour {
341*8f3e7eeaSChristopher Kilgour 	return pkt->access_address_offenses;
342*8f3e7eeaSChristopher Kilgour }
343*8f3e7eeaSChristopher Kilgour 
344*8f3e7eeaSChristopher Kilgour unsigned lell_get_channel_index(const lell_packet *pkt)
345*8f3e7eeaSChristopher Kilgour {
346*8f3e7eeaSChristopher Kilgour 	return pkt->channel_idx;
347*8f3e7eeaSChristopher Kilgour }
348*8f3e7eeaSChristopher Kilgour 
349*8f3e7eeaSChristopher Kilgour unsigned lell_get_channel_k(const lell_packet *pkt)
350*8f3e7eeaSChristopher Kilgour {
351*8f3e7eeaSChristopher Kilgour 	return pkt->channel_k;
352*8f3e7eeaSChristopher Kilgour }
353*8f3e7eeaSChristopher Kilgour 
354*8f3e7eeaSChristopher Kilgour const char * lell_get_adv_type_str(const lell_packet *pkt)
355*8f3e7eeaSChristopher Kilgour {
356*8f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt))
357e25b118aSDominic Spill 		return NULL;
358*8f3e7eeaSChristopher Kilgour 	if (pkt->adv_type < COUNT_OF(ADV_TYPE_NAMES))
359*8f3e7eeaSChristopher Kilgour 		return ADV_TYPE_NAMES[pkt->adv_type];
360e25b118aSDominic Spill 	return "UNKNOWN";
361e25b118aSDominic Spill }
362e25b118aSDominic Spill 
363*8f3e7eeaSChristopher Kilgour static void _dump_addr(const char *name, const uint8_t *buf, int offset, int random) {
364e25b118aSDominic Spill 	int i;
365e25b118aSDominic Spill 	printf("    %s%02x", name, buf[offset+5]);
366e25b118aSDominic Spill 	for (i = 4; i >= 0; --i)
367e25b118aSDominic Spill 		printf(":%02x", buf[offset+i]);
368e25b118aSDominic Spill 	printf(" (%s)\n", random ? "random" : "public");
369e25b118aSDominic Spill }
370e25b118aSDominic Spill 
371*8f3e7eeaSChristopher Kilgour static void _dump_8(const char *name, const uint8_t *buf, int offset) {
372e25b118aSDominic Spill 	printf("    %s%02x (%d)\n", name, buf[offset], buf[offset]);
373e25b118aSDominic Spill }
374e25b118aSDominic Spill 
375*8f3e7eeaSChristopher Kilgour static void _dump_16(const char *name, const uint8_t *buf, int offset) {
376e25b118aSDominic Spill 	uint16_t val = buf[offset+1] << 8 | buf[offset];
377e25b118aSDominic Spill 	printf("    %s%04x (%d)\n", name, val, val);
378e25b118aSDominic Spill }
379e25b118aSDominic Spill 
380*8f3e7eeaSChristopher Kilgour static void _dump_24(const char *name, const uint8_t *buf, int offset) {
381e25b118aSDominic Spill 	uint16_t val = buf[offset+2] << 16 | buf[offset+1] << 8 | buf[offset];
382e25b118aSDominic Spill 	printf("    %s%06x\n", name, val);
383e25b118aSDominic Spill }
384e25b118aSDominic Spill 
385*8f3e7eeaSChristopher Kilgour static void _dump_32(const char *name, const uint8_t *buf, int offset) {
386e25b118aSDominic Spill 	uint32_t val = buf[offset+3] << 24 |
387e25b118aSDominic Spill 				   buf[offset+2] << 16 |
388e25b118aSDominic Spill 				   buf[offset+1] << 8 |
389e25b118aSDominic Spill 				   buf[offset+0];
390e25b118aSDominic Spill 	printf("    %s%08x\n", name, val);
391e25b118aSDominic Spill }
392e25b118aSDominic Spill 
393*8f3e7eeaSChristopher Kilgour static void _dump_uuid(const uint8_t *uuid) {
394e25b118aSDominic Spill 	int i;
395e25b118aSDominic Spill 	for (i = 0; i < 4; ++i)
396e25b118aSDominic Spill 		printf("%02x", uuid[i]);
397e25b118aSDominic Spill 	printf("-");
398e25b118aSDominic Spill 	for (i = 4; i < 6; ++i)
399e25b118aSDominic Spill 		printf("%02x", uuid[i]);
400e25b118aSDominic Spill 	printf("-");
401e25b118aSDominic Spill 	for (i = 6; i < 8; ++i)
402e25b118aSDominic Spill 		printf("%02x", uuid[i]);
403e25b118aSDominic Spill 	printf("-");
404e25b118aSDominic Spill 	for (i = 8; i < 10; ++i)
405e25b118aSDominic Spill 		printf("%02x", uuid[i]);
406e25b118aSDominic Spill 	printf("-");
407e25b118aSDominic Spill 	for (i = 10; i < 16; ++i)
408e25b118aSDominic Spill 		printf("%02x", uuid[i]);
409e25b118aSDominic Spill }
410e25b118aSDominic Spill 
411e25b118aSDominic Spill // Refer to pg 1735 of Bluetooth Core Spec 4.0
412*8f3e7eeaSChristopher Kilgour static void _dump_scan_rsp_data(const uint8_t *buf, int len) {
413e25b118aSDominic Spill 	int pos = 0;
414e25b118aSDominic Spill 	int sublen, i;
415e25b118aSDominic Spill 	uint8_t type;
416e25b118aSDominic Spill 	uint16_t val;
417e25b118aSDominic Spill 	char *cval;
418e25b118aSDominic Spill 
419e25b118aSDominic Spill 	while (pos < len) {
420e25b118aSDominic Spill 		sublen = buf[pos];
421e25b118aSDominic Spill 		++pos;
422e25b118aSDominic Spill 		if (pos + sublen > len) {
423e25b118aSDominic Spill 			printf("Error: attempt to read past end of buffer (%d + %d > %d)\n", pos, sublen, len);
424e25b118aSDominic Spill 			return;
425e25b118aSDominic Spill 		}
426e25b118aSDominic Spill 		if (sublen == 0) {
427e25b118aSDominic Spill 			printf("Early return due to 0 length\n");
428e25b118aSDominic Spill 			return;
429e25b118aSDominic Spill 		}
430e25b118aSDominic Spill 		type = buf[pos];
431e25b118aSDominic Spill 		printf("        Type %02x", type);
432e25b118aSDominic Spill 		switch (type) {
433e25b118aSDominic Spill 			case 0x01:
434e25b118aSDominic Spill 				printf(" (Flags)\n");
435e25b118aSDominic Spill 				printf("           ");
436e25b118aSDominic Spill 				for (i = 0; i < 8; ++i)
437e25b118aSDominic Spill 					printf("%d", buf[pos+1] & (1 << (7-i)) ? 1 : 0);
438e25b118aSDominic Spill 				printf("\n");
439e25b118aSDominic Spill 				break;
44045000095SMike Ryan 			case 0x06:
44145000095SMike Ryan 				printf(" (128-bit Service UUIDs, more available)\n");
44245000095SMike Ryan 				goto print128;
443e25b118aSDominic Spill 			case 0x07:
444e25b118aSDominic Spill 				printf(" (128-bit Service UUIDs)\n");
44545000095SMike Ryan print128:
446e25b118aSDominic Spill 				if ((sublen - 1) % 16 == 0) {
447e25b118aSDominic Spill 					uint8_t uuid[16];
448e25b118aSDominic Spill 					for (i = 0; i < sublen - 1; ++i) {
449e25b118aSDominic Spill 						uuid[15 - (i % 16)] = buf[pos+1+i];
450e25b118aSDominic Spill 						if ((i & 15) == 15) {
451e25b118aSDominic Spill 							printf("           ");
452e25b118aSDominic Spill 							_dump_uuid(uuid);
453e25b118aSDominic Spill 							printf("\n");
454e25b118aSDominic Spill 						}
455e25b118aSDominic Spill 					}
456e25b118aSDominic Spill 				}
457e25b118aSDominic Spill 				else {
458e25b118aSDominic Spill 					printf("Wrong length (%d, must be divisible by 16)\n", sublen-1);
459e25b118aSDominic Spill 				}
460e25b118aSDominic Spill 				break;
461e25b118aSDominic Spill 			case 0x09:
462e25b118aSDominic Spill 				printf(" (Complete Local Name)\n");
463e25b118aSDominic Spill 				printf("           ");
464e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
465e25b118aSDominic Spill 					printf("%c", isprint(buf[pos+i]) ? buf[pos+i] : '.');
466e25b118aSDominic Spill 				printf("\n");
467e25b118aSDominic Spill 				break;
468e25b118aSDominic Spill 			case 0x0a:
469e25b118aSDominic Spill 				printf(" (Tx Power Level)\n");
470e25b118aSDominic Spill 				printf("           ");
471e25b118aSDominic Spill 				if (sublen-1 == 1) {
472e25b118aSDominic Spill 					cval = (char *)&buf[pos+1];
473e25b118aSDominic Spill 					printf("%d dBm\n", *cval);
474e25b118aSDominic Spill 				} else {
475e25b118aSDominic Spill 					printf("Wrong length (%d, should be 1)\n", sublen-1);
476e25b118aSDominic Spill 				}
477e25b118aSDominic Spill 				break;
478e25b118aSDominic Spill 			case 0x12:
479e25b118aSDominic Spill 				printf(" (Slave Connection Interval Range)\n");
480e25b118aSDominic Spill 				printf("           ");
481e25b118aSDominic Spill 				if (sublen-1 == 4) {
482e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
483e25b118aSDominic Spill 					printf("(%0.2f, ", val * 1.25);
484e25b118aSDominic Spill 					val = (buf[pos+4] << 8) | buf[pos+3];
485e25b118aSDominic Spill 					printf("%0.2f) ms\n", val * 1.25);
486e25b118aSDominic Spill 				}
487e25b118aSDominic Spill 				else {
488e25b118aSDominic Spill 					printf("Wrong length (%d, should be 4)\n", sublen-1);
489e25b118aSDominic Spill 				}
490e25b118aSDominic Spill 				break;
491e25b118aSDominic Spill 			case 0x16:
492e25b118aSDominic Spill 				printf(" (Service Data)\n");
493e25b118aSDominic Spill 				printf("           ");
494e25b118aSDominic Spill 				if (sublen-1 >= 2) {
495e25b118aSDominic Spill 					val = (buf[pos+2] << 8) | buf[pos+1];
496e25b118aSDominic Spill 					printf("UUID: %02x", val);
497e25b118aSDominic Spill 					if (sublen-1 > 2) {
498e25b118aSDominic Spill 						printf(", Additional:");
499e25b118aSDominic Spill 						for (i = 3; i < sublen; ++i)
500e25b118aSDominic Spill 							printf(" %02x", buf[pos+i]);
501e25b118aSDominic Spill 					}
502e25b118aSDominic Spill 					printf("\n");
503e25b118aSDominic Spill 				}
504e25b118aSDominic Spill 				else {
505e25b118aSDominic Spill 					printf("Wrong length (%d, should be >= 2)\n", sublen-1);
506e25b118aSDominic Spill 				}
507e25b118aSDominic Spill 				break;
508e25b118aSDominic Spill 			default:
509e25b118aSDominic Spill 				printf("\n");
510e25b118aSDominic Spill 				printf("           ");
511e25b118aSDominic Spill 				for (i = 1; i < sublen; ++i)
512e25b118aSDominic Spill 					printf(" %02x", buf[pos+i]);
513e25b118aSDominic Spill 				printf("\n");
514e25b118aSDominic Spill 		}
515e25b118aSDominic Spill 		pos += sublen;
516e25b118aSDominic Spill 	}
517e25b118aSDominic Spill }
518e25b118aSDominic Spill 
519*8f3e7eeaSChristopher Kilgour void lell_print(const lell_packet *pkt)
520*8f3e7eeaSChristopher Kilgour {
521387b6603SIvan Krasin 	int i, opcode;
522*8f3e7eeaSChristopher Kilgour 	if (lell_packet_is_data(pkt)) {
523*8f3e7eeaSChristopher Kilgour 		int llid = pkt->symbols[4] & 0x3;
524e25b118aSDominic Spill 		static const char *llid_str[] = {
525e25b118aSDominic Spill 			"Reserved",
526e25b118aSDominic Spill 			"LL Data PDU / empty or L2CAP continuation",
527e25b118aSDominic Spill 			"LL Data PDU / L2CAP start",
528e25b118aSDominic Spill 			"LL Control PDU",
529e25b118aSDominic Spill 		};
530e25b118aSDominic Spill 
531*8f3e7eeaSChristopher Kilgour 		printf("Data / AA %08x (%s) / %2d bytes\n", pkt->access_address,
532*8f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
533*8f3e7eeaSChristopher Kilgour 		       pkt->length);
534*8f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
535e25b118aSDominic Spill 		printf("    LLID: %d / %s\n", llid, llid_str[llid]);
536*8f3e7eeaSChristopher Kilgour 		printf("    NESN: %d  SN: %d  MD: %d\n", (pkt->symbols[4] >> 2) & 1,
537*8f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 3) & 1,
538*8f3e7eeaSChristopher Kilgour 												 (pkt->symbols[4] >> 4) & 1);
539387b6603SIvan Krasin 		switch (llid) {
540387b6603SIvan Krasin 		case 3: // LL Control PDU
541*8f3e7eeaSChristopher Kilgour 			opcode = pkt->symbols[6];
542387b6603SIvan Krasin 			static const char *opcode_str[] = {
543387b6603SIvan Krasin 				"LL_CONNECTION_UPDATE_REQ",
544387b6603SIvan Krasin 				"LL_CHANNEL_MAP_REQ",
545387b6603SIvan Krasin 				"LL_TERMINATE_IND",
546387b6603SIvan Krasin 				"LL_ENC_REQ",
547387b6603SIvan Krasin 				"LL_ENC_RSP",
548387b6603SIvan Krasin 				"LL_START_ENC_REQ",
549387b6603SIvan Krasin 				"LL_START_ENC_RSP",
550387b6603SIvan Krasin 				"LL_UNKNOWN_RSP",
551387b6603SIvan Krasin 				"LL_FEATURE_REQ",
552387b6603SIvan Krasin 				"LL_FEATURE_RSP",
553387b6603SIvan Krasin 				"LL_PAUSE_ENC_REQ",
554387b6603SIvan Krasin 				"LL_PAUSE_ENC_RSP",
555387b6603SIvan Krasin 				"LL_VERSION_IND",
556387b6603SIvan Krasin 				"LL_REJECT_IND",
557b3394904SMike Ryan 				"LL_SLAVE_FEATURE_REQ",
558b3394904SMike Ryan 				"LL_CONNECTION_PARAM_REQ",
559b3394904SMike Ryan 				"LL_CONNECTION_PARAM_RSP",
560b3394904SMike Ryan 				"LL_REJECT_IND_EXT",
561b3394904SMike Ryan 				"LL_PING_REQ",
562b3394904SMike Ryan 				"LL_PING_RSP",
563387b6603SIvan Krasin 				"Reserved for Future Use",
564387b6603SIvan Krasin 			};
565ba100f3dSMike Ryan 			printf("    Opcode: %d / %s\n", opcode, opcode_str[(opcode<0x14)?opcode:0x14]);
566387b6603SIvan Krasin 			break;
567387b6603SIvan Krasin 		default:
568387b6603SIvan Krasin 			break;
569387b6603SIvan Krasin 		}
570e25b118aSDominic Spill 	} else {
571*8f3e7eeaSChristopher Kilgour 		printf("Advertising / AA %08x (%s)/ %2d bytes\n", pkt->access_address,
572*8f3e7eeaSChristopher Kilgour 		       pkt->flags.as_bits.access_address_ok ? "valid" : "invalid",
573*8f3e7eeaSChristopher Kilgour 		       pkt->length);
574*8f3e7eeaSChristopher Kilgour 		printf("    Channel Index: %d\n", pkt->channel_idx);
575*8f3e7eeaSChristopher Kilgour 		printf("    Type:  %s\n", lell_get_adv_type_str(pkt));
576e25b118aSDominic Spill 
577*8f3e7eeaSChristopher Kilgour 		switch(pkt->adv_type) {
578e25b118aSDominic Spill 			case ADV_IND:
579*8f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
580*8f3e7eeaSChristopher Kilgour 				if (pkt->length-6 > 0) {
581e25b118aSDominic Spill 					printf("    AdvData:");
582*8f3e7eeaSChristopher Kilgour 					for (i = 0; i < pkt->length - 6; ++i)
583*8f3e7eeaSChristopher Kilgour 						printf(" %02x", pkt->symbols[12+i]);
584e25b118aSDominic Spill 					printf("\n");
585*8f3e7eeaSChristopher Kilgour 					_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
586e25b118aSDominic Spill 				}
587e25b118aSDominic Spill 				break;
588e25b118aSDominic Spill 			case SCAN_REQ:
589*8f3e7eeaSChristopher Kilgour 				_dump_addr("ScanA: ", pkt->symbols, 6, pkt->adv_tx_add);
590*8f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
591e25b118aSDominic Spill 				break;
592e25b118aSDominic Spill 			case SCAN_RSP:
593*8f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 6, pkt->adv_tx_add);
594e25b118aSDominic Spill 				printf("    ScanRspData:");
595*8f3e7eeaSChristopher Kilgour 				for (i = 0; i < pkt->length - 6; ++i)
596*8f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[12+i]);
597e25b118aSDominic Spill 				printf("\n");
598*8f3e7eeaSChristopher Kilgour 				_dump_scan_rsp_data(&pkt->symbols[12], pkt->length-6);
599e25b118aSDominic Spill 				break;
600e25b118aSDominic Spill 			case CONNECT_REQ:
601*8f3e7eeaSChristopher Kilgour 				_dump_addr("InitA: ", pkt->symbols, 6, pkt->adv_tx_add);
602*8f3e7eeaSChristopher Kilgour 				_dump_addr("AdvA:  ", pkt->symbols, 12, pkt->adv_rx_add);
603*8f3e7eeaSChristopher Kilgour 				_dump_32("AA:    ", pkt->symbols, 18);
604*8f3e7eeaSChristopher Kilgour 				_dump_24("CRCInit: ", pkt->symbols, 22);
605*8f3e7eeaSChristopher Kilgour 				_dump_8("WinSize: ", pkt->symbols, 25);
606*8f3e7eeaSChristopher Kilgour 				_dump_16("WinOffset: ", pkt->symbols, 26);
607*8f3e7eeaSChristopher Kilgour 				_dump_16("Interval: ", pkt->symbols, 28);
608*8f3e7eeaSChristopher Kilgour 				_dump_16("Latency: ", pkt->symbols, 30);
609*8f3e7eeaSChristopher Kilgour 				_dump_16("Timeout: ", pkt->symbols, 32);
610e25b118aSDominic Spill 
611e25b118aSDominic Spill 				printf("    ChM:");
612e25b118aSDominic Spill 				for (i = 0; i < 5; ++i)
613*8f3e7eeaSChristopher Kilgour 					printf(" %02x", pkt->symbols[34+i]);
614e25b118aSDominic Spill 				printf("\n");
615e25b118aSDominic Spill 
616*8f3e7eeaSChristopher Kilgour 				printf("    Hop: %d\n", pkt->symbols[39] & 0x1f);
617e25b118aSDominic Spill 				printf("    SCA: %d, %s\n",
618*8f3e7eeaSChristopher Kilgour 						pkt->symbols[39] >> 5,
619*8f3e7eeaSChristopher Kilgour 						CONNECT_SCA[pkt->symbols[39] >> 5]);
620e25b118aSDominic Spill 				break;
621e25b118aSDominic Spill 		}
622e25b118aSDominic Spill 	}
623e25b118aSDominic Spill 
624e25b118aSDominic Spill 	printf("\n");
625e25b118aSDominic Spill 	printf("    Data: ");
626*8f3e7eeaSChristopher Kilgour 	for (i = 6; i < 6 + pkt->length; ++i)
627*8f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[i]);
628e25b118aSDominic Spill 	printf("\n");
629e25b118aSDominic Spill 
630e25b118aSDominic Spill 	printf("    CRC:  ");
631e25b118aSDominic Spill 	for (i = 0; i < 3; ++i)
632*8f3e7eeaSChristopher Kilgour 		printf(" %02x", pkt->symbols[6 + pkt->length + i]);
633e25b118aSDominic Spill 	printf("\n");
634e25b118aSDominic Spill }
635