Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/bluetooth/hccontrol/le.c
105655 views
1
/*
2
* le.c
3
*
4
* Copyright (c) 2015 Takanori Watanabe <[email protected]>
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*
28
* $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $
29
*/
30
31
#include <sys/types.h>
32
#include <sys/ioctl.h>
33
#include <sys/sysctl.h>
34
#include <sys/select.h>
35
#include <assert.h>
36
#include <bitstring.h>
37
#include <err.h>
38
#include <errno.h>
39
#include <netgraph/ng_message.h>
40
#include <errno.h>
41
#include <stdbool.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
#include <stdint.h>
47
#define L2CAP_SOCKET_CHECKED
48
#include <bluetooth.h>
49
#include "hccontrol.h"
50
51
static int le_set_scan_param(int s, int argc, char *argv[]);
52
static int le_set_scan_enable(int s, int argc, char *argv[]);
53
static int parse_param(int argc, char *argv[], char *buf, int *len);
54
static int le_set_scan_response(int s, int argc, char *argv[]);
55
static int le_read_supported_states(int s, int argc, char *argv[]);
56
static int le_read_local_supported_features(int s, int argc ,char *argv[]);
57
static int set_le_event_mask(int s, uint64_t mask);
58
static int set_event_mask(int s, uint64_t mask);
59
static int le_enable(int s, int argc, char *argv[]);
60
static int le_set_advertising_enable(int s, int argc, char *argv[]);
61
static int le_set_advertising_param(int s, int argc, char *argv[]);
62
static int le_read_advertising_channel_tx_power(int s, int argc, char *argv[]);
63
static int le_scan(int s, int argc, char *argv[]);
64
static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose);
65
static int le_read_white_list_size(int s, int argc, char *argv[]);
66
static int le_clear_white_list(int s, int argc, char *argv[]);
67
static int le_add_device_to_white_list(int s, int argc, char *argv[]);
68
static int le_remove_device_from_white_list(int s, int argc, char *argv[]);
69
static int le_connect(int s, int argc, char *argv[]);
70
static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose);
71
static int le_read_channel_map(int s, int argc, char *argv[]);
72
static void handle_le_remote_features_event(ng_hci_event_pkt_t* e);
73
static int le_rand(int s, int argc, char *argv[]);
74
75
static int
76
le_set_scan_param(int s, int argc, char *argv[])
77
{
78
int type;
79
int interval;
80
int window;
81
int adrtype;
82
int policy;
83
int n;
84
85
ng_hci_le_set_scan_parameters_cp cp;
86
ng_hci_le_set_scan_parameters_rp rp;
87
88
if (argc != 5)
89
return (USAGE);
90
91
if (strcmp(argv[0], "active") == 0)
92
type = 1;
93
else if (strcmp(argv[0], "passive") == 0)
94
type = 0;
95
else
96
return (USAGE);
97
98
interval = (int)(atof(argv[1])/0.625);
99
interval = (interval < 4)? 4: interval;
100
window = (int)(atof(argv[2])/0.625);
101
window = (window < 4) ? 4 : interval;
102
103
if (strcmp(argv[3], "public") == 0)
104
adrtype = 0;
105
else if (strcmp(argv[3], "random") == 0)
106
adrtype = 1;
107
else
108
return (USAGE);
109
110
if (strcmp(argv[4], "all") == 0)
111
policy = 0;
112
else if (strcmp(argv[4], "whitelist") == 0)
113
policy = 1;
114
else
115
return (USAGE);
116
117
cp.le_scan_type = type;
118
cp.le_scan_interval = interval;
119
cp.own_address_type = adrtype;
120
cp.le_scan_window = window;
121
cp.scanning_filter_policy = policy;
122
n = sizeof(rp);
123
124
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
125
NG_HCI_OCF_LE_SET_SCAN_PARAMETERS),
126
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
127
return (ERROR);
128
129
if (rp.status != 0x00) {
130
fprintf(stdout, "Status: %s [%#02x]\n",
131
hci_status2str(rp.status), rp.status);
132
return (FAILED);
133
}
134
135
return (OK);
136
}
137
138
static int
139
le_set_scan_enable(int s, int argc, char *argv[])
140
{
141
ng_hci_le_set_scan_enable_cp cp;
142
ng_hci_le_set_scan_enable_rp rp;
143
int n, enable = 0;
144
145
if (argc != 1)
146
return (USAGE);
147
148
if (strcmp(argv[0], "enable") == 0)
149
enable = 1;
150
else if (strcmp(argv[0], "disable") != 0)
151
return (USAGE);
152
153
n = sizeof(rp);
154
cp.le_scan_enable = enable;
155
cp.filter_duplicates = 0;
156
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
157
NG_HCI_OCF_LE_SET_SCAN_ENABLE),
158
(void *)&cp, sizeof(cp),
159
(void *)&rp, &n) == ERROR)
160
return (ERROR);
161
162
if (rp.status != 0x00) {
163
fprintf(stdout, "Status: %s [%#02x]\n",
164
hci_status2str(rp.status), rp.status);
165
return (FAILED);
166
}
167
168
fprintf(stdout, "LE Scan: %s\n",
169
enable? "Enabled" : "Disabled");
170
171
return (OK);
172
}
173
174
static int
175
parse_param(int argc, char *argv[], char *buf, int *len)
176
{
177
char *buflast = buf + (*len);
178
char *curbuf = buf;
179
char *token,*lenpos;
180
int ch;
181
int datalen;
182
uint16_t value;
183
optreset = 1;
184
optind = 0;
185
while ((ch = getopt(argc, argv , "n:f:u:")) != -1) {
186
switch(ch){
187
case 'n':
188
datalen = strlen(optarg);
189
if ((curbuf + datalen + 2) >= buflast)
190
goto done;
191
curbuf[0] = datalen + 1;
192
curbuf[1] = 8;
193
curbuf += 2;
194
memcpy(curbuf, optarg, datalen);
195
curbuf += datalen;
196
break;
197
case 'f':
198
if (curbuf+3 > buflast)
199
goto done;
200
curbuf[0] = 2;
201
curbuf[1] = 1;
202
curbuf[2] = (uint8_t)strtol(optarg, NULL, 16);
203
curbuf += 3;
204
break;
205
case 'u':
206
if ((buf+2) >= buflast)
207
goto done;
208
lenpos = curbuf;
209
curbuf[1] = 2;
210
*lenpos = 1;
211
curbuf += 2;
212
while ((token = strsep(&optarg, ",")) != NULL) {
213
value = strtol(token, NULL, 16);
214
if ((curbuf+2) >= buflast)
215
break;
216
curbuf[0] = value &0xff;
217
curbuf[1] = (value>>8)&0xff;
218
curbuf += 2;
219
*lenpos += 2;
220
}
221
222
}
223
}
224
done:
225
*len = curbuf - buf;
226
227
return (OK);
228
}
229
230
static int
231
le_set_scan_response(int s, int argc, char *argv[])
232
{
233
ng_hci_le_set_scan_response_data_cp cp;
234
ng_hci_le_set_scan_response_data_rp rp;
235
int n;
236
int len;
237
char buf[NG_HCI_ADVERTISING_DATA_SIZE];
238
239
len = sizeof(buf);
240
parse_param(argc, argv, buf, &len);
241
memset(cp.scan_response_data, 0, sizeof(cp.scan_response_data));
242
cp.scan_response_data_length = len;
243
memcpy(cp.scan_response_data, buf, len);
244
n = sizeof(rp);
245
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
246
NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA),
247
(void *)&cp, sizeof(cp),
248
(void *)&rp, &n) == ERROR)
249
return (ERROR);
250
251
if (rp.status != 0x00) {
252
fprintf(stdout, "Status: %s [%#02x]\n",
253
hci_status2str(rp.status), rp.status);
254
return (FAILED);
255
}
256
257
return (OK);
258
}
259
260
static int
261
le_read_local_supported_features(int s, int argc ,char *argv[])
262
{
263
ng_hci_le_read_local_supported_features_rp rp;
264
int n = sizeof(rp);
265
266
union {
267
uint64_t raw;
268
uint8_t octets[8];
269
} le_features;
270
271
char buffer[2048];
272
273
if (hci_simple_request(s,
274
NG_HCI_OPCODE(NG_HCI_OGF_LE,
275
NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES),
276
(void *)&rp, &n) == ERROR)
277
return (ERROR);
278
279
if (rp.status != 0x00) {
280
fprintf(stdout, "Status: %s [%#02x]\n",
281
hci_status2str(rp.status), rp.status);
282
return (FAILED);
283
}
284
285
le_features.raw = rp.le_features;
286
287
fprintf(stdout, "LE Features: ");
288
for(int i = 0; i < 8; i++)
289
fprintf(stdout, " %#02x", le_features.octets[i]);
290
fprintf(stdout, "\n%s\n", hci_le_features2str(le_features.octets,
291
buffer, sizeof(buffer)));
292
fprintf(stdout, "\n");
293
294
return (OK);
295
}
296
297
static int
298
le_read_supported_states(int s, int argc, char *argv[])
299
{
300
ng_hci_le_read_supported_states_rp rp;
301
int n = sizeof(rp);
302
303
if (hci_simple_request(s, NG_HCI_OPCODE(
304
NG_HCI_OGF_LE,
305
NG_HCI_OCF_LE_READ_SUPPORTED_STATES),
306
(void *)&rp, &n) == ERROR)
307
return (ERROR);
308
309
if (rp.status != 0x00) {
310
fprintf(stdout, "Status: %s [%#02x]\n",
311
hci_status2str(rp.status), rp.status);
312
return (FAILED);
313
}
314
315
fprintf(stdout, "LE States: %jx\n", rp.le_states);
316
317
return (OK);
318
}
319
320
static int
321
set_le_event_mask(int s, uint64_t mask)
322
{
323
ng_hci_le_set_event_mask_cp semc;
324
ng_hci_le_set_event_mask_rp rp;
325
int i, n;
326
327
n = sizeof(rp);
328
329
for (i=0; i < NG_HCI_LE_EVENT_MASK_SIZE; i++) {
330
semc.event_mask[i] = mask&0xff;
331
mask >>= 8;
332
}
333
if(hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
334
NG_HCI_OCF_LE_SET_EVENT_MASK),
335
(void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR)
336
return (ERROR);
337
338
if (rp.status != 0x00) {
339
fprintf(stdout, "Status: %s [%#02x]\n",
340
hci_status2str(rp.status), rp.status);
341
return (FAILED);
342
}
343
344
return (OK);
345
}
346
347
static int
348
set_event_mask(int s, uint64_t mask)
349
{
350
ng_hci_set_event_mask_cp semc;
351
ng_hci_set_event_mask_rp rp;
352
int i, n;
353
354
n = sizeof(rp);
355
356
for (i=0; i < NG_HCI_EVENT_MASK_SIZE; i++) {
357
semc.event_mask[i] = mask&0xff;
358
mask >>= 8;
359
}
360
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
361
NG_HCI_OCF_SET_EVENT_MASK),
362
(void *)&semc, sizeof(semc), (void *)&rp, &n) == ERROR)
363
return (ERROR);
364
365
if (rp.status != 0x00) {
366
fprintf(stdout, "Status: %s [%#02x]\n",
367
hci_status2str(rp.status), rp.status);
368
return (FAILED);
369
}
370
371
return (OK);
372
}
373
374
static
375
int le_enable(int s, int argc, char *argv[])
376
{
377
int result;
378
379
if (argc != 1)
380
return (USAGE);
381
382
if (strcasecmp(argv[0], "enable") == 0) {
383
result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT |
384
NG_HCI_EVENT_MASK_LE);
385
if (result != OK)
386
return result;
387
result = set_le_event_mask(s, NG_HCI_LE_EVENT_MASK_ALL);
388
if (result == OK) {
389
fprintf(stdout, "LE enabled\n");
390
return (OK);
391
} else
392
return result;
393
} else if (strcasecmp(argv[0], "disable") == 0) {
394
result = set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT);
395
if (result == OK) {
396
fprintf(stdout, "LE disabled\n");
397
return (OK);
398
} else
399
return result;
400
} else
401
return (USAGE);
402
}
403
404
static int
405
le_set_advertising_enable(int s, int argc, char *argv[])
406
{
407
ng_hci_le_set_advertise_enable_cp cp;
408
ng_hci_le_set_advertise_enable_rp rp;
409
int n, enable = 0;
410
411
if (argc != 1)
412
return USAGE;
413
414
if (strcmp(argv[0], "enable") == 0)
415
enable = 1;
416
else if (strcmp(argv[0], "disable") != 0)
417
return USAGE;
418
419
n = sizeof(rp);
420
cp.advertising_enable = enable;
421
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
422
NG_HCI_OCF_LE_SET_ADVERTISE_ENABLE),
423
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
424
return (ERROR);
425
426
if (rp.status != 0x00) {
427
fprintf(stdout, "Status: %s [%#02x]\n",
428
hci_status2str(rp.status), rp.status);
429
return (FAILED);
430
}
431
fprintf(stdout, "LE Advertising %s\n", (enable ? "enabled" : "disabled"));
432
433
return (OK);
434
}
435
436
static int
437
le_set_advertising_param(int s, int argc, char *argv[])
438
{
439
ng_hci_le_set_advertising_parameters_cp cp;
440
ng_hci_le_set_advertising_parameters_rp rp;
441
442
int n, ch;
443
444
cp.advertising_interval_min = 0x800;
445
cp.advertising_interval_max = 0x800;
446
cp.advertising_type = 0;
447
cp.own_address_type = 0;
448
cp.direct_address_type = 0;
449
450
cp.advertising_channel_map = 7;
451
cp.advertising_filter_policy = 0;
452
453
optreset = 1;
454
optind = 0;
455
while ((ch = getopt(argc, argv , "m:M:t:o:p:a:c:f:")) != -1) {
456
switch(ch) {
457
case 'm':
458
cp.advertising_interval_min =
459
(uint16_t)(strtod(optarg, NULL)/0.625);
460
break;
461
case 'M':
462
cp.advertising_interval_max =
463
(uint16_t)(strtod(optarg, NULL)/0.625);
464
break;
465
case 't':
466
cp.advertising_type =
467
(uint8_t)strtod(optarg, NULL);
468
break;
469
case 'o':
470
cp.own_address_type =
471
(uint8_t)strtod(optarg, NULL);
472
break;
473
case 'p':
474
cp.direct_address_type =
475
(uint8_t)strtod(optarg, NULL);
476
break;
477
case 'a':
478
if (!bt_aton(optarg, &cp.direct_address)) {
479
struct hostent *he = NULL;
480
481
if ((he = bt_gethostbyname(optarg)) == NULL)
482
return (USAGE);
483
484
memcpy(&cp.direct_address, he->h_addr, sizeof(cp.direct_address));
485
}
486
break;
487
case 'c':
488
cp.advertising_channel_map =
489
(uint8_t)strtod(optarg, NULL);
490
break;
491
case 'f':
492
cp.advertising_filter_policy =
493
(uint8_t)strtod(optarg, NULL);
494
break;
495
}
496
}
497
498
n = sizeof(rp);
499
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
500
NG_HCI_OCF_LE_SET_ADVERTISING_PARAMETERS),
501
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
502
return (ERROR);
503
504
if (rp.status != 0x00) {
505
fprintf(stdout, "Status: %s [%#02x]\n",
506
hci_status2str(rp.status), rp.status);
507
return (FAILED);
508
}
509
510
return (OK);
511
}
512
513
static int
514
le_read_advertising_channel_tx_power(int s, int argc, char *argv[])
515
{
516
ng_hci_le_read_advertising_channel_tx_power_rp rp;
517
int n;
518
519
n = sizeof(rp);
520
521
if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
522
NG_HCI_OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER),
523
(void *)&rp, &n) == ERROR)
524
return (ERROR);
525
526
if (rp.status != 0x00) {
527
fprintf(stdout, "Status: %s [%#02x]\n",
528
hci_status2str(rp.status), rp.status);
529
return (FAILED);
530
}
531
532
fprintf(stdout, "Advertising transmit power level: %d dBm\n",
533
(int8_t)rp.transmit_power_level);
534
535
return (OK);
536
}
537
538
static int
539
le_set_advertising_data(int s, int argc, char *argv[])
540
{
541
ng_hci_le_set_advertising_data_cp cp;
542
ng_hci_le_set_advertising_data_rp rp;
543
int n, len;
544
545
n = sizeof(rp);
546
547
char buf[NG_HCI_ADVERTISING_DATA_SIZE];
548
549
len = sizeof(buf);
550
parse_param(argc, argv, buf, &len);
551
memset(cp.advertising_data, 0, sizeof(cp.advertising_data));
552
cp.advertising_data_length = len;
553
memcpy(cp.advertising_data, buf, len);
554
555
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
556
NG_HCI_OCF_LE_SET_ADVERTISING_DATA),
557
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
558
return (ERROR);
559
560
if (rp.status != 0x00) {
561
fprintf(stdout, "Status: %s [%#02x]\n",
562
hci_status2str(rp.status), rp.status);
563
return (FAILED);
564
}
565
566
return (OK);
567
}
568
static int
569
le_read_buffer_size(int s, int argc, char *argv[])
570
{
571
union {
572
ng_hci_le_read_buffer_size_rp v1;
573
ng_hci_le_read_buffer_size_rp_v2 v2;
574
} rp;
575
576
int n, ch;
577
uint8_t v;
578
uint16_t cmd;
579
580
optreset = 1;
581
optind = 0;
582
583
/* Default to version 1*/
584
v = 1;
585
cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE;
586
587
while ((ch = getopt(argc, argv , "v:")) != -1) {
588
switch(ch) {
589
case 'v':
590
v = (uint8_t)strtol(optarg, NULL, 16);
591
if (v == 2)
592
cmd = NG_HCI_OCF_LE_READ_BUFFER_SIZE_V2;
593
else if (v > 2)
594
return (USAGE);
595
break;
596
default:
597
v = 1;
598
}
599
}
600
601
n = sizeof(rp);
602
if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, cmd),
603
(void *)&rp, &n) == ERROR)
604
return (ERROR);
605
606
if (rp.v1.status != 0x00) {
607
fprintf(stdout, "Status: %s [%#02x]\n",
608
hci_status2str(rp.v1.status), rp.v1.status);
609
return (FAILED);
610
}
611
612
fprintf(stdout, "ACL data packet length: %d\n",
613
rp.v1.hc_le_data_packet_length);
614
fprintf(stdout, "Number of ACL data packets: %d\n",
615
rp.v1.hc_total_num_le_data_packets);
616
617
if (v == 2) {
618
fprintf(stdout, "ISO data packet length: %d\n",
619
rp.v2.hc_iso_data_packet_length);
620
fprintf(stdout, "Number of ISO data packets: %d\n",
621
rp.v2.hc_total_num_iso_data_packets);
622
}
623
624
return (OK);
625
}
626
627
static int
628
le_scan(int s, int argc, char *argv[])
629
{
630
int n, bufsize, scancount, numscans;
631
bool verbose;
632
uint8_t active = 0;
633
char ch;
634
635
char b[512];
636
ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
637
638
ng_hci_le_set_scan_parameters_cp scan_param_cp;
639
ng_hci_le_set_scan_parameters_rp scan_param_rp;
640
641
ng_hci_le_set_scan_enable_cp scan_enable_cp;
642
ng_hci_le_set_scan_enable_rp scan_enable_rp;
643
644
optreset = 1;
645
optind = 0;
646
verbose = false;
647
numscans = 1;
648
649
while ((ch = getopt(argc, argv , "an:v")) != -1) {
650
switch(ch) {
651
case 'a':
652
active = 1;
653
break;
654
case 'n':
655
numscans = (uint8_t)strtol(optarg, NULL, 10);
656
break;
657
case 'v':
658
verbose = true;
659
break;
660
}
661
}
662
663
scan_param_cp.le_scan_type = active;
664
scan_param_cp.le_scan_interval = (uint16_t)(100/0.625);
665
scan_param_cp.le_scan_window = (uint16_t)(50/0.625);
666
/* Address type public */
667
scan_param_cp.own_address_type = 0;
668
/* 'All' filter policy */
669
scan_param_cp.scanning_filter_policy = 0;
670
n = sizeof(scan_param_rp);
671
672
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
673
NG_HCI_OCF_LE_SET_SCAN_PARAMETERS),
674
(void *)&scan_param_cp, sizeof(scan_param_cp),
675
(void *)&scan_param_rp, &n) == ERROR)
676
return (ERROR);
677
678
if (scan_param_rp.status != 0x00) {
679
fprintf(stdout, "LE_Set_Scan_Parameters failed. Status: %s [%#02x]\n",
680
hci_status2str(scan_param_rp.status),
681
scan_param_rp.status);
682
return (FAILED);
683
}
684
685
/* Enable scanning */
686
n = sizeof(scan_enable_rp);
687
scan_enable_cp.le_scan_enable = 1;
688
scan_enable_cp.filter_duplicates = 1;
689
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
690
NG_HCI_OCF_LE_SET_SCAN_ENABLE),
691
(void *)&scan_enable_cp, sizeof(scan_enable_cp),
692
(void *)&scan_enable_rp, &n) == ERROR)
693
return (ERROR);
694
695
if (scan_enable_rp.status != 0x00) {
696
fprintf(stdout, "LE_Scan_Enable enable failed. Status: %s [%#02x]\n",
697
hci_status2str(scan_enable_rp.status),
698
scan_enable_rp.status);
699
return (FAILED);
700
}
701
702
scancount = 0;
703
while (scancount < numscans) {
704
/* wait for scan events */
705
bufsize = sizeof(b);
706
if (hci_recv(s, b, &bufsize) == ERROR) {
707
return (ERROR);
708
}
709
710
if (bufsize < sizeof(*e)) {
711
errno = EIO;
712
return (ERROR);
713
}
714
scancount++;
715
if (e->event == NG_HCI_EVENT_LE) {
716
fprintf(stdout, "Scan %d\n", scancount);
717
handle_le_event(e, verbose);
718
}
719
}
720
721
fprintf(stdout, "Scan complete\n");
722
723
/* Disable scanning */
724
n = sizeof(scan_enable_rp);
725
scan_enable_cp.le_scan_enable = 0;
726
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
727
NG_HCI_OCF_LE_SET_SCAN_ENABLE),
728
(void *)&scan_enable_cp, sizeof(scan_enable_cp),
729
(void *)&scan_enable_rp, &n) == ERROR)
730
return (ERROR);
731
732
if (scan_enable_rp.status != 0x00) {
733
fprintf(stdout, "LE_Scan_Enable disable failed. Status: %s [%#02x]\n",
734
hci_status2str(scan_enable_rp.status),
735
scan_enable_rp.status);
736
return (FAILED);
737
}
738
739
return (OK);
740
}
741
742
static void handle_le_event(ng_hci_event_pkt_t* e, bool verbose)
743
{
744
int rc;
745
ng_hci_le_ep *leer =
746
(ng_hci_le_ep *)(e + 1);
747
ng_hci_le_advertising_report_ep *advrep =
748
(ng_hci_le_advertising_report_ep *)(leer + 1);
749
ng_hci_le_advreport *reports =
750
(ng_hci_le_advreport *)(advrep + 1);
751
752
if (leer->subevent_code == NG_HCI_LEEV_ADVREP) {
753
fprintf(stdout, "Scan result, num_reports: %d\n",
754
advrep->num_reports);
755
for(rc = 0; rc < advrep->num_reports; rc++) {
756
uint8_t length = (uint8_t)reports[rc].length_data;
757
fprintf(stdout, "\tBD_ADDR %s \n",
758
hci_bdaddr2str(&reports[rc].bdaddr));
759
fprintf(stdout, "\tAddress type: %s\n",
760
hci_addrtype2str(reports[rc].addr_type));
761
if (length > 0 && verbose) {
762
dump_adv_data(length, reports[rc].data);
763
print_adv_data(length, reports[rc].data);
764
fprintf(stdout,
765
"\tRSSI: %d dBm\n",
766
(int8_t)reports[rc].data[length]);
767
fprintf(stdout, "\n");
768
}
769
}
770
}
771
}
772
773
static int
774
le_read_white_list_size(int s, int argc, char *argv[])
775
{
776
ng_hci_le_read_white_list_size_rp rp;
777
int n;
778
779
n = sizeof(rp);
780
781
if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
782
NG_HCI_OCF_LE_READ_WHITE_LIST_SIZE),
783
(void *)&rp, &n) == ERROR)
784
return (ERROR);
785
786
if (rp.status != 0x00) {
787
fprintf(stdout, "Status: %s [%#02x]\n",
788
hci_status2str(rp.status), rp.status);
789
return (FAILED);
790
}
791
792
fprintf(stdout, "White list size: %d\n",
793
(uint8_t)rp.white_list_size);
794
795
return (OK);
796
}
797
798
static int
799
le_clear_white_list(int s, int argc, char *argv[])
800
{
801
ng_hci_le_clear_white_list_rp rp;
802
int n;
803
804
n = sizeof(rp);
805
806
if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
807
NG_HCI_OCF_LE_CLEAR_WHITE_LIST),
808
(void *)&rp, &n) == ERROR)
809
return (ERROR);
810
811
if (rp.status != 0x00) {
812
fprintf(stdout, "Status: %s [%#02x]\n",
813
hci_status2str(rp.status), rp.status);
814
return (FAILED);
815
}
816
817
fprintf(stdout, "White list cleared\n");
818
819
return (OK);
820
}
821
822
static int
823
le_add_device_to_white_list(int s, int argc, char *argv[])
824
{
825
ng_hci_le_add_device_to_white_list_cp cp;
826
ng_hci_le_add_device_to_white_list_rp rp;
827
int n;
828
char ch;
829
optreset = 1;
830
optind = 0;
831
bool addr_set = false;
832
833
n = sizeof(rp);
834
835
cp.address_type = 0x00;
836
837
while ((ch = getopt(argc, argv , "t:a:")) != -1) {
838
switch(ch) {
839
case 't':
840
if (strcmp(optarg, "public") == 0)
841
cp.address_type = 0x00;
842
else if (strcmp(optarg, "random") == 0)
843
cp.address_type = 0x01;
844
else
845
return (USAGE);
846
break;
847
case 'a':
848
addr_set = true;
849
if (!bt_aton(optarg, &cp.address)) {
850
struct hostent *he = NULL;
851
852
if ((he = bt_gethostbyname(optarg)) == NULL)
853
return (USAGE);
854
855
memcpy(&cp.address, he->h_addr,
856
sizeof(cp.address));
857
}
858
break;
859
}
860
}
861
862
if (addr_set == false)
863
return (USAGE);
864
865
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
866
NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST),
867
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
868
return (ERROR);
869
870
if (rp.status != 0x00) {
871
fprintf(stdout, "Status: %s [%#02x]\n",
872
hci_status2str(rp.status), rp.status);
873
return (FAILED);
874
}
875
876
fprintf(stdout, "Address added to white list\n");
877
878
return (OK);
879
}
880
881
static int
882
le_remove_device_from_white_list(int s, int argc, char *argv[])
883
{
884
ng_hci_le_remove_device_from_white_list_cp cp;
885
ng_hci_le_remove_device_from_white_list_rp rp;
886
int n;
887
char ch;
888
optreset = 1;
889
optind = 0;
890
bool addr_set = false;
891
892
n = sizeof(rp);
893
894
cp.address_type = 0x00;
895
896
while ((ch = getopt(argc, argv , "t:a:")) != -1) {
897
switch(ch) {
898
case 't':
899
if (strcmp(optarg, "public") == 0)
900
cp.address_type = 0x00;
901
else if (strcmp(optarg, "random") == 0)
902
cp.address_type = 0x01;
903
else
904
return (USAGE);
905
break;
906
case 'a':
907
addr_set = true;
908
if (!bt_aton(optarg, &cp.address)) {
909
struct hostent *he = NULL;
910
911
if ((he = bt_gethostbyname(optarg)) == NULL)
912
return (USAGE);
913
914
memcpy(&cp.address, he->h_addr,
915
sizeof(cp.address));
916
}
917
break;
918
}
919
}
920
921
if (addr_set == false)
922
return (USAGE);
923
924
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
925
NG_HCI_OCF_LE_ADD_DEVICE_TO_WHITE_LIST),
926
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
927
return (ERROR);
928
929
if (rp.status != 0x00) {
930
fprintf(stdout, "Status: %s [%#02x]\n",
931
hci_status2str(rp.status), rp.status);
932
return (FAILED);
933
}
934
935
fprintf(stdout, "Address removed from white list\n");
936
937
return (OK);
938
}
939
940
static int
941
le_connect(int s, int argc, char *argv[])
942
{
943
ng_hci_le_create_connection_cp cp;
944
ng_hci_status_rp rp;
945
char b[512];
946
ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
947
948
int n, scancount, bufsize;
949
char ch;
950
bool addr_set = false;
951
bool verbose = false;
952
953
optreset = 1;
954
optind = 0;
955
956
/* minimal scan interval (2.5ms) */
957
cp.scan_interval = htole16(4);
958
cp.scan_window = htole16(4);
959
960
/* Don't use the whitelist */
961
cp.filter_policy = 0x00;
962
963
/* Default to public peer address */
964
cp.peer_addr_type = 0x00;
965
966
/* Own address type public */
967
cp.own_address_type = 0x00;
968
969
/* 18.75ms min connection interval */
970
cp.conn_interval_min = htole16(0x000F);
971
/* 18.75ms max connection interval */
972
cp.conn_interval_max = htole16(0x000F);
973
974
/* 0 events connection latency */
975
cp.conn_latency = htole16(0x0000);
976
977
/* 32s supervision timeout */
978
cp.supervision_timeout = htole16(0x0C80);
979
980
/* Min CE Length 0.625 ms */
981
cp.min_ce_length = htole16(1);
982
/* Max CE Length 0.625 ms */
983
cp.max_ce_length = htole16(1);
984
985
while ((ch = getopt(argc, argv , "a:t:v")) != -1) {
986
switch(ch) {
987
case 't':
988
if (strcmp(optarg, "public") == 0)
989
cp.peer_addr_type = 0x00;
990
else if (strcmp(optarg, "random") == 0)
991
cp.peer_addr_type = 0x01;
992
else
993
return (USAGE);
994
break;
995
case 'a':
996
addr_set = true;
997
if (!bt_aton(optarg, &cp.peer_addr)) {
998
struct hostent *he = NULL;
999
1000
if ((he = bt_gethostbyname(optarg)) == NULL)
1001
return (USAGE);
1002
1003
memcpy(&cp.peer_addr, he->h_addr,
1004
sizeof(cp.peer_addr));
1005
}
1006
break;
1007
case 'v':
1008
verbose = true;
1009
break;
1010
}
1011
}
1012
1013
if (addr_set == false)
1014
return (USAGE);
1015
1016
n = sizeof(rp);
1017
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
1018
NG_HCI_OCF_LE_CREATE_CONNECTION),
1019
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
1020
return (ERROR);
1021
1022
if (rp.status != 0x00) {
1023
fprintf(stdout,
1024
"Create connection failed. Status: %s [%#02x]\n",
1025
hci_status2str(rp.status), rp.status);
1026
return (FAILED);
1027
}
1028
1029
scancount = 0;
1030
while (scancount < 3) {
1031
/* wait for connection events */
1032
bufsize = sizeof(b);
1033
if (hci_recv(s, b, &bufsize) == ERROR) {
1034
return (ERROR);
1035
}
1036
1037
if (bufsize < sizeof(*e)) {
1038
errno = EIO;
1039
return (ERROR);
1040
}
1041
scancount++;
1042
if (e->event == NG_HCI_EVENT_LE) {
1043
handle_le_connection_event(e, verbose);
1044
break;
1045
}
1046
}
1047
1048
return (OK);
1049
}
1050
1051
static void handle_le_connection_event(ng_hci_event_pkt_t* e, bool verbose)
1052
{
1053
ng_hci_le_ep *ev_pkt;
1054
ng_hci_le_connection_complete_ep *conn_event;
1055
1056
ev_pkt = (ng_hci_le_ep *)(e + 1);
1057
1058
if (ev_pkt->subevent_code == NG_HCI_LEEV_CON_COMPL) {
1059
conn_event =(ng_hci_le_connection_complete_ep *)(ev_pkt + 1);
1060
fprintf(stdout, "Handle: %d\n", le16toh(conn_event->handle));
1061
if (verbose) {
1062
fprintf(stdout,
1063
"Status: %s\n",
1064
hci_status2str(conn_event->status));
1065
fprintf(stdout,
1066
"Role: %s\n",
1067
hci_role2str(conn_event->role));
1068
fprintf(stdout,
1069
"Address Type: %s\n",
1070
hci_addrtype2str(conn_event->address_type));
1071
fprintf(stdout,
1072
"Address: %s\n",
1073
hci_bdaddr2str(&conn_event->address));
1074
fprintf(stdout,
1075
"Interval: %.2fms\n",
1076
6.25 * le16toh(conn_event->interval));
1077
fprintf(stdout,
1078
"Latency: %d events\n", conn_event->latency);
1079
fprintf(stdout,
1080
"Supervision timeout: %dms\n",
1081
10 * le16toh(conn_event->supervision_timeout));
1082
fprintf(stdout,
1083
"Master clock accuracy: %s\n",
1084
hci_mc_accuracy2str(
1085
conn_event->master_clock_accuracy));
1086
}
1087
}
1088
}
1089
1090
static int
1091
le_read_channel_map(int s, int argc, char *argv[])
1092
{
1093
ng_hci_le_read_channel_map_cp cp;
1094
ng_hci_le_read_channel_map_rp rp;
1095
int n;
1096
char buffer[2048];
1097
1098
/* parse command parameters */
1099
switch (argc) {
1100
case 1:
1101
/* connection handle */
1102
if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
1103
return (USAGE);
1104
1105
cp.connection_handle = (uint16_t) (n & 0x0fff);
1106
cp.connection_handle = htole16(cp.connection_handle);
1107
break;
1108
1109
default:
1110
return (USAGE);
1111
}
1112
1113
n = sizeof(rp);
1114
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
1115
NG_HCI_OCF_LE_READ_CHANNEL_MAP),
1116
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
1117
return (ERROR);
1118
1119
if (rp.status != 0x00) {
1120
fprintf(stdout,
1121
"Read channel map failed. Status: %s [%#02x]\n",
1122
hci_status2str(rp.status), rp.status);
1123
return (FAILED);
1124
}
1125
1126
fprintf(stdout, "Connection handle: %d\n",
1127
le16toh(rp.connection_handle));
1128
fprintf(stdout, "Used channels:\n");
1129
fprintf(stdout, "\n%s\n", hci_le_chanmap2str(rp.le_channel_map,
1130
buffer, sizeof(buffer)));
1131
1132
return (OK);
1133
} /* le_read_channel_map */
1134
1135
static int
1136
le_read_remote_features(int s, int argc, char *argv[])
1137
{
1138
ng_hci_le_read_remote_used_features_cp cp;
1139
ng_hci_status_rp rp;
1140
int n, bufsize;
1141
char b[512];
1142
1143
ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
1144
1145
/* parse command parameters */
1146
switch (argc) {
1147
case 1:
1148
/* connection handle */
1149
if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
1150
return (USAGE);
1151
1152
cp.connection_handle = (uint16_t) (n & 0x0fff);
1153
cp.connection_handle = htole16(cp.connection_handle);
1154
break;
1155
1156
default:
1157
return (USAGE);
1158
}
1159
1160
n = sizeof(rp);
1161
if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
1162
NG_HCI_OCF_LE_READ_REMOTE_USED_FEATURES),
1163
(void *)&cp, sizeof(cp), (void *)&rp, &n) == ERROR)
1164
return (ERROR);
1165
1166
if (rp.status != 0x00) {
1167
fprintf(stdout,
1168
"Read remote features failed. Status: %s [%#02x]\n",
1169
hci_status2str(rp.status), rp.status);
1170
return (FAILED);
1171
}
1172
1173
/* wait for connection events */
1174
bufsize = sizeof(b);
1175
if (hci_recv(s, b, &bufsize) == ERROR) {
1176
return (ERROR);
1177
}
1178
1179
if (bufsize < sizeof(*e)) {
1180
errno = EIO;
1181
return (ERROR);
1182
}
1183
if (e->event == NG_HCI_EVENT_LE) {
1184
handle_le_remote_features_event(e);
1185
}
1186
1187
return (OK);
1188
} /* le_read_remote_features */
1189
1190
static void handle_le_remote_features_event(ng_hci_event_pkt_t* e)
1191
{
1192
ng_hci_le_ep *ev_pkt;
1193
ng_hci_le_read_remote_features_ep *feat_event;
1194
char buffer[2048];
1195
1196
ev_pkt = (ng_hci_le_ep *)(e + 1);
1197
1198
if (ev_pkt->subevent_code == NG_HCI_LEEV_READ_REMOTE_FEATURES_COMPL) {
1199
feat_event =(ng_hci_le_read_remote_features_ep *)(ev_pkt + 1);
1200
fprintf(stdout, "Handle: %d\n",
1201
le16toh(feat_event->connection_handle));
1202
fprintf(stdout,
1203
"Status: %s\n",
1204
hci_status2str(feat_event->status));
1205
fprintf(stdout, "Features:\n%s\n",
1206
hci_le_features2str(feat_event->features,
1207
buffer, sizeof(buffer)));
1208
}
1209
} /* handle_le_remote_features_event */
1210
1211
static int le_rand(int s, int argc, char *argv[])
1212
{
1213
ng_hci_le_rand_rp rp;
1214
int n;
1215
1216
n = sizeof(rp);
1217
1218
if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE,
1219
NG_HCI_OCF_LE_RAND),
1220
(void *)&rp, &n) == ERROR)
1221
return (ERROR);
1222
1223
if (rp.status != 0x00) {
1224
fprintf(stdout, "Status: %s [%#02x]\n",
1225
hci_status2str(rp.status), rp.status);
1226
return (FAILED);
1227
}
1228
1229
fprintf(stdout,
1230
"Random number : %08llx\n",
1231
(unsigned long long)le64toh(rp.random_number));
1232
1233
return (OK);
1234
}
1235
1236
1237
1238
struct hci_command le_commands[] = {
1239
{
1240
"le_enable",
1241
"le_enable [enable|disable] \n"
1242
"Enable LE event ",
1243
&le_enable,
1244
},
1245
{
1246
"le_read_local_supported_features",
1247
"le_read_local_supported_features\n"
1248
"read local supported features mask",
1249
&le_read_local_supported_features,
1250
},
1251
{
1252
"le_read_supported_states",
1253
"le_read_supported_states\n"
1254
"read supported status"
1255
,
1256
&le_read_supported_states,
1257
},
1258
{
1259
"le_set_scan_response",
1260
"le_set_scan_response -n $name -f $flag -u $uuid16,$uuid16 \n"
1261
"set LE scan response data"
1262
,
1263
&le_set_scan_response,
1264
},
1265
{
1266
"le_set_scan_enable",
1267
"le_set_scan_enable [enable|disable] \n"
1268
"enable or disable LE device scan",
1269
&le_set_scan_enable
1270
},
1271
{
1272
"le_set_scan_param",
1273
"le_set_scan_param [active|passive] interval(ms) window(ms) [public|random] [all|whitelist] \n"
1274
"set LE device scan parameter",
1275
&le_set_scan_param
1276
},
1277
{
1278
"le_set_advertising_enable",
1279
"le_set_advertising_enable [enable|disable] \n"
1280
"start or stop advertising",
1281
&le_set_advertising_enable
1282
},
1283
{
1284
"le_read_advertising_channel_tx_power",
1285
"le_read_advertising_channel_tx_power\n"
1286
"read host advertising transmit poser level (dBm)",
1287
&le_read_advertising_channel_tx_power
1288
},
1289
{
1290
"le_set_advertising_param",
1291
"le_set_advertising_param [-m min_interval(ms)] [-M max_interval(ms)]\n"
1292
"[-t advertising_type] [-o own_address_type] [-p peer_address_type]\n"
1293
"[-c advertising_channel_map] [-f advertising_filter_policy]\n"
1294
"[-a peer_address]\n"
1295
"set LE device advertising parameters",
1296
&le_set_advertising_param
1297
},
1298
{
1299
"le_set_advertising_data",
1300
"le_set_advertising_data -n $name -f $flag -u $uuid16,$uuid16 \n"
1301
"set LE device advertising packed data",
1302
&le_set_advertising_data
1303
},
1304
{
1305
"le_read_buffer_size",
1306
"le_read_buffer_size [-v 1|2]\n"
1307
"Read the maximum size of ACL and ISO data packets",
1308
&le_read_buffer_size
1309
},
1310
{
1311
"le_scan",
1312
"le_scan [-a] [-v] [-n number_of_scans]\n"
1313
"Do an LE scan",
1314
&le_scan
1315
},
1316
{
1317
"le_read_white_list_size",
1318
"le_read_white_list_size\n"
1319
"Read total number of white list entries that can be stored",
1320
&le_read_white_list_size
1321
},
1322
{
1323
"le_clear_white_list",
1324
"le_clear_white_list\n"
1325
"Clear the white list in the controller",
1326
&le_clear_white_list
1327
},
1328
{
1329
"le_add_device_to_white_list",
1330
"le_add_device_to_white_list\n"
1331
"[-t public|random] -a address\n"
1332
"Add device to the white list",
1333
&le_add_device_to_white_list
1334
},
1335
{
1336
"le_remove_device_from_white_list",
1337
"le_remove_device_from_white_list\n"
1338
"[-t public|random] -a address\n"
1339
"Remove device from the white list",
1340
&le_remove_device_from_white_list
1341
},
1342
{
1343
"le_connect",
1344
"le_connect -a address [-t public|random] [-v]\n"
1345
"Connect to an LE device",
1346
&le_connect
1347
},
1348
{
1349
"le_read_channel_map",
1350
"le_read_channel_map <connection_handle>\n"
1351
"Read the channel map for a connection",
1352
&le_read_channel_map
1353
},
1354
{
1355
"le_read_remote_features",
1356
"le_read_remote_features <connection_handle>\n"
1357
"Read supported features for the device\n"
1358
"identified by the connection handle",
1359
&le_read_remote_features
1360
},
1361
{
1362
"le_rand",
1363
"le_rand\n"
1364
"Generate 64 bits of random data",
1365
&le_rand
1366
},
1367
{
1368
NULL,
1369
}
1370
};
1371
1372