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