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