diff options
author | sjg <sjg@FreeBSD.org> | 2015-05-27 01:19:58 +0000 |
---|---|---|
committer | sjg <sjg@FreeBSD.org> | 2015-05-27 01:19:58 +0000 |
commit | 65145fa4c81da358fcbc3b650156dab705dfa34e (patch) | |
tree | 55c065b6730aaac2afb6c29933ee6ec5fa4c4249 /usr.sbin/bluetooth/hccontrol | |
parent | 60ff4eb0dff94a04d75d0d52a3957aaaf5f8c693 (diff) | |
parent | e6b664c390af88d4a87208bc042ce503da664c3b (diff) | |
download | FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.zip FreeBSD-src-65145fa4c81da358fcbc3b650156dab705dfa34e.tar.gz |
Merge sync of head
Diffstat (limited to 'usr.sbin/bluetooth/hccontrol')
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/hccontrol.8 | 4 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/hccontrol.c | 8 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/hccontrol.h | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/host_controller_baseband.c | 84 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/info.c | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/le.c | 356 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/link_control.c | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/link_policy.c | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/node.c | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/hccontrol/util.c | 15 |
11 files changed, 470 insertions, 7 deletions
diff --git a/usr.sbin/bluetooth/hccontrol/Makefile b/usr.sbin/bluetooth/hccontrol/Makefile index 592247e..a81fda4 100644 --- a/usr.sbin/bluetooth/hccontrol/Makefile +++ b/usr.sbin/bluetooth/hccontrol/Makefile @@ -3,12 +3,11 @@ PROG= hccontrol MAN= hccontrol.8 -SRCS= send_recv.c link_policy.c link_control.c \ +SRCS= send_recv.c link_policy.c link_control.c le.c\ host_controller_baseband.c info.c status.c node.c hccontrol.c \ util.c WARNS?= 2 -DPADD= ${LIBBLUETOOTH} -LDADD= -lbluetooth +LIBADD= bluetooth .include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.8 b/usr.sbin/bluetooth/hccontrol/hccontrol.8 index af0a67f..d8c5263 100644 --- a/usr.sbin/bluetooth/hccontrol/hccontrol.8 +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.8 @@ -25,12 +25,12 @@ .\" $Id: hccontrol.8,v 1.6 2003/08/06 21:26:38 max Exp $ .\" $FreeBSD$ .\" -.Dd June 14, 2002 +.Dd February 7, 2015 .Dt HCCONTROL 8 .Os .Sh NAME .Nm hccontrol -.Nd HCI configuration utility +.Nd Bluetooth HCI configuration utility .Sh SYNOPSIS .Nm .Op Fl hN diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c index 089869b..b72854f 100644 --- a/usr.sbin/bluetooth/hccontrol/hccontrol.c +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <sys/ioctl.h> #include <sys/sysctl.h> @@ -143,6 +144,7 @@ socket_open(char const *node) bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1); bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1); bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1); + bit_set(filter.event_mask, NG_HCI_EVENT_LE -1); if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, (void * const) &filter, sizeof(filter)) < 0) @@ -181,6 +183,7 @@ do_hci_command(char const *node, int argc, char **argv) print_hci_command(host_controller_baseband_commands); print_hci_command(info_commands); print_hci_command(status_commands); + print_hci_command(le_commands); print_hci_command(node_commands); fprintf(stdout, "\nFor more information use " \ "'help command'\n"); @@ -212,6 +215,11 @@ do_hci_command(char const *node, int argc, char **argv) if (c != NULL) goto execute; + c = find_hci_command(cmd, le_commands); + if (c != NULL) + goto execute; + + c = find_hci_command(cmd, node_commands); if (c == NULL) { fprintf(stdout, "Unknown command: \"%s\"\n", cmd); diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.h b/usr.sbin/bluetooth/hccontrol/hccontrol.h index cd56ebf..c96aab0 100644 --- a/usr.sbin/bluetooth/hccontrol/hccontrol.h +++ b/usr.sbin/bluetooth/hccontrol/hccontrol.h @@ -53,6 +53,7 @@ extern struct hci_command host_controller_baseband_commands[]; extern struct hci_command info_commands[]; extern struct hci_command status_commands[]; extern struct hci_command node_commands[]; +extern struct hci_command le_commands[]; int hci_request (int, int, char const *, int, char *, int *); int hci_simple_request (int, int, char *, int *); diff --git a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c index aae5dd1..532ca1c 100644 --- a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c +++ b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <errno.h> #include <stdio.h> @@ -1487,6 +1488,78 @@ hci_write_page_scan_mode(int s, int argc, char **argv) return (OK); } /* hci_write_page_scan_mode */ +static int +hci_read_le_host_supported_command(int s, int argc, char **argv) +{ + ng_hci_read_le_host_supported_rp rp; + int n; + n = sizeof(rp); + if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_READ_LE_HOST_SUPPORTED), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + fprintf(stdout, "LE Host support: %#02x\n", rp.le_supported_host); + fprintf(stdout, "Simulateneouse LE Host : %#02x\n", rp.simultaneous_le_host); + + return (OK); + +} +static int +hci_write_le_host_supported_command(int s, int argc, char **argv) +{ + ng_hci_write_le_host_supported_cp cp; + ng_hci_write_le_host_supported_rp rp; + + int n; + + cp.le_supported_host = 0; + cp.simultaneous_le_host = 0; + switch (argc) { + case 2: + if (sscanf(argv[1], "%d", &n) != 1 || (n != 0 && n != 1)){ + printf("ARGC2: %d\n", n); + return (USAGE); + } + cp.simultaneous_le_host = (n &1); + + case 1: + if (sscanf(argv[0], "%d", &n) != 1 || (n != 0 && n != 1)){ + printf("ARGC1: %d\n", n); + return (USAGE); + } + + cp.le_supported_host = (n &1); + break; + + default: + return (USAGE); + } + + + /* send command */ + n = sizeof(rp); + if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_WRITE_LE_HOST_SUPPORTED), + (char const *) &cp, sizeof(cp), + (char *) &rp, &n) == ERROR) + return (ERROR); + + if (rp.status != 0x00) { + fprintf(stdout, "Status: %s [%#02x]\n", + hci_status2str(rp.status), rp.status); + return (FAILED); + } + + return (OK); +} + struct hci_command host_controller_baseband_commands[] = { { "reset", @@ -1872,6 +1945,17 @@ struct hci_command host_controller_baseband_commands[] = { "\t0x03 - Optional Page Scan Mode III", &hci_write_page_scan_mode }, +{ +"read_le_host_supported_command", \ +"Read if this host is in le supported mode and stimulatenouse le supported mode", +&hci_read_le_host_supported_command, +}, +{ +"write_le_host_supported_command", \ +"write_le_host_supported_command le_host[0|1] stimultajeous_le[0|1]", +&hci_write_le_host_supported_command, +}, + { NULL, } }; diff --git a/usr.sbin/bluetooth/hccontrol/info.c b/usr.sbin/bluetooth/hccontrol/info.c index d7bad36..ee9d1a1 100644 --- a/usr.sbin/bluetooth/hccontrol/info.c +++ b/usr.sbin/bluetooth/hccontrol/info.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <errno.h> #include <stdio.h> diff --git a/usr.sbin/bluetooth/hccontrol/le.c b/usr.sbin/bluetooth/hccontrol/le.c new file mode 100644 index 0000000..afb151e --- /dev/null +++ b/usr.sbin/bluetooth/hccontrol/le.c @@ -0,0 +1,356 @@ +/* + * le.c + * + * Copyright (c) 2015 Takanori Watanabe <takawata@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $ + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/sysctl.h> +#include <sys/bitstring.h> +#include <sys/select.h> +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <netgraph/ng_message.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#define L2CAP_SOCKET_CHECKED +#include <bluetooth.h> +#include "hccontrol.h" + +static int le_set_scan_param(int s, int argc, char *argv[]); +static int le_set_scan_enable(int s, int argc, char *argv[]); +static int parse_param(int argc, char *argv[], char *buf, int *len); +static int le_set_scan_response(int s, int argc, char *argv[]); +static int le_read_supported_status(int s, int argc, char *argv[]); +static int le_read_local_supported_features(int s, int argc ,char *argv[]); +static int set_le_event_mask(int s, uint64_t mask); +static int set_event_mask(int s, uint64_t mask); +static int le_enable(int s, int argc, char *argv[]); + +static int +le_set_scan_param(int s, int argc, char *argv[]) +{ + int type; + int interval; + int window; + int adrtype; + int policy; + int e, n; + + ng_hci_le_set_scan_parameters_cp cp; + ng_hci_le_set_scan_parameters_rp rp; + + if (argc != 5) + return USAGE; + + if (strcmp(argv[0], "active") == 0) + type = 1; + else if (strcmp(argv[0], "passive") == 0) + type = 0; + else + return USAGE; + + interval = (int)(atof(argv[1])/0.625); + interval = (interval < 4)? 4: interval; + window = (int)(atof(argv[2])/0.625); + window = (window < 4) ? 4 : interval; + + if (strcmp(argv[3], "public") == 0) + adrtype = 0; + else if (strcmp(argv[0], "random") == 0) + adrtype = 1; + else + return USAGE; + + if (strcmp(argv[4], "all") == 0) + policy = 0; + else if (strcmp(argv[4], "whitelist") == 0) + policy = 1; + else + return USAGE; + + cp.le_scan_type = type; + cp.le_scan_interval = interval; + cp.own_address_type = adrtype; + cp.le_scan_window = window; + cp.scanning_filter_policy = policy; + n = sizeof(rp); + e = hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, + NG_HCI_OCF_LE_SET_SCAN_PARAMETERS), + (void *)&cp, sizeof(cp), (void *)&rp, &n); + + return 0; +} + +static int +le_set_scan_enable(int s, int argc, char *argv[]) +{ + ng_hci_le_set_scan_enable_cp cp; + ng_hci_le_set_scan_enable_rp rp; + int e, n, enable = 0; + + if (argc != 1) + return USAGE; + + if (strcmp(argv[0], "enable") == 0) + enable = 1; + else if (strcmp(argv[0], "disable") != 0) + return USAGE; + + n = sizeof(rp); + cp.le_scan_enable = enable; + cp.filter_duplicates = 0; + e = hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, + NG_HCI_OCF_LE_SET_SCAN_ENABLE), + (void *)&cp, sizeof(cp), (void *)&rp, &n); + + if (e != 0 || rp.status != 0) + return ERROR; + + return OK; +} + +static int +parse_param(int argc, char *argv[], char *buf, int *len) +{ + char *buflast = buf + (*len); + char *curbuf = buf; + char *token,*lenpos; + int ch; + int datalen; + uint16_t value; + optreset = 1; + optind = 0; + while ((ch = getopt(argc, argv , "n:f:u:")) != -1) { + switch(ch){ + case 'n': + datalen = strlen(optarg); + if ((curbuf + datalen + 2) >= buflast) + goto done; + curbuf[0] = datalen + 1; + curbuf[1] = 8; + curbuf += 2; + memcpy(curbuf, optarg, datalen); + curbuf += datalen; + break; + case 'f': + if (curbuf+3 > buflast) + goto done; + curbuf[0] = 2; + curbuf[1] = 1; + curbuf[2] = atoi(optarg); + curbuf += 3; + break; + case 'u': + lenpos = buf; + if ((buf+2) >= buflast) + goto done; + curbuf[1] = 2; + *lenpos = 1; + curbuf += 2; + while ((token = strsep(&optarg, ",")) != NULL) { + value = strtol(token, NULL, 16); + if ((curbuf+2) >= buflast) + break; + curbuf[0] = value &0xff; + curbuf[1] = (value>>8)&0xff; + curbuf += 2; + } + + } + } +done: + *len = curbuf - buf; + + return OK; +} + +static int +le_set_scan_response(int s, int argc, char *argv[]) +{ + ng_hci_le_set_scan_response_data_cp cp; + ng_hci_le_set_scan_response_data_rp rp; + int n; + int e; + int len; + char buf[NG_HCI_ADVERTISING_DATA_SIZE]; + + len = sizeof(buf); + parse_param(argc, argv, buf, &len); + memset(cp.scan_response_data, 0, sizeof(cp.scan_response_data)); + cp.scan_response_data_length = len; + memcpy(cp.scan_response_data, buf, len); + n = sizeof(rp); + e = hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, + NG_HCI_OCF_LE_SET_SCAN_RESPONSE_DATA), + (void *)&cp, sizeof(cp), (void *)&rp, &n); + + printf("SET SCAN RESPONSE %d %d %d\n", e, rp.status, n); + + return OK; +} + +static int +le_read_local_supported_features(int s, int argc ,char *argv[]) +{ + ng_hci_le_read_local_supported_features_rp rp; + int e; + int n = sizeof(rp); + + e = hci_simple_request(s, + NG_HCI_OPCODE(NG_HCI_OGF_LE, + NG_HCI_OCF_LE_READ_LOCAL_SUPPORTED_FEATURES), + (void *)&rp, &n); + + printf("LOCAL SUPPORTED: %d %d %jx\n", e, rp.status, + (uintmax_t) rp.le_features); + + return 0; +} + +static int +le_read_supported_status(int s, int argc, char *argv[]) +{ + ng_hci_le_read_supported_status_rp rp; + int e; + int n = sizeof(rp); + + e = hci_simple_request(s, NG_HCI_OPCODE( + NG_HCI_OGF_LE, + NG_HCI_OCF_LE_READ_SUPPORTED_STATUS), + (void *)&rp, &n); + + printf("LE_STATUS: %d %d %jx\n", e, rp.status, (uintmax_t)rp.le_status); + + return 0; +} + +static int +set_le_event_mask(int s, uint64_t mask) +{ + ng_hci_le_set_event_mask_cp semc; + ng_hci_le_set_event_mask_rp rp; + int i, n ,e; + + n = sizeof(rp); + + for (i=0; i < NG_HCI_LE_EVENT_MASK_SIZE; i++) { + semc.event_mask[i] = mask&0xff; + mask >>= 8; + } + e = hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LE, + NG_HCI_OCF_LE_SET_EVENT_MASK), + (void *)&semc, sizeof(semc), (void *)&rp, &n); + + return 0; +} + +static int +set_event_mask(int s, uint64_t mask) +{ + ng_hci_set_event_mask_cp semc; + ng_hci_set_event_mask_rp rp; + int i, n, e; + + n = sizeof(rp); + + for (i=0; i < NG_HCI_EVENT_MASK_SIZE; i++) { + semc.event_mask[i] = mask&0xff; + mask >>= 8; + } + e = hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND, + NG_HCI_OCF_SET_EVENT_MASK), + (void *)&semc, sizeof(semc), (void *)&rp, &n); + + return 0; +} + +static +int le_enable(int s, int argc, char *argv[]) +{ + if (argc != 1) + return USAGE; + + if (strcasecmp(argv[0], "enable") == 0) { + set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT | + NG_HCI_EVENT_MASK_LE); + set_le_event_mask(s, NG_HCI_LE_EVENT_MASK_ALL); + } else if (strcasecmp(argv[0], "disble") == 0) + set_event_mask(s, NG_HCI_EVENT_MASK_DEFAULT); + else + return USAGE; + + return OK; +} + +struct hci_command le_commands[] = { +{ + "le_enable", + "le_enable [enable|disable] \n" + "Enable LE event ", + &le_enable, +}, + { + "le_read_local_supported_features", + "le_read_local_supported_features\n" + "read local supported features mask", + &le_read_local_supported_features, + }, + { + "le_read_supported_status", + "le_read_supported_status\n" + "read supported status" + , + &le_read_supported_status, + }, + { + "le_set_scan_response", + "le_set_scan_response -n $name -f $flag -u $uuid16,$uuid16 \n" + "set LE scan response data" + , + &le_set_scan_response, + }, + { + "le_set_scan_enable", + "le_set_scan_enable [enable|disable] \n" + "enable or disable LE device scan", + &le_set_scan_enable + }, + { + "le_set_scan_param", + "le_set_scan_param [active|passive] interval(ms) window(ms) [public|random] [all|whitelist] \n" + "set LE device scan parameter", + &le_set_scan_param + }, +}; diff --git a/usr.sbin/bluetooth/hccontrol/link_control.c b/usr.sbin/bluetooth/hccontrol/link_control.c index 536520a..a55426c 100644 --- a/usr.sbin/bluetooth/hccontrol/link_control.c +++ b/usr.sbin/bluetooth/hccontrol/link_control.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <errno.h> #include <stdio.h> diff --git a/usr.sbin/bluetooth/hccontrol/link_policy.c b/usr.sbin/bluetooth/hccontrol/link_policy.c index 67b32d5..8142b23 100644 --- a/usr.sbin/bluetooth/hccontrol/link_policy.c +++ b/usr.sbin/bluetooth/hccontrol/link_policy.c @@ -29,6 +29,7 @@ * $FreeBSD$ */ +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <errno.h> #include <stdio.h> diff --git a/usr.sbin/bluetooth/hccontrol/node.c b/usr.sbin/bluetooth/hccontrol/node.c index ede2153..fb6fd19 100644 --- a/usr.sbin/bluetooth/hccontrol/node.c +++ b/usr.sbin/bluetooth/hccontrol/node.c @@ -30,6 +30,7 @@ */ #include <sys/ioctl.h> +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <errno.h> #include <netgraph/ng_message.h> diff --git a/usr.sbin/bluetooth/hccontrol/util.c b/usr.sbin/bluetooth/hccontrol/util.c index 4bb5000..1b05170 100644 --- a/usr.sbin/bluetooth/hccontrol/util.c +++ b/usr.sbin/bluetooth/hccontrol/util.c @@ -30,6 +30,7 @@ */ #include <sys/param.h> +#define L2CAP_SOCKET_CHECKED #include <bluetooth.h> #include <stdio.h> #include <string.h> @@ -151,7 +152,12 @@ hci_ver2str(int ver) /* 0x00 */ "Bluetooth HCI Specification 1.0B", /* 0x01 */ "Bluetooth HCI Specification 1.1", /* 0x02 */ "Bluetooth HCI Specification 1.2", - /* 0x03 */ "Bluetooth HCI Specification 2.0" + /* 0x03 */ "Bluetooth HCI Specification 2.0", + /* 0x04 */ "Bluetooth HCI Specification 2.1", + /* 0x05 */ "Bluetooth HCI Specification 3.0", + /* 0x06 */ "Bluetooth HCI Specification 4.0", + /* 0x07 */ "Bluetooth HCI Specification 4.1", + /* 0x08 */ "Bluetooth HCI Specification 4.2" }; return (ver >= SIZE(t)? "?" : t[ver]); @@ -164,7 +170,12 @@ hci_lmpver2str(int ver) /* 0x00 */ "Bluetooth LMP 1.0", /* 0x01 */ "Bluetooth LMP 1.1", /* 0x02 */ "Bluetooth LMP 1.2", - /* 0x03 */ "Bluetooth LMP 2.0" + /* 0x03 */ "Bluetooth LMP 2.0", + /* 0x04 */ "Bluetooth LMP 2.1", + /* 0x04 */ "Bluetooth LMP 3.0", + /* 0x04 */ "Bluetooth LMP 4.0", + /* 0x04 */ "Bluetooth LMP 4.1", + /* 0x04 */ "Bluetooth LMP 4.2" }; return (ver >= SIZE(t)? "?" : t[ver]); |