xref: /freebsd/usr.sbin/bluetooth/hccontrol/le.c (revision fe75646a0234a261c0013bf1840fdac4acaf0cec)
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  */
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