<commit_before>/** @file
 * @brief Bluetooth shell module
 *
 * Provide some Bluetooth shell commands that can be useful to applications.
 */

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <zephyr.h>

#include <settings/settings.h>

#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>

#include <shell/shell.h>

#include "bt.h"
#include "gatt.h"
#include "ll.h"

#define DEVICE_NAME		CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN		(sizeof(DEVICE_NAME) - 1)
#define CREDITS			10
#define DATA_MTU		(23 * CREDITS)
#define DATA_BREDR_MTU		48

#define BT_SHELL_MODULE "bt"

static u8_t selected_id = BT_ID_DEFAULT;

#if defined(CONFIG_BT_CONN)
struct bt_conn *default_conn;

/* Connection context for BR/EDR legacy pairing in sec mode 3 */
static struct bt_conn *pairing_conn;
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
NET_BUF_POOL_DEFINE(data_tx_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
NET_BUF_POOL_DEFINE(data_rx_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
#endif

#if defined(CONFIG_BT_BREDR)
NET_BUF_POOL_DEFINE(data_bredr_pool, 1, DATA_BREDR_MTU, BT_BUF_USER_DATA_MIN,
		    NULL);

#define SDP_CLIENT_USER_BUF_LEN		512
NET_BUF_POOL_DEFINE(sdp_client_pool, CONFIG_BT_MAX_CONN,
		    SDP_CLIENT_USER_BUF_LEN, BT_BUF_USER_DATA_MIN, NULL);
#endif /* CONFIG_BT_BREDR */

#if defined(CONFIG_BT_RFCOMM)

static struct bt_sdp_attribute spp_attrs[] = {
	BT_SDP_NEW_SERVICE,
	BT_SDP_LIST(
		BT_SDP_ATTR_SVCLASS_ID_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
			BT_SDP_ARRAY_16(BT_SDP_SERIAL_PORT_SVCLASS)
		},
		)
	),
	BT_SDP_LIST(
		BT_SDP_ATTR_PROTO_DESC_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 12),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)
			},
			)
		},
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 5),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_PROTO_RFCOMM)
			},
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UINT8),
				BT_SDP_ARRAY_8(BT_RFCOMM_CHAN_SPP)
			},
			)
		},
		)
	),
	BT_SDP_LIST(
		BT_SDP_ATTR_PROFILE_DESC_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_SERIAL_PORT_SVCLASS)
			},
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
				BT_SDP_ARRAY_16(0x0102)
			},
			)
		},
		)
	),
	BT_SDP_SERVICE_NAME("Serial Port"),
};

static struct bt_sdp_record spp_rec = BT_SDP_RECORD(spp_attrs);

#endif /* CONFIG_BT_RFCOMM */

#define NAME_LEN 30

static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;

	switch (data->type) {
	case BT_DATA_NAME_SHORTENED:
	case BT_DATA_NAME_COMPLETE:
		memcpy(name, data->data, min(data->data_len, NAME_LEN - 1));
		return false;
	default:
		return true;
	}
}

static void device_found(const bt_addr_le_t *addr, s8_t rssi, u8_t evtype,
			 struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	char name[NAME_LEN];

	(void)memset(name, 0, sizeof(name));

	bt_data_parse(buf, data_cb, name);

	bt_addr_le_to_str(addr, le_addr, sizeof(le_addr));
	printk("[DEVICE]: %s, AD evt type %u, RSSI %i %s\n", le_addr, evtype,
	       rssi, name);
}

#if !defined(CONFIG_BT_CONN)
static const char *current_prompt(void)
{
	return NULL;
}
#endif /* !CONFIG_BT_CONN */

#if defined(CONFIG_BT_CONN)
static const char *current_prompt(void)
{
	static char str[BT_ADDR_LE_STR_LEN + 2];
	static struct bt_conn_info info;

	if (!default_conn) {
		return NULL;
	}

	if (bt_conn_get_info(default_conn, &info) < 0) {
		return NULL;
	}

	if (info.type != BT_CONN_TYPE_LE) {
		return NULL;
	}

	bt_addr_le_to_str(info.le.dst, str, sizeof(str) - 2);
	strcat(str, "> ");
	return str;
}

static void conn_addr_str(struct bt_conn *conn, char *addr, size_t len)
{
	struct bt_conn_info info;

	if (bt_conn_get_info(conn, &info) < 0) {
		addr[0] = '\0';
		return;
	}

	switch (info.type) {
#if defined(CONFIG_BT_BREDR)
	case BT_CONN_TYPE_BR:
		bt_addr_to_str(info.br.dst, addr, len);
		break;
#endif
	case BT_CONN_TYPE_LE:
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, len);
		break;
	}
}

static void connected(struct bt_conn *conn, u8_t err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));

	if (err) {
		printk("Failed to connect to %s (%u)\n", addr, err);
		goto done;
	}

	printk("Connected: %s\n", addr);

	if (!default_conn) {
		default_conn = bt_conn_ref(conn);
	}

done:
	/* clear connection reference for sec mode 3 pairing */
	if (pairing_conn) {
		bt_conn_unref(pairing_conn);
		pairing_conn = NULL;
	}
}

static void disconnected(struct bt_conn *conn, u8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));
	printk("Disconnected: %s (reason %u)\n", addr, reason);

	if (default_conn == conn) {
		bt_conn_unref(default_conn);
		default_conn = NULL;
	}
}

static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	printk("LE conn  param req: int (0x%04x, 0x%04x) lat %d to %d\n",
	       param->interval_min, param->interval_max, param->latency,
	       param->timeout);

	return true;
}

static void le_param_updated(struct bt_conn *conn, u16_t interval,
			     u16_t latency, u16_t timeout)
{
	printk("LE conn param updated: int 0x%04x lat %d to %d\n", interval,
	       latency, timeout);
}

#if defined(CONFIG_BT_BREDR)
static u8_t sdp_hfp_ag_user(struct bt_conn *conn,
			       struct bt_sdp_client_result *result)
{
	char addr[BT_ADDR_STR_LEN];
	u16_t param, version;
	u16_t features;
	int res;

	conn_addr_str(conn, addr, sizeof(addr));

	if (result) {
		printk("SDP HFPAG data@%p (len %u) hint %u from remote %s\n",
			result->resp_buf, result->resp_buf->len,
			result->next_record_hint, addr);

		/*
		 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
		 * get HFPAG Server Channel Number operating on RFCOMM protocol.
		 */
		res = bt_sdp_get_proto_param(result->resp_buf,
					     BT_SDP_PROTO_RFCOMM, &param);
		if (res < 0) {
			printk("Error getting Server CN, err %d\n", res);
			goto done;
		}
		printk("HFPAG Server CN param 0x%04x\n", param);

		res = bt_sdp_get_profile_version(result->resp_buf,
						 BT_SDP_HANDSFREE_SVCLASS,
						 &version);
		if (res < 0) {
			printk("Error getting profile version, err %d\n", res);
			goto done;
		}
		printk("HFP version param 0x%04x\n", version);

		/*
		 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
		 * get profile Supported Features mask.
		 */
		res = bt_sdp_get_features(result->resp_buf, &features);
		if (res < 0) {
			printk("Error getting HFPAG Features, err %d\n", res);
			goto done;
		}
		printk("HFPAG Supported Features param 0x%04x\n", features);
	} else {
		printk("No SDP HFPAG data from remote %s\n", addr);
	}
done:
	return BT_SDP_DISCOVER_UUID_CONTINUE;
}

static u8_t sdp_a2src_user(struct bt_conn *conn,
			      struct bt_sdp_client_result *result)
{
	char addr[BT_ADDR_STR_LEN];
	u16_t param, version;
	u16_t features;
	int res;

	conn_addr_str(conn, addr, sizeof(addr));

	if (result) {
		printk("SDP A2SRC data@%p (len %u) hint %u from remote %s\n",
			result->resp_buf, result->resp_buf->len,
			result->next_record_hint, addr);

		/*
		 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
		 * get A2SRC Server PSM Number.
		 */
		res = bt_sdp_get_proto_param(result->resp_buf,
					     BT_SDP_PROTO_L2CAP, &param);
		if (res < 0) {
			printk("A2SRC PSM Number not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC Server PSM Number param 0x%04x\n", param);

		/*
		 * Focus to get BT_SDP_ATTR_PROFILE_DESC_LIST attribute item to
		 * get profile version number.
		 */
		res = bt_sdp_get_profile_version(result->resp_buf,
						 BT_SDP_ADVANCED_AUDIO_SVCLASS,
						 &version);
		if (res < 0) {
			printk("A2SRC version not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC version param 0x%04x\n", version);

		/*
		 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
		 * get profile supported features mask.
		 */
		res = bt_sdp_get_features(result->resp_buf, &features);
		if (res < 0) {
			printk("A2SRC Features not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC Supported Features param 0x%04x\n", features);
	} else {
		printk("No SDP A2SRC data from remote %s\n", addr);
	}
done:
	return BT_SDP_DISCOVER_UUID_CONTINUE;
}

static struct bt_sdp_discover_params discov_hfpag = {
	.uuid = BT_UUID_DECLARE_16(BT_SDP_HANDSFREE_AGW_SVCLASS),
	.func = sdp_hfp_ag_user,
	.pool = &sdp_client_pool,
};

static struct bt_sdp_discover_params discov_a2src = {
	.uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SOURCE_SVCLASS),
	.func = sdp_a2src_user,
	.pool = &sdp_client_pool,
};

static struct bt_sdp_discover_params discov;
#endif

#if defined(CONFIG_BT_SMP)
static void identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa,
			      const bt_addr_le_t *identity)
{
	char addr_identity[BT_ADDR_LE_STR_LEN];
	char addr_rpa[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(identity, addr_identity, sizeof(addr_identity));
	bt_addr_le_to_str(rpa, addr_rpa, sizeof(addr_rpa));

	printk("Identity resolved %s -> %s\n", addr_rpa, addr_identity);
}
#endif

#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
static void security_changed(struct bt_conn *conn, bt_security_t level)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));
	printk("Security changed: %s level %u\n", addr, level);
}
#endif

static struct bt_conn_cb conn_callbacks = {
	.connected = connected,
	.disconnected = disconnected,
	.le_param_req = le_param_req,
	.le_param_updated = le_param_updated,
#if defined(CONFIG_BT_SMP)
	.identity_resolved = identity_resolved,
#endif
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
	.security_changed = security_changed,
#endif
};
#endif /* CONFIG_BT_CONN */

static int char2hex(const char *c, u8_t *x)
{
	if (*c >= '0' && *c <= '9') {
		*x = *c - '0';
	} else if (*c >= 'a' && *c <= 'f') {
		*x = *c - 'a' + 10;
	} else if (*c >= 'A' && *c <= 'F') {
		*x = *c - 'A' + 10;
	} else {
		return -EINVAL;
	}

	return 0;
}

static int str2bt_addr(const char *str, bt_addr_t *addr)
{
	int i, j;
	u8_t tmp;

	if (strlen(str) != 17) {
		return -EINVAL;
	}

	for (i = 5, j = 1; *str != '\0'; str++, j++) {
		if (!(j % 3) && (*str != ':')) {
			return -EINVAL;
		} else if (*str == ':') {
			i--;
			continue;
		}

		addr->val[i] = addr->val[i] << 4;

		if (char2hex(str, &tmp) < 0) {
			return -EINVAL;
		}

		addr->val[i] |= tmp;
	}

	return 0;
}

static int str2bt_addr_le(const char *str, const char *type, bt_addr_le_t *addr)
{
	int err;

	err = str2bt_addr(str, &addr->a);
	if (err < 0) {
		return err;
	}

	if (!strcmp(type, "public") || !strcmp(type, "(public)")) {
		addr->type = BT_ADDR_LE_PUBLIC;
	} else if (!strcmp(type, "random") || !strcmp(type, "(random)")) {
		addr->type = BT_ADDR_LE_RANDOM;
	} else {
		return -EINVAL;
	}

	return 0;
}

static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

#if defined(CONFIG_BT_CONN)
	default_conn = NULL;

	bt_conn_cb_register(&conn_callbacks);
#endif /* CONFIG_BT_CONN */
}

static int cmd_init(int argc, char *argv[])
{
	int err;

	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}

	return 0;
}

#if defined(CONFIG_BT_HCI) || defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
static void hexdump(const u8_t *data, size_t len)
{
	int n = 0;

	while (len--) {
		if (n % 16 == 0) {
			printk("%08X ", n);
		}

		printk("%02X ", *data++);

		n++;
		if (n % 8 == 0) {
			if (n % 16 == 0) {
				printk("\n");
			} else {
				printk(" ");
			}
		}
	}

	if (n % 16) {
		printk("\n");
	}
}
#endif /* CONFIG_BT_HCI || CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */

#if defined(CONFIG_BT_HCI)
static int cmd_hci_cmd(int argc, char *argv[])
{
	u8_t ogf;
	u16_t ocf;
	struct net_buf *buf = NULL, *rsp;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	ogf = strtoul(argv[1], NULL, 16);
	ocf = strtoul(argv[2], NULL, 16);

	if (argc > 3) {
		int i;

		buf = bt_hci_cmd_create(BT_OP(ogf, ocf), argc - 3);

		for (i = 3; i < argc; i++) {
			net_buf_add_u8(buf, strtoul(argv[i], NULL, 16));
		}
	}

	err = bt_hci_cmd_send_sync(BT_OP(ogf, ocf), buf, &rsp);
	if (err) {
		printk("HCI command failed (err %d)\n", err);
	} else {
		hexdump(rsp->data, rsp->len);
		net_buf_unref(rsp);
	}

	return 0;
}
#endif /* CONFIG_BT_HCI */

static int cmd_name(int argc, char *argv[])
{
	int err;

	if (argc < 2) {
		printk("Bluetooth Local Name: %s\n", bt_get_name());
		return 0;
	}

	err = bt_set_name(argv[1]);
	if (err) {
		printk("Unable to set name %s (err %d)", argv[1], err);
	}

	return 0;
}

static int cmd_id_create(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc > 1) {
		err = str2bt_addr_le(argv[1], "random", &addr);
		if (err) {
			printk("Invalid address\n");
			return err;
		}
	} else {
		bt_addr_le_copy(&addr, BT_ADDR_LE_ANY);
	}

	err = bt_id_create(&addr, NULL);
	if (err < 0) {
		printk("Creating new ID failed (err %d)\n", err);
		return 0;
	}

	printk("New identity (%d) created: %s\n", err, bt_addr_le_str(&addr));

	return 0;
}

static int cmd_id_reset(int argc, char *argv[])
{
	bt_addr_le_t addr;
	u8_t id;
	int err;

	if (argc < 2) {
		printk("Identity identifier not specified\n");
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	if (argc > 2) {
		err = str2bt_addr_le(argv[2], "random", &addr);
		if (err) {
			printk("Invalid address\n");
			return err;
		}
	} else {
		bt_addr_le_copy(&addr, BT_ADDR_LE_ANY);
	}

	err = bt_id_reset(id, &addr, NULL);
	if (err < 0) {
		printk("Resetting ID %u failed (err %d)\n", id, err);
		return 0;
	}

	printk("Identity %u reset: %s\n", id, bt_addr_le_str(&addr));

	return 0;
}

static int cmd_id_delete(int argc, char *argv[])
{
	u8_t id;
	int err;

	if (argc < 2) {
		printk("Identity identifier not specified\n");
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	err = bt_id_delete(id);
	if (err < 0) {
		printk("Deleting ID %u failed (err %d)\n", id, err);
		return 0;
	}

	printk("Identity %u deleted\n", id);

	return 0;
}

static int cmd_id_show(int argc, char *argv[])
{
	bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
	size_t i, count = CONFIG_BT_ID_MAX;

	bt_id_get(addrs, &count);

	for (i = 0; i < count; i++) {
		printk("%s%zu: %s\n", i == selected_id ? "*" : " ", i,
		       bt_addr_le_str(&addrs[i]));
	}

	return 0;
}

static int cmd_id_select(int argc, char *argv[])
{
	bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
	size_t count = CONFIG_BT_ID_MAX;
	u8_t id;

	if (argc < 2) {
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	bt_id_get(addrs, &count);
	if (count <= id) {
		printk("Invalid identity\n");
		return 0;
	}

	printk("Selected identity: %s\n", bt_addr_le_str(&addrs[id]));
	selected_id = id;

	return 0;
}

static void cmd_active_scan_on(int dups)
{
	int err;
	struct bt_le_scan_param param = {
			.type       = BT_HCI_LE_SCAN_ACTIVE,
			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE,
			.interval   = BT_GAP_SCAN_FAST_INTERVAL,
			.window     = BT_GAP_SCAN_FAST_WINDOW };

	if (dups >= 0) {
		param.filter_dup = dups;
	}

	err = bt_le_scan_start(&param, device_found);
	if (err) {
		printk("Bluetooth set active scan failed (err %d)\n", err);
	} else {
		printk("Bluetooth active scan enabled\n");
	}
}

static void cmd_passive_scan_on(int dups)
{
	struct bt_le_scan_param param = {
			.type       = BT_HCI_LE_SCAN_PASSIVE,
			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE,
			.interval   = 0x10,
			.window     = 0x10 };
	int err;

	if (dups >= 0) {
		param.filter_dup = dups;
	}

	err = bt_le_scan_start(&param, device_found);
	if (err) {
		printk("Bluetooth set passive scan failed (err %d)\n", err);
	} else {
		printk("Bluetooth passive scan enabled\n");
	}
}

static void cmd_scan_off(void)
{
	int err;

	err = bt_le_scan_stop();
	if (err) {
		printk("Stopping scanning failed (err %d)\n", err);
	} else {
		printk("Scan successfully stopped\n");
	}
}

static int cmd_scan(int argc, char *argv[])
{
	const char *action;
	int dups = -1;

	if (argc < 2) {
		return -EINVAL;
	}

	/* Parse duplicate filtering data */
	if (argc >= 3) {
		const char *dup_filter = argv[2];

		if (!strcmp(dup_filter, "dups")) {
			dups = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE;
		} else if (!strcmp(dup_filter, "nodups")) {
			dups = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE;
		} else {
			return -EINVAL;
		}
	}

	action = argv[1];
	if (!strcmp(action, "on")) {
		cmd_active_scan_on(dups);
	} else if (!strcmp(action, "off")) {
		cmd_scan_off();
	} else if (!strcmp(action, "passive")) {
		cmd_passive_scan_on(dups);
	} else {
		return -EINVAL;
	}

	return 0;
}

static const struct bt_data ad_discov[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
};

static int cmd_advertise(int argc, char *argv[])
{
	struct bt_le_adv_param param;
	const struct bt_data *ad, *scan_rsp;
	size_t ad_len, scan_rsp_len;
	int err;

	if (argc < 2) {
		goto fail;
	}

	if (!strcmp(argv[1], "off")) {
		if (bt_le_adv_stop() < 0) {
			printk("Failed to stop advertising\n");
		} else {
			printk("Advertising stopped\n");
		}

		return 0;
	}

	param.id = selected_id;
	param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
	param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;

	if (!strcmp(argv[1], "on")) {
		param.options = (BT_LE_ADV_OPT_CONNECTABLE |
				 BT_LE_ADV_OPT_USE_NAME);
	} else if (!strcmp(argv[1], "scan")) {
		param.options = BT_LE_ADV_OPT_USE_NAME;
	} else if (!strcmp(argv[1], "nconn")) {
		param.options = 0;
		scan_rsp = NULL;
		scan_rsp_len = 0;
	} else {
		goto fail;
	}

	/* Parse advertisement data */
	if (argc >= 3) {
		const char *mode = argv[2];

		if (!strcmp(mode, "discov")) {
			ad = ad_discov;
			ad_len = ARRAY_SIZE(ad_discov);
		} else if (!strcmp(mode, "non_discov")) {
			ad = NULL;
			ad_len = 0;
		} else {
			goto fail;
		}
	} else {
		ad = ad_discov;
		ad_len = ARRAY_SIZE(ad_discov);
	}

	err = bt_le_adv_start(&param, ad, ad_len, scan_rsp, scan_rsp_len);
	if (err < 0) {
		printk("Failed to start advertising (err %d)\n", err);
	} else {
		printk("Advertising started\n");
	}

	return 0;

fail:
	return -EINVAL;
}

#if defined(CONFIG_BT_CONN)
static int cmd_connect_le(int argc, char *argv[])
{
	int err;
	bt_addr_le_t addr;
	struct bt_conn *conn;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_create_le(&addr, BT_LE_CONN_PARAM_DEFAULT);

	if (!conn) {
		printk("Connection failed\n");
	} else {

		printk("Connection pending\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static int cmd_disconnect(int argc, char *argv[])
{
	struct bt_conn *conn;
	int err;

	if (default_conn && argc < 3) {
		conn = bt_conn_ref(default_conn);
	} else {
		bt_addr_le_t addr;

		if (argc < 3) {
			return -EINVAL;
		}

		err = str2bt_addr_le(argv[1], argv[2], &addr);
		if (err) {
			printk("Invalid peer address (err %d)\n", err);
			return 0;
		}

		conn = bt_conn_lookup_addr_le(selected_id, &addr);
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
	if (err) {
		printk("Disconnection failed (err %d)\n", err);
	}

	bt_conn_unref(conn);

	return 0;
}

static int cmd_auto_conn(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	if (argc < 4) {
		bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
	} else if (!strcmp(argv[3], "on")) {
		bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
	} else if (!strcmp(argv[3], "off")) {
		bt_le_set_auto_conn(&addr, NULL);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int cmd_directed_adv(int argc, char *argv[])
{
	int err;
	bt_addr_le_t addr;
	struct bt_conn *conn;
	struct bt_le_adv_param *param = BT_LE_ADV_CONN_DIR;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	if (argc > 3) {
		if (!strcmp(argv[3], "low")) {
			param = BT_LE_ADV_CONN_DIR_LOW_DUTY;
		} else {
			return -EINVAL;
		}
	}

	conn = bt_conn_create_slave_le(&addr, param);
	if (!conn) {
		printk("Failed to start directed advertising\n");
	} else {
		printk("Started directed advertising\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static int cmd_select(int argc, char *argv[])
{
	struct bt_conn *conn;
	bt_addr_le_t addr;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &addr);
	if (!conn) {
		printk("No matching connection found\n");
		return 0;
	}

	if (default_conn) {
		bt_conn_unref(default_conn);
	}

	default_conn = conn;

	return 0;
}

static int cmd_conn_update(int argc, char *argv[])
{
	struct bt_le_conn_param param;
	int err;

	if (argc < 5) {
		return -EINVAL;
	}

	param.interval_min = strtoul(argv[1], NULL, 16);
	param.interval_max = strtoul(argv[2], NULL, 16);
	param.latency = strtoul(argv[3], NULL, 16);
	param.timeout = strtoul(argv[4], NULL, 16);

	err = bt_conn_le_param_update(default_conn, &param);
	if (err) {
		printk("conn update failed (err %d).\n", err);
	} else {
		printk("conn update initiated.\n");
	}

	return 0;
}

static int cmd_oob(int argc, char *argv[])
{
	char addr[BT_ADDR_LE_STR_LEN];
	struct bt_le_oob oob;
	int err;

	err = bt_le_oob_get_local(selected_id, &oob);
	if (err) {
		printk("OOB data failed\n");
		return 0;
	}

	bt_addr_le_to_str(&oob.addr, addr, sizeof(addr));

	printk("OOB data:\n");
	printk("  addr %s\n", addr);

	return 0;
}

static int cmd_clear(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc < 2) {
		printk("Specify remote address or \"all\"\n");
		return 0;
	}

	if (strcmp(argv[1], "all") == 0) {
		err = bt_unpair(selected_id, NULL);
		if (err) {
			printk("Failed to clear pairings (err %d)\n", err);
		} else {
			printk("Pairings successfully cleared\n");
		}

		return 0;
	}

	if (argc < 3) {
#if defined(CONFIG_BT_BREDR)
		addr.type = BT_ADDR_LE_PUBLIC;
		err = str2bt_addr(argv[1], &addr.a);
#else
		printk("Both address and address type needed\n");
		return 0;
#endif
	} else {
		err = str2bt_addr_le(argv[1], argv[2], &addr);
	}

	if (err) {
		printk("Invalid address\n");
		return 0;
	}

	err = bt_unpair(selected_id, &addr);
	if (err) {
		printk("Failed to clear pairing (err %d)\n", err);
	} else {
		printk("Pairing successfully cleared\n");
	}

	return 0;
}
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
static int cmd_security(int argc, char *argv[])
{
	int err, sec;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	sec = *argv[1] - '0';

	err = bt_conn_security(default_conn, sec);
	if (err) {
		printk("Setting security failed (err %d)\n", err);
	}

	return 0;
}

static int cmd_bondable(int argc, char *argv[])
{
	const char *bondable;

	if (argc < 2) {
		return -EINVAL;
	}

	bondable = argv[1];
	if (!strcmp(bondable, "on")) {
		bt_set_bondable(true);
	} else if (!strcmp(bondable, "off")) {
		bt_set_bondable(false);
	} else {
		return -EINVAL;
	}

	return 0;
}

static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];
	char passkey_str[7];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	snprintk(passkey_str, 7, "%06u", passkey);

	printk("Passkey for %s: %s\n", addr, passkey_str);
}

static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];
	char passkey_str[7];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	snprintk(passkey_str, 7, "%<PASSWORD>", passkey);

	printk("Confirm passkey for %s: %s\n", addr, passkey_str);
}

static void auth_passkey_entry(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Enter passkey for %s\n", addr);
}

static void auth_cancel(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));

	printk("Pairing cancelled: %s\n", addr);

	/* clear connection reference for sec mode 3 pairing */
	if (pairing_conn) {
		bt_conn_unref(pairing_conn);
		pairing_conn = NULL;
	}
}

static void auth_pairing_confirm(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Confirm pairing for %s\n", addr);
}

static void auth_pairing_complete(struct bt_conn *conn, bool bonded)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("%s with %s\n", bonded ? "Bonded" : "Paired",  addr);
}

static void auth_pairing_failed(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing failed with %s\n", addr);
}

#if defined(CONFIG_BT_BREDR)
static void auth_pincode_entry(struct bt_conn *conn, bool highsec)
{
	char addr[BT_ADDR_STR_LEN];
	struct bt_conn_info info;

	if (bt_conn_get_info(conn, &info) < 0) {
		return;
	}

	if (info.type != BT_CONN_TYPE_BR) {
		return;
	}

	bt_addr_to_str(info.br.dst, addr, sizeof(addr));

	if (highsec) {
		printk("Enter 16 digits wide PIN code for %s\n", addr);
	} else {
		printk("Enter PIN code for %s\n", addr);
	}

	/*
	 * Save connection info since in security mode 3 (link level enforced
	 * security) PIN request callback is called before connected callback
	 */
	if (!default_conn && !pairing_conn) {
		pairing_conn = bt_conn_ref(conn);
	}
}
#endif

static struct bt_conn_auth_cb auth_cb_display = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = NULL,
	.passkey_confirm = NULL,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_display_yes_no = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = NULL,
	.passkey_confirm = auth_passkey_confirm,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_input = {
	.passkey_display = NULL,
	.passkey_entry = auth_passkey_entry,
	.passkey_confirm = NULL,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_confirm = {
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_all = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = auth_passkey_entry,
	.passkey_confirm = auth_passkey_confirm,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static int cmd_auth(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (!strcmp(argv[1], "all")) {
		bt_conn_auth_cb_register(&auth_cb_all);
	} else if (!strcmp(argv[1], "input")) {
		bt_conn_auth_cb_register(&auth_cb_input);
	} else if (!strcmp(argv[1], "display")) {
		bt_conn_auth_cb_register(&auth_cb_display);
	} else if (!strcmp(argv[1], "yesno")) {
		bt_conn_auth_cb_register(&auth_cb_display_yes_no);
	} else if (!strcmp(argv[1], "confirm")) {
		bt_conn_auth_cb_register(&auth_cb_confirm);
	} else if (!strcmp(argv[1], "none")) {
		bt_conn_auth_cb_register(NULL);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int cmd_auth_cancel(int argc, char *argv[])
{
	struct bt_conn *conn;

	if (default_conn) {
		conn = default_conn;
	} else if (pairing_conn) {
		conn = pairing_conn;
	} else {
		conn = NULL;
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_cancel(conn);

	return 0;
}

static int cmd_auth_passkey_confirm(int argc, char *argv[])
{
	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_passkey_confirm(default_conn);

	return 0;
}

static int cmd_auth_pairing_confirm(int argc, char *argv[])
{
	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_pairing_confirm(default_conn);

	return 0;
}

#if defined(CONFIG_BT_FIXED_PASSKEY)
static int cmd_fixed_passkey(int argc, char *argv[])
{
	unsigned int passkey;
	int err;

	if (argc < 2) {
		bt_passkey_set(BT_PASSKEY_INVALID);
		printk("Fixed passkey cleared\n");
		return 0;
	}

	passkey = atoi(argv[1]);
	if (passkey > 999999) {
		printk("Passkey should be between 0-999999\n");
		return 0;
	}

	err = bt_passkey_set(passkey);
	if (err) {
		printk("Setting fixed passkey failed (err %d)\n", err);
	}

	return 0;
}
#endif

static int cmd_auth_passkey(int argc, char *argv[])
{
	unsigned int passkey;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	passkey = atoi(argv[1]);
	if (passkey > 999999) {
		printk("Passkey should be between 0-999999\n");
		return 0;
	}

	bt_conn_auth_passkey_entry(default_conn, passkey);

	return 0;
}
#endif /* CONFIG_BT_SMP) || CONFIG_BT_BREDR */

#if defined(CONFIG_BT_BREDR)
static int cmd_auth_pincode(int argc, char *argv[])
{
	struct bt_conn *conn;
	u8_t max = 16;

	if (default_conn) {
		conn = default_conn;
	} else if (pairing_conn) {
		conn = pairing_conn;
	} else {
		conn = NULL;
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	if (strlen(argv[1]) > max) {
		printk("PIN code value invalid - enter max %u digits\n", max);
		return 0;
	}

	printk("PIN code \"%s\" applied\n", argv[1]);

	bt_conn_auth_pincode_entry(conn, argv[1]);

	return 0;
}

static int cmd_connect_bredr(int argc, char *argv[])
{
	struct bt_conn *conn;
	bt_addr_t addr;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	err = str2bt_addr(argv[1], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_create_br(&addr, BT_BR_CONN_PARAM_DEFAULT);
	if (!conn) {
		printk("Connection failed\n");
	} else {

		printk("Connection pending\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static void br_device_found(const bt_addr_t *addr, s8_t rssi,
				  const u8_t cod[3], const u8_t eir[240])
{
	char br_addr[BT_ADDR_STR_LEN];
	char name[239];
	int len = 240;

	(void)memset(name, 0, sizeof(name));

	while (len) {
		if (len < 2) {
			break;
		};

		/* Look for early termination */
		if (!eir[0]) {
			break;
		}

		/* Check if field length is correct */
		if (eir[0] > len - 1) {
			break;
		}

		switch (eir[1]) {
		case BT_DATA_NAME_SHORTENED:
		case BT_DATA_NAME_COMPLETE:
			if (eir[0] > sizeof(name) - 1) {
				memcpy(name, &eir[2], sizeof(name) - 1);
			} else {
				memcpy(name, &eir[2], eir[0] - 1);
			}
			break;
		default:
			break;
		}

		/* Parse next AD Structure */
		len -= eir[0] + 1;
		eir += eir[0] + 1;
	}

	bt_addr_to_str(addr, br_addr, sizeof(br_addr));

	printk("[DEVICE]: %s, RSSI %i %s\n", br_addr, rssi, name);
}

static struct bt_br_discovery_result br_discovery_results[5];

static void br_discovery_complete(struct bt_br_discovery_result *results,
				  size_t count)
{
	size_t i;

	printk("BR/EDR discovery complete\n");

	for (i = 0; i < count; i++) {
		br_device_found(&results[i].addr, results[i].rssi,
				results[i].cod, results[i].eir);
	}
}

static int cmd_bredr_discovery(int argc, char *argv[])
{
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];
	if (!strcmp(action, "on")) {
		struct bt_br_discovery_param param;

		param.limited = false;
		param.length = 8;

		if (argc > 2) {
			param.length = atoi(argv[2]);
		}

		if (argc > 3 && !strcmp(argv[3], "limited")) {
			param.limited = true;
		}

		if (bt_br_discovery_start(&param, br_discovery_results,
					  ARRAY_SIZE(br_discovery_results),
					  br_discovery_complete) < 0) {
			printk("Failed to start discovery\n");
			return 0;
		}

		printk("Discovery started\n");
	} else if (!strcmp(action, "off")) {
		if (bt_br_discovery_stop()) {
			printk("Failed to stop discovery\n");
			return 0;
		}

		printk("Discovery stopped\n");
	} else {
		return -EINVAL;
	}

	return 0;
}

#endif /* CONFIG_BT_BREDR */

#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
static u32_t l2cap_rate;

static void l2cap_recv_metrics(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	static u32_t len;
	static u32_t cycle_stamp;
	u32_t delta;

	delta = k_cycle_get_32() - cycle_stamp;
	delta = SYS_CLOCK_HW_CYCLES_TO_NS(delta);

	/* if last data rx-ed was greater than 1 second in the past,
	 * reset the metrics.
	 */
	if (delta > 1000000000) {
		len = 0;
		l2cap_rate = 0;
		cycle_stamp = k_cycle_get_32();
	} else {
		len += buf->len;
		l2cap_rate = ((u64_t)len << 3) * 1000000000 / delta;
	}
}

static void l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	printk("Incoming data channel %p len %u\n", chan, buf->len);

	if (buf->len) {
		hexdump(buf->data, buf->len);
	}
}

static void l2cap_connected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p connected\n", chan);
}

static void l2cap_disconnected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p disconnected\n", chan);
}

static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
{
	/* print if metrics is disabled */
	if (chan->ops->recv != l2cap_recv_metrics) {
		printk("Channel %p requires buffer\n", chan);
	}

	return net_buf_alloc(&data_rx_pool, K_FOREVER);
}

static struct bt_l2cap_chan_ops l2cap_ops = {
	.alloc_buf	= l2cap_alloc_buf,
	.recv		= l2cap_recv,
	.connected	= l2cap_connected,
	.disconnected	= l2cap_disconnected,
};

static struct bt_l2cap_le_chan l2cap_chan = {
	.chan.ops	= &l2cap_ops,
	.rx.mtu		= DATA_MTU,
};

static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
	printk("Incoming conn %p\n", conn);

	if (l2cap_chan.chan.conn) {
		printk("No channels available\n");
		return -ENOMEM;
	}

	*chan = &l2cap_chan.chan;

	return 0;
}

static struct bt_l2cap_server server = {
	.accept		= l2cap_accept,
};

static int cmd_l2cap_register(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (server.psm) {
		printk("Already registered\n");
		return 0;
	}

	server.psm = strtoul(argv[1], NULL, 16);

	if (argc > 2) {
		server.sec_level = strtoul(argv[2], NULL, 10);
	}

	if (bt_l2cap_server_register(&server) < 0) {
		printk("Unable to register psm\n");
		server.psm = 0;
	} else {
		printk("L2CAP psm %u sec_level %u registered\n", server.psm,
		       server.sec_level);
	}

	return 0;
}

static int cmd_l2cap_connect(int argc, char *argv[])
{
	u16_t psm;
	int err;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	if (l2cap_chan.chan.conn) {
		printk("Channel already in use\n");
		return -EINVAL;
	}

	psm = strtoul(argv[1], NULL, 16);

	err = bt_l2cap_chan_connect(default_conn, &l2cap_chan.chan, psm);
	if (err < 0) {
		printk("Unable to connect to psm %u (err %u)\n", psm, err);
	} else {
		printk("L2CAP connection pending\n");
	}

	return 0;
}

static int cmd_l2cap_disconnect(int argc, char *argv[])
{
	int err;

	err = bt_l2cap_chan_disconnect(&l2cap_chan.chan);
	if (err) {
		printk("Unable to disconnect: %u\n", -err);
	}

	return 0;
}

static int cmd_l2cap_send(int argc, char *argv[])
{
	static u8_t buf_data[DATA_MTU] = { [0 ... (DATA_MTU - 1)] = 0xff };
	int ret, len, count = 1;
	struct net_buf *buf;

	if (argc > 1) {
		count = strtoul(argv[1], NULL, 10);
	}

	len = min(l2cap_chan.tx.mtu, DATA_MTU - BT_L2CAP_CHAN_SEND_RESERVE);

	while (count--) {
		buf = net_buf_alloc(&data_tx_pool, K_FOREVER);
		net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE);

		net_buf_add_mem(buf, buf_data, len);
		ret = bt_l2cap_chan_send(&l2cap_chan.chan, buf);
		if (ret < 0) {
			printk("Unable to send: %d\n", -ret);
			net_buf_unref(buf);
			break;
		}
	}

	return 0;
}

static int cmd_l2cap_metrics(int argc, char *argv[])
{
	const char *action;

	if (argc < 2) {
		printk("l2cap rate: %u bps.\n", l2cap_rate);

		return 0;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		l2cap_ops.recv = l2cap_recv_metrics;
	} else if (!strcmp(action, "off")) {
		l2cap_ops.recv = l2cap_recv;
	} else {
		return -EINVAL;
	}

	printk("l2cap metrics %s.\n", action);

	return 0;
}

#endif

#if defined(CONFIG_BT_BREDR)
static void l2cap_bredr_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	printk("Incoming data channel %p len %u\n", chan, buf->len);
}

static void l2cap_bredr_connected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p connected\n", chan);
}

static void l2cap_bredr_disconnected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p disconnected\n", chan);
}

static struct net_buf *l2cap_bredr_alloc_buf(struct bt_l2cap_chan *chan)
{
	printk("Channel %p requires buffer\n", chan);

	return net_buf_alloc(&data_bredr_pool, K_FOREVER);
}

static struct bt_l2cap_chan_ops l2cap_bredr_ops = {
	.alloc_buf	= l2cap_bredr_alloc_buf,
	.recv		= l2cap_bredr_recv,
	.connected	= l2cap_bredr_connected,
	.disconnected	= l2cap_bredr_disconnected,
};

static struct bt_l2cap_br_chan l2cap_bredr_chan = {
	.chan.ops	= &l2cap_bredr_ops,
	 /* Set for now min. MTU */
	.rx.mtu		= 48,
};

static int l2cap_bredr_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
	printk("Incoming BR/EDR conn %p\n", conn);

	if (l2cap_bredr_chan.chan.conn) {
		printk("No channels available");
		return -ENOMEM;
	}

	*chan = &l2cap_bredr_chan.chan;

	return 0;
}

static struct bt_l2cap_server br_server = {
	.accept = l2cap_bredr_accept,
};

static int cmd_bredr_l2cap_register(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (br_server.psm) {
		printk("Already registered\n");
		return 0;
	}

	br_server.psm = strtoul(argv[1], NULL, 16);

	if (bt_l2cap_br_server_register(&br_server) < 0) {
		printk("Unable to register psm\n");
		br_server.psm = 0;
	} else {
		printk("L2CAP psm %u registered\n", br_server.psm);
	}

	return 0;
}

#if defined(CONFIG_BT_RFCOMM)
static void rfcomm_bredr_recv(struct bt_rfcomm_dlc *dlci, struct net_buf *buf)
{
	printk("Incoming data dlc %p len %u\n", dlci, buf->len);
}

static void rfcomm_bredr_connected(struct bt_rfcomm_dlc *dlci)
{
	printk("Dlc %p connected\n", dlci);
}

static void rfcomm_bredr_disconnected(struct bt_rfcomm_dlc *dlci)
{
	printk("Dlc %p disconnected\n", dlci);
}

static struct bt_rfcomm_dlc_ops rfcomm_bredr_ops = {
	.recv		= rfcomm_bredr_recv,
	.connected	= rfcomm_bredr_connected,
	.disconnected	= rfcomm_bredr_disconnected,
};

static struct bt_rfcomm_dlc rfcomm_dlc = {
	.ops = &rfcomm_bredr_ops,
	.mtu = 30,
};

static int rfcomm_bredr_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc)
{
	printk("Incoming RFCOMM conn %p\n", conn);

	if (rfcomm_dlc.session) {
		printk("No channels available");
		return -ENOMEM;
	}

	*dlc = &rfcomm_dlc;

	return 0;
}

struct bt_rfcomm_server rfcomm_server = {
	.accept = &rfcomm_bredr_accept,
};

static int cmd_bredr_rfcomm_register(int argc, char *argv[])
{
	int ret;

	if (rfcomm_server.channel) {
		printk("Already registered\n");
		return 0;
	}

	rfcomm_server.channel = BT_RFCOMM_CHAN_SPP;

	ret = bt_rfcomm_server_register(&rfcomm_server);
	if (ret < 0) {
		printk("Unable to register channel %x\n", ret);
		rfcomm_server.channel = 0;
	} else {
		printk("RFCOMM channel %u registered\n", rfcomm_server.channel);
		bt_sdp_register_service(&spp_rec);
	}

	return 0;
}

static int cmd_rfcomm_connect(int argc, char *argv[])
{
	u8_t channel;
	int err;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	channel = strtoul(argv[1], NULL, 16);

	err = bt_rfcomm_dlc_connect(default_conn, &rfcomm_dlc, channel);
	if (err < 0) {
		printk("Unable to connect to channel %d (err %u)\n",
		       channel, err);
	} else {
		printk("RFCOMM connection pending\n");
	}

	return 0;
}

static int cmd_rfcomm_send(int argc, char *argv[])
{
	u8_t buf_data[DATA_BREDR_MTU] = { [0 ... (DATA_BREDR_MTU - 1)] =
					    0xff };
	int ret, len, count = 1;
	struct net_buf *buf;

	if (argc > 1) {
		count = strtoul(argv[1], NULL, 10);
	}

	while (count--) {
		buf = bt_rfcomm_create_pdu(&data_bredr_pool);
		/* Should reserve one byte in tail for FCS */
		len = min(rfcomm_dlc.mtu, net_buf_tailroom(buf) - 1);

		net_buf_add_mem(buf, buf_data, len);
		ret = bt_rfcomm_dlc_send(&rfcomm_dlc, buf);
		if (ret < 0) {
			printk("Unable to send: %d\n", -ret);
			net_buf_unref(buf);
			break;
		}
	}

	return 0;
}

static int cmd_rfcomm_disconnect(int argc, char *argv[])
{
	int err;

	err = bt_rfcomm_dlc_disconnect(&rfcomm_dlc);
	if (err) {
		printk("Unable to disconnect: %u\n", -err);
	}

	return 0;
}

#endif /* CONFIG_BT_RFCOMM) */

static int cmd_bredr_discoverable(int argc, char *argv[])
{
	int err;
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		err = bt_br_set_discoverable(true);
	} else if (!strcmp(action, "off")) {
		err = bt_br_set_discoverable(false);
	} else {
		return -EINVAL;
	}

	if (err) {
		printk("BR/EDR set/reset discoverable failed (err %d)\n", err);
	} else {
		printk("BR/EDR set/reset discoverable done\n");
	}

	return 0;
}

static int cmd_bredr_connectable(int argc, char *argv[])
{
	int err;
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		err = bt_br_set_connectable(true);
	} else if (!strcmp(action, "off")) {
		err = bt_br_set_connectable(false);
	} else {
		return -EINVAL;
	}

	if (err) {
		printk("BR/EDR set/rest connectable failed (err %d)\n", err);
	} else {
		printk("BR/EDR set/reset connectable done\n");
	}

	return 0;
}

static int cmd_bredr_oob(int argc, char *argv[])
{
	char addr[BT_ADDR_STR_LEN];
	struct bt_br_oob oob;
	int err;

	err = bt_br_oob_get_local(&oob);
	if (err) {
		printk("BR/EDR OOB data failed\n");
		return 0;
	}

	bt_addr_to_str(&oob.addr, addr, sizeof(addr));

	printk("BR/EDR OOB data:\n");
	printk("  addr %s\n", addr);

	return 0;
}

static int cmd_bredr_sdp_find_record(int argc, char *argv[])
{
	int err = 0, res;
	const char *action;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "HFPAG")) {
		discov = discov_hfpag;
	} else if (!strcmp(action, "A2SRC")) {
		discov = discov_a2src;
	} else {
		err = -EINVAL;
	}

	if (err) {
		printk("SDP UUID to resolve not valid (err %d)\n", err);
		printk("Supported UUID are \'HFPAG\' \'A2SRC\' only\n");
		return err;
	}

	printk("SDP UUID \'%s\' gets applied\n", action);

	res = bt_sdp_discover(default_conn, &discov);
	if (res) {
		printk("SDP discovery failed: result %d\n", res);
	} else {
		printk("SDP discovery started\n");
	}

	return 0;
}
#endif

#define HELP_NONE "[none]"
#define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"

static const struct shell_cmd bt_commands[] = {
	{ "init", cmd_init, HELP_ADDR_LE },
#if defined(CONFIG_BT_HCI)
	{ "hci-cmd", cmd_hci_cmd, "<ogf> <ocf> [data]" },
#endif
	{ "id-create", cmd_id_create, "[addr]" },
	{ "id-reset", cmd_id_reset, "<id> [addr]" },
	{ "id-delete", cmd_id_delete, "<id>" },
	{ "id-show", cmd_id_show, HELP_NONE },
	{ "id-select", cmd_id_select, "<id>" },
	{ "name", cmd_name, "[name]" },
	{ "scan", cmd_scan,
	  "<value: on, passive, off> <dup filter: dups, nodups>" },
	{ "advertise", cmd_advertise,
	  "<type: off, on, scan, nconn> <mode: discov, non_discov>" },
#if defined(CONFIG_BT_CONN)
	{ "connect", cmd_connect_le, HELP_ADDR_LE },
	{ "disconnect", cmd_disconnect, HELP_NONE },
	{ "auto-conn", cmd_auto_conn, HELP_ADDR_LE },
	{ "directed-adv", cmd_directed_adv, HELP_ADDR_LE " [mode: low]" },
	{ "select", cmd_select, HELP_ADDR_LE },
	{ "conn-update", cmd_conn_update, "<min> <max> <latency> <timeout>" },
	{ "oob", cmd_oob },
	{ "clear", cmd_clear },
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
	{ "security", cmd_security, "<security level: 0, 1, 2, 3>" },
	{ "bondable", cmd_bondable, "<bondable: on, off>" },
	{ "auth", cmd_auth,
	  "<auth method: all, input, display, yesno, confirm, none>" },
	{ "auth-cancel", cmd_auth_cancel, HELP_NONE },
	{ "auth-passkey", cmd_auth_passkey, "<passkey>" },
	{ "auth-passkey-confirm", cmd_auth_passkey_confirm, HELP_NONE },
	{ "auth-pairing-confirm", cmd_auth_pairing_confirm, HELP_NONE },
#if defined(CONFIG_BT_FIXED_PASSKEY)
	{ "fixed-passkey", cmd_fixed_passkey, "[passkey]" },
#endif<commit_msg>Bluetooth: shell: Add shell command set channel map<commit_after>/** @file
 * @brief Bluetooth shell module
 *
 * Provide some Bluetooth shell commands that can be useful to applications.
 */

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <zephyr.h>

#include <settings/settings.h>

#include <bluetooth/hci.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>

#include <shell/shell.h>

#include "bt.h"
#include "gatt.h"
#include "ll.h"

#define DEVICE_NAME		CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN		(sizeof(DEVICE_NAME) - 1)
#define CREDITS			10
#define DATA_MTU		(23 * CREDITS)
#define DATA_BREDR_MTU		48

#define BT_SHELL_MODULE "bt"

static u8_t selected_id = BT_ID_DEFAULT;

#if defined(CONFIG_BT_CONN)
struct bt_conn *default_conn;

/* Connection context for BR/EDR legacy pairing in sec mode 3 */
static struct bt_conn *pairing_conn;
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
NET_BUF_POOL_DEFINE(data_tx_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
NET_BUF_POOL_DEFINE(data_rx_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
#endif

#if defined(CONFIG_BT_BREDR)
NET_BUF_POOL_DEFINE(data_bredr_pool, 1, DATA_BREDR_MTU, BT_BUF_USER_DATA_MIN,
		    NULL);

#define SDP_CLIENT_USER_BUF_LEN		512
NET_BUF_POOL_DEFINE(sdp_client_pool, CONFIG_BT_MAX_CONN,
		    SDP_CLIENT_USER_BUF_LEN, BT_BUF_USER_DATA_MIN, NULL);
#endif /* CONFIG_BT_BREDR */

#if defined(CONFIG_BT_RFCOMM)

static struct bt_sdp_attribute spp_attrs[] = {
	BT_SDP_NEW_SERVICE,
	BT_SDP_LIST(
		BT_SDP_ATTR_SVCLASS_ID_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
			BT_SDP_ARRAY_16(BT_SDP_SERIAL_PORT_SVCLASS)
		},
		)
	),
	BT_SDP_LIST(
		BT_SDP_ATTR_PROTO_DESC_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 12),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)
			},
			)
		},
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 5),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_PROTO_RFCOMM)
			},
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UINT8),
				BT_SDP_ARRAY_8(BT_RFCOMM_CHAN_SPP)
			},
			)
		},
		)
	),
	BT_SDP_LIST(
		BT_SDP_ATTR_PROFILE_DESC_LIST,
		BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
		BT_SDP_DATA_ELEM_LIST(
		{
			BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
			BT_SDP_DATA_ELEM_LIST(
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
				BT_SDP_ARRAY_16(BT_SDP_SERIAL_PORT_SVCLASS)
			},
			{
				BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
				BT_SDP_ARRAY_16(0x0102)
			},
			)
		},
		)
	),
	BT_SDP_SERVICE_NAME("Serial Port"),
};

static struct bt_sdp_record spp_rec = BT_SDP_RECORD(spp_attrs);

#endif /* CONFIG_BT_RFCOMM */

#define NAME_LEN 30

static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;

	switch (data->type) {
	case BT_DATA_NAME_SHORTENED:
	case BT_DATA_NAME_COMPLETE:
		memcpy(name, data->data, min(data->data_len, NAME_LEN - 1));
		return false;
	default:
		return true;
	}
}

static void device_found(const bt_addr_le_t *addr, s8_t rssi, u8_t evtype,
			 struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	char name[NAME_LEN];

	(void)memset(name, 0, sizeof(name));

	bt_data_parse(buf, data_cb, name);

	bt_addr_le_to_str(addr, le_addr, sizeof(le_addr));
	printk("[DEVICE]: %s, AD evt type %u, RSSI %i %s\n", le_addr, evtype,
	       rssi, name);
}

#if !defined(CONFIG_BT_CONN)
static const char *current_prompt(void)
{
	return NULL;
}
#endif /* !CONFIG_BT_CONN */

#if defined(CONFIG_BT_CONN)
static const char *current_prompt(void)
{
	static char str[BT_ADDR_LE_STR_LEN + 2];
	static struct bt_conn_info info;

	if (!default_conn) {
		return NULL;
	}

	if (bt_conn_get_info(default_conn, &info) < 0) {
		return NULL;
	}

	if (info.type != BT_CONN_TYPE_LE) {
		return NULL;
	}

	bt_addr_le_to_str(info.le.dst, str, sizeof(str) - 2);
	strcat(str, "> ");
	return str;
}

static void conn_addr_str(struct bt_conn *conn, char *addr, size_t len)
{
	struct bt_conn_info info;

	if (bt_conn_get_info(conn, &info) < 0) {
		addr[0] = '\0';
		return;
	}

	switch (info.type) {
#if defined(CONFIG_BT_BREDR)
	case BT_CONN_TYPE_BR:
		bt_addr_to_str(info.br.dst, addr, len);
		break;
#endif
	case BT_CONN_TYPE_LE:
		bt_addr_le_to_str(bt_conn_get_dst(conn), addr, len);
		break;
	}
}

static void connected(struct bt_conn *conn, u8_t err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));

	if (err) {
		printk("Failed to connect to %s (%u)\n", addr, err);
		goto done;
	}

	printk("Connected: %s\n", addr);

	if (!default_conn) {
		default_conn = bt_conn_ref(conn);
	}

done:
	/* clear connection reference for sec mode 3 pairing */
	if (pairing_conn) {
		bt_conn_unref(pairing_conn);
		pairing_conn = NULL;
	}
}

static void disconnected(struct bt_conn *conn, u8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));
	printk("Disconnected: %s (reason %u)\n", addr, reason);

	if (default_conn == conn) {
		bt_conn_unref(default_conn);
		default_conn = NULL;
	}
}

static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
{
	printk("LE conn  param req: int (0x%04x, 0x%04x) lat %d to %d\n",
	       param->interval_min, param->interval_max, param->latency,
	       param->timeout);

	return true;
}

static void le_param_updated(struct bt_conn *conn, u16_t interval,
			     u16_t latency, u16_t timeout)
{
	printk("LE conn param updated: int 0x%04x lat %d to %d\n", interval,
	       latency, timeout);
}

#if defined(CONFIG_BT_BREDR)
static u8_t sdp_hfp_ag_user(struct bt_conn *conn,
			       struct bt_sdp_client_result *result)
{
	char addr[BT_ADDR_STR_LEN];
	u16_t param, version;
	u16_t features;
	int res;

	conn_addr_str(conn, addr, sizeof(addr));

	if (result) {
		printk("SDP HFPAG data@%p (len %u) hint %u from remote %s\n",
			result->resp_buf, result->resp_buf->len,
			result->next_record_hint, addr);

		/*
		 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
		 * get HFPAG Server Channel Number operating on RFCOMM protocol.
		 */
		res = bt_sdp_get_proto_param(result->resp_buf,
					     BT_SDP_PROTO_RFCOMM, &param);
		if (res < 0) {
			printk("Error getting Server CN, err %d\n", res);
			goto done;
		}
		printk("HFPAG Server CN param 0x%04x\n", param);

		res = bt_sdp_get_profile_version(result->resp_buf,
						 BT_SDP_HANDSFREE_SVCLASS,
						 &version);
		if (res < 0) {
			printk("Error getting profile version, err %d\n", res);
			goto done;
		}
		printk("HFP version param 0x%04x\n", version);

		/*
		 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
		 * get profile Supported Features mask.
		 */
		res = bt_sdp_get_features(result->resp_buf, &features);
		if (res < 0) {
			printk("Error getting HFPAG Features, err %d\n", res);
			goto done;
		}
		printk("HFPAG Supported Features param 0x%04x\n", features);
	} else {
		printk("No SDP HFPAG data from remote %s\n", addr);
	}
done:
	return BT_SDP_DISCOVER_UUID_CONTINUE;
}

static u8_t sdp_a2src_user(struct bt_conn *conn,
			      struct bt_sdp_client_result *result)
{
	char addr[BT_ADDR_STR_LEN];
	u16_t param, version;
	u16_t features;
	int res;

	conn_addr_str(conn, addr, sizeof(addr));

	if (result) {
		printk("SDP A2SRC data@%p (len %u) hint %u from remote %s\n",
			result->resp_buf, result->resp_buf->len,
			result->next_record_hint, addr);

		/*
		 * Focus to get BT_SDP_ATTR_PROTO_DESC_LIST attribute item to
		 * get A2SRC Server PSM Number.
		 */
		res = bt_sdp_get_proto_param(result->resp_buf,
					     BT_SDP_PROTO_L2CAP, &param);
		if (res < 0) {
			printk("A2SRC PSM Number not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC Server PSM Number param 0x%04x\n", param);

		/*
		 * Focus to get BT_SDP_ATTR_PROFILE_DESC_LIST attribute item to
		 * get profile version number.
		 */
		res = bt_sdp_get_profile_version(result->resp_buf,
						 BT_SDP_ADVANCED_AUDIO_SVCLASS,
						 &version);
		if (res < 0) {
			printk("A2SRC version not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC version param 0x%04x\n", version);

		/*
		 * Focus to get BT_SDP_ATTR_SUPPORTED_FEATURES attribute item to
		 * get profile supported features mask.
		 */
		res = bt_sdp_get_features(result->resp_buf, &features);
		if (res < 0) {
			printk("A2SRC Features not found, err %d\n", res);
			goto done;
		}
		printk("A2SRC Supported Features param 0x%04x\n", features);
	} else {
		printk("No SDP A2SRC data from remote %s\n", addr);
	}
done:
	return BT_SDP_DISCOVER_UUID_CONTINUE;
}

static struct bt_sdp_discover_params discov_hfpag = {
	.uuid = BT_UUID_DECLARE_16(BT_SDP_HANDSFREE_AGW_SVCLASS),
	.func = sdp_hfp_ag_user,
	.pool = &sdp_client_pool,
};

static struct bt_sdp_discover_params discov_a2src = {
	.uuid = BT_UUID_DECLARE_16(BT_SDP_AUDIO_SOURCE_SVCLASS),
	.func = sdp_a2src_user,
	.pool = &sdp_client_pool,
};

static struct bt_sdp_discover_params discov;
#endif

#if defined(CONFIG_BT_SMP)
static void identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa,
			      const bt_addr_le_t *identity)
{
	char addr_identity[BT_ADDR_LE_STR_LEN];
	char addr_rpa[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(identity, addr_identity, sizeof(addr_identity));
	bt_addr_le_to_str(rpa, addr_rpa, sizeof(addr_rpa));

	printk("Identity resolved %s -> %s\n", addr_rpa, addr_identity);
}
#endif

#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
static void security_changed(struct bt_conn *conn, bt_security_t level)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));
	printk("Security changed: %s level %u\n", addr, level);
}
#endif

static struct bt_conn_cb conn_callbacks = {
	.connected = connected,
	.disconnected = disconnected,
	.le_param_req = le_param_req,
	.le_param_updated = le_param_updated,
#if defined(CONFIG_BT_SMP)
	.identity_resolved = identity_resolved,
#endif
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
	.security_changed = security_changed,
#endif
};
#endif /* CONFIG_BT_CONN */

static int char2hex(const char *c, u8_t *x)
{
	if (*c >= '0' && *c <= '9') {
		*x = *c - '0';
	} else if (*c >= 'a' && *c <= 'f') {
		*x = *c - 'a' + 10;
	} else if (*c >= 'A' && *c <= 'F') {
		*x = *c - 'A' + 10;
	} else {
		return -EINVAL;
	}

	return 0;
}

static int hexstr2array(const char *str, u8_t *array, u8_t size)
{
	int i, j;
	u8_t tmp;

	if (strlen(str) != ((size * 2) + (size - 1))) {
		return -EINVAL;
	}

	for (i = size - 1, j = 1; *str != '\0'; str++, j++) {
		if (!(j % 3) && (*str != ':')) {
			return -EINVAL;
		} else if (*str == ':') {
			i--;
			continue;
		}

		array[i] = array[i] << 4;

		if (char2hex(str, &tmp) < 0) {
			return -EINVAL;
		}

		array[i] |= tmp;
	}

	return 0;
}

static int str2bt_addr(const char *str, bt_addr_t *addr)
{
	return hexstr2array(str, addr->val, 6);
}

static int str2bt_addr_le(const char *str, const char *type, bt_addr_le_t *addr)
{
	int err;

	err = str2bt_addr(str, &addr->a);
	if (err < 0) {
		return err;
	}

	if (!strcmp(type, "public") || !strcmp(type, "(public)")) {
		addr->type = BT_ADDR_LE_PUBLIC;
	} else if (!strcmp(type, "random") || !strcmp(type, "(random)")) {
		addr->type = BT_ADDR_LE_RANDOM;
	} else {
		return -EINVAL;
	}

	return 0;
}

static void bt_ready(int err)
{
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

#if defined(CONFIG_BT_CONN)
	default_conn = NULL;

	bt_conn_cb_register(&conn_callbacks);
#endif /* CONFIG_BT_CONN */
}

static int cmd_init(int argc, char *argv[])
{
	int err;

	err = bt_enable(bt_ready);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
	}

	return 0;
}

#if defined(CONFIG_BT_HCI) || defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
static void hexdump(const u8_t *data, size_t len)
{
	int n = 0;

	while (len--) {
		if (n % 16 == 0) {
			printk("%08X ", n);
		}

		printk("%02X ", *data++);

		n++;
		if (n % 8 == 0) {
			if (n % 16 == 0) {
				printk("\n");
			} else {
				printk(" ");
			}
		}
	}

	if (n % 16) {
		printk("\n");
	}
}
#endif /* CONFIG_BT_HCI || CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */

#if defined(CONFIG_BT_HCI)
static int cmd_hci_cmd(int argc, char *argv[])
{
	u8_t ogf;
	u16_t ocf;
	struct net_buf *buf = NULL, *rsp;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	ogf = strtoul(argv[1], NULL, 16);
	ocf = strtoul(argv[2], NULL, 16);

	if (argc > 3) {
		int i;

		buf = bt_hci_cmd_create(BT_OP(ogf, ocf), argc - 3);

		for (i = 3; i < argc; i++) {
			net_buf_add_u8(buf, strtoul(argv[i], NULL, 16));
		}
	}

	err = bt_hci_cmd_send_sync(BT_OP(ogf, ocf), buf, &rsp);
	if (err) {
		printk("HCI command failed (err %d)\n", err);
	} else {
		hexdump(rsp->data, rsp->len);
		net_buf_unref(rsp);
	}

	return 0;
}
#endif /* CONFIG_BT_HCI */

static int cmd_name(int argc, char *argv[])
{
	int err;

	if (argc < 2) {
		printk("Bluetooth Local Name: %s\n", bt_get_name());
		return 0;
	}

	err = bt_set_name(argv[1]);
	if (err) {
		printk("Unable to set name %s (err %d)", argv[1], err);
	}

	return 0;
}

static int cmd_id_create(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc > 1) {
		err = str2bt_addr_le(argv[1], "random", &addr);
		if (err) {
			printk("Invalid address\n");
			return err;
		}
	} else {
		bt_addr_le_copy(&addr, BT_ADDR_LE_ANY);
	}

	err = bt_id_create(&addr, NULL);
	if (err < 0) {
		printk("Creating new ID failed (err %d)\n", err);
		return 0;
	}

	printk("New identity (%d) created: %s\n", err, bt_addr_le_str(&addr));

	return 0;
}

static int cmd_id_reset(int argc, char *argv[])
{
	bt_addr_le_t addr;
	u8_t id;
	int err;

	if (argc < 2) {
		printk("Identity identifier not specified\n");
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	if (argc > 2) {
		err = str2bt_addr_le(argv[2], "random", &addr);
		if (err) {
			printk("Invalid address\n");
			return err;
		}
	} else {
		bt_addr_le_copy(&addr, BT_ADDR_LE_ANY);
	}

	err = bt_id_reset(id, &addr, NULL);
	if (err < 0) {
		printk("Resetting ID %u failed (err %d)\n", id, err);
		return 0;
	}

	printk("Identity %u reset: %s\n", id, bt_addr_le_str(&addr));

	return 0;
}

static int cmd_id_delete(int argc, char *argv[])
{
	u8_t id;
	int err;

	if (argc < 2) {
		printk("Identity identifier not specified\n");
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	err = bt_id_delete(id);
	if (err < 0) {
		printk("Deleting ID %u failed (err %d)\n", id, err);
		return 0;
	}

	printk("Identity %u deleted\n", id);

	return 0;
}

static int cmd_id_show(int argc, char *argv[])
{
	bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
	size_t i, count = CONFIG_BT_ID_MAX;

	bt_id_get(addrs, &count);

	for (i = 0; i < count; i++) {
		printk("%s%zu: %s\n", i == selected_id ? "*" : " ", i,
		       bt_addr_le_str(&addrs[i]));
	}

	return 0;
}

static int cmd_id_select(int argc, char *argv[])
{
	bt_addr_le_t addrs[CONFIG_BT_ID_MAX];
	size_t count = CONFIG_BT_ID_MAX;
	u8_t id;

	if (argc < 2) {
		return -EINVAL;
	}

	id = strtol(argv[1], NULL, 10);

	bt_id_get(addrs, &count);
	if (count <= id) {
		printk("Invalid identity\n");
		return 0;
	}

	printk("Selected identity: %s\n", bt_addr_le_str(&addrs[id]));
	selected_id = id;

	return 0;
}

static void cmd_active_scan_on(int dups)
{
	int err;
	struct bt_le_scan_param param = {
			.type       = BT_HCI_LE_SCAN_ACTIVE,
			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE,
			.interval   = BT_GAP_SCAN_FAST_INTERVAL,
			.window     = BT_GAP_SCAN_FAST_WINDOW };

	if (dups >= 0) {
		param.filter_dup = dups;
	}

	err = bt_le_scan_start(&param, device_found);
	if (err) {
		printk("Bluetooth set active scan failed (err %d)\n", err);
	} else {
		printk("Bluetooth active scan enabled\n");
	}
}

static void cmd_passive_scan_on(int dups)
{
	struct bt_le_scan_param param = {
			.type       = BT_HCI_LE_SCAN_PASSIVE,
			.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE,
			.interval   = 0x10,
			.window     = 0x10 };
	int err;

	if (dups >= 0) {
		param.filter_dup = dups;
	}

	err = bt_le_scan_start(&param, device_found);
	if (err) {
		printk("Bluetooth set passive scan failed (err %d)\n", err);
	} else {
		printk("Bluetooth passive scan enabled\n");
	}
}

static void cmd_scan_off(void)
{
	int err;

	err = bt_le_scan_stop();
	if (err) {
		printk("Stopping scanning failed (err %d)\n", err);
	} else {
		printk("Scan successfully stopped\n");
	}
}

static int cmd_scan(int argc, char *argv[])
{
	const char *action;
	int dups = -1;

	if (argc < 2) {
		return -EINVAL;
	}

	/* Parse duplicate filtering data */
	if (argc >= 3) {
		const char *dup_filter = argv[2];

		if (!strcmp(dup_filter, "dups")) {
			dups = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE;
		} else if (!strcmp(dup_filter, "nodups")) {
			dups = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE;
		} else {
			return -EINVAL;
		}
	}

	action = argv[1];
	if (!strcmp(action, "on")) {
		cmd_active_scan_on(dups);
	} else if (!strcmp(action, "off")) {
		cmd_scan_off();
	} else if (!strcmp(action, "passive")) {
		cmd_passive_scan_on(dups);
	} else {
		return -EINVAL;
	}

	return 0;
}

static const struct bt_data ad_discov[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
};

static int cmd_advertise(int argc, char *argv[])
{
	struct bt_le_adv_param param;
	const struct bt_data *ad, *scan_rsp;
	size_t ad_len, scan_rsp_len;
	int err;

	if (argc < 2) {
		goto fail;
	}

	if (!strcmp(argv[1], "off")) {
		if (bt_le_adv_stop() < 0) {
			printk("Failed to stop advertising\n");
		} else {
			printk("Advertising stopped\n");
		}

		return 0;
	}

	param.id = selected_id;
	param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
	param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;

	if (!strcmp(argv[1], "on")) {
		param.options = (BT_LE_ADV_OPT_CONNECTABLE |
				 BT_LE_ADV_OPT_USE_NAME);
	} else if (!strcmp(argv[1], "scan")) {
		param.options = BT_LE_ADV_OPT_USE_NAME;
	} else if (!strcmp(argv[1], "nconn")) {
		param.options = 0;
		scan_rsp = NULL;
		scan_rsp_len = 0;
	} else {
		goto fail;
	}

	/* Parse advertisement data */
	if (argc >= 3) {
		const char *mode = argv[2];

		if (!strcmp(mode, "discov")) {
			ad = ad_discov;
			ad_len = ARRAY_SIZE(ad_discov);
		} else if (!strcmp(mode, "non_discov")) {
			ad = NULL;
			ad_len = 0;
		} else {
			goto fail;
		}
	} else {
		ad = ad_discov;
		ad_len = ARRAY_SIZE(ad_discov);
	}

	err = bt_le_adv_start(&param, ad, ad_len, scan_rsp, scan_rsp_len);
	if (err < 0) {
		printk("Failed to start advertising (err %d)\n", err);
	} else {
		printk("Advertising started\n");
	}

	return 0;

fail:
	return -EINVAL;
}

#if defined(CONFIG_BT_CONN)
static int cmd_connect_le(int argc, char *argv[])
{
	int err;
	bt_addr_le_t addr;
	struct bt_conn *conn;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_create_le(&addr, BT_LE_CONN_PARAM_DEFAULT);

	if (!conn) {
		printk("Connection failed\n");
	} else {

		printk("Connection pending\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static int cmd_disconnect(int argc, char *argv[])
{
	struct bt_conn *conn;
	int err;

	if (default_conn && argc < 3) {
		conn = bt_conn_ref(default_conn);
	} else {
		bt_addr_le_t addr;

		if (argc < 3) {
			return -EINVAL;
		}

		err = str2bt_addr_le(argv[1], argv[2], &addr);
		if (err) {
			printk("Invalid peer address (err %d)\n", err);
			return 0;
		}

		conn = bt_conn_lookup_addr_le(selected_id, &addr);
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
	if (err) {
		printk("Disconnection failed (err %d)\n", err);
	}

	bt_conn_unref(conn);

	return 0;
}

static int cmd_auto_conn(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	if (argc < 4) {
		bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
	} else if (!strcmp(argv[3], "on")) {
		bt_le_set_auto_conn(&addr, BT_LE_CONN_PARAM_DEFAULT);
	} else if (!strcmp(argv[3], "off")) {
		bt_le_set_auto_conn(&addr, NULL);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int cmd_directed_adv(int argc, char *argv[])
{
	int err;
	bt_addr_le_t addr;
	struct bt_conn *conn;
	struct bt_le_adv_param *param = BT_LE_ADV_CONN_DIR;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	if (argc > 3) {
		if (!strcmp(argv[3], "low")) {
			param = BT_LE_ADV_CONN_DIR_LOW_DUTY;
		} else {
			return -EINVAL;
		}
	}

	conn = bt_conn_create_slave_le(&addr, param);
	if (!conn) {
		printk("Failed to start directed advertising\n");
	} else {
		printk("Started directed advertising\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static int cmd_select(int argc, char *argv[])
{
	struct bt_conn *conn;
	bt_addr_le_t addr;
	int err;

	if (argc < 3) {
		return -EINVAL;
	}

	err = str2bt_addr_le(argv[1], argv[2], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &addr);
	if (!conn) {
		printk("No matching connection found\n");
		return 0;
	}

	if (default_conn) {
		bt_conn_unref(default_conn);
	}

	default_conn = conn;

	return 0;
}

static int cmd_conn_update(int argc, char *argv[])
{
	struct bt_le_conn_param param;
	int err;

	if (argc < 5) {
		return -EINVAL;
	}

	param.interval_min = strtoul(argv[1], NULL, 16);
	param.interval_max = strtoul(argv[2], NULL, 16);
	param.latency = strtoul(argv[3], NULL, 16);
	param.timeout = strtoul(argv[4], NULL, 16);

	err = bt_conn_le_param_update(default_conn, &param);
	if (err) {
		printk("conn update failed (err %d).\n", err);
	} else {
		printk("conn update initiated.\n");
	}

	return 0;
}

static int cmd_oob(int argc, char *argv[])
{
	char addr[BT_ADDR_LE_STR_LEN];
	struct bt_le_oob oob;
	int err;

	err = bt_le_oob_get_local(selected_id, &oob);
	if (err) {
		printk("OOB data failed\n");
		return 0;
	}

	bt_addr_le_to_str(&oob.addr, addr, sizeof(addr));

	printk("OOB data:\n");
	printk("  addr %s\n", addr);

	return 0;
}

static int cmd_clear(int argc, char *argv[])
{
	bt_addr_le_t addr;
	int err;

	if (argc < 2) {
		printk("Specify remote address or \"all\"\n");
		return 0;
	}

	if (strcmp(argv[1], "all") == 0) {
		err = bt_unpair(selected_id, NULL);
		if (err) {
			printk("Failed to clear pairings (err %d)\n", err);
		} else {
			printk("Pairings successfully cleared\n");
		}

		return 0;
	}

	if (argc < 3) {
#if defined(CONFIG_BT_BREDR)
		addr.type = BT_ADDR_LE_PUBLIC;
		err = str2bt_addr(argv[1], &addr.a);
#else
		printk("Both address and address type needed\n");
		return 0;
#endif
	} else {
		err = str2bt_addr_le(argv[1], argv[2], &addr);
	}

	if (err) {
		printk("Invalid address\n");
		return 0;
	}

	err = bt_unpair(selected_id, &addr);
	if (err) {
		printk("Failed to clear pairing (err %d)\n", err);
	} else {
		printk("Pairing successfully cleared\n");
	}

	return 0;
}

static int cmd_chan_map(int argc, char *argv[])
{
	u8_t chan_map[5];
	int err;

	if (argc != 2) {
		return -EINVAL;
	}

	err = hexstr2array(argv[1], chan_map, 5);
	if (err) {
		printk("Invalid channel map\n");
		return err;
	}

	err = bt_le_set_chan_map(chan_map);
	if (err) {
		printk("Failed to set channel map (err %d)\n", err);
	} else {
		printk("Channel map set\n");
	}

	return 0;
}
#endif /* CONFIG_BT_CONN */

#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
static int cmd_security(int argc, char *argv[])
{
	int err, sec;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	sec = *argv[1] - '0';

	err = bt_conn_security(default_conn, sec);
	if (err) {
		printk("Setting security failed (err %d)\n", err);
	}

	return 0;
}

static int cmd_bondable(int argc, char *argv[])
{
	const char *bondable;

	if (argc < 2) {
		return -EINVAL;
	}

	bondable = argv[1];
	if (!strcmp(bondable, "on")) {
		bt_set_bondable(true);
	} else if (!strcmp(bondable, "off")) {
		bt_set_bondable(false);
	} else {
		return -EINVAL;
	}

	return 0;
}

static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];
	char passkey_str[7];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	snprintk(passkey_str, 7, "%06u", passkey);

	printk("Passkey for %s: %s\n", addr, passkey_str);
}

static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
{
	char addr[BT_ADDR_LE_STR_LEN];
	char passkey_str[7];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	snprintk(passkey_str, 7, "%06u", passkey);

	printk("Confirm passkey for %s: %s\n", addr, passkey_str);
}

static void auth_passkey_entry(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Enter passkey for %s\n", addr);
}

static void auth_cancel(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	conn_addr_str(conn, addr, sizeof(addr));

	printk("Pairing cancelled: %s\n", addr);

	/* clear connection reference for sec mode 3 pairing */
	if (pairing_conn) {
		bt_conn_unref(pairing_conn);
		pairing_conn = NULL;
	}
}

static void auth_pairing_confirm(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Confirm pairing for %s\n", addr);
}

static void auth_pairing_complete(struct bt_conn *conn, bool bonded)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("%s with %s\n", bonded ? "Bonded" : "Paired",  addr);
}

static void auth_pairing_failed(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	printk("Pairing failed with %s\n", addr);
}

#if defined(CONFIG_BT_BREDR)
static void auth_pincode_entry(struct bt_conn *conn, bool highsec)
{
	char addr[BT_ADDR_STR_LEN];
	struct bt_conn_info info;

	if (bt_conn_get_info(conn, &info) < 0) {
		return;
	}

	if (info.type != BT_CONN_TYPE_BR) {
		return;
	}

	bt_addr_to_str(info.br.dst, addr, sizeof(addr));

	if (highsec) {
		printk("Enter 16 digits wide PIN code for %s\n", addr);
	} else {
		printk("Enter PIN code for %s\n", addr);
	}

	/*
	 * Save connection info since in security mode 3 (link level enforced
	 * security) PIN request callback is called before connected callback
	 */
	if (!default_conn && !pairing_conn) {
		pairing_conn = bt_conn_ref(conn);
	}
}
#endif

static struct bt_conn_auth_cb auth_cb_display = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = NULL,
	.passkey_confirm = NULL,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_display_yes_no = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = NULL,
	.passkey_confirm = auth_passkey_confirm,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_input = {
	.passkey_display = NULL,
	.passkey_entry = auth_passkey_entry,
	.passkey_confirm = NULL,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_confirm = {
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static struct bt_conn_auth_cb auth_cb_all = {
	.passkey_display = auth_passkey_display,
	.passkey_entry = auth_passkey_entry,
	.passkey_confirm = auth_passkey_confirm,
#if defined(CONFIG_BT_BREDR)
	.pincode_entry = auth_pincode_entry,
#endif
	.cancel = auth_cancel,
	.pairing_confirm = auth_pairing_confirm,
	.pairing_failed = auth_pairing_failed,
	.pairing_complete = auth_pairing_complete,
};

static int cmd_auth(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (!strcmp(argv[1], "all")) {
		bt_conn_auth_cb_register(&auth_cb_all);
	} else if (!strcmp(argv[1], "input")) {
		bt_conn_auth_cb_register(&auth_cb_input);
	} else if (!strcmp(argv[1], "display")) {
		bt_conn_auth_cb_register(&auth_cb_display);
	} else if (!strcmp(argv[1], "yesno")) {
		bt_conn_auth_cb_register(&auth_cb_display_yes_no);
	} else if (!strcmp(argv[1], "confirm")) {
		bt_conn_auth_cb_register(&auth_cb_confirm);
	} else if (!strcmp(argv[1], "none")) {
		bt_conn_auth_cb_register(NULL);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int cmd_auth_cancel(int argc, char *argv[])
{
	struct bt_conn *conn;

	if (default_conn) {
		conn = default_conn;
	} else if (pairing_conn) {
		conn = pairing_conn;
	} else {
		conn = NULL;
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_cancel(conn);

	return 0;
}

static int cmd_auth_passkey_confirm(int argc, char *argv[])
{
	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_passkey_confirm(default_conn);

	return 0;
}

static int cmd_auth_pairing_confirm(int argc, char *argv[])
{
	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	bt_conn_auth_pairing_confirm(default_conn);

	return 0;
}

#if defined(CONFIG_BT_FIXED_PASSKEY)
static int cmd_fixed_passkey(int argc, char *argv[])
{
	unsigned int passkey;
	int err;

	if (argc < 2) {
		bt_passkey_set(BT_PASSKEY_INVALID);
		printk("Fixed passkey cleared\n");
		return 0;
	}

	passkey = atoi(argv[1]);
	if (passkey > 999999) {
		printk("Passkey should be between 0-999999\n");
		return 0;
	}

	err = bt_passkey_set(passkey);
	if (err) {
		printk("Setting fixed passkey failed (err %d)\n", err);
	}

	return 0;
}
#endif

static int cmd_auth_passkey(int argc, char *argv[])
{
	unsigned int passkey;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	passkey = atoi(argv[1]);
	if (passkey > 999999) {
		printk("Passkey should be between 0-999999\n");
		return 0;
	}

	bt_conn_auth_passkey_entry(default_conn, passkey);

	return 0;
}
#endif /* CONFIG_BT_SMP) || CONFIG_BT_BREDR */

#if defined(CONFIG_BT_BREDR)
static int cmd_auth_pincode(int argc, char *argv[])
{
	struct bt_conn *conn;
	u8_t max = 16;

	if (default_conn) {
		conn = default_conn;
	} else if (pairing_conn) {
		conn = pairing_conn;
	} else {
		conn = NULL;
	}

	if (!conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	if (strlen(argv[1]) > max) {
		printk("PIN code value invalid - enter max %u digits\n", max);
		return 0;
	}

	printk("PIN code \"%s\" applied\n", argv[1]);

	bt_conn_auth_pincode_entry(conn, argv[1]);

	return 0;
}

static int cmd_connect_bredr(int argc, char *argv[])
{
	struct bt_conn *conn;
	bt_addr_t addr;
	int err;

	if (argc < 2) {
		return -EINVAL;
	}

	err = str2bt_addr(argv[1], &addr);
	if (err) {
		printk("Invalid peer address (err %d)\n", err);
		return 0;
	}

	conn = bt_conn_create_br(&addr, BT_BR_CONN_PARAM_DEFAULT);
	if (!conn) {
		printk("Connection failed\n");
	} else {

		printk("Connection pending\n");

		/* unref connection obj in advance as app user */
		bt_conn_unref(conn);
	}

	return 0;
}

static void br_device_found(const bt_addr_t *addr, s8_t rssi,
				  const u8_t cod[3], const u8_t eir[240])
{
	char br_addr[BT_ADDR_STR_LEN];
	char name[239];
	int len = 240;

	(void)memset(name, 0, sizeof(name));

	while (len) {
		if (len < 2) {
			break;
		};

		/* Look for early termination */
		if (!eir[0]) {
			break;
		}

		/* Check if field length is correct */
		if (eir[0] > len - 1) {
			break;
		}

		switch (eir[1]) {
		case BT_DATA_NAME_SHORTENED:
		case BT_DATA_NAME_COMPLETE:
			if (eir[0] > sizeof(name) - 1) {
				memcpy(name, &eir[2], sizeof(name) - 1);
			} else {
				memcpy(name, &eir[2], eir[0] - 1);
			}
			break;
		default:
			break;
		}

		/* Parse next AD Structure */
		len -= eir[0] + 1;
		eir += eir[0] + 1;
	}

	bt_addr_to_str(addr, br_addr, sizeof(br_addr));

	printk("[DEVICE]: %s, RSSI %i %s\n", br_addr, rssi, name);
}

static struct bt_br_discovery_result br_discovery_results[5];

static void br_discovery_complete(struct bt_br_discovery_result *results,
				  size_t count)
{
	size_t i;

	printk("BR/EDR discovery complete\n");

	for (i = 0; i < count; i++) {
		br_device_found(&results[i].addr, results[i].rssi,
				results[i].cod, results[i].eir);
	}
}

static int cmd_bredr_discovery(int argc, char *argv[])
{
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];
	if (!strcmp(action, "on")) {
		struct bt_br_discovery_param param;

		param.limited = false;
		param.length = 8;

		if (argc > 2) {
			param.length = atoi(argv[2]);
		}

		if (argc > 3 && !strcmp(argv[3], "limited")) {
			param.limited = true;
		}

		if (bt_br_discovery_start(&param, br_discovery_results,
					  ARRAY_SIZE(br_discovery_results),
					  br_discovery_complete) < 0) {
			printk("Failed to start discovery\n");
			return 0;
		}

		printk("Discovery started\n");
	} else if (!strcmp(action, "off")) {
		if (bt_br_discovery_stop()) {
			printk("Failed to stop discovery\n");
			return 0;
		}

		printk("Discovery stopped\n");
	} else {
		return -EINVAL;
	}

	return 0;
}

#endif /* CONFIG_BT_BREDR */

#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
static u32_t l2cap_rate;

static void l2cap_recv_metrics(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	static u32_t len;
	static u32_t cycle_stamp;
	u32_t delta;

	delta = k_cycle_get_32() - cycle_stamp;
	delta = SYS_CLOCK_HW_CYCLES_TO_NS(delta);

	/* if last data rx-ed was greater than 1 second in the past,
	 * reset the metrics.
	 */
	if (delta > 1000000000) {
		len = 0;
		l2cap_rate = 0;
		cycle_stamp = k_cycle_get_32();
	} else {
		len += buf->len;
		l2cap_rate = ((u64_t)len << 3) * 1000000000 / delta;
	}
}

static void l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	printk("Incoming data channel %p len %u\n", chan, buf->len);

	if (buf->len) {
		hexdump(buf->data, buf->len);
	}
}

static void l2cap_connected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p connected\n", chan);
}

static void l2cap_disconnected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p disconnected\n", chan);
}

static struct net_buf *l2cap_alloc_buf(struct bt_l2cap_chan *chan)
{
	/* print if metrics is disabled */
	if (chan->ops->recv != l2cap_recv_metrics) {
		printk("Channel %p requires buffer\n", chan);
	}

	return net_buf_alloc(&data_rx_pool, K_FOREVER);
}

static struct bt_l2cap_chan_ops l2cap_ops = {
	.alloc_buf	= l2cap_alloc_buf,
	.recv		= l2cap_recv,
	.connected	= l2cap_connected,
	.disconnected	= l2cap_disconnected,
};

static struct bt_l2cap_le_chan l2cap_chan = {
	.chan.ops	= &l2cap_ops,
	.rx.mtu		= DATA_MTU,
};

static int l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
	printk("Incoming conn %p\n", conn);

	if (l2cap_chan.chan.conn) {
		printk("No channels available\n");
		return -ENOMEM;
	}

	*chan = &l2cap_chan.chan;

	return 0;
}

static struct bt_l2cap_server server = {
	.accept		= l2cap_accept,
};

static int cmd_l2cap_register(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (server.psm) {
		printk("Already registered\n");
		return 0;
	}

	server.psm = strtoul(argv[1], NULL, 16);

	if (argc > 2) {
		server.sec_level = strtoul(argv[2], NULL, 10);
	}

	if (bt_l2cap_server_register(&server) < 0) {
		printk("Unable to register psm\n");
		server.psm = 0;
	} else {
		printk("L2CAP psm %u sec_level %u registered\n", server.psm,
		       server.sec_level);
	}

	return 0;
}

static int cmd_l2cap_connect(int argc, char *argv[])
{
	u16_t psm;
	int err;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	if (l2cap_chan.chan.conn) {
		printk("Channel already in use\n");
		return -EINVAL;
	}

	psm = strtoul(argv[1], NULL, 16);

	err = bt_l2cap_chan_connect(default_conn, &l2cap_chan.chan, psm);
	if (err < 0) {
		printk("Unable to connect to psm %u (err %u)\n", psm, err);
	} else {
		printk("L2CAP connection pending\n");
	}

	return 0;
}

static int cmd_l2cap_disconnect(int argc, char *argv[])
{
	int err;

	err = bt_l2cap_chan_disconnect(&l2cap_chan.chan);
	if (err) {
		printk("Unable to disconnect: %u\n", -err);
	}

	return 0;
}

static int cmd_l2cap_send(int argc, char *argv[])
{
	static u8_t buf_data[DATA_MTU] = { [0 ... (DATA_MTU - 1)] = 0xff };
	int ret, len, count = 1;
	struct net_buf *buf;

	if (argc > 1) {
		count = strtoul(argv[1], NULL, 10);
	}

	len = min(l2cap_chan.tx.mtu, DATA_MTU - BT_L2CAP_CHAN_SEND_RESERVE);

	while (count--) {
		buf = net_buf_alloc(&data_tx_pool, K_FOREVER);
		net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE);

		net_buf_add_mem(buf, buf_data, len);
		ret = bt_l2cap_chan_send(&l2cap_chan.chan, buf);
		if (ret < 0) {
			printk("Unable to send: %d\n", -ret);
			net_buf_unref(buf);
			break;
		}
	}

	return 0;
}

static int cmd_l2cap_metrics(int argc, char *argv[])
{
	const char *action;

	if (argc < 2) {
		printk("l2cap rate: %u bps.\n", l2cap_rate);

		return 0;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		l2cap_ops.recv = l2cap_recv_metrics;
	} else if (!strcmp(action, "off")) {
		l2cap_ops.recv = l2cap_recv;
	} else {
		return -EINVAL;
	}

	printk("l2cap metrics %s.\n", action);

	return 0;
}

#endif

#if defined(CONFIG_BT_BREDR)
static void l2cap_bredr_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
{
	printk("Incoming data channel %p len %u\n", chan, buf->len);
}

static void l2cap_bredr_connected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p connected\n", chan);
}

static void l2cap_bredr_disconnected(struct bt_l2cap_chan *chan)
{
	printk("Channel %p disconnected\n", chan);
}

static struct net_buf *l2cap_bredr_alloc_buf(struct bt_l2cap_chan *chan)
{
	printk("Channel %p requires buffer\n", chan);

	return net_buf_alloc(&data_bredr_pool, K_FOREVER);
}

static struct bt_l2cap_chan_ops l2cap_bredr_ops = {
	.alloc_buf	= l2cap_bredr_alloc_buf,
	.recv		= l2cap_bredr_recv,
	.connected	= l2cap_bredr_connected,
	.disconnected	= l2cap_bredr_disconnected,
};

static struct bt_l2cap_br_chan l2cap_bredr_chan = {
	.chan.ops	= &l2cap_bredr_ops,
	 /* Set for now min. MTU */
	.rx.mtu		= 48,
};

static int l2cap_bredr_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
{
	printk("Incoming BR/EDR conn %p\n", conn);

	if (l2cap_bredr_chan.chan.conn) {
		printk("No channels available");
		return -ENOMEM;
	}

	*chan = &l2cap_bredr_chan.chan;

	return 0;
}

static struct bt_l2cap_server br_server = {
	.accept = l2cap_bredr_accept,
};

static int cmd_bredr_l2cap_register(int argc, char *argv[])
{
	if (argc < 2) {
		return -EINVAL;
	}

	if (br_server.psm) {
		printk("Already registered\n");
		return 0;
	}

	br_server.psm = strtoul(argv[1], NULL, 16);

	if (bt_l2cap_br_server_register(&br_server) < 0) {
		printk("Unable to register psm\n");
		br_server.psm = 0;
	} else {
		printk("L2CAP psm %u registered\n", br_server.psm);
	}

	return 0;
}

#if defined(CONFIG_BT_RFCOMM)
static void rfcomm_bredr_recv(struct bt_rfcomm_dlc *dlci, struct net_buf *buf)
{
	printk("Incoming data dlc %p len %u\n", dlci, buf->len);
}

static void rfcomm_bredr_connected(struct bt_rfcomm_dlc *dlci)
{
	printk("Dlc %p connected\n", dlci);
}

static void rfcomm_bredr_disconnected(struct bt_rfcomm_dlc *dlci)
{
	printk("Dlc %p disconnected\n", dlci);
}

static struct bt_rfcomm_dlc_ops rfcomm_bredr_ops = {
	.recv		= rfcomm_bredr_recv,
	.connected	= rfcomm_bredr_connected,
	.disconnected	= rfcomm_bredr_disconnected,
};

static struct bt_rfcomm_dlc rfcomm_dlc = {
	.ops = &rfcomm_bredr_ops,
	.mtu = 30,
};

static int rfcomm_bredr_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc)
{
	printk("Incoming RFCOMM conn %p\n", conn);

	if (rfcomm_dlc.session) {
		printk("No channels available");
		return -ENOMEM;
	}

	*dlc = &rfcomm_dlc;

	return 0;
}

struct bt_rfcomm_server rfcomm_server = {
	.accept = &rfcomm_bredr_accept,
};

static int cmd_bredr_rfcomm_register(int argc, char *argv[])
{
	int ret;

	if (rfcomm_server.channel) {
		printk("Already registered\n");
		return 0;
	}

	rfcomm_server.channel = BT_RFCOMM_CHAN_SPP;

	ret = bt_rfcomm_server_register(&rfcomm_server);
	if (ret < 0) {
		printk("Unable to register channel %x\n", ret);
		rfcomm_server.channel = 0;
	} else {
		printk("RFCOMM channel %u registered\n", rfcomm_server.channel);
		bt_sdp_register_service(&spp_rec);
	}

	return 0;
}

static int cmd_rfcomm_connect(int argc, char *argv[])
{
	u8_t channel;
	int err;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	channel = strtoul(argv[1], NULL, 16);

	err = bt_rfcomm_dlc_connect(default_conn, &rfcomm_dlc, channel);
	if (err < 0) {
		printk("Unable to connect to channel %d (err %u)\n",
		       channel, err);
	} else {
		printk("RFCOMM connection pending\n");
	}

	return 0;
}

static int cmd_rfcomm_send(int argc, char *argv[])
{
	u8_t buf_data[DATA_BREDR_MTU] = { [0 ... (DATA_BREDR_MTU - 1)] =
					    0xff };
	int ret, len, count = 1;
	struct net_buf *buf;

	if (argc > 1) {
		count = strtoul(argv[1], NULL, 10);
	}

	while (count--) {
		buf = bt_rfcomm_create_pdu(&data_bredr_pool);
		/* Should reserve one byte in tail for FCS */
		len = min(rfcomm_dlc.mtu, net_buf_tailroom(buf) - 1);

		net_buf_add_mem(buf, buf_data, len);
		ret = bt_rfcomm_dlc_send(&rfcomm_dlc, buf);
		if (ret < 0) {
			printk("Unable to send: %d\n", -ret);
			net_buf_unref(buf);
			break;
		}
	}

	return 0;
}

static int cmd_rfcomm_disconnect(int argc, char *argv[])
{
	int err;

	err = bt_rfcomm_dlc_disconnect(&rfcomm_dlc);
	if (err) {
		printk("Unable to disconnect: %u\n", -err);
	}

	return 0;
}

#endif /* CONFIG_BT_RFCOMM) */

static int cmd_bredr_discoverable(int argc, char *argv[])
{
	int err;
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		err = bt_br_set_discoverable(true);
	} else if (!strcmp(action, "off")) {
		err = bt_br_set_discoverable(false);
	} else {
		return -EINVAL;
	}

	if (err) {
		printk("BR/EDR set/reset discoverable failed (err %d)\n", err);
	} else {
		printk("BR/EDR set/reset discoverable done\n");
	}

	return 0;
}

static int cmd_bredr_connectable(int argc, char *argv[])
{
	int err;
	const char *action;

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "on")) {
		err = bt_br_set_connectable(true);
	} else if (!strcmp(action, "off")) {
		err = bt_br_set_connectable(false);
	} else {
		return -EINVAL;
	}

	if (err) {
		printk("BR/EDR set/rest connectable failed (err %d)\n", err);
	} else {
		printk("BR/EDR set/reset connectable done\n");
	}

	return 0;
}

static int cmd_bredr_oob(int argc, char *argv[])
{
	char addr[BT_ADDR_STR_LEN];
	struct bt_br_oob oob;
	int err;

	err = bt_br_oob_get_local(&oob);
	if (err) {
		printk("BR/EDR OOB data failed\n");
		return 0;
	}

	bt_addr_to_str(&oob.addr, addr, sizeof(addr));

	printk("BR/EDR OOB data:\n");
	printk("  addr %s\n", addr);

	return 0;
}

static int cmd_bredr_sdp_find_record(int argc, char *argv[])
{
	int err = 0, res;
	const char *action;

	if (!default_conn) {
		printk("Not connected\n");
		return 0;
	}

	if (argc < 2) {
		return -EINVAL;
	}

	action = argv[1];

	if (!strcmp(action, "HFPAG")) {
		discov = discov_hfpag;
	} else if (!strcmp(action, "A2SRC")) {
		discov = discov_a2src;
	} else {
		err = -EINVAL;
	}

	if (err) {
		printk("SDP UUID to resolve not valid (err %d)\n", err);
		printk("Supported UUID are \'HFPAG\' \'A2SRC\' only\n");
		return err;
	}

	printk("SDP UUID \'%s\' gets applied\n", action);

	res = bt_sdp_discover(default_conn, &discov);
	if (res) {
		printk("SDP discovery failed: result %d\n", res);
	} else {
		printk("SDP discovery started\n");
	}

	return 0;
}
#endif

#define HELP_NONE "[none]"
#define HELP_ADDR_LE "<address: XX:XX:XX:XX:XX:XX> <type: (public|random)>"

static const struct shell_cmd bt_commands[] = {
	{ "init", cmd_init, HELP_ADDR_LE },
#if defined(CONFIG_BT_HCI)
	{ "hci-cmd", cmd_hci_cmd, "<ogf> <ocf> [data]" },
#endif
	{ "id-create", cmd_id_create, "[addr]" },
	{ "id-reset", cmd_id_reset, "<id> [addr]" },
	{ "id-delete", cmd_id_delete, "<id>" },
	{ "id-show", cmd_id_show, HELP_NONE },
	{ "id-select", cmd_id_select, "<id>" },
	{ "name", cmd_name, "[name]" },
	{ "scan", cmd_scan,
	  "<value: on, passive, off> <dup filter: dups, nodups>" },
	{ "advertise", cmd_advertise,
	  "<type: off, on, scan, nconn> <mode: discov, non_discov>" },
#if defined(CONFIG_BT_CONN)
	{ "connect", cmd_connect_le, HELP_ADDR_LE },
	{ "disconnect", cmd_disconnect, HELP_NONE },
	{ "auto-conn", cmd_auto_conn, HELP_ADDR_LE },
	{ "directed-adv", cmd_directed_adv, HELP_ADDR_LE " [mode: low]" },
	{ "select", cmd_select, HELP_ADDR_LE },
	{ "conn-update", cmd_conn_update, "<min> <max> <latency> <timeout>" },
	{ "oob", cmd_oob },
	{ "clear", cmd_clear },
	{ "channel-map", cmd_chan_map, "<channel-map: XX:XX:XX:XX:XX> (36-0)" },
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
	{ "security", cmd_security, "<security level: 0, 1, 2, 3>" },
	{ "bondable", cmd_bondable, "<bondable: on, off>" },
	{ "auth", cmd_auth,
	  "<auth method: all, input, display, yesno, confirm, none>" },
	{ "auth-cancel", cmd_auth_cancel, HELP_NONE },
	{ "auth-passkey", cmd_auth_passkey, "<passkey>" },
	{ "auth-passkey-confirm", cmd_auth_passkey_confirm, HELP_NONE },
	{ "auth-pairing-confirm", cmd_auth_pairing_confirm, HELP_NONE },
#if defined(CONFIG_BT_FIXED_PASSKEY)
	{ "fixed-passkey", cmd_fixed_passkey, "[passkey]" },
#endif
{'fraction_non_alphanumeric': 0.14575047951365935, 'fraction_numerical': 0.01507352542776953, 'mean_word_length': 4.176239623064842, 'pattern_counts': {'":': 0, '<': 213, '<?xml version=': 0, '>': 197, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6246085', 'n_tokens_mistral': 41348, 'n_tokens_neox': 38399, 'n_words': 8430}