wenbo13579 пре 2 година
родитељ
комит
0664b92ea1

+ 53 - 0
example/tester/README

@@ -0,0 +1,53 @@
+Title: Bluetooth tester application
+
+Description:
+
+Tester application uses binary protocol to control Zephyr stack and is aimed at
+automated testing. It requires two serial ports to operate.
+The first serial is used by Bluetooth Testing Protocol (BTP) to drive Bluetooth
+stack. BTP commands and events are received and buffered for further processing
+over the same serial.
+
+BTP specification can be found in auto-pts project repository:
+https://github.com/intel/auto-pts
+The auto-pts is an automation framework for PTS Bluetooth testing tool provided
+by Bluetooth SIG.
+
+See https://docs.zephyrproject.org/latest/guides/bluetooth/index.html for full
+documentation about how to use this test.
+
+--------------------------------------------------------------------------------
+
+Supported Profiles:
+
+GAP, GATT, SM
+--------------------------------------------------------------------------------
+
+Building and running on QEMU:
+
+QEMU should have connection with the external host Bluetooth hardware.
+The btproxy tool from BlueZ can be used to give access to a Bluetooth controller
+attached to the Linux host OS:
+
+$ sudo tools/btproxy -u
+Listening on /tmp/bt-server-bredr
+
+/tmp/bt-server-bredr option is already set in Makefile through QEMU_EXTRA_FLAGS.
+
+To build tester application for QEMU use BOARD=qemu_cortex_m3 and
+CONF_FILE=qemu.conf. After this qemu can be started through the "run"
+build target.
+
+Note: Target board have to support enough UARTs for BTP and controller.
+      We recommend using qemu_cortex_m3.
+
+'bt-stack-tester' UNIX socket (previously set in Makefile) can be used for now
+to control tester application.
+--------------------------------------------------------------------------------
+
+Next, build and flash tester application by employing the "flash" build
+target.
+
+Use serial client, e.g. PUTTY to communicate over the serial port
+(typically /dev/ttyUSBx) with the tester using BTP.
+

+ 58 - 0
example/tester/app_main.c

@@ -0,0 +1,58 @@
+#include <stddef.h>
+#include <stdio.h>
+
+#include "base/common.h"
+
+#include <drivers/hci_driver.h>
+#include "btp/btp.h"
+
+#define LOG_MODULE_NAME tester
+#include "logging/bt_log.h"
+
+bool socket_connected = false;
+
+void bt_ready(int err)
+{
+    if (err)
+    {
+        BT_INFO("Bluetooth init failed (err %d)\n", err);
+        return;
+    }
+    BT_INFO("Bluetooth initialized\n");
+
+    tester_init();
+//
+//    //Socket initialization for BTP communication
+//    socket_connected = tester_socket_init();
+//    if (socket_connected){
+//        BT_INFO("Start to initialize BTP.\n");
+//        tester_init_core();
+//        int ret = tester_btp_pkt_send(BTP_SERVICE_ID_CORE, BTP_CORE_EV_IUT_READY, BTP_INDEX_NONE,NULL, 0);
+//        if (ret < 0){
+//            BT_INFO("IUT ready event failed\n");
+//            return;
+//        }
+//    }
+//
+//    BT_INFO("Sent IUT ready event\n");
+}
+
+
+
+void app_polling_work(void)
+{
+    //Read socket and Process
+//    if (socket_connected) {
+//        testr_btp_pkt_process();
+//    } else{
+//        socket_connected = tester_socket_init();
+//        if (socket_connected){
+//            tester_init();
+//            int ret = tester_btp_pkt_send(BTP_SERVICE_ID_CORE, BTP_CORE_EV_IUT_READY, BTP_INDEX_NONE,NULL, 0);
+//            if(!ret){
+//                BT_INFO("Sent IUT ready event\n");
+//            }
+//        }
+//    }
+    tester_polling_work();
+}

+ 86 - 0
example/tester/autoconfig.h

@@ -0,0 +1,86 @@
+#define CONFIG_BT 1
+#define CONFIG_BT_LOG_LEVEL_INF 1
+#define CONFIG_BT_LOG_LEVEL 3
+#define CONFIG_BT_PERIPHERAL 1
+#define CONFIG_BT_CENTRAL 1
+#define CONFIG_BT_BROADCASTER 1
+#define CONFIG_BT_OBSERVER 1
+#define CONFIG_BT_CONN 1
+#define CONFIG_BT_MAX_CONN 2
+#define CONFIG_BT_CONN_TX 1
+#define CONFIG_BT_PHY_UPDATE 1
+#define CONFIG_BT_DATA_LEN_UPDATE 1
+#define CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC 1000
+#define CONFIG_SYS_CLOCK_TICKS_PER_SEC 1000
+#define CONFIG_SYS_CLOCK_MAX_TIMEOUT_DAYS 365
+#define CONFIG_BT_BUF_ACL_TX_SIZE 27
+#define CONFIG_BT_BUF_ACL_TX_COUNT 6
+#define CONFIG_BT_BUF_ACL_RX_SIZE 69
+#define CONFIG_BT_BUF_ACL_RX_COUNT 6
+#define CONFIG_BT_BUF_EVT_RX_SIZE 68
+#define CONFIG_BT_BUF_EVT_RX_COUNT 10
+#define CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE 43
+#define CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT 3
+#define CONFIG_BT_BUF_CMD_TX_SIZE 255
+#define CONFIG_BT_BUF_CMD_TX_COUNT 6
+#define CONFIG_BT_RPA 1
+#define CONFIG_BT_ASSERT 1
+#define CONFIG_BT_ASSERT_VERBOSE 1
+#define CONFIG_BT_DEBUG_NONE 1
+#define CONFIG_BT_HCI_RESERVE 0
+#define CONFIG_BT_RX_PRIO 8
+#define CONFIG_BT_DRIVER_RX_HIGH_PRIO 6
+#define CONFIG_TINYCRYPT 1
+#define CONFIG_TINYCRYPT_SHA256 1
+#define CONFIG_TINYCRYPT_SHA256_HMAC 1
+#define CONFIG_TINYCRYPT_SHA256_HMAC_PRNG 1
+#define CONFIG_TINYCRYPT_AES 1
+#define CONFIG_TINYCRYPT_AES_CMAC 1
+#define CONFIG_BT_HOST_CRYPTO 1
+#define CONFIG_BT_HOST_CRYPTO_PRNG 1
+#define CONFIG_BT_FILTER_ACCEPT_LIST 1
+#define CONFIG_BT_LIM_ADV_TIMEOUT 30
+#define CONFIG_BT_CONN_TX_MAX 6
+#define CONFIG_BT_AUTO_PHY_UPDATE 1
+#define CONFIG_BT_AUTO_DATA_LEN_UPDATE 1
+#define CONFIG_BT_SMP 1
+#define CONFIG_BT_SIGNING 1
+#define CONFIG_BT_SMP_APP_PAIRING_ACCEPT 1
+#define CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE 1
+#define CONFIG_BT_BONDABLE 1
+#define CONFIG_BT_SMP_MIN_ENC_KEY_SIZE 7
+#define CONFIG_BT_L2CAP_TX_BUF_COUNT 6
+#define CONFIG_BT_L2CAP_TX_FRAG_COUNT 0
+#define CONFIG_BT_L2CAP_TX_MTU 65
+#define CONFIG_BT_GATT_FIXED_SERVICES_SIZE 7
+#define CONFIG_BT_ATT_PREPARE_COUNT 0
+#define CONFIG_BT_ATT_RETRY_ON_SEC_ERR 1
+#define CONFIG_BT_GATT_AUTO_RESUBSCRIBE 1
+#define CONFIG_BT_GATT_AUTO_SEC_REQ 1
+#define CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION 1
+#define CONFIG_BT_GATT_CLIENT 1
+#define CONFIG_BT_GATT_READ_MULTIPLE 1
+#define CONFIG_BT_GATT_READ_MULT_VAR_LEN 1
+#define CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS 1
+#define CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS 1
+#define CONFIG_BT_PERIPHERAL_PREF_MIN_INT 24
+#define CONFIG_BT_PERIPHERAL_PREF_MAX_INT 40
+#define CONFIG_BT_PERIPHERAL_PREF_LATENCY 0
+#define CONFIG_BT_PERIPHERAL_PREF_TIMEOUT 42
+#define CONFIG_BT_DEVICE_NAME_GATT_WRITABLE 1
+#define CONFIG_DEVICE_NAME_GATT_WRITABLE_ENCRYPT 1
+#define CONFIG_BT_MAX_PAIRED 2
+#define CONFIG_BT_CREATE_CONN_TIMEOUT 3
+#define CONFIG_BT_CONN_PARAM_UPDATE_TIMEOUT 5000
+#define CONFIG_BT_CONN_PARAM_RETRY_COUNT 3
+#define CONFIG_BT_CONN_PARAM_RETRY_TIMEOUT 5000
+#define CONFIG_BT_BACKGROUND_SCAN_INTERVAL 2048
+#define CONFIG_BT_BACKGROUND_SCAN_WINDOW 18
+#define CONFIG_BT_DEVICE_NAME_DYNAMIC 1
+#define CONFIG_BT_DEVICE_NAME_MAX 32
+#define CONFIG_BT_DEVICE_NAME "Tester"
+#define CONFIG_BT_DEVICE_APPEARANCE 0
+#define CONFIG_BT_ID_MAX 1
+#define CONFIG_BT_ECC 1
+#define CONFIG_BT_CRYPTO 1
+#define CONFIG_BT_COMPANY_ID 0x05F1

+ 558 - 0
example/tester/btp.c

@@ -0,0 +1,558 @@
+/* bttester.c - Bluetooth Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/common.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <base/types.h>
+#include <base/common.h>
+#include <bluetooth/bluetooth.h>
+#include <base/byteorder.h>
+
+#include <common/timer.h>
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "rtthread_driver_serial.h"
+
+#define LOG_MODULE_NAME bttester
+#include <logging/bt_log.h>
+
+#include "btp/btp.h"
+
+#define CMD_QUEUED 2
+struct btp_buf {
+	intptr_t _reserved;
+	union {
+		uint8_t data[BTP_MTU];
+		struct btp_hdr hdr;
+	};
+};
+
+static struct btp_buf cmd_buf[CMD_QUEUED];
+
+static K_FIFO_DEFINE(cmds_queue);
+static K_FIFO_DEFINE(avail_queue);
+
+static void supported_commands(uint8_t *data, uint16_t len)
+{
+	uint8_t buf[1];
+	struct core_read_supported_commands_rp *rp = (void *) buf;
+
+	(void)memset(buf, 0, sizeof(buf));
+
+	tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS);
+	tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES);
+	tester_set_bit(buf, CORE_REGISTER_SERVICE);
+	tester_set_bit(buf, CORE_UNREGISTER_SERVICE);
+
+	tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS,
+		    BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf));
+}
+
+static void supported_services(uint8_t *data, uint16_t len)
+{
+	uint8_t buf[2];
+	struct core_read_supported_services_rp *rp = (void *) buf;
+
+	(void)memset(buf, 0, sizeof(buf));
+
+	tester_set_bit(buf, BTP_SERVICE_ID_CORE);
+	tester_set_bit(buf, BTP_SERVICE_ID_GAP);
+	tester_set_bit(buf, BTP_SERVICE_ID_GATT);
+#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
+	tester_set_bit(buf, BTP_SERVICE_ID_L2CAP);
+#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
+#if defined(CONFIG_BT_MESH)
+	tester_set_bit(buf, BTP_SERVICE_ID_MESH);
+#endif /* CONFIG_BT_MESH */
+#if defined(CONFIG_BT_VCP_VOL_REND)
+	tester_set_bit(buf, BTP_SERVICE_ID_VCS);
+#endif /* CONFIG_BT_VCP_VOL_REND */
+#if defined(CONFIG_BT_IAS) || defined(CONFIG_BT_IAS_CLIENT)
+	tester_set_bit(buf, BTP_SERVICE_ID_IAS);
+#endif /* CONFIG_BT_IAS */
+#if defined(CONFIG_BT_AICS) || defined(CONFIG_BT_AICS_CLIENT)
+	tester_set_bit(buf, BTP_SERVICE_ID_AICS);
+#endif /*CONFIG_BT_AICS */
+#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
+	tester_set_bit(buf, BTP_SERVICE_ID_VOCS);
+#endif /* CONFIG_BT_VOCS */
+
+	tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES,
+		    BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf));
+}
+
+static void register_service(uint8_t *data, uint16_t len)
+{
+	struct core_register_service_cmd *cmd = (void *) data;
+	uint8_t status;
+
+	switch (cmd->id) {
+	case BTP_SERVICE_ID_GAP:
+		status = tester_init_gap();
+		/* Rsp with success status will be handled by bt enable cb */
+		if (status == BTP_STATUS_FAILED) {
+			goto rsp;
+		}
+		return;
+	case BTP_SERVICE_ID_GATT:
+		status = tester_init_gatt();
+		break;
+#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
+	case BTP_SERVICE_ID_L2CAP:
+		status = tester_init_l2cap();
+#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
+		break;
+#if defined(CONFIG_BT_MESH)
+	case BTP_SERVICE_ID_MESH:
+		status = tester_init_mesh();
+		break;
+#endif /* CONFIG_BT_MESH */
+#if defined(CONFIG_BT_VCP_VOL_REND)
+	case BTP_SERVICE_ID_VCS:
+		status = tester_init_vcp();
+		break;
+#endif /* CONFIG_BT_VCP_VOL_REND */
+#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
+	case BTP_SERVICE_ID_VOCS:
+		status = tester_init_vcp();
+		break;
+#endif /* CONFIG_BT_VOCS */
+#if defined(CONFIG_BT_AICS) || defined(CONFIG_BT_AICS_CLIENT)
+	case BTP_SERVICE_ID_AICS:
+		status = tester_init_vcp();
+		break;
+#endif /* CONFIG_BT_AICS */
+#if defined(CONFIG_BT_IAS) || defined(CONFIG_BT_IAS_CLIENT)
+	case BTP_SERVICE_ID_IAS:
+		status = BTP_STATUS_SUCCESS;
+		break;
+#endif /* CONFIG_BT_IAS */
+	default:
+		BT_WARN("unknown id: 0x%02x", cmd->id);
+		status = BTP_STATUS_FAILED;
+		break;
+	}
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
+		   status);
+}
+
+static void unregister_service(uint8_t *data, uint16_t len)
+{
+	struct core_unregister_service_cmd *cmd = (void *) data;
+	uint8_t status;
+
+	switch (cmd->id) {
+	case BTP_SERVICE_ID_GAP:
+		status = tester_unregister_gap();
+		break;
+	case BTP_SERVICE_ID_GATT:
+		status = tester_unregister_gatt();
+		break;
+#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
+	case BTP_SERVICE_ID_L2CAP:
+		status = tester_unregister_l2cap();
+		break;
+#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
+#if defined(CONFIG_BT_MESH)
+	case BTP_SERVICE_ID_MESH:
+		status = tester_unregister_mesh();
+		break;
+#endif /* CONFIG_BT_MESH */
+#if defined(CONFIG_BT_VCP_VOL_REND)
+	case BTP_SERVICE_ID_VCS:
+		status = tester_unregister_vcp();
+		break;
+#endif /* CONFIG_BT_VCP_VOL_REND */
+#if defined(CONFIG_BT_AICS) || defined(CONFIG_BT_AICS_CLIENT)
+	case BTP_SERVICE_ID_AICS:
+		status = tester_unregister_vcp();
+		break;
+#endif /* CONFIG_BT_AICS */
+#if defined(CONFIG_BT_VOCS) || defined(CONFIG_BT_VOCS_CLIENT)
+	case BTP_SERVICE_ID_VOCS:
+		status = tester_unregister_vcp();
+		break;
+#endif /* CONFIG_BT_VOCS */
+	default:
+		BT_WARN("unknown id: 0x%x", cmd->id);
+		status = BTP_STATUS_FAILED;
+		break;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE,
+		   status);
+}
+
+static void handle_core(uint8_t opcode, uint8_t index, uint8_t *data,
+			uint16_t len)
+{
+	if (index != BTP_INDEX_NONE) {
+		BT_WARN("index != BTP_INDEX_NONE: 0x%x", index);
+		tester_rsp(BTP_SERVICE_ID_CORE, opcode, index, BTP_STATUS_FAILED);
+		return;
+	}
+
+	switch (opcode) {
+	case CORE_READ_SUPPORTED_COMMANDS:
+		supported_commands(data, len);
+		return;
+	case CORE_READ_SUPPORTED_SERVICES:
+		supported_services(data, len);
+		return;
+	case CORE_REGISTER_SERVICE:
+		register_service(data, len);
+		return;
+	case CORE_UNREGISTER_SERVICE:
+		unregister_service(data, len);
+		return;
+	default:
+		BT_WARN("unknown opcode: 0x%x", opcode);
+		tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE,
+			   BTP_STATUS_UNKNOWN_CMD);
+		return;
+	}
+}
+
+// static void cmd_handler(void *p1, void *p2, void *p3)
+static void cmd_handler(void)
+{
+	// while (1) 
+	{
+		struct btp_buf *cmd;
+		uint16_t len;
+
+		// if(k_fifo_size(&cmds_queue))
+		// {
+		// 	BT_INFO("cmd_handler1, size:%d", k_fifo_size(&cmds_queue));
+		// }
+
+		cmd = k_fifo_get(&cmds_queue, K_FOREVER);
+		if(cmd == NULL)
+		{
+			return;
+		}
+
+		len = sys_le16_to_cpu(cmd->hdr.len);
+
+		BT_INFO("service:%d, len:%d", cmd->hdr.service, len);
+
+		/* TODO
+		 * verify if service is registered before calling handler
+		 */
+
+		switch (cmd->hdr.service) {
+		case BTP_SERVICE_ID_CORE:
+			handle_core(cmd->hdr.opcode, cmd->hdr.index,
+				    cmd->hdr.data, len);
+			break;
+		case BTP_SERVICE_ID_GAP:
+			tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index,
+					  cmd->hdr.data, len);
+			BT_ERR("BTP_SERVICE_ID_GAP");
+			break;
+		case BTP_SERVICE_ID_GATT:
+			tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index,
+					   cmd->hdr.data, len);
+			break;
+#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
+		case BTP_SERVICE_ID_L2CAP:
+			tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index,
+					    cmd->hdr.data, len);
+#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
+			break;
+#if defined(CONFIG_BT_MESH)
+		case BTP_SERVICE_ID_MESH:
+			tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index,
+					   cmd->hdr.data, len);
+			break;
+#endif /* CONFIG_BT_MESH */
+#if defined(CONFIG_BT_VCP_VOL_REND)
+		case BTP_SERVICE_ID_VCS:
+			tester_handle_vcs(cmd->hdr.opcode, cmd->hdr.index,
+					  cmd->hdr.data, len);
+			break;
+#endif /* CONFIG_BT_VCP_VOL_REND */
+#if defined(CONFIG_BT_AICS)
+		case BTP_SERVICE_ID_AICS:
+			tester_handle_aics(cmd->hdr.opcode, cmd->hdr.index,
+					   cmd->hdr.data, len);
+			break;
+#endif /* CONFIG_BT_AICS */
+#if defined(CONFIG_BT_VOCS)
+		case BTP_SERVICE_ID_VOCS:
+			tester_handle_vocs(cmd->hdr.opcode, cmd->hdr.index,
+					   cmd->hdr.data, len);
+			break;
+#endif /* CONFIG_BT_VOCS */
+		default:
+			BT_WARN("unknown service: 0x%x", cmd->hdr.service);
+			tester_rsp(cmd->hdr.service, cmd->hdr.opcode,
+				   cmd->hdr.index, BTP_STATUS_FAILED);
+			break;
+		}
+
+		k_fifo_put(&avail_queue, cmd);
+	}
+}
+
+static uint8_t *recv_cb(uint8_t *buf, size_t *off)
+{
+	struct btp_hdr *cmd = (void *) buf;
+	struct btp_buf *new_buf;
+	uint16_t len;
+	
+	// BT_INFO("off: %x", *off);
+
+	if (*off < sizeof(*cmd)) {
+		return buf;
+	}
+
+	len = sys_le16_to_cpu(cmd->len);
+
+	// BT_INFO("off: %x, len:%x", *off, len);
+
+	if (len > BTP_MTU - sizeof(*cmd)) {
+		BT_ERR("BT tester: invalid packet length");
+		*off = 0;
+		return buf;
+	}
+
+	if (*off < sizeof(*cmd) + len) {
+		return buf;
+	}
+
+	new_buf =  k_fifo_get(&avail_queue, K_NO_WAIT);
+	if (!new_buf) {
+		BT_ERR("BT tester: RX overflow");
+		*off = 0;
+		return buf;
+	}
+
+		
+		// BT_INFO("cmd_handler, size:%d", k_fifo_size(&cmds_queue));
+	k_fifo_put(&cmds_queue, CONTAINER_OF(buf, struct btp_buf, data));
+		// BT_INFO("cmd_handler, size:%d", k_fifo_size(&cmds_queue));
+
+	*off = 0;
+	return new_buf->data;
+}
+#if 0
+#if defined(CONFIG_UART_PIPE)
+/* Uart Pipe */
+static void uart_init(uint8_t *data)
+{
+	uart_pipe_register(data, BTP_MTU, recv_cb);
+}
+
+static void uart_send(uint8_t *data, size_t len)
+{
+	uart_pipe_send(data, len);
+}
+#else /* !CONFIG_UART_PIPE */
+static uint8_t *recv_buf;
+static size_t recv_off;
+//static const struct device *const dev =
+//	DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+
+static void timer_expiry_cb(struct k_timer *timer)
+{
+	uint8_t c;
+
+//	while (uart_poll_in(dev, &c) == 0) {
+//		recv_buf[recv_off++] = c;
+//		recv_buf = recv_cb(recv_buf, &recv_off);
+//	}
+}
+
+K_TIMER_DEFINE(timer, timer_expiry_cb, NULL);
+
+/* Uart Poll */
+static void uart_init(uint8_t *data)
+{
+//	__ASSERT_NO_MSG(device_is_ready(dev));
+
+	recv_buf = data;
+
+	k_timer_start(&timer, K_MSEC(10), K_MSEC(10));
+}
+
+static void uart_send(uint8_t *data, size_t len)
+{
+	int i;
+
+//	for (i = 0; i < len; i++) {
+//		uart_poll_out(dev, data[i]);
+//	}
+}
+#endif /* CONFIG_UART_PIPE */
+#endif
+
+struct h4_uart_config{
+    const char *name;
+    int flowcontrol;        // RT-Thread not support CTS/RTS flowcontrol now, default is true.
+    struct serial_configure rt_config;
+};
+
+static struct h4_uart_config uart_config = {
+    .rt_config = RT_SERIAL_CONFIG_DEFAULT,
+};
+
+static char device_name[10];
+int uart_init_params(int idx, int rate, int databits, int stopbits, int parity, bool flowcontrol)
+{
+    printk("uart_init_params idx: %d, rate: %d, databits: %d, stopbits: %d, parity: %d, flowcontrol: %d\n",
+           idx, rate, databits, stopbits, parity, flowcontrol);
+
+    rt_sprintf(device_name, "uart%d", idx);
+
+    uart_config.name        = device_name;
+    uart_config.flowcontrol = flowcontrol;
+
+    uart_config.rt_config.baud_rate   = rate;
+    uart_config.rt_config.data_bits   = databits;
+    uart_config.rt_config.stop_bits   = stopbits;
+    uart_config.rt_config.parity      = parity;
+
+#if defined(RTTHREAD_VERSION) && RTTHREAD_VERSION > 40003    //< rtthread version larger than v4.0.3
+    uart_config.rt_config.flowcontrol = flowcontrol;
+#endif
+
+    return 0;
+}
+static rt_device_t h4_uart;
+
+static uint8_t *recv_buf;
+static size_t recv_off;
+
+/* Uart Poll */
+static void uart_init(uint8_t *data)
+{
+//	__ASSERT_NO_MSG(device_is_ready(dev));
+	uart_init_params(1, 115200, 8, 1, 0, 0);
+
+	recv_buf = data;
+	
+    printk("uart_init, uart_config.name: %s\n", uart_config.name);
+
+    h4_uart = rt_device_find(uart_config.name);
+//    printk("uart_init, h4_uart: 0x%x\n", h4_uart);
+    RT_ASSERT(h4_uart);
+
+    rt_err_t err;
+
+    if ((err = rt_device_open(h4_uart, RT_DEVICE_FLAG_INT_RX))) {
+        printk("Open uart_init error\n");
+        return;
+    }
+    if ((err = rt_device_control(h4_uart, RT_DEVICE_CTRL_CONFIG, &uart_config.rt_config))) {
+        printk("Control uart_init error\n");
+        return;
+    }
+}
+
+static void uart_send(uint8_t *data, size_t len)
+{
+//	int i;
+
+//	for (i = 0; i < len; i++) {
+//		uart_poll_out(dev, data[i]);
+//	}
+	rt_device_write(h4_uart, 0, data, len);
+}
+
+
+static int hci_driver_h4_recv(uint8_t *buf, uint16_t len)
+{
+	if(!h4_uart)
+	{
+		return 0;
+	}
+    return rt_device_read(h4_uart, 0, buf, len);
+}
+
+
+void tester_rx_polling(void)
+{
+	uint8_t c;
+
+	while (hci_driver_h4_recv(&c, 1) != 0) {
+		// BT_INFO("c:%x", c);
+
+		recv_buf[recv_off++] = c;
+		recv_buf = recv_cb(recv_buf, &recv_off);
+		
+	}
+}
+
+
+void tester_polling_work(void)
+{
+	tester_rx_polling();
+	cmd_handler();
+}
+
+
+void tester_init(void)
+{
+	int i;
+	struct btp_buf *buf;
+
+	BT_DBG("Initializing tester");
+
+	for (i = 0; i < CMD_QUEUED; i++) {
+		k_fifo_put(&avail_queue, &cmd_buf[i]);
+	}
+
+//	k_thread_create(&cmd_thread, stack, STACKSIZE, cmd_handler,
+//			NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
+
+	buf = k_fifo_get(&avail_queue, K_NO_WAIT);
+
+	uart_init(buf->data);
+
+	tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE,
+		    NULL, 0);
+}
+
+void tester_send(uint8_t service, uint8_t opcode, uint8_t index, uint8_t *data,
+		 size_t len)
+{
+	struct btp_hdr msg;
+
+	msg.service = service;
+	msg.opcode = opcode;
+	msg.index = index;
+	msg.len = len;
+
+	uart_send((uint8_t *)&msg, sizeof(msg));
+	if (data && len) {
+		uart_send(data, len);
+	}
+}
+
+void tester_rsp(uint8_t service, uint8_t opcode, uint8_t index, uint8_t status)
+{
+	struct btp_status s;
+
+	if (status == BTP_STATUS_SUCCESS) {
+		tester_send(service, opcode, index, NULL, 0);
+		return;
+	}
+
+	s.code = status;
+	tester_send(service, BTP_STATUS, index, (uint8_t *) &s, sizeof(s));
+}

+ 53 - 0
example/tester/btp/btp.h

@@ -0,0 +1,53 @@
+/* bttester.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+#include "bttester.h"
+#include "btp_core.h"
+#include "btp_gap.h"
+#include "btp_gatt.h"
+#include "btp_l2cap.h"
+
+#define BTP_MTU 1024
+#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
+
+#define BTP_INDEX_NONE		0xff
+
+#define BTP_SERVICE_ID_CORE	0
+#define BTP_SERVICE_ID_GAP	1
+#define BTP_SERVICE_ID_GATT	2
+#define BTP_SERVICE_ID_L2CAP	3
+#define BTP_SERVICE_ID_MESH	4
+#define BTP_SERVICE_ID_MESH_MDL	5
+#define BTP_SERVICE_GATT_CLIENT	6
+#define BTP_SERVICE_GATT_SERVER	7
+#define BTP_SERVICE_ID_VCS	8
+#define BTP_SERVICE_ID_IAS	9
+#define BTP_SERVICE_ID_AICS	10
+#define BTP_SERVICE_ID_VOCS	11
+
+#define BTP_STATUS_SUCCESS	0x00
+#define BTP_STATUS_FAILED	0x01
+#define BTP_STATUS_UNKNOWN_CMD	0x02
+#define BTP_STATUS_NOT_READY	0x03
+
+struct btp_hdr {
+	uint8_t  service;
+	uint8_t  opcode;
+	uint8_t  index;
+	uint16_t len;
+	uint8_t  data[];
+} __packed;
+
+#define BTP_STATUS			0x00
+struct btp_status {
+	uint8_t code;
+} __packed;

+ 35 - 0
example/tester/btp/btp_core.h

@@ -0,0 +1,35 @@
+/* bttester.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+/* Core Service */
+#define CORE_READ_SUPPORTED_COMMANDS	0x01
+struct core_read_supported_commands_rp {
+	uint8_t data[0];
+} __packed;
+
+#define CORE_READ_SUPPORTED_SERVICES	0x02
+struct core_read_supported_services_rp {
+	uint8_t data[0];
+} __packed;
+
+#define CORE_REGISTER_SERVICE		0x03
+struct core_register_service_cmd {
+	uint8_t id;
+} __packed;
+
+#define CORE_UNREGISTER_SERVICE		0x04
+struct core_unregister_service_cmd {
+	uint8_t id;
+} __packed;
+
+/* events */
+#define CORE_EV_IUT_READY		0x80

+ 339 - 0
example/tester/btp/btp_gap.h

@@ -0,0 +1,339 @@
+/* gap.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+/* GAP Service */
+/* commands */
+#define GAP_READ_SUPPORTED_COMMANDS		0x01
+struct gap_read_supported_commands_rp {
+	uint8_t data[0];
+} __packed;
+
+#define GAP_READ_CONTROLLER_INDEX_LIST	0x02
+struct gap_read_controller_index_list_rp {
+	uint8_t num;
+	uint8_t index[];
+} __packed;
+
+#define GAP_SETTINGS_POWERED		0
+#define GAP_SETTINGS_CONNECTABLE		1
+#define GAP_SETTINGS_FAST_CONNECTABLE	2
+#define GAP_SETTINGS_DISCOVERABLE		3
+#define GAP_SETTINGS_BONDABLE		4
+#define GAP_SETTINGS_LINK_SEC_3		5
+#define GAP_SETTINGS_SSP			6
+#define GAP_SETTINGS_BREDR			7
+#define GAP_SETTINGS_HS			8
+#define GAP_SETTINGS_LE			9
+#define GAP_SETTINGS_ADVERTISING		10
+#define GAP_SETTINGS_SC			11
+#define GAP_SETTINGS_DEBUG_KEYS		12
+#define GAP_SETTINGS_PRIVACY		13
+#define GAP_SETTINGS_CONTROLLER_CONFIG	14
+#define GAP_SETTINGS_STATIC_ADDRESS		15
+
+#define GAP_READ_CONTROLLER_INFO	0x03
+struct gap_read_controller_info_rp {
+	uint8_t  address[6];
+	uint32_t supported_settings;
+	uint32_t current_settings;
+	uint8_t  cod[3];
+	uint8_t  name[249];
+	uint8_t  short_name[11];
+} __packed;
+
+#define GAP_RESET			0x04
+struct gap_reset_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_SET_POWERED		0x05
+struct gap_set_powered_cmd {
+	uint8_t powered;
+} __packed;
+struct gap_set_powered_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_SET_CONNECTABLE		0x06
+struct gap_set_connectable_cmd {
+	uint8_t connectable;
+} __packed;
+struct gap_set_connectable_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_SET_FAST_CONNECTABLE	0x07
+struct gap_set_fast_connectable_cmd {
+	uint8_t fast_connectable;
+} __packed;
+struct gap_set_fast_connectable_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_NON_DISCOVERABLE	0x00
+#define GAP_GENERAL_DISCOVERABLE	0x01
+#define GAP_LIMITED_DISCOVERABLE	0x02
+
+#define GAP_SET_DISCOVERABLE	0x08
+struct gap_set_discoverable_cmd {
+	uint8_t discoverable;
+} __packed;
+struct gap_set_discoverable_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_SET_BONDABLE		0x09
+struct gap_set_bondable_cmd {
+	uint8_t bondable;
+} __packed;
+struct gap_set_bondable_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_START_ADVERTISING	0x0a
+struct gap_start_advertising_cmd {
+	uint8_t adv_data_len;
+	uint8_t scan_rsp_len;
+	uint8_t adv_sr_data[];
+} __packed;
+struct gap_start_advertising_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_STOP_ADVERTISING	0x0b
+struct gap_stop_advertising_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_DISCOVERY_FLAG_LE		0x01
+#define GAP_DISCOVERY_FLAG_BREDR		0x02
+#define GAP_DISCOVERY_FLAG_LIMITED		0x04
+#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN	0x08
+#define GAP_DISCOVERY_FLAG_LE_OBSERVE	0x10
+#define GAP_DISCOVERY_FLAG_OWN_ID_ADDR	0x20
+
+#define GAP_START_DISCOVERY			0x0c
+struct gap_start_discovery_cmd {
+	uint8_t flags;
+} __packed;
+
+#define GAP_STOP_DISCOVERY		0x0d
+
+#define GAP_CONNECT			0x0e
+struct gap_connect_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_DISCONNECT		0x0f
+struct gap_disconnect_cmd {
+	uint8_t  address_type;
+	uint8_t  address[6];
+} __packed;
+
+#define GAP_IO_CAP_DISPLAY_ONLY		0
+#define GAP_IO_CAP_DISPLAY_YESNO		1
+#define GAP_IO_CAP_KEYBOARD_ONLY		2
+#define GAP_IO_CAP_NO_INPUT_OUTPUT		3
+#define GAP_IO_CAP_KEYBOARD_DISPLAY		4
+
+#define GAP_SET_IO_CAP			0x10
+struct gap_set_io_cap_cmd {
+	uint8_t io_cap;
+} __packed;
+
+#define GAP_PAIR			0x11
+struct gap_pair_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_UNPAIR			0x12
+struct gap_unpair_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_PASSKEY_ENTRY		0x13
+struct gap_passkey_entry_cmd {
+	uint8_t  address_type;
+	uint8_t  address[6];
+	uint32_t passkey;
+} __packed;
+
+#define GAP_PASSKEY_CONFIRM		0x14
+struct gap_passkey_confirm_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t match;
+} __packed;
+
+#define GAP_START_DIRECTED_ADV_HD		BIT(0)
+#define GAP_START_DIRECTED_ADV_OWN_ID	BIT(1)
+#define GAP_START_DIRECTED_ADV_PEER_RPA	BIT(2)
+
+#define GAP_START_DIRECTED_ADV		0x15
+struct gap_start_directed_adv_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t options;
+} __packed;
+struct gap_start_directed_adv_rp {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_CONN_PARAM_UPDATE		0x16
+struct gap_conn_param_update_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t interval_min;
+	uint16_t interval_max;
+	uint16_t latency;
+	uint16_t timeout;
+} __packed;
+
+#define GAP_PAIRING_CONSENT		0x17
+struct gap_pairing_consent_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t consent;
+} __packed;
+
+#define GAP_OOB_LEGACY_SET_DATA	0x18
+struct gap_oob_legacy_set_data_cmd {
+	uint8_t oob_data[16];
+} __packed;
+
+#define GAP_OOB_SC_GET_LOCAL_DATA	0x19
+struct gap_oob_sc_get_local_data_rp {
+	uint8_t rand[16];
+	uint8_t conf[16];
+} __packed;
+
+#define GAP_OOB_SC_SET_REMOTE_DATA	0x1a
+struct gap_oob_sc_set_remote_data_cmd {
+	uint8_t rand[16];
+	uint8_t conf[16];
+} __packed;
+
+#define GAP_SET_MITM		0x1b
+struct gap_set_mitm {
+	uint8_t mitm;
+} __packed;
+
+#define GAP_SET_FILTER_LIST		0x1c
+struct gap_set_filter_list {
+	uint8_t cnt;
+	bt_addr_le_t addr[0];
+} __packed;
+
+/* events */
+#define GAP_EV_NEW_SETTINGS		0x80
+struct gap_new_settings_ev {
+	uint32_t current_settings;
+} __packed;
+
+#define GAP_DEVICE_FOUND_FLAG_RSSI	0x01
+#define GAP_DEVICE_FOUND_FLAG_AD	0x02
+#define GAP_DEVICE_FOUND_FLAG_SD	0x04
+
+#define GAP_EV_DEVICE_FOUND		0x81
+struct gap_device_found_ev {
+	uint8_t  address_type;
+	uint8_t  address[6];
+	int8_t   rssi;
+	uint8_t  flags;
+	uint16_t eir_data_len;
+	uint8_t  eir_data[];
+} __packed;
+
+#define GAP_EV_DEVICE_CONNECTED	0x82
+struct gap_device_connected_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t timeout;
+} __packed;
+
+#define GAP_EV_DEVICE_DISCONNECTED	0x83
+struct gap_device_disconnected_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_EV_PASSKEY_DISPLAY	0x84
+struct gap_passkey_display_ev {
+	uint8_t  address_type;
+	uint8_t  address[6];
+	uint32_t passkey;
+} __packed;
+
+#define GAP_EV_PASSKEY_ENTRY_REQ	0x85
+struct gap_passkey_entry_req_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_EV_PASSKEY_CONFIRM_REQ	0x86
+struct gap_passkey_confirm_req_ev {
+	uint8_t  address_type;
+	uint8_t  address[6];
+	uint32_t passkey;
+} __packed;
+
+#define GAP_EV_IDENTITY_RESOLVED	0x87
+struct gap_identity_resolved_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t identity_address_type;
+	uint8_t identity_address[6];
+} __packed;
+
+#define GAP_EV_CONN_PARAM_UPDATE	0x88
+struct gap_conn_param_update_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t interval;
+	uint16_t latency;
+	uint16_t timeout;
+} __packed;
+
+#define GAP_SEC_LEVEL_UNAUTH_ENC	0x01
+#define GAP_SEC_LEVEL_AUTH_ENC	0x02
+#define GAP_SEC_LEVEL_AUTH_SC	0x03
+
+#define GAP_EV_SEC_LEVEL_CHANGED	0x89
+struct gap_sec_level_changed_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t sec_level;
+} __packed;
+
+#define GAP_EV_PAIRING_CONSENT_REQ	0x8a
+struct gap_pairing_consent_req_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_EV_BOND_LOST	0x8b
+struct gap_bond_lost_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GAP_EV_PAIRING_FAILED	0x8c
+struct gap_bond_pairing_failed_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t reason;
+} __packed;

+ 378 - 0
example/tester/btp/btp_gatt.h

@@ -0,0 +1,378 @@
+/* gatt.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+/* GATT Service */
+/* commands */
+#define GATT_READ_SUPPORTED_COMMANDS	0x01
+struct gatt_read_supported_commands_rp {
+	uint8_t data[0];
+} __packed;
+
+#define GATT_SERVICE_PRIMARY		0x00
+#define GATT_SERVICE_SECONDARY		0x01
+
+#define GATT_ADD_SERVICE		0x02
+struct gatt_add_service_cmd {
+	uint8_t type;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+struct gatt_add_service_rp {
+	uint16_t svc_id;
+} __packed;
+
+#define GATT_ADD_CHARACTERISTIC		0x03
+struct gatt_add_characteristic_cmd {
+	uint16_t svc_id;
+	uint8_t properties;
+	uint8_t permissions;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+struct gatt_add_characteristic_rp {
+	uint16_t char_id;
+} __packed;
+
+#define GATT_ADD_DESCRIPTOR		0x04
+struct gatt_add_descriptor_cmd {
+	uint16_t char_id;
+	uint8_t permissions;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+struct gatt_add_descriptor_rp {
+	uint16_t desc_id;
+} __packed;
+
+#define GATT_ADD_INCLUDED_SERVICE	0x05
+struct gatt_add_included_service_cmd {
+	uint16_t svc_id;
+} __packed;
+struct gatt_add_included_service_rp {
+	uint16_t included_service_id;
+} __packed;
+
+#define GATT_SET_VALUE			0x06
+	struct gatt_set_value_cmd {
+	uint16_t attr_id;
+	uint16_t len;
+	uint8_t value[];
+} __packed;
+
+#define GATT_START_SERVER		0x07
+struct gatt_start_server_rp {
+	uint16_t db_attr_off;
+	uint8_t db_attr_cnt;
+} __packed;
+
+#define GATT_RESET_SERVER		0x08
+
+#define GATT_SET_ENC_KEY_SIZE		0x09
+struct gatt_set_enc_key_size_cmd {
+	uint16_t attr_id;
+	uint8_t key_size;
+} __packed;
+
+/* Gatt Client */
+struct gatt_service {
+	uint16_t start_handle;
+	uint16_t end_handle;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+
+struct gatt_included {
+	uint16_t included_handle;
+	struct gatt_service service;
+} __packed;
+
+struct gatt_characteristic {
+	uint16_t characteristic_handle;
+	uint16_t value_handle;
+	uint8_t properties;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+
+struct gatt_descriptor {
+	uint16_t descriptor_handle;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+
+#define GATT_EXCHANGE_MTU		0x0a
+struct gatt_exchange_mtu_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define GATT_DISC_ALL_PRIM		0x0b
+struct gatt_disc_all_prim_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+struct gatt_disc_all_prim_rp {
+	uint8_t services_count;
+	struct gatt_service services[];
+} __packed;
+
+#define GATT_DISC_PRIM_UUID		0x0c
+struct gatt_disc_prim_uuid_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+struct gatt_disc_prim_rp {
+	uint8_t services_count;
+	struct gatt_service services[];
+} __packed;
+
+#define GATT_FIND_INCLUDED		0x0d
+struct gatt_find_included_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t start_handle;
+	uint16_t end_handle;
+} __packed;
+struct gatt_find_included_rp {
+	uint8_t services_count;
+	struct gatt_included included[];
+} __packed;
+
+#define GATT_DISC_ALL_CHRC		0x0e
+struct gatt_disc_all_chrc_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t start_handle;
+	uint16_t end_handle;
+} __packed;
+struct gatt_disc_chrc_rp {
+	uint8_t characteristics_count;
+	struct gatt_characteristic characteristics[];
+} __packed;
+
+#define GATT_DISC_CHRC_UUID		0x0f
+struct gatt_disc_chrc_uuid_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t start_handle;
+	uint16_t end_handle;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+
+#define GATT_DISC_ALL_DESC		0x10
+struct gatt_disc_all_desc_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t start_handle;
+	uint16_t end_handle;
+} __packed;
+struct gatt_disc_all_desc_rp {
+	uint8_t descriptors_count;
+	struct gatt_descriptor descriptors[];
+} __packed;
+
+#define GATT_READ			0x11
+struct gatt_read_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+} __packed;
+struct gatt_read_rp {
+	uint8_t att_response;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+struct gatt_char_value {
+	uint16_t handle;
+	uint8_t data_len;
+	uint8_t data[0];
+} __packed;
+
+#define GATT_READ_UUID			0x12
+struct gatt_read_uuid_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t start_handle;
+	uint16_t end_handle;
+	uint8_t uuid_length;
+	uint8_t uuid[];
+} __packed;
+struct gatt_read_uuid_rp {
+	uint8_t att_response;
+	uint8_t values_count;
+	struct gatt_char_value values[0];
+} __packed;
+
+#define GATT_READ_LONG			0x13
+struct gatt_read_long_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t offset;
+} __packed;
+struct gatt_read_long_rp {
+	uint8_t att_response;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define GATT_READ_MULTIPLE		0x14
+struct gatt_read_multiple_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t handles_count;
+	uint16_t handles[];
+} __packed;
+struct gatt_read_multiple_rp {
+	uint8_t att_response;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define GATT_WRITE_WITHOUT_RSP		0x15
+struct gatt_write_without_rsp_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define GATT_SIGNED_WRITE_WITHOUT_RSP	0x16
+struct gatt_signed_write_without_rsp_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define GATT_WRITE			0x17
+struct gatt_write_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+struct gatt_write_rp {
+	uint8_t att_response;
+} __packed;
+
+#define GATT_WRITE_LONG			0x18
+struct gatt_write_long_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t offset;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+struct gatt_write_long_rp {
+	uint8_t att_response;
+} __packed;
+
+#define GATT_RELIABLE_WRITE		0x19
+struct gatt_reliable_write_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+	uint16_t offset;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+struct gatt_reliable_write_rp {
+	uint8_t att_response;
+} __packed;
+
+#define GATT_CFG_NOTIFY			0x1a
+#define GATT_CFG_INDICATE		0x1b
+struct gatt_cfg_notify_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t enable;
+	uint16_t ccc_handle;
+} __packed;
+
+#define GATT_GET_ATTRIBUTES		0x1c
+struct gatt_get_attributes_cmd {
+	uint16_t start_handle;
+	uint16_t end_handle;
+	uint8_t type_length;
+	uint8_t type[];
+} __packed;
+struct gatt_get_attributes_rp {
+	uint8_t attrs_count;
+	uint8_t attrs[];
+} __packed;
+struct gatt_attr {
+	uint16_t handle;
+	uint8_t permission;
+	uint8_t type_length;
+	uint8_t type[];
+} __packed;
+
+#define GATT_GET_ATTRIBUTE_VALUE	0x1d
+struct gatt_get_attribute_value_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t handle;
+} __packed;
+struct gatt_get_attribute_value_rp {
+	uint8_t att_response;
+	uint16_t value_length;
+	uint8_t value[];
+} __packed;
+
+#define GATT_CHANGE_DB			0x1e
+struct gatt_change_db_cmd {
+	uint16_t start_handle;
+	uint8_t visibility;
+} __packed;
+
+#define GATT_EATT_CONNECT		0x1f
+struct gatt_eatt_connect_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t num_channels;
+} __packed;
+
+#define GATT_READ_MULTIPLE_VAR		0x20
+
+#define GATT_NOTIFY_MULTIPLE		0x21
+struct gatt_cfg_notify_mult_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t cnt;
+	uint16_t attr_id[];
+} __packed;
+/* GATT events */
+#define GATT_EV_NOTIFICATION		0x80
+struct gatt_notification_ev {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t type;
+	uint16_t handle;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define GATT_EV_ATTR_VALUE_CHANGED	0x81
+struct gatt_attr_value_changed_ev {
+	uint16_t handle;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;

+ 136 - 0
example/tester/btp/btp_l2cap.h

@@ -0,0 +1,136 @@
+/* l2cap.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+/* L2CAP Service */
+/* commands */
+#define L2CAP_READ_SUPPORTED_COMMANDS	0x01
+struct l2cap_read_supported_commands_rp {
+	uint8_t data[0];
+} __packed;
+
+#define L2CAP_CONNECT_OPT_ECFC		0x01
+#define L2CAP_CONNECT_OPT_HOLD_CREDIT	0x02
+
+#define L2CAP_CONNECT			0x02
+struct l2cap_connect_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t psm;
+	uint16_t mtu;
+	uint8_t num;
+	uint8_t options;
+} __packed;
+struct l2cap_connect_rp {
+	uint8_t num;
+	uint8_t chan_id[];
+} __packed;
+
+#define L2CAP_DISCONNECT		0x03
+struct l2cap_disconnect_cmd {
+	uint8_t chan_id;
+} __packed;
+
+#define L2CAP_SEND_DATA			0x04
+struct l2cap_send_data_cmd {
+	uint8_t chan_id;
+	uint16_t data_len;
+	uint8_t data[];
+} __packed;
+
+#define L2CAP_TRANSPORT_BREDR		0x00
+#define L2CAP_TRANSPORT_LE		0x01
+
+#define L2CAP_CONNECTION_RESPONSE_SUCCESS		0x00
+#define L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHEN		0x01
+#define L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHOR		0x02
+#define L2CAP_CONNECTION_RESPONSE_INSUFF_ENC_KEY		0x03
+
+#define L2CAP_LISTEN			0x05
+struct l2cap_listen_cmd {
+	uint16_t psm;
+	uint8_t transport;
+	uint16_t mtu;
+	uint16_t response;
+} __packed;
+
+#define L2CAP_ACCEPT_CONNECTION		0x06
+struct l2cap_accept_connection_cmd {
+	uint8_t chan_id;
+	uint16_t result;
+} __packed;
+
+#define L2CAP_RECONFIGURE		0x07
+struct l2cap_reconfigure_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint16_t mtu;
+	uint8_t num;
+	uint8_t chan_id[];
+} __packed;
+
+#define L2CAP_CREDITS		0x08
+struct l2cap_credits_cmd {
+	uint8_t chan_id;
+} __packed;
+
+#define L2CAP_DISCONNECT_EATT_CHANS		0x09
+struct l2cap_disconnect_eatt_chans_cmd {
+	uint8_t address_type;
+	uint8_t address[6];
+	uint8_t count;
+} __packed;
+
+/* events */
+#define L2CAP_EV_CONNECTION_REQ		0x80
+struct l2cap_connection_req_ev {
+	uint8_t chan_id;
+	uint16_t psm;
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define L2CAP_EV_CONNECTED		0x81
+struct l2cap_connected_ev {
+	uint8_t chan_id;
+	uint16_t psm;
+	uint16_t mtu_remote;
+	uint16_t mps_remote;
+	uint16_t mtu_local;
+	uint16_t mps_local;
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define L2CAP_EV_DISCONNECTED		0x82
+struct l2cap_disconnected_ev {
+	uint16_t result;
+	uint8_t chan_id;
+	uint16_t psm;
+	uint8_t address_type;
+	uint8_t address[6];
+} __packed;
+
+#define L2CAP_EV_DATA_RECEIVED		0x83
+struct l2cap_data_received_ev {
+	uint8_t chan_id;
+	uint16_t data_length;
+	uint8_t data[];
+} __packed;
+
+#define L2CAP_EV_RECONFIGURED		0x84
+struct l2cap_reconfigured_ev {
+	uint8_t chan_id;
+	uint16_t mtu_remote;
+	uint16_t mps_remote;
+	uint16_t mtu_local;
+	uint16_t mps_local;
+} __packed;

+ 56 - 0
example/tester/btp/bttester.h

@@ -0,0 +1,56 @@
+/* bttester.h - Bluetooth tester headers */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ * Copyright (c) 2022 Codecoup
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/types.h>
+#include <base/util.h>
+#include <bluetooth/addr.h>
+
+static inline void tester_set_bit(uint8_t *addr, unsigned int bit)
+{
+	uint8_t *p = addr + (bit / 8U);
+
+	*p |= BIT(bit % 8);
+}
+
+static inline uint8_t tester_test_bit(const uint8_t *addr, unsigned int bit)
+{
+	const uint8_t *p = addr + (bit / 8U);
+
+	return *p & BIT(bit % 8);
+}
+
+void tester_init(void);
+void tester_rsp(uint8_t service, uint8_t opcode, uint8_t index, uint8_t status);
+void tester_send(uint8_t service, uint8_t opcode, uint8_t index, uint8_t *data,
+		 size_t len);
+
+uint8_t tester_init_gatt(void);
+uint8_t tester_unregister_gatt(void);
+void tester_handle_gatt(uint8_t opcode, uint8_t index, uint8_t *data,
+			uint16_t len);
+
+uint8_t tester_init_l2cap(void);
+uint8_t tester_unregister_l2cap(void);
+void tester_handle_l2cap(uint8_t opcode, uint8_t index, uint8_t *data,
+			 uint16_t len);
+
+uint8_t tester_init_mesh(void);
+uint8_t tester_unregister_mesh(void);
+void tester_handle_mesh(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len);
+
+uint8_t tester_init_vcp(void);
+uint8_t tester_unregister_vcp(void);
+void tester_handle_vcs(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len);
+void tester_handle_aics(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len);
+void tester_handle_vocs(uint8_t opcode, uint8_t index, uint8_t *data, uint16_t len);
+
+uint8_t tester_init_gap(void);
+uint8_t tester_unregister_gap(void);
+void tester_handle_gap(uint8_t opcode, uint8_t index, uint8_t *data,
+		       uint16_t len);

+ 1395 - 0
example/tester/btp_gap.c

@@ -0,0 +1,1395 @@
+/* gap.c - Bluetooth GAP Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/atomic.h>
+#include <base/types.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <base/common.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/conn.h>
+#include <bluetooth/gatt.h>
+#include <bluetooth/hci.h>
+
+#include <base/byteorder.h>
+#include <common/net_buf.h>
+
+#include "host/hci_core.h"
+
+#define LOG_MODULE_NAME bttester_gap
+#include <logging/bt_log.h>
+
+#include "btp/btp.h"
+
+#define CONTROLLER_INDEX 0
+#define CONTROLLER_NAME "btp_tester"
+
+#define BT_LE_AD_DISCOV_MASK (BT_LE_AD_LIMITED | BT_LE_AD_GENERAL)
+#define ADV_BUF_LEN (sizeof(struct gap_device_found_ev) + 2 * 31)
+
+static atomic_t current_settings;
+struct bt_conn_auth_cb cb;
+static uint8_t oob_legacy_tk[16] = { 0 };
+
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+static struct bt_le_oob oob_sc_local = { 0 };
+static struct bt_le_oob oob_sc_remote = { 0 };
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+
+/* connection parameters for rejection test */
+#define REJECT_INTERVAL_MIN 0x0C80
+#define REJECT_INTERVAL_MAX 0x0C80
+#define REJECT_LATENCY 0x0000
+#define REJECT_SUPERVISION_TIMEOUT 0x0C80
+
+#if defined(CONFIG_BT_PRIVACY)
+static struct {
+	bt_addr_le_t addr;
+	bool supported;
+} cars[CONFIG_BT_MAX_PAIRED];
+
+static uint8_t read_car_cb(struct bt_conn *conn, uint8_t err,
+			  struct bt_gatt_read_params *params, const void *data,
+			  uint16_t length);
+
+static struct bt_gatt_read_params read_car_params = {
+		.func = read_car_cb,
+		.by_uuid.uuid = BT_UUID_CENTRAL_ADDR_RES,
+		.by_uuid.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
+		.by_uuid.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
+};
+
+static uint8_t read_car_cb(struct bt_conn *conn, uint8_t err,
+			  struct bt_gatt_read_params *params, const void *data,
+			  uint16_t length)
+{
+	struct bt_conn_info info;
+	bool supported = false;
+
+	if (!err && data && length == 1) {
+		const uint8_t *tmp = data;
+
+		/* only 0 or 1 are valid values */
+		if (tmp[0] == 1) {
+			supported = true;
+		}
+	}
+
+	bt_conn_get_info(conn, &info);
+
+	for (int i = 0; i < CONFIG_BT_MAX_PAIRED; i++) {
+		if (bt_addr_le_eq(info.le.dst, &cars[i].addr)) {
+			cars[i].supported = supported;
+			break;
+		}
+	}
+
+	return BT_GATT_ITER_STOP;
+}
+#endif
+
+static void le_connected(struct bt_conn *conn, uint8_t err)
+{
+	struct gap_device_connected_ev ev;
+	struct bt_conn_info info;
+
+	if (err) {
+		return;
+	}
+
+	bt_conn_get_info(conn, &info);
+
+	memcpy(ev.address, info.le.dst->a.val, sizeof(ev.address));
+	ev.address_type = info.le.dst->type;
+	ev.interval = sys_cpu_to_le16(info.le.interval);
+	ev.latency = sys_cpu_to_le16(info.le.latency);
+	ev.timeout = sys_cpu_to_le16(info.le.timeout);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void le_disconnected(struct bt_conn *conn, uint8_t reason)
+{
+	struct gap_device_disconnected_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_DISCONNECTED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void le_identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa,
+				 const bt_addr_le_t *identity)
+{
+	struct gap_identity_resolved_ev ev;
+
+	ev.address_type = rpa->type;
+	memcpy(ev.address, rpa->a.val, sizeof(ev.address));
+
+	ev.identity_address_type = identity->type;
+	memcpy(ev.identity_address, identity->a.val,
+	       sizeof(ev.identity_address));
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_IDENTITY_RESOLVED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void le_param_updated(struct bt_conn *conn, uint16_t interval,
+			     uint16_t latency, uint16_t timeout)
+{
+	struct gap_conn_param_update_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+	ev.interval = sys_cpu_to_le16(interval);
+	ev.latency = sys_cpu_to_le16(latency);
+	ev.timeout = sys_cpu_to_le16(timeout);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_CONN_PARAM_UPDATE,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
+{
+	/* reject update if all parameters match reject pattern */
+	if ((param->interval_min == REJECT_INTERVAL_MIN) &&
+			(param->interval_max == REJECT_INTERVAL_MAX) &&
+			(param->latency == REJECT_LATENCY) &&
+			(param->timeout == REJECT_SUPERVISION_TIMEOUT)) {
+		return false;
+	}
+
+	return true;
+}
+
+static void le_security_changed(struct bt_conn *conn, bt_security_t level,
+				enum bt_security_err err)
+{
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+	struct gap_sec_level_changed_ev sec_ev;
+	struct gap_bond_lost_ev bond_ev;
+	struct bt_conn_info info;
+
+	switch (err) {
+	case BT_SECURITY_ERR_SUCCESS:
+		memcpy(sec_ev.address, addr->a.val, sizeof(sec_ev.address));
+		sec_ev.address_type = addr->type;
+		/* enum matches BTP values */
+		sec_ev.sec_level = level;
+
+		tester_send(BTP_SERVICE_ID_GAP, GAP_EV_SEC_LEVEL_CHANGED,
+			    CONTROLLER_INDEX, (uint8_t *) &sec_ev, sizeof(sec_ev));
+		break;
+	case BT_SECURITY_ERR_PIN_OR_KEY_MISSING:
+		/* for central role this means that peer have no LTK when we
+		 * started encryption procedure
+		 *
+		 * This means bond is lost and we restart pairing to re-bond
+		 */
+		if (bt_conn_get_info(conn, &info) == 0 &&
+		    info.role == BT_CONN_ROLE_CENTRAL) {
+			BT_DBG("Bond lost");
+
+			(void)memcpy(bond_ev.address, addr->a.val, sizeof(bond_ev.address));
+			bond_ev.address_type = addr->type;
+
+			tester_send(BTP_SERVICE_ID_GAP, GAP_EV_BOND_LOST,
+				    CONTROLLER_INDEX, (uint8_t *)&bond_ev, sizeof(bond_ev));
+
+			(void)bt_conn_set_security(conn, BT_SECURITY_L2 | BT_SECURITY_FORCE_PAIR);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static struct bt_conn_cb conn_callbacks = {
+	.connected = le_connected,
+	.disconnected = le_disconnected,
+	.identity_resolved = le_identity_resolved,
+	.le_param_updated = le_param_updated,
+	.le_param_req = le_param_req,
+	.security_changed = le_security_changed,
+};
+
+static void supported_commands(uint8_t *data, uint16_t len)
+{
+	uint8_t cmds[4];
+	struct gap_read_supported_commands_rp *rp = (void *) &cmds;
+
+	(void)memset(cmds, 0, sizeof(cmds));
+
+	tester_set_bit(cmds, GAP_READ_SUPPORTED_COMMANDS);
+	tester_set_bit(cmds, GAP_READ_CONTROLLER_INDEX_LIST);
+	tester_set_bit(cmds, GAP_READ_CONTROLLER_INFO);
+	tester_set_bit(cmds, GAP_SET_CONNECTABLE);
+	tester_set_bit(cmds, GAP_SET_DISCOVERABLE);
+	tester_set_bit(cmds, GAP_SET_BONDABLE);
+	tester_set_bit(cmds, GAP_START_ADVERTISING);
+	tester_set_bit(cmds, GAP_START_DIRECTED_ADV);
+	tester_set_bit(cmds, GAP_STOP_ADVERTISING);
+	tester_set_bit(cmds, GAP_START_DISCOVERY);
+	tester_set_bit(cmds, GAP_STOP_DISCOVERY);
+	tester_set_bit(cmds, GAP_CONNECT);
+	tester_set_bit(cmds, GAP_DISCONNECT);
+	tester_set_bit(cmds, GAP_SET_IO_CAP);
+	tester_set_bit(cmds, GAP_PAIR);
+	tester_set_bit(cmds, GAP_PASSKEY_ENTRY);
+	tester_set_bit(cmds, GAP_PASSKEY_CONFIRM);
+	tester_set_bit(cmds, GAP_CONN_PARAM_UPDATE);
+	tester_set_bit(cmds, GAP_SET_MITM);
+	tester_set_bit(cmds, GAP_OOB_LEGACY_SET_DATA);
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+	tester_set_bit(cmds, GAP_OOB_SC_GET_LOCAL_DATA);
+	tester_set_bit(cmds, GAP_OOB_SC_SET_REMOTE_DATA);
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+	tester_set_bit(cmds, GAP_SET_FILTER_LIST);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_READ_SUPPORTED_COMMANDS,
+		    CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds));
+}
+
+static void controller_index_list(uint8_t *data,  uint16_t len)
+{
+	struct gap_read_controller_index_list_rp *rp;
+	uint8_t buf[sizeof(*rp) + 1];
+
+	rp = (void *) buf;
+
+	rp->num = 1U;
+	rp->index[0] = CONTROLLER_INDEX;
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INDEX_LIST,
+		    BTP_INDEX_NONE, (uint8_t *) rp, sizeof(buf));
+}
+
+static void controller_info(uint8_t *data, uint16_t len)
+{
+	struct gap_read_controller_info_rp rp;
+	uint32_t supported_settings;
+
+	(void)memset(&rp, 0, sizeof(rp));
+
+	struct bt_le_oob oob_local = { 0 };
+
+	bt_le_oob_get_local(BT_ID_DEFAULT, &oob_local);
+	memcpy(rp.address, &oob_local.addr.a, sizeof(bt_addr_t));
+
+	/*
+	 * Re-use the oob data read here in get_oob_sc_local_data()
+	 */
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+	oob_sc_local = oob_local;
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+
+	/*
+	 * If privacy is used, the device uses random type address, otherwise
+	 * static random or public type address is used.
+	 */
+#if !defined(CONFIG_BT_PRIVACY)
+	if (oob_local.addr.type == BT_ADDR_LE_RANDOM) {
+		atomic_set_bit(&current_settings, GAP_SETTINGS_STATIC_ADDRESS);
+	}
+#endif /* CONFIG_BT_PRIVACY */
+
+	supported_settings = BIT(GAP_SETTINGS_POWERED);
+	supported_settings |= BIT(GAP_SETTINGS_CONNECTABLE);
+	supported_settings |= BIT(GAP_SETTINGS_BONDABLE);
+	supported_settings |= BIT(GAP_SETTINGS_LE);
+	supported_settings |= BIT(GAP_SETTINGS_ADVERTISING);
+
+	rp.supported_settings = sys_cpu_to_le32(supported_settings);
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	memcpy(rp.name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME));
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INFO,
+		    CONTROLLER_INDEX, (uint8_t *) &rp, sizeof(rp));
+}
+
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+static const char *oob_config_str(int oob_config)
+{
+	switch (oob_config) {
+	case BT_CONN_OOB_LOCAL_ONLY:
+		return "Local";
+	case BT_CONN_OOB_REMOTE_ONLY:
+		return "Remote";
+	case BT_CONN_OOB_BOTH_PEERS:
+		return "Local and Remote";
+	case BT_CONN_OOB_NO_DATA:
+	default:
+		return "no";
+	}
+}
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+
+static void oob_data_request(struct bt_conn *conn,
+			     struct bt_conn_oob_info *oob_info)
+{
+	struct bt_conn_info info;
+	int err = bt_conn_get_info(conn, &info);
+
+	if (err) {
+		return;
+	}
+
+	char addr[BT_ADDR_LE_STR_LEN];
+
+	bt_addr_le_to_str(info.le.dst, addr, sizeof(addr));
+
+	switch (oob_info->type) {
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+	case BT_CONN_OOB_LE_SC:
+	{
+		BT_DBG("Set %s OOB SC data for %s, ",
+			oob_config_str(oob_info->lesc.oob_config),
+			addr);
+
+		struct bt_le_oob_sc_data *oobd_local =
+			oob_info->lesc.oob_config != BT_CONN_OOB_REMOTE_ONLY ?
+				      &oob_sc_local.le_sc_data :
+				      NULL;
+
+		struct bt_le_oob_sc_data *oobd_remote =
+			oob_info->lesc.oob_config != BT_CONN_OOB_LOCAL_ONLY ?
+				      &oob_sc_remote.le_sc_data :
+				      NULL;
+
+		if (oobd_remote) {
+			/* Assume that oob_sc_remote
+			 * corresponds to the currently connected peer
+			 */
+			bt_addr_le_copy(&oob_sc_remote.addr, info.le.remote);
+		}
+
+		if (oobd_local &&
+		    !bt_addr_le_eq(info.le.local, &oob_sc_local.addr)) {
+			bt_addr_le_to_str(info.le.local, addr, sizeof(addr));
+			BT_DBG("No OOB data available for local %s",
+				addr);
+			bt_conn_auth_cancel(conn);
+			return;
+		}
+
+		err = bt_le_oob_set_sc_data(conn, oobd_local, oobd_remote);
+		if (err) {
+			BT_DBG("bt_le_oob_set_sc_data failed with: %d", err);
+		}
+
+		break;
+	}
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+
+#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
+	case BT_CONN_OOB_LE_LEGACY:
+		BT_DBG("Legacy OOB TK requested from remote %s", addr);
+
+		err = bt_le_oob_set_legacy_tk(conn, oob_legacy_tk);
+		if (err < 0) {
+			BT_ERR("Failed to set OOB TK: %d", err);
+		}
+
+		break;
+#endif /* !defined(CONFIG_BT_SMP_SC_PAIR_ONLY) */
+	default:
+		BT_ERR("Unhandled OOB type %d", oob_info->type);
+		break;
+	}
+}
+
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+static void get_oob_sc_local_data(void)
+{
+	cb.oob_data_request = oob_data_request;
+	struct gap_oob_sc_get_local_data_rp rp = { 0 };
+
+	memcpy(&rp.conf[0], &oob_sc_local.le_sc_data.c[0], sizeof(rp.conf));
+	memcpy(&rp.rand[0], &oob_sc_local.le_sc_data.r[0], sizeof(rp.rand));
+	tester_send(BTP_SERVICE_ID_GAP, GAP_OOB_SC_GET_LOCAL_DATA,
+		    CONTROLLER_INDEX, (uint8_t *)&rp, sizeof(rp));
+}
+
+static void set_oob_sc_remote_data(const uint8_t *data, uint16_t len)
+{
+	cb.oob_data_request = oob_data_request;
+	bt_set_oob_data_flag(true);
+
+	const struct gap_oob_sc_set_remote_data_cmd *cmd = (void *)data;
+
+	/* Note that the .addr field
+	 * will be set by the oob_data_request callback
+	 */
+	memcpy(&oob_sc_remote.le_sc_data.r[0], &cmd->rand[0],
+	       sizeof(oob_sc_remote.le_sc_data.r));
+	memcpy(&oob_sc_remote.le_sc_data.c[0], &cmd->conf[0],
+	       sizeof(oob_sc_remote.le_sc_data.c));
+
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_SC_SET_REMOTE_DATA,
+		   CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+
+static void set_connectable(uint8_t *data, uint16_t len)
+{
+	const struct gap_set_connectable_cmd *cmd = (void *) data;
+	struct gap_set_connectable_rp rp;
+
+	if (cmd->connectable) {
+		atomic_set_bit(&current_settings, GAP_SETTINGS_CONNECTABLE);
+	} else {
+		atomic_clear_bit(&current_settings, GAP_SETTINGS_CONNECTABLE);
+	}
+
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_SET_CONNECTABLE, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+}
+
+static uint8_t ad_flags = BT_LE_AD_NO_BREDR;
+static struct bt_data ad[10] = {
+	BT_DATA(BT_DATA_FLAGS, &ad_flags, sizeof(ad_flags)),
+};
+static struct bt_data sd[10];
+
+static void set_discoverable(uint8_t *data, uint16_t len)
+{
+	const struct gap_set_discoverable_cmd *cmd = (void *) data;
+	struct gap_set_discoverable_rp rp;
+
+	switch (cmd->discoverable) {
+	case GAP_NON_DISCOVERABLE:
+		ad_flags &= ~(BT_LE_AD_GENERAL | BT_LE_AD_LIMITED);
+		atomic_clear_bit(&current_settings, GAP_SETTINGS_DISCOVERABLE);
+		break;
+	case GAP_GENERAL_DISCOVERABLE:
+		ad_flags &= ~BT_LE_AD_LIMITED;
+		ad_flags |= BT_LE_AD_GENERAL;
+		atomic_set_bit(&current_settings, GAP_SETTINGS_DISCOVERABLE);
+		break;
+	case GAP_LIMITED_DISCOVERABLE:
+		ad_flags &= ~BT_LE_AD_GENERAL;
+		ad_flags |= BT_LE_AD_LIMITED;
+		atomic_set_bit(&current_settings, GAP_SETTINGS_DISCOVERABLE);
+		break;
+	default:
+		BT_WARN("unknown mode: 0x%x", cmd->discoverable);
+		tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		return;
+	}
+
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+}
+
+static void set_bondable(uint8_t *data, uint16_t len)
+{
+	const struct gap_set_bondable_cmd *cmd = (void *) data;
+	struct gap_set_bondable_rp rp;
+
+	BT_DBG("cmd->bondable: %d", cmd->bondable);
+
+	if (cmd->bondable) {
+		atomic_set_bit(&current_settings, GAP_SETTINGS_BONDABLE);
+	} else {
+		atomic_clear_bit(&current_settings, GAP_SETTINGS_BONDABLE);
+	}
+
+	bt_set_bondable(cmd->bondable);
+
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_SET_BONDABLE, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+}
+
+static void start_advertising(const uint8_t *data, uint16_t len)
+{
+	const struct gap_start_advertising_cmd *cmd = (void *) data;
+	struct gap_start_advertising_rp rp;
+	uint8_t adv_len, sd_len;
+	bool adv_conn;
+	int i;
+
+			BT_ERR("start_advertising 1");
+	for (i = 0, adv_len = 1U; i < cmd->adv_data_len; adv_len++) {
+		if (adv_len >= ARRAY_SIZE(ad)) {
+			BT_ERR("ad[] Out of memory");
+			goto fail;
+		}
+
+		ad[adv_len].type = cmd->adv_sr_data[i++];
+		ad[adv_len].data_len = cmd->adv_sr_data[i++];
+		ad[adv_len].data = &cmd->adv_sr_data[i];
+		i += ad[adv_len].data_len;
+	}
+			BT_ERR("start_advertising 2");
+
+	for (sd_len = 0U; i < cmd->adv_data_len+cmd->scan_rsp_len; sd_len++) {
+		if (sd_len >= ARRAY_SIZE(sd)) {
+			BT_ERR("sd[] Out of memory");
+			goto fail;
+		}
+
+		sd[sd_len].type = cmd->adv_sr_data[i++];
+		sd[sd_len].data_len = cmd->adv_sr_data[i++];
+		sd[sd_len].data = &cmd->adv_sr_data[i];
+		i += sd[sd_len].data_len;
+	}
+
+			BT_ERR("start_advertising 3");
+	adv_conn = atomic_test_bit(&current_settings, GAP_SETTINGS_CONNECTABLE);
+
+			BT_ERR("start_advertising 4");
+	/* BTP API don't allow to set empty scan response data. */
+	if (bt_le_adv_start(adv_conn ? BT_LE_ADV_CONN : BT_LE_ADV_NCONN,
+			    ad, adv_len, sd_len ? sd : NULL, sd_len) < 0) {
+		BT_ERR("Failed to start advertising");
+		goto fail;
+	}
+
+			BT_ERR("start_advertising 5");
+	atomic_set_bit(&current_settings, GAP_SETTINGS_ADVERTISING);
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+			BT_ERR("start_advertising 6");
+	tester_send(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+	return;
+fail:
+			BT_ERR("start_advertising 7");
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void start_directed_advertising(const uint8_t *data, uint16_t len)
+{
+	const struct gap_start_directed_adv_cmd *cmd = (void *)data;
+	struct gap_start_directed_adv_rp rp;
+	struct bt_le_adv_param adv_param;
+	uint16_t options = sys_le16_to_cpu(cmd->options);
+	const bt_addr_le_t *peer = (bt_addr_le_t *)data;
+
+	adv_param = *BT_LE_ADV_CONN_DIR(peer);
+
+	if (!(options & GAP_START_DIRECTED_ADV_HD)) {
+		adv_param.options |= BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY;
+		adv_param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
+		adv_param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
+	}
+
+	if (options & GAP_START_DIRECTED_ADV_PEER_RPA) {
+#if defined(CONFIG_BT_PRIVACY)
+		/* check if peer supports Central Address Resolution */
+		for (int i = 0; i < CONFIG_BT_MAX_PAIRED; i++) {
+			if (bt_addr_le_eq(peer, &cars[i].addr)) {
+				if (cars[i].supported) {
+					adv_param.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA;
+				}
+			}
+		}
+#endif
+	}
+
+	if (bt_le_adv_start(&adv_param, NULL, 0, NULL, 0) < 0) {
+		BT_ERR("Failed to start advertising");
+		goto fail;
+	}
+
+	atomic_set_bit(&current_settings, GAP_SETTINGS_ADVERTISING);
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_START_DIRECTED_ADV,
+		    CONTROLLER_INDEX, (uint8_t *)&rp, sizeof(rp));
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DIRECTED_ADV, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void stop_advertising(const uint8_t *data, uint16_t len)
+{
+	struct gap_stop_advertising_rp rp;
+	int err;
+
+	err = bt_le_adv_stop();
+	if (err < 0) {
+		tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		BT_ERR("Failed to stop advertising: %d", err);
+		return;
+	}
+
+	atomic_clear_bit(&current_settings, GAP_SETTINGS_ADVERTISING);
+	rp.current_settings = sys_cpu_to_le32(current_settings);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+}
+
+static uint8_t get_ad_flags(struct net_buf_simple *ad)
+{
+	uint8_t len, i;
+
+	/* Parse advertisement to get flags */
+	for (i = 0U; i < ad->len; i += len - 1) {
+		len = ad->data[i++];
+		if (!len) {
+			break;
+		}
+
+		/* Check if field length is correct */
+		if (len > (ad->len - i) || (ad->len - i) < 1) {
+			break;
+		}
+
+		switch (ad->data[i++]) {
+		case BT_DATA_FLAGS:
+			return ad->data[i];
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static uint8_t discovery_flags;
+static struct net_buf_simple *adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN);
+
+static void store_adv(const bt_addr_le_t *addr, int8_t rssi,
+		      struct net_buf_simple *ad)
+{
+	struct gap_device_found_ev *ev;
+
+	/* cleanup */
+	net_buf_simple_init(adv_buf, 0);
+
+	ev = net_buf_simple_add(adv_buf, sizeof(*ev));
+
+	memcpy(ev->address, addr->a.val, sizeof(ev->address));
+	ev->address_type = addr->type;
+	ev->rssi = rssi;
+	ev->flags = GAP_DEVICE_FOUND_FLAG_AD | GAP_DEVICE_FOUND_FLAG_RSSI;
+	ev->eir_data_len = ad->len;
+	memcpy(net_buf_simple_add(adv_buf, ad->len), ad->data, ad->len);
+}
+
+static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t evtype,
+			 struct net_buf_simple *ad)
+{
+	/* if General/Limited Discovery - parse Advertising data to get flags */
+	if (!(discovery_flags & GAP_DISCOVERY_FLAG_LE_OBSERVE) &&
+	    (evtype != BT_GAP_ADV_TYPE_SCAN_RSP)) {
+		uint8_t flags = get_ad_flags(ad);
+
+		/* ignore non-discoverable devices */
+		if (!(flags & BT_LE_AD_DISCOV_MASK)) {
+			BT_DBG("Non discoverable, skipping");
+			return;
+		}
+
+		/* if Limited Discovery - ignore general discoverable devices */
+		if ((discovery_flags & GAP_DISCOVERY_FLAG_LIMITED) &&
+		    !(flags & BT_LE_AD_LIMITED)) {
+			BT_DBG("General discoverable, skipping");
+			return;
+		}
+	}
+
+	/* attach Scan Response data */
+	if (evtype == BT_GAP_ADV_TYPE_SCAN_RSP) {
+		struct gap_device_found_ev *ev;
+		bt_addr_le_t a;
+
+		/* skip if there is no pending advertisement */
+		if (!adv_buf->len) {
+			BT_INFO("No pending advertisement, skipping");
+			return;
+		}
+
+		ev = (void *) adv_buf->data;
+		a.type = ev->address_type;
+		memcpy(a.a.val, ev->address, sizeof(a.a.val));
+
+		/*
+		 * in general, the Scan Response comes right after the
+		 * Advertisement, but if not if send stored event and ignore
+		 * this one
+		 */
+		if (!bt_addr_le_eq(addr, &a)) {
+			BT_INFO("Address does not match, skipping");
+			goto done;
+		}
+
+		ev->eir_data_len += ad->len;
+		ev->flags |= GAP_DEVICE_FOUND_FLAG_SD;
+
+		memcpy(net_buf_simple_add(adv_buf, ad->len), ad->data, ad->len);
+
+		goto done;
+	}
+
+	/*
+	 * if there is another pending advertisement, send it and store the
+	 * current one
+	 */
+	if (adv_buf->len) {
+		tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
+			    CONTROLLER_INDEX, adv_buf->data, adv_buf->len);
+		net_buf_simple_reset(adv_buf);
+	}
+
+	store_adv(addr, rssi, ad);
+
+	/* if Active Scan and scannable event - wait for Scan Response */
+	if ((discovery_flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) &&
+	    (evtype == BT_GAP_ADV_TYPE_ADV_IND ||
+	     evtype == BT_GAP_ADV_TYPE_ADV_SCAN_IND)) {
+		BT_DBG("Waiting for scan response");
+		return;
+	}
+done:
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
+		    CONTROLLER_INDEX, adv_buf->data, adv_buf->len);
+	net_buf_simple_reset(adv_buf);
+}
+
+static void start_discovery(const uint8_t *data, uint16_t len)
+{
+	const struct gap_start_discovery_cmd *cmd = (void *) data;
+	uint8_t status;
+
+	/* only LE scan is supported */
+	if (cmd->flags & GAP_DISCOVERY_FLAG_BREDR) {
+		status = BTP_STATUS_FAILED;
+		BT_WARN("BR/EDR not supported");
+		goto reply;
+	}
+
+	if (bt_le_scan_start(cmd->flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN ?
+			     BT_LE_SCAN_ACTIVE : BT_LE_SCAN_PASSIVE,
+			     device_found) < 0) {
+		status = BTP_STATUS_FAILED;
+		BT_ERR("Failed to start scanning");
+		goto reply;
+	}
+
+	net_buf_simple_init(adv_buf, 0);
+	discovery_flags = cmd->flags;
+
+	status = BTP_STATUS_SUCCESS;
+reply:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DISCOVERY, CONTROLLER_INDEX,
+		   status);
+}
+
+static void stop_discovery(const uint8_t *data, uint16_t len)
+{
+	uint8_t status = BTP_STATUS_SUCCESS;
+	int err;
+
+	err = bt_le_scan_stop();
+	if (err < 0) {
+		BT_ERR("Failed to stop scanning: %d", err);
+		status = BTP_STATUS_FAILED;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_DISCOVERY, CONTROLLER_INDEX,
+		   status);
+}
+
+static void connect(const uint8_t *data, uint16_t len)
+{
+	const bt_addr_le_t *addr = (const bt_addr_le_t *)data;
+	uint8_t status;
+	int err;
+
+	if (!bt_addr_le_eq(addr, BT_ADDR_LE_ANY)) {
+		struct bt_conn *conn;
+
+		err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
+					BT_LE_CONN_PARAM_DEFAULT, &conn);
+		if (err) {
+			BT_ERR("Failed to create connection (%d)", err);
+			status = BTP_STATUS_FAILED;
+			goto rsp;
+		}
+
+		bt_conn_unref(conn);
+	} else {
+		err = bt_conn_le_create_auto(BT_CONN_LE_CREATE_CONN,
+					     BT_LE_CONN_PARAM_DEFAULT);
+		if (err) {
+			BT_ERR("Failed to create auto connection (%d)", err);
+			status = BTP_STATUS_FAILED;
+			goto rsp;
+		}
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONNECT, CONTROLLER_INDEX, status);
+}
+
+static void disconnect(const uint8_t *data, uint16_t len)
+{
+	struct bt_conn *conn;
+	uint8_t status;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		status = BTP_STATUS_FAILED;
+		BT_ERR("Unknown connection");
+		goto rsp;
+	}
+
+	if (bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) {
+		BT_ERR("Failed to disconnect");
+		status = BTP_STATUS_FAILED;
+	} else {
+		status = BTP_STATUS_SUCCESS;
+	}
+
+	bt_conn_unref(conn);
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_DISCONNECT, CONTROLLER_INDEX,
+		   status);
+}
+
+static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
+{
+	struct gap_passkey_display_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+	ev.passkey = sys_cpu_to_le32(passkey);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_DISPLAY,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_entry(struct bt_conn *conn)
+{
+	struct gap_passkey_entry_req_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_ENTRY_REQ,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey)
+{
+	struct gap_passkey_confirm_req_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+	ev.passkey = sys_cpu_to_le32(passkey);
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_CONFIRM_REQ,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+}
+
+static void auth_cancel(struct bt_conn *conn)
+{
+	/* TODO */
+}
+
+enum bt_security_err auth_pairing_accept(struct bt_conn *conn,
+					 const struct bt_conn_pairing_feat *const feat)
+{
+	struct gap_bond_lost_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	if (!bt_addr_le_is_bonded(BT_ID_DEFAULT, addr)) {
+		return BT_SECURITY_ERR_SUCCESS;
+	}
+
+	/* If a peer is already bonded and tries to pair again then it means that
+	 * the it has lost its bond information.
+	 */
+	BT_DBG("Bond lost");
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_BOND_LOST, CONTROLLER_INDEX, (uint8_t *)&ev,
+		    sizeof(ev));
+
+	return BT_SECURITY_ERR_SUCCESS;
+}
+
+void auth_pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
+{
+	struct gap_bond_pairing_failed_ev ev;
+	const bt_addr_le_t *addr = bt_conn_get_dst(conn);
+
+	memcpy(ev.address, addr->a.val, sizeof(ev.address));
+	ev.address_type = addr->type;
+	ev.reason = reason;
+
+	tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PAIRING_FAILED, CONTROLLER_INDEX,
+		    (uint8_t *)&ev, sizeof(ev));
+}
+
+static void auth_pairing_complete(struct bt_conn *conn, bool bonded)
+{
+#if defined(CONFIG_BT_PRIVACY)
+	/* Read peer's Central Address Resolution if bonded */
+	if (bonded) {
+		bt_gatt_read(conn, &read_car_params);
+	}
+#endif
+}
+
+static struct bt_conn_auth_info_cb auth_info_cb = {
+	.pairing_failed = auth_pairing_failed,
+	.pairing_complete = auth_pairing_complete,
+};
+
+static void set_io_cap(const uint8_t *data, uint16_t len)
+{
+	const struct gap_set_io_cap_cmd *cmd = (void *) data;
+	uint8_t status;
+
+	/* Reset io cap requirements */
+	(void)memset(&cb, 0, sizeof(cb));
+	bt_conn_auth_cb_register(NULL);
+
+	BT_DBG("io_cap: %d", cmd->io_cap);
+
+	switch (cmd->io_cap) {
+	case GAP_IO_CAP_DISPLAY_ONLY:
+		cb.cancel = auth_cancel;
+		cb.passkey_display = auth_passkey_display;
+		break;
+	case GAP_IO_CAP_KEYBOARD_DISPLAY:
+		cb.cancel = auth_cancel;
+		cb.passkey_display = auth_passkey_display;
+		cb.passkey_entry = auth_passkey_entry;
+		cb.passkey_confirm = auth_passkey_confirm;
+		break;
+	case GAP_IO_CAP_NO_INPUT_OUTPUT:
+		cb.cancel = auth_cancel;
+		break;
+	case GAP_IO_CAP_KEYBOARD_ONLY:
+		cb.cancel = auth_cancel;
+		cb.passkey_entry = auth_passkey_entry;
+		break;
+	case GAP_IO_CAP_DISPLAY_YESNO:
+		cb.cancel = auth_cancel;
+		cb.passkey_display = auth_passkey_display;
+		cb.passkey_confirm = auth_passkey_confirm;
+		break;
+	default:
+		BT_WARN("Unhandled io_cap: 0x%x", cmd->io_cap);
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+//	cb.pairing_accept = auth_pairing_accept;
+
+	if (bt_conn_auth_cb_register(&cb)) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_IO_CAP, CONTROLLER_INDEX,
+		   status);
+}
+
+static void pair(const uint8_t *data, uint16_t len)
+{
+	struct bt_conn *conn;
+	uint8_t status;
+	int err;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	err = bt_conn_set_security(conn, BT_SECURITY_L2);
+	if (err < 0) {
+		BT_ERR("Failed to set security: %d", err);
+		status = BTP_STATUS_FAILED;
+		bt_conn_unref(conn);
+		goto rsp;
+	}
+
+	bt_conn_unref(conn);
+	status = BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_PAIR, CONTROLLER_INDEX, status);
+}
+
+static void unpair(const uint8_t *data, uint16_t len)
+{
+	struct gap_unpair_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	bt_addr_le_t addr;
+	uint8_t status;
+	int err;
+
+	addr.type = cmd->address_type;
+	memcpy(addr.a.val, cmd->address, sizeof(addr.a.val));
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &addr);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		goto keys;
+	}
+
+	err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
+
+	bt_conn_unref(conn);
+
+	if (err < 0) {
+		BT_ERR("Failed to disconnect: %d", err);
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+keys:
+	err = bt_unpair(BT_ID_DEFAULT, &addr);
+
+	status = err < 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_UNPAIR, CONTROLLER_INDEX, status);
+}
+
+static void passkey_entry(const uint8_t *data, uint16_t len)
+{
+	const struct gap_passkey_entry_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	uint8_t status;
+	int err;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	err = bt_conn_auth_passkey_entry(conn, sys_le32_to_cpu(cmd->passkey));
+	if (err < 0) {
+		BT_ERR("Failed to enter passkey: %d", err);
+	}
+
+	bt_conn_unref(conn);
+	status = err < 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_ENTRY, CONTROLLER_INDEX,
+		   status);
+}
+
+static void passkey_confirm(const uint8_t *data, uint16_t len)
+{
+	const struct gap_passkey_confirm_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	uint8_t status;
+	int err;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	if (cmd->match) {
+		err = bt_conn_auth_passkey_confirm(conn);
+		if (err < 0) {
+			BT_ERR("Failed to confirm passkey: %d", err);
+		}
+	} else {
+		err = bt_conn_auth_cancel(conn);
+		if (err < 0) {
+			BT_ERR("Failed to cancel auth: %d", err);
+		}
+	}
+
+	bt_conn_unref(conn);
+	status = err < 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_CONFIRM, CONTROLLER_INDEX,
+		   status);
+}
+
+
+static void conn_param_update(const uint8_t *data, uint16_t len)
+{
+	const struct gap_conn_param_update_cmd *cmd = (void *) data;
+	struct bt_le_conn_param param = {
+		.interval_min = sys_le16_to_cpu(cmd->interval_min),
+		.interval_max = sys_le16_to_cpu(cmd->interval_max),
+		.latency = sys_le16_to_cpu(cmd->latency),
+		.timeout = sys_le16_to_cpu(cmd->timeout),
+	};
+	struct bt_conn *conn;
+	uint8_t status = BTP_STATUS_FAILED;
+	int err;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		goto rsp;
+	}
+
+	err = bt_conn_le_param_update(conn, &param);
+	if (err < 0) {
+		BT_ERR("Failed to update params: %d", err);
+	}
+
+	bt_conn_unref(conn);
+	status = err < 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONN_PARAM_UPDATE, CONTROLLER_INDEX,
+		   status);
+}
+
+static void set_mitm(const uint8_t *data, uint16_t len)
+{
+	BT_WARN("Use CONFIG_BT_SMP_ENFORCE_MITM instead");
+
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_MITM, CONTROLLER_INDEX,
+		   BTP_STATUS_SUCCESS);
+}
+
+static void set_oob_legacy_data(const uint8_t *data, uint16_t len)
+{
+	const struct gap_oob_legacy_set_data_cmd *cmd = (void *) data;
+
+	memcpy(oob_legacy_tk, cmd->oob_data, 16);
+
+	bt_set_oob_data_flag(true);
+	cb.oob_data_request = oob_data_request;
+
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_OOB_LEGACY_SET_DATA,
+		   CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
+}
+
+static void set_filter_list(const uint8_t *data, uint16_t len)
+{
+	const struct gap_set_filter_list *cmd = (const void *) data;
+	uint8_t status;
+	int err;
+
+	if (len < sizeof(*cmd) ||
+	    len != (sizeof(*cmd) + (cmd->cnt * sizeof(cmd->addr[0])))) {
+		status = BTP_STATUS_FAILED;
+		goto failed;
+	}
+
+	(void)bt_le_filter_accept_list_clear();
+
+	for (int i = 0; i < cmd->cnt; i++) {
+		err = bt_le_filter_accept_list_add(&cmd->addr[i]);
+		if (err < 0) {
+			status = BTP_STATUS_FAILED;
+			goto failed;
+		}
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+failed:
+	tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_FILTER_LIST,
+		   CONTROLLER_INDEX, status);
+}
+
+void tester_handle_gap(uint8_t opcode, uint8_t index, uint8_t *data,
+		       uint16_t len)
+{
+	BT_DBG("opcode111: 0x%x", opcode);
+	switch (opcode) {
+	case GAP_READ_SUPPORTED_COMMANDS:
+	case GAP_READ_CONTROLLER_INDEX_LIST:
+		if (index != BTP_INDEX_NONE){
+			tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+				   BTP_STATUS_FAILED);
+			BT_WARN("index != BTP_INDEX_NONE: opcode: 0x%x "
+				"index: 0x%x", opcode, index);
+			return;
+		}
+		break;
+	default:
+		BT_DBG("index1111: 0x%02x", index);
+		if (index != CONTROLLER_INDEX){
+			tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+				   BTP_STATUS_FAILED);
+			BT_WARN("index != CONTROLLER_INDEX: opcode: 0x%x "
+				 "index: 0x%x", opcode, index);
+			return;
+		}
+		break;
+	}
+
+	BT_DBG("opcode1: 0x%02x", opcode);
+	switch (opcode) {
+	case GAP_READ_SUPPORTED_COMMANDS:
+		supported_commands(data, len);
+		return;
+	case GAP_READ_CONTROLLER_INDEX_LIST:
+		controller_index_list(data, len);
+		return;
+	case GAP_READ_CONTROLLER_INFO:
+		controller_info(data, len);
+		return;
+	case GAP_SET_CONNECTABLE:
+		set_connectable(data, len);
+		return;
+	case GAP_SET_DISCOVERABLE:
+		set_discoverable(data, len);
+		return;
+	case GAP_SET_BONDABLE:
+		set_bondable(data, len);
+		return;
+	case GAP_START_ADVERTISING:
+		start_advertising(data, len);
+		return;
+	case GAP_START_DIRECTED_ADV:
+		start_directed_advertising(data, len);
+		return;
+	case GAP_STOP_ADVERTISING:
+		stop_advertising(data, len);
+		return;
+	case GAP_START_DISCOVERY:
+		start_discovery(data, len);
+		return;
+	case GAP_STOP_DISCOVERY:
+		stop_discovery(data, len);
+		return;
+	case GAP_CONNECT:
+		connect(data, len);
+		return;
+	case GAP_DISCONNECT:
+		disconnect(data, len);
+		return;
+	case GAP_SET_IO_CAP:
+		set_io_cap(data, len);
+		return;
+	case GAP_PAIR:
+		pair(data, len);
+		return;
+	case GAP_UNPAIR:
+		unpair(data, len);
+		return;
+	case GAP_PASSKEY_ENTRY:
+		passkey_entry(data, len);
+		return;
+	case GAP_PASSKEY_CONFIRM:
+		passkey_confirm(data, len);
+		return;
+	case GAP_CONN_PARAM_UPDATE:
+		conn_param_update(data, len);
+		return;
+	case GAP_SET_MITM:
+		set_mitm(data, len);
+		return;
+	case GAP_OOB_LEGACY_SET_DATA:
+		set_oob_legacy_data(data, len);
+		return;
+#if !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)
+	case GAP_OOB_SC_GET_LOCAL_DATA:
+		get_oob_sc_local_data();
+		return;
+	case GAP_OOB_SC_SET_REMOTE_DATA:
+		set_oob_sc_remote_data(data, len);
+		return;
+#endif /* !defined(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY) */
+	case GAP_SET_FILTER_LIST:
+		set_filter_list(data, len);
+		return;
+	default:
+		BT_WARN("Unknown opcode: 0x%x", opcode);
+		tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
+			   BTP_STATUS_UNKNOWN_CMD);
+		return;
+	}
+}
+
+static void tester_init_gap_cb(int err)
+{
+	if (err) {
+		tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE,
+			   BTP_INDEX_NONE, BTP_STATUS_FAILED);
+		BT_WARN("Error: %d", err);
+		return;
+	}
+
+	atomic_clear(&current_settings);
+	atomic_set_bit(&current_settings, GAP_SETTINGS_POWERED);
+	atomic_set_bit(&current_settings, GAP_SETTINGS_CONNECTABLE);
+	atomic_set_bit(&current_settings, GAP_SETTINGS_BONDABLE);
+	atomic_set_bit(&current_settings, GAP_SETTINGS_LE);
+#if defined(CONFIG_BT_PRIVACY)
+	atomic_set_bit(&current_settings, GAP_SETTINGS_PRIVACY);
+#endif /* CONFIG_BT_PRIVACY */
+
+	bt_conn_cb_register(&conn_callbacks);
+
+	tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
+		   BTP_STATUS_SUCCESS);
+}
+
+uint8_t tester_init_gap(void)
+{
+	int err;
+
+// 	(void)memset(&cb, 0, sizeof(cb));
+// 	bt_conn_auth_cb_register(NULL);
+// //	cb.pairing_accept = auth_pairing_accept;
+// 	if (bt_conn_auth_cb_register(&cb)) {
+// 		return BTP_STATUS_FAILED;
+// 	}
+// 	bt_conn_auth_info_cb_register(&auth_info_cb);
+
+// 	err = bt_enable(tester_init_gap_cb);
+// 	if (err < 0) {
+// 		BT_ERR("Unable to enable Bluetooth: %d", err);
+// 		return BTP_STATUS_FAILED;
+// 	}
+
+	tester_init_gap_cb(0);
+
+	return BTP_STATUS_SUCCESS;
+}
+
+uint8_t tester_unregister_gap(void)
+{
+	return BTP_STATUS_SUCCESS;
+}

+ 2360 - 0
example/tester/btp_gatt.c

@@ -0,0 +1,2360 @@
+/* gatt.c - Bluetooth GATT Server Tester */
+
+/*
+ * Copyright (c) 2015-2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <base/types.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <base/common.h>
+#include <bluetooth/att.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/conn.h>
+#include <bluetooth/gatt.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/l2cap.h>
+#include <base/byteorder.h>
+#include <base/printk.h>
+#include <base/__assert.h>
+#include <common/net_buf.h>
+
+#include <common/bt_buf.h>
+
+#define LOG_MODULE_NAME bttester_gatt
+#include <logging/bt_log.h>
+
+#include "btp/btp.h"
+
+#define CONTROLLER_INDEX 0
+#define MAX_BUFFER_SIZE 2048
+#define MAX_UUID_LEN 16
+
+#define MAX_SUBSCRIPTIONS 2
+
+#define UNUSED_SUBSCRIBE_CCC_HANDLE 0x0000
+
+/* This masks Permission bits from GATT API */
+#define GATT_PERM_MASK		(BT_GATT_PERM_READ | \
+					 BT_GATT_PERM_READ_AUTHEN | \
+					 BT_GATT_PERM_READ_ENCRYPT | \
+					 BT_GATT_PERM_WRITE | \
+					 BT_GATT_PERM_WRITE_AUTHEN | \
+					 BT_GATT_PERM_WRITE_ENCRYPT | \
+					 BT_GATT_PERM_PREPARE_WRITE)
+#define GATT_PERM_ENC_READ_MASK	(BT_GATT_PERM_READ_ENCRYPT | \
+					 BT_GATT_PERM_READ_AUTHEN)
+#define GATT_PERM_ENC_WRITE_MASK	(BT_GATT_PERM_WRITE_ENCRYPT | \
+					 BT_GATT_PERM_WRITE_AUTHEN)
+#define GATT_PERM_READ_AUTHORIZATION	0x40
+#define GATT_PERM_WRITE_AUTHORIZATION	0x80
+
+/* GATT server context */
+#define SERVER_MAX_SERVICES		10
+#define SERVER_MAX_ATTRIBUTES		50
+#define SERVER_BUF_SIZE			2048
+#define MAX_CCC_COUNT			2
+
+/* bt_gatt_attr_next cannot be used on non-registered services */
+#define NEXT_DB_ATTR(attr) (attr + 1)
+#define LAST_DB_ATTR (server_db + (attr_count - 1))
+
+#define server_buf_push(_len)	net_buf_push(server_buf, ROUND_UP(_len, 4))
+#define server_buf_pull(_len)	net_buf_pull(server_buf, ROUND_UP(_len, 4))
+
+static struct bt_gatt_service server_svcs[SERVER_MAX_SERVICES];
+static const struct bt_gatt_service_static *sevice_list[SERVER_MAX_SERVICES];
+static struct bt_gatt_attr server_db[SERVER_MAX_ATTRIBUTES];
+static struct net_buf *server_buf;
+SPOOL_DEFINE(server_pool, 1, SERVER_BUF_SIZE, 0);
+
+static uint8_t attr_count;
+static uint8_t svc_attr_count;
+static uint8_t svc_count;
+static bool ccc_added;
+
+/*
+ * gatt_buf - cache used by a gatt client (to cache data read/discovered)
+ * and gatt server (to store attribute user_data).
+ * It is not intended to be used by client and server at the same time.
+ */
+static struct {
+	uint16_t len;
+	uint8_t buf[MAX_BUFFER_SIZE];
+} gatt_buf;
+
+struct get_attr_data {
+	struct net_buf_simple *buf;
+	struct bt_conn *conn;
+};
+
+struct ccc_value {
+	struct bt_gatt_attr *attr;
+	struct bt_gatt_attr *ccc;
+	uint8_t value;
+};
+
+static struct ccc_value ccc_values[MAX_CCC_COUNT];
+
+static int ccc_find_by_attr(uint16_t handle)
+{
+	for (int i = 0; i < MAX_CCC_COUNT; i++) {
+
+		if (ccc_values[i].attr == NULL){
+			continue;
+		}
+		if (handle == ccc_values[i].attr->handle) {
+			return i;
+		}
+	}
+
+	return -ENOENT;
+}
+
+static int ccc_find_by_ccc(const struct bt_gatt_attr *attr)
+{
+	for (int i = 0; i < MAX_CCC_COUNT; i++) {
+		if (attr == ccc_values[i].ccc) {
+			return i;
+		}
+	}
+
+	return -ENOENT;
+}
+
+static void *gatt_buf_add(const void *data, size_t len)
+{
+	void *ptr = gatt_buf.buf + gatt_buf.len;
+
+	if ((len + gatt_buf.len) > MAX_BUFFER_SIZE) {
+		return NULL;
+	}
+
+	if (data) {
+		memcpy(ptr, data, len);
+	} else {
+		(void)memset(ptr, 0, len);
+	}
+
+	gatt_buf.len += len;
+
+	BT_DBG("%d/%d used", gatt_buf.len, MAX_BUFFER_SIZE);
+
+	return ptr;
+}
+
+static void *gatt_buf_reserve(size_t len)
+{
+	return gatt_buf_add(NULL, len);
+}
+
+static void gatt_buf_clear(void)
+{
+	(void)memset(&gatt_buf, 0, sizeof(gatt_buf));
+}
+
+union uuid {
+	struct bt_uuid uuid;
+	struct bt_uuid_16 u16;
+	struct bt_uuid_128 u128;
+};
+
+static struct bt_gatt_attr *gatt_db_add(const struct bt_gatt_attr *pattern,
+					size_t user_data_len)
+{
+	static struct bt_gatt_attr *attr = server_db;
+	const union uuid *u = CONTAINER_OF(pattern->uuid, union uuid, uuid);
+	size_t uuid_size = u->uuid.type == BT_UUID_TYPE_16 ? sizeof(u->u16) :
+							     sizeof(u->u128);
+
+	/* Return NULL if database is full */
+	if (attr == &server_db[SERVER_MAX_ATTRIBUTES - 1]) {
+		return NULL;
+	}
+
+	/* First attribute in db must be service */
+	if (!svc_count) {
+		return NULL;
+	}
+
+	memcpy(attr, pattern, sizeof(*attr));
+
+	/* Store the UUID. */
+	attr->uuid = server_buf_push(uuid_size);
+	memcpy((void *) attr->uuid, &u->uuid, uuid_size);
+
+	/* Copy user_data to the buffer. */
+	if (user_data_len) {
+		attr->user_data = server_buf_push(user_data_len);
+		memcpy(attr->user_data, pattern->user_data, user_data_len);
+	}
+
+	BT_DBG("handle 0x%04x", attr->handle);
+
+	attr_count++;
+	svc_attr_count++;
+
+	return attr++;
+}
+
+/* Convert UUID from BTP command to bt_uuid */
+static uint8_t btp2bt_uuid(const uint8_t *uuid, uint8_t len,
+			   struct bt_uuid *bt_uuid)
+{
+	uint16_t le16;
+
+	switch (len) {
+	case 0x02: /* UUID 16 */
+		bt_uuid->type = BT_UUID_TYPE_16;
+		memcpy(&le16, uuid, sizeof(le16));
+		BT_UUID_16(bt_uuid)->val = sys_le16_to_cpu(le16);
+		break;
+	case 0x10: /* UUID 128*/
+		bt_uuid->type = BT_UUID_TYPE_128;
+		memcpy(BT_UUID_128(bt_uuid)->val, uuid, 16);
+		break;
+	default:
+		return BTP_STATUS_FAILED;
+	}
+
+	return BTP_STATUS_SUCCESS;
+}
+
+static void supported_commands(uint8_t *data, uint16_t len)
+{
+	uint8_t cmds[5];
+	struct gatt_read_supported_commands_rp *rp = (void *) cmds;
+
+	(void)memset(cmds, 0, sizeof(cmds));
+
+	tester_set_bit(cmds, GATT_READ_SUPPORTED_COMMANDS);
+	tester_set_bit(cmds, GATT_ADD_SERVICE);
+	tester_set_bit(cmds, GATT_ADD_CHARACTERISTIC);
+	tester_set_bit(cmds, GATT_ADD_DESCRIPTOR);
+	tester_set_bit(cmds, GATT_ADD_INCLUDED_SERVICE);
+	tester_set_bit(cmds, GATT_SET_VALUE);
+	tester_set_bit(cmds, GATT_START_SERVER);
+	tester_set_bit(cmds, GATT_SET_ENC_KEY_SIZE);
+	tester_set_bit(cmds, GATT_EXCHANGE_MTU);
+	tester_set_bit(cmds, GATT_DISC_PRIM_UUID);
+	tester_set_bit(cmds, GATT_FIND_INCLUDED);
+	tester_set_bit(cmds, GATT_DISC_ALL_CHRC);
+	tester_set_bit(cmds, GATT_DISC_CHRC_UUID);
+	tester_set_bit(cmds, GATT_DISC_ALL_DESC);
+	tester_set_bit(cmds, GATT_READ);
+	tester_set_bit(cmds, GATT_READ_LONG);
+	tester_set_bit(cmds, GATT_READ_MULTIPLE);
+	tester_set_bit(cmds, GATT_WRITE_WITHOUT_RSP);
+	tester_set_bit(cmds, GATT_SIGNED_WRITE_WITHOUT_RSP);
+	tester_set_bit(cmds, GATT_WRITE);
+	tester_set_bit(cmds, GATT_WRITE_LONG);
+	tester_set_bit(cmds, GATT_CFG_NOTIFY);
+	tester_set_bit(cmds, GATT_CFG_INDICATE);
+	tester_set_bit(cmds, GATT_GET_ATTRIBUTES);
+	tester_set_bit(cmds, GATT_GET_ATTRIBUTE_VALUE);
+	tester_set_bit(cmds, GATT_DISC_ALL_PRIM);
+	tester_set_bit(cmds, GATT_READ_MULTIPLE_VAR);
+	tester_set_bit(cmds, GATT_EATT_CONNECT);
+	tester_set_bit(cmds, GATT_NOTIFY_MULTIPLE);
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_READ_SUPPORTED_COMMANDS,
+		    CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds));
+}
+
+static int register_service(void)
+{
+	BT_DBG("Currently service count is %d", svc_count);
+	int err;
+	BT_DBG("Enter service register with attr_count%d", attr_count);
+	BT_DBG("Enter service register with svc_attr_count%d", svc_attr_count);
+
+	server_svcs[svc_count - 1].attrs = server_db + (attr_count - svc_attr_count);
+	server_svcs[svc_count - 1].attr_count = svc_attr_count;
+	sevice_list[svc_count + 1] = &server_svcs[svc_count - 1]; //First two are gatt and gap services
+
+	err = bt_gatt_service_init(svc_count + 2, sevice_list);
+	if (!err) {
+		/* Service registered, reset the counter */
+		svc_attr_count = 0U;
+	}
+
+	return err;
+	// int err;
+
+	// server_svcs[svc_count].attrs = server_db +
+	// 			       (attr_count - svc_attr_count);
+	// server_svcs[svc_count].attr_count = svc_attr_count;
+
+	// err = bt_gatt_service_register(&server_svcs[svc_count]);
+	// if (!err) {
+	// 	/* Service registered, reset the counter */
+	// 	svc_attr_count = 0U;
+	// }
+
+	// return err;
+}
+
+static void add_service(uint8_t *data, uint16_t len)
+{
+	const struct gatt_add_service_cmd *cmd = (void *) data;
+	struct gatt_add_service_rp rp;
+	struct bt_gatt_attr *attr_svc = NULL;
+	union uuid uuid;
+	size_t uuid_size;
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	uuid_size = uuid.uuid.type == BT_UUID_TYPE_16 ? sizeof(uuid.u16) :
+							sizeof(uuid.u128);
+
+	/* Register last defined service */
+	if (svc_attr_count) {
+		if (register_service()) {
+			goto fail;
+		}
+	}
+
+	svc_count++;
+
+	switch (cmd->type) {
+	case GATT_SERVICE_PRIMARY:
+		attr_svc = gatt_db_add(&(struct bt_gatt_attr)
+				       BT_GATT_PRIMARY_SERVICE(&uuid.uuid),
+				       uuid_size);
+		break;
+	case GATT_SERVICE_SECONDARY:
+		attr_svc = gatt_db_add(&(struct bt_gatt_attr)
+				       BT_GATT_SECONDARY_SERVICE(&uuid.uuid),
+				       uuid_size);
+		break;
+	}
+
+	if (!attr_svc) {
+		svc_count--;
+		goto fail;
+	}
+
+	rp.svc_id = sys_cpu_to_le16(attr_svc->handle);
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_ADD_SERVICE, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_ADD_SERVICE, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+struct gatt_value {
+	uint16_t len;
+	uint8_t *data;
+	uint8_t enc_key_size;
+	uint8_t flags[1];
+};
+
+enum {
+	GATT_VALUE_CCC_FLAG,
+	GATT_VALUE_READ_AUTHOR_FLAG,
+	GATT_VALUE_WRITE_AUTHOR_FLAG,
+};
+
+static ssize_t read_value(struct bt_conn *conn, const struct bt_gatt_attr *attr,
+			  void *buf, uint16_t len, uint16_t offset)
+{
+	const struct gatt_value *value = attr->user_data;
+
+	if (tester_test_bit(value->flags, GATT_VALUE_READ_AUTHOR_FLAG)) {
+		return BT_GATT_ERR(BT_ATT_ERR_AUTHORIZATION);
+	}
+
+	if ((attr->perm & GATT_PERM_ENC_READ_MASK) && (conn != NULL) &&
+	    (value->enc_key_size > bt_conn_enc_key_size(conn))) {
+		return BT_GATT_ERR(BT_ATT_ERR_ENCRYPTION_KEY_SIZE);
+	}
+
+	return bt_gatt_attr_read(conn, attr, buf, len, offset, value->data,
+				 value->len);
+}
+
+static void attr_value_changed_ev(uint16_t handle, const uint8_t *value, uint16_t len)
+{
+	uint8_t buf[len + sizeof(struct gatt_attr_value_changed_ev)];
+	struct gatt_attr_value_changed_ev *ev = (void *) buf;
+
+	ev->handle = sys_cpu_to_le16(handle);
+	ev->data_length = sys_cpu_to_le16(len);
+	memcpy(ev->data, value, len);
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_EV_ATTR_VALUE_CHANGED,
+		    CONTROLLER_INDEX, buf, sizeof(buf));
+}
+
+static ssize_t write_value(struct bt_conn *conn,
+			   const struct bt_gatt_attr *attr, const void *buf,
+			   uint16_t len, uint16_t offset, uint8_t flags)
+{
+	struct gatt_value *value = attr->user_data;
+
+	if (tester_test_bit(value->flags, GATT_VALUE_WRITE_AUTHOR_FLAG)) {
+		return BT_GATT_ERR(BT_ATT_ERR_AUTHORIZATION);
+	}
+
+	if ((attr->perm & GATT_PERM_ENC_WRITE_MASK) &&
+	    (value->enc_key_size > bt_conn_enc_key_size(conn))) {
+		return BT_GATT_ERR(BT_ATT_ERR_ENCRYPTION_KEY_SIZE);
+	}
+
+	/* Don't write anything if prepare flag is set */
+	if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
+		return 0;
+	}
+
+	if (offset > value->len) {
+		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
+	}
+
+	if (offset + len > value->len) {
+		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
+	}
+
+	memcpy(value->data + offset, buf, len);
+
+	/* Maximum attribute value size is 512 bytes */
+	__ASSERT_NO_MSG(value->len < 512);
+
+	attr_value_changed_ev(attr->handle, value->data, value->len);
+
+	return len;
+}
+
+struct add_characteristic {
+	uint16_t char_id;
+	uint8_t properties;
+	uint8_t permissions;
+	const struct bt_uuid *uuid;
+};
+
+static int alloc_characteristic(struct add_characteristic *ch)
+{
+	struct bt_gatt_attr *attr_chrc, *attr_value;
+	struct bt_gatt_chrc *chrc_data;
+	struct gatt_value value;
+
+	/* Add Characteristic Declaration */
+	attr_chrc = gatt_db_add(&(struct bt_gatt_attr)
+				BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC,
+						  BT_GATT_PERM_READ,
+						  bt_gatt_attr_read_chrc, NULL,
+						  (&(struct bt_gatt_chrc){})),
+				sizeof(*chrc_data));
+	if (!attr_chrc) {
+		return -EINVAL;
+	}
+
+	(void)memset(&value, 0, sizeof(value));
+
+	if (ch->permissions & GATT_PERM_READ_AUTHORIZATION) {
+		tester_set_bit(value.flags, GATT_VALUE_READ_AUTHOR_FLAG);
+
+		/* To maintain backward compatibility, set Read Permission */
+		if (!(ch->permissions & GATT_PERM_ENC_READ_MASK)) {
+			ch->permissions |= BT_GATT_PERM_READ;
+		}
+	}
+
+	if (ch->permissions & GATT_PERM_WRITE_AUTHORIZATION) {
+		tester_set_bit(value.flags, GATT_VALUE_WRITE_AUTHOR_FLAG);
+
+		/* To maintain backward compatibility, set Write Permission */
+		if (!(ch->permissions & GATT_PERM_ENC_WRITE_MASK)) {
+			ch->permissions |= BT_GATT_PERM_WRITE;
+		}
+	}
+
+	/* Allow prepare writes */
+	ch->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+	/* Add Characteristic Value */
+	attr_value = gatt_db_add(&(struct bt_gatt_attr)
+				 BT_GATT_ATTRIBUTE(ch->uuid,
+					ch->permissions & GATT_PERM_MASK,
+					read_value, write_value, &value),
+					sizeof(value));
+	if (!attr_value) {
+		server_buf_pull(sizeof(*chrc_data));
+		/* Characteristic attribute uuid has constant length */
+		server_buf_pull(sizeof(uint16_t));
+		return -EINVAL;
+	}
+
+	chrc_data = attr_chrc->user_data;
+	chrc_data->properties = ch->properties;
+	chrc_data->uuid = attr_value->uuid;
+
+	ch->char_id = attr_chrc->handle;
+	return 0;
+}
+
+static void add_characteristic(uint8_t *data, uint16_t len)
+{
+	const struct gatt_add_characteristic_cmd *cmd = (void *) data;
+	struct gatt_add_characteristic_rp rp;
+	struct add_characteristic cmd_data;
+	union uuid uuid;
+
+	/* Pre-set char_id */
+	cmd_data.char_id = 0U;
+	cmd_data.permissions = cmd->permissions;
+	cmd_data.properties = cmd->properties;
+	cmd_data.uuid = &uuid.uuid;
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	/* characteristic must be added only sequential */
+	if (cmd->svc_id) {
+		goto fail;
+	}
+
+	if (alloc_characteristic(&cmd_data)) {
+		goto fail;
+	}
+
+	rp.char_id = sys_cpu_to_le16(cmd_data.char_id);
+
+	ccc_added = false;
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_ADD_CHARACTERISTIC,
+		    CONTROLLER_INDEX, (uint8_t *) &rp, sizeof(rp));
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_ADD_CHARACTERISTIC,
+		   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
+{
+	int i = ccc_find_by_ccc(attr);
+
+	if (i >= 0) {
+		ccc_values[i].value = value;
+	}
+}
+
+static struct bt_gatt_attr ccc = BT_GATT_CCC(ccc_cfg_changed,
+					     BT_GATT_PERM_READ |
+					     BT_GATT_PERM_WRITE);
+
+static struct bt_gatt_attr *add_ccc(struct bt_gatt_attr *attr)
+{
+	struct bt_gatt_attr *attr_desc;
+	struct bt_gatt_chrc *chrc = attr->user_data;
+	struct gatt_value *value = NEXT_DB_ATTR(attr)->user_data;
+	int i;
+
+	/* Fail if another CCC already exist for this characteristic */
+	if (ccc_added) {
+		return NULL;
+	}
+
+	/* Check characteristic properties */
+	if (!(chrc->properties &
+	    (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) {
+		return NULL;
+	}
+
+	/* Add CCC descriptor to GATT database */
+	attr_desc = gatt_db_add(&ccc, 0);
+	if (!attr_desc) {
+		return NULL;
+	}
+
+	i = ccc_find_by_ccc(NULL);
+	if (i >= 0) {
+		ccc_values[i].attr = attr;
+		ccc_values[i].ccc = attr_desc;
+		ccc_values[i].value = 0;
+	}
+
+	tester_set_bit(value->flags, GATT_VALUE_CCC_FLAG);
+	ccc_added = true;
+
+	return attr_desc;
+}
+
+static struct bt_gatt_attr *add_cep(const struct bt_gatt_attr *attr_chrc)
+{
+	struct bt_gatt_chrc *chrc = attr_chrc->user_data;
+	struct bt_gatt_cep cep_value;
+
+	/* Extended Properties bit shall be set */
+	if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) {
+		return NULL;
+	}
+
+	cep_value.properties = 0x0000;
+
+	/* Add CEP descriptor to GATT database */
+	return gatt_db_add(&(struct bt_gatt_attr) BT_GATT_CEP(&cep_value),
+			   sizeof(cep_value));
+}
+
+struct add_descriptor {
+	uint16_t desc_id;
+	uint8_t permissions;
+	const struct bt_uuid *uuid;
+};
+
+static int alloc_descriptor(struct bt_gatt_attr *attr,
+			    struct add_descriptor *d)
+{
+	struct bt_gatt_attr *attr_desc;
+	struct gatt_value value;
+
+	if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) {
+		attr_desc = add_cep(attr);
+	} else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) {
+		attr_desc = add_ccc(attr);
+	} else {
+		(void)memset(&value, 0, sizeof(value));
+
+		if (d->permissions & GATT_PERM_READ_AUTHORIZATION) {
+			tester_set_bit(value.flags,
+				       GATT_VALUE_READ_AUTHOR_FLAG);
+
+			/*
+			 * To maintain backward compatibility,
+			 * set Read Permission
+			 */
+			if (!(d->permissions & GATT_PERM_ENC_READ_MASK)) {
+				d->permissions |= BT_GATT_PERM_READ;
+			}
+		}
+
+		if (d->permissions & GATT_PERM_WRITE_AUTHORIZATION) {
+			tester_set_bit(value.flags,
+				       GATT_VALUE_WRITE_AUTHOR_FLAG);
+
+			/*
+			 * To maintain backward compatibility,
+			 * set Write Permission
+			 */
+			if (!(d->permissions & GATT_PERM_ENC_WRITE_MASK)) {
+				d->permissions |= BT_GATT_PERM_WRITE;
+			}
+		}
+
+		/* Allow prepare writes */
+		d->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+		attr_desc = gatt_db_add(&(struct bt_gatt_attr)
+					BT_GATT_DESCRIPTOR(d->uuid,
+						d->permissions & GATT_PERM_MASK,
+						read_value, write_value,
+						&value), sizeof(value));
+	}
+
+	if (!attr_desc) {
+		return -EINVAL;
+	}
+
+	d->desc_id = attr_desc->handle;
+	return 0;
+}
+
+static struct bt_gatt_attr *get_base_chrc(struct bt_gatt_attr *attr)
+{
+	struct bt_gatt_attr *tmp;
+
+	for (tmp = attr; tmp > server_db; tmp--) {
+		/* Service Declaration cannot precede Descriptor declaration */
+		if (!bt_uuid_cmp(tmp->uuid, BT_UUID_GATT_PRIMARY) ||
+		    !bt_uuid_cmp(tmp->uuid, BT_UUID_GATT_SECONDARY)) {
+			break;
+		}
+
+		if (!bt_uuid_cmp(tmp->uuid, BT_UUID_GATT_CHRC)) {
+			return tmp;
+		}
+	}
+
+	return NULL;
+}
+
+static void add_descriptor(uint8_t *data, uint16_t len)
+{
+	const struct gatt_add_descriptor_cmd *cmd = (void *) data;
+	struct gatt_add_descriptor_rp rp;
+	struct add_descriptor cmd_data;
+	struct bt_gatt_attr *chrc;
+	union uuid uuid;
+
+	/* Must be declared first svc or at least 3 attrs (svc+char+char val) */
+	if (!svc_count || attr_count < 3) {
+		goto fail;
+	}
+
+	/* Pre-set desc_id */
+	cmd_data.desc_id = 0U;
+	cmd_data.permissions = cmd->permissions;
+	cmd_data.uuid = &uuid.uuid;
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	/* descriptor can be added only sequential */
+	if (cmd->char_id) {
+		goto fail;
+	}
+
+	/* Lookup preceding Characteristic Declaration here */
+	chrc = get_base_chrc(LAST_DB_ATTR);
+	if (!chrc) {
+		goto fail;
+	}
+
+	if (alloc_descriptor(chrc, &cmd_data)) {
+		goto fail;
+	}
+
+	rp.desc_id = sys_cpu_to_le16(cmd_data.desc_id);
+	tester_send(BTP_SERVICE_ID_GATT, GATT_ADD_DESCRIPTOR, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_ADD_DESCRIPTOR,
+		   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static int alloc_included(struct bt_gatt_attr *attr,
+			  uint16_t *included_service_id, uint16_t svc_handle)
+{
+	struct bt_gatt_attr *attr_incl;
+
+	/*
+	 * user_data_len is set to 0 to NOT allocate memory in server_buf for
+	 * user_data, just to assign to it attr pointer.
+	 */
+	attr_incl = gatt_db_add(&(struct bt_gatt_attr)
+				BT_GATT_INCLUDE_SERVICE(attr), 0);
+
+	if (!attr_incl) {
+		return -EINVAL;
+	}
+
+	attr_incl->user_data = attr;
+
+	*included_service_id = attr_incl->handle;
+	return 0;
+}
+
+static void add_included(uint8_t *data, uint16_t len)
+{
+	const struct gatt_add_included_service_cmd *cmd = (void *) data;
+	struct gatt_add_included_service_rp rp;
+	struct bt_gatt_attr *svc;
+	uint16_t included_service_id = 0U;
+
+	if (!svc_count || !cmd->svc_id) {
+		goto fail;
+	}
+
+	svc = &server_db[cmd->svc_id - 1];
+
+	/* Fail if attribute stored under requested handle is not a service */
+	if (bt_uuid_cmp(svc->uuid, BT_UUID_GATT_PRIMARY) &&
+	    bt_uuid_cmp(svc->uuid, BT_UUID_GATT_SECONDARY)) {
+		goto fail;
+	}
+
+	if (alloc_included(svc, &included_service_id, cmd->svc_id)) {
+		goto fail;
+	}
+
+	rp.included_service_id = sys_cpu_to_le16(included_service_id);
+	tester_send(BTP_SERVICE_ID_GATT, GATT_ADD_INCLUDED_SERVICE,
+		    CONTROLLER_INDEX, (uint8_t *) &rp, sizeof(rp));
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_ADD_INCLUDED_SERVICE,
+		   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static uint8_t set_cep_value(struct bt_gatt_attr *attr, const void *value,
+			     const uint16_t len)
+{
+	struct bt_gatt_cep *cep_value = attr->user_data;
+	uint16_t properties;
+
+	if (len != sizeof(properties)) {
+		return BTP_STATUS_FAILED;
+	}
+
+	memcpy(&properties, value, len);
+	cep_value->properties = sys_le16_to_cpu(properties);
+
+	return BTP_STATUS_SUCCESS;
+}
+
+struct set_value {
+	const uint8_t *value;
+	uint16_t len;
+};
+
+struct bt_gatt_indicate_params indicate_params;
+
+static void indicate_cb(struct bt_conn *conn,
+			struct bt_gatt_indicate_params *params, uint8_t err)
+{
+	if (err != 0U) {
+	    BT_ERR("Indication fail");
+	} else {
+		BT_DBG("Indication success");
+	}
+}
+
+static uint8_t alloc_value(struct bt_gatt_attr *attr, struct set_value *data)
+{
+	struct gatt_value *value;
+	uint8_t ccc_value;
+	int i;
+
+	/* Value has been already set while adding CCC to the gatt_db */
+	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC)) {
+		return BTP_STATUS_SUCCESS;
+	}
+
+	/* Set CEP value */
+	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CEP)) {
+		return set_cep_value(attr, data->value, data->len);
+	}
+
+	value = attr->user_data;
+
+	/* Check if attribute value has been already set */
+	if (!value->len) {
+		value->data = server_buf_push(data->len);
+		value->len = data->len;
+	}
+
+	/* Fail if value length doesn't match  */
+	if (value->len != data->len) {
+		return BTP_STATUS_FAILED;
+	}
+
+	memcpy(value->data, data->value, value->len);
+
+	/** Handle of attribute is 1 less that handle to its value */
+	i = ccc_find_by_attr(attr->handle - 1);
+
+	if (i < 0) {
+		ccc_value = 0;
+	} else {
+		ccc_value = ccc_values[i].value;
+	}
+
+	if (tester_test_bit(value->flags, GATT_VALUE_CCC_FLAG) && ccc_value) {
+		if (ccc_value == BT_GATT_CCC_NOTIFY) {
+			bt_gatt_notify(NULL, attr, value->data, value->len);
+		} else {
+			indicate_params.attr = attr;
+			indicate_params.data = value->data;
+			indicate_params.len = value->len;
+			indicate_params.func = indicate_cb;
+			indicate_params.destroy = NULL;
+//			indicate_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+			bt_gatt_indicate(NULL, &indicate_params);
+		}
+	}
+
+	return BTP_STATUS_SUCCESS;
+}
+
+static void set_value(uint8_t *data, uint16_t len)
+{
+	const struct gatt_set_value_cmd *cmd = (void *) data;
+	struct set_value cmd_data;
+	uint8_t status;
+
+	/* Pre-set btp_status */
+	cmd_data.value = cmd->value;
+	cmd_data.len = sys_le16_to_cpu(cmd->len);
+
+	if (!cmd->attr_id) {
+		status = alloc_value(LAST_DB_ATTR, &cmd_data);
+	} else {
+		/* set value of local attr, corrected by pre set attr handles */
+		status = alloc_value(&server_db[cmd->attr_id -
+				     server_db[0].handle], &cmd_data);
+	}
+
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_SET_VALUE, CONTROLLER_INDEX,
+		   status);
+}
+
+static void start_server(uint8_t *data, uint16_t len)
+{
+	struct gatt_start_server_rp rp;
+
+	/* Register last defined service */
+	if (svc_attr_count) {
+		if (register_service()) {
+			tester_rsp(BTP_SERVICE_ID_GATT, GATT_START_SERVER,
+				   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+			return;
+		}
+	}
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_START_SERVER, CONTROLLER_INDEX,
+		    (uint8_t *) &rp, sizeof(rp));
+}
+
+static int set_attr_enc_key_size(const struct bt_gatt_attr *attr,
+				 uint8_t key_size)
+{
+	struct gatt_value *value;
+
+	/* Fail if requested attribute is a service */
+	if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) ||
+	    !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY) ||
+	    !bt_uuid_cmp(attr->uuid, BT_UUID_GATT_INCLUDE)) {
+		return -EINVAL;
+	}
+
+	/* Fail if permissions are not set */
+	if (!(attr->perm & (GATT_PERM_ENC_READ_MASK |
+			    GATT_PERM_ENC_WRITE_MASK))) {
+		return -EINVAL;
+	}
+
+	value = attr->user_data;
+	value->enc_key_size = key_size;
+
+	return 0;
+}
+
+static void set_enc_key_size(uint8_t *data, uint16_t len)
+{
+	const struct gatt_set_enc_key_size_cmd *cmd = (void *) data;
+	uint8_t status;
+
+	/* Fail if requested key size is invalid */
+	if (cmd->key_size < 0x07 || cmd->key_size > 0x0f) {
+		status = BTP_STATUS_FAILED;
+		goto fail;
+	}
+
+	if (!cmd->attr_id) {
+		status = set_attr_enc_key_size(LAST_DB_ATTR, cmd->key_size);
+	} else {
+		/* set value of local attr, corrected by pre set attr handles */
+		status = set_attr_enc_key_size(&server_db[cmd->attr_id -
+					       server_db[0].handle],
+					       cmd->key_size);
+	}
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_SET_ENC_KEY_SIZE, CONTROLLER_INDEX,
+		   status);
+}
+
+static void exchange_func(struct bt_conn *conn, uint8_t err,
+			  struct bt_gatt_exchange_params *params)
+{
+	if (err) {
+		tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+
+		return;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU, CONTROLLER_INDEX,
+		   BTP_STATUS_SUCCESS);
+}
+
+static struct bt_gatt_exchange_params exchange_params;
+
+static void exchange_mtu(uint8_t *data, uint16_t len)
+{
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail;
+	}
+
+	exchange_params.func = exchange_func;
+
+	if (bt_gatt_exchange_mtu(conn, &exchange_params) < 0) {
+		bt_conn_unref(conn);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_EXCHANGE_MTU,
+		   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+}
+
+static struct bt_gatt_discover_params discover_params;
+static union uuid uuid;
+static uint8_t btp_opcode;
+
+static void discover_destroy(struct bt_gatt_discover_params *params)
+{
+	(void)memset(params, 0, sizeof(*params));
+	gatt_buf_clear();
+}
+
+static uint8_t disc_prim_cb(struct bt_conn *conn,
+			 const struct bt_gatt_attr *attr,
+			 struct bt_gatt_discover_params *params)
+{
+	struct bt_gatt_service_val *data;
+	struct gatt_disc_prim_rp *rp = (void *) gatt_buf.buf;
+	struct gatt_service *service;
+	uint8_t uuid_length;
+
+	if (!attr) {
+		tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+			    CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	data = attr->user_data;
+
+	uuid_length = data->uuid->type == BT_UUID_TYPE_16 ? 2 : 16;
+
+	service = gatt_buf_reserve(sizeof(*service) + uuid_length);
+	if (!service) {
+		tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	service->start_handle = sys_cpu_to_le16(attr->handle);
+	service->end_handle = sys_cpu_to_le16(data->end_handle);
+	service->uuid_length = uuid_length;
+
+	if (data->uuid->type == BT_UUID_TYPE_16) {
+		uint16_t u16 = sys_cpu_to_le16(BT_UUID_16(data->uuid)->val);
+
+		memcpy(service->uuid, &u16, uuid_length);
+	} else {
+		memcpy(service->uuid, BT_UUID_128(data->uuid)->val,
+		       uuid_length);
+	}
+
+	rp->services_count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void disc_all_prim(uint8_t *data, uint16_t len)
+{
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_disc_prim_rp))) {
+		goto fail;
+	}
+
+	discover_params.uuid = NULL;
+	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
+	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
+	discover_params.type = BT_GATT_DISCOVER_PRIMARY;
+	discover_params.func = disc_prim_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	btp_opcode = GATT_DISC_ALL_PRIM;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_PRIM, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void disc_prim_uuid(uint8_t *data, uint16_t len)
+{
+	const struct gatt_disc_prim_uuid_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_disc_prim_rp))) {
+		goto fail;
+	}
+
+	discover_params.uuid = &uuid.uuid;
+	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
+	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
+	discover_params.type = BT_GATT_DISCOVER_PRIMARY;
+	discover_params.func = disc_prim_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	btp_opcode = GATT_DISC_PRIM_UUID;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_PRIM_UUID, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static uint8_t find_included_cb(struct bt_conn *conn,
+				const struct bt_gatt_attr *attr,
+				struct bt_gatt_discover_params *params)
+{
+	struct bt_gatt_include *data;
+	struct gatt_find_included_rp *rp = (void *) gatt_buf.buf;
+	struct gatt_included *included;
+	uint8_t uuid_length;
+
+	if (!attr) {
+		tester_send(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED,
+			    CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	data = attr->user_data;
+
+	uuid_length = data->uuid->type == BT_UUID_TYPE_16 ? 2 : 16;
+
+	included = gatt_buf_reserve(sizeof(*included) + uuid_length);
+	if (!included) {
+		tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	included->included_handle = attr->handle;
+	included->service.start_handle = sys_cpu_to_le16(data->start_handle);
+	included->service.end_handle = sys_cpu_to_le16(data->end_handle);
+	included->service.uuid_length = uuid_length;
+
+	if (data->uuid->type == BT_UUID_TYPE_16) {
+		uint16_t u16 = sys_cpu_to_le16(BT_UUID_16(data->uuid)->val);
+
+		memcpy(included->service.uuid, &u16, uuid_length);
+	} else {
+		memcpy(included->service.uuid, BT_UUID_128(data->uuid)->val,
+		       uuid_length);
+	}
+
+	rp->services_count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void find_included(uint8_t *data, uint16_t len)
+{
+	const struct gatt_find_included_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_find_included_rp))) {
+		goto fail;
+	}
+
+	discover_params.start_handle = sys_le16_to_cpu(cmd->start_handle);
+	discover_params.end_handle = sys_le16_to_cpu(cmd->end_handle);
+	discover_params.type = BT_GATT_DISCOVER_INCLUDE;
+	discover_params.func = find_included_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_FIND_INCLUDED, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static uint8_t disc_chrc_cb(struct bt_conn *conn,
+			    const struct bt_gatt_attr *attr,
+			    struct bt_gatt_discover_params *params)
+{
+	struct bt_gatt_chrc *data;
+	struct gatt_disc_chrc_rp *rp = (void *) gatt_buf.buf;
+	struct gatt_characteristic *chrc;
+	uint8_t uuid_length;
+
+	if (!attr) {
+		tester_send(BTP_SERVICE_ID_GATT, btp_opcode,
+			    CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	data = attr->user_data;
+
+	uuid_length = data->uuid->type == BT_UUID_TYPE_16 ? 2 : 16;
+
+	chrc = gatt_buf_reserve(sizeof(*chrc) + uuid_length);
+	if (!chrc) {
+		tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	chrc->characteristic_handle = sys_cpu_to_le16(attr->handle);
+	chrc->properties = data->properties;
+	chrc->value_handle = sys_cpu_to_le16(attr->handle + 1);
+	chrc->uuid_length = uuid_length;
+
+	if (data->uuid->type == BT_UUID_TYPE_16) {
+		uint16_t u16 = sys_cpu_to_le16(BT_UUID_16(data->uuid)->val);
+
+		memcpy(chrc->uuid, &u16, uuid_length);
+	} else {
+		memcpy(chrc->uuid, BT_UUID_128(data->uuid)->val, uuid_length);
+	}
+
+	rp->characteristics_count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void disc_all_chrc(uint8_t *data, uint16_t len)
+{
+	const struct gatt_disc_all_chrc_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) {
+		goto fail;
+	}
+
+	discover_params.start_handle = sys_le16_to_cpu(cmd->start_handle);
+	discover_params.end_handle = sys_le16_to_cpu(cmd->end_handle);
+	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
+	discover_params.func = disc_chrc_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	/* TODO should be handled as user_data via CONTAINER_OF macro */
+	btp_opcode = GATT_DISC_ALL_CHRC;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_CHRC, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void disc_chrc_uuid(uint8_t *data, uint16_t len)
+{
+	const struct gatt_disc_chrc_uuid_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_disc_chrc_rp))) {
+		goto fail;
+	}
+
+	discover_params.uuid = &uuid.uuid;
+	discover_params.start_handle = sys_le16_to_cpu(cmd->start_handle);
+	discover_params.end_handle = sys_le16_to_cpu(cmd->end_handle);
+	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
+	discover_params.func = disc_chrc_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	/* TODO should be handled as user_data via CONTAINER_OF macro */
+	btp_opcode = GATT_DISC_CHRC_UUID;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_CHRC_UUID, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static uint8_t disc_all_desc_cb(struct bt_conn *conn,
+				const struct bt_gatt_attr *attr,
+				struct bt_gatt_discover_params *params)
+{
+	struct gatt_disc_all_desc_rp *rp = (void *) gatt_buf.buf;
+	struct gatt_descriptor *descriptor;
+	uint8_t uuid_length;
+
+	if (!attr) {
+		tester_send(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC,
+			    CONTROLLER_INDEX, gatt_buf.buf, gatt_buf.len);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	uuid_length = attr->uuid->type == BT_UUID_TYPE_16 ? 2 : 16;
+
+	descriptor = gatt_buf_reserve(sizeof(*descriptor) + uuid_length);
+	if (!descriptor) {
+		tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		discover_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	descriptor->descriptor_handle = sys_cpu_to_le16(attr->handle);
+	descriptor->uuid_length = uuid_length;
+
+	if (attr->uuid->type == BT_UUID_TYPE_16) {
+		uint16_t u16 = sys_cpu_to_le16(BT_UUID_16(attr->uuid)->val);
+
+		memcpy(descriptor->uuid, &u16, uuid_length);
+	} else {
+		memcpy(descriptor->uuid, BT_UUID_128(attr->uuid)->val,
+		       uuid_length);
+	}
+
+	rp->descriptors_count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void disc_all_desc(uint8_t *data, uint16_t len)
+{
+	const struct gatt_disc_all_desc_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_disc_all_desc_rp))) {
+		goto fail;
+	}
+
+	discover_params.start_handle = sys_le16_to_cpu(cmd->start_handle);
+	discover_params.end_handle = sys_le16_to_cpu(cmd->end_handle);
+	discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
+	discover_params.func = disc_all_desc_cb;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	if (bt_gatt_discover(conn, &discover_params) < 0) {
+		discover_destroy(&discover_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_DISC_ALL_DESC, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static struct bt_gatt_read_params read_params;
+
+static void read_destroy(struct bt_gatt_read_params *params)
+{
+	(void)memset(params, 0, sizeof(*params));
+	gatt_buf_clear();
+}
+
+static uint8_t read_cb(struct bt_conn *conn, uint8_t err,
+		       struct bt_gatt_read_params *params, const void *data,
+		       uint16_t length)
+{
+	struct gatt_read_rp *rp = (void *) gatt_buf.buf;
+
+	/* Respond to the Lower Tester with ATT Error received */
+	if (err) {
+		rp->att_response = err;
+	}
+
+	/* read complete */
+	if (!data) {
+		tester_send(BTP_SERVICE_ID_GATT, btp_opcode, CONTROLLER_INDEX,
+			    gatt_buf.buf, gatt_buf.len);
+		read_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	if (!gatt_buf_add(data, length)) {
+		tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		read_destroy(params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	rp->data_length += length;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static uint8_t read_uuid_cb(struct bt_conn *conn, uint8_t err,
+		       struct bt_gatt_read_params *params, const void *data,
+		       uint16_t length)
+{
+	struct gatt_read_uuid_rp *rp = (void *)gatt_buf.buf;
+	struct gatt_char_value value;
+
+	/* Respond to the Lower Tester with ATT Error received */
+	if (err) {
+		rp->att_response = err;
+	}
+
+	/* read complete */
+	if (!data) {
+		tester_send(BTP_SERVICE_ID_GATT, btp_opcode, CONTROLLER_INDEX,
+			    gatt_buf.buf, gatt_buf.len);
+		read_destroy(params);
+
+		return BT_GATT_ITER_STOP;
+	}
+
+	value.handle = params->by_uuid.start_handle;
+	value.data_len = length;
+
+	if (!gatt_buf_add(&value, sizeof(struct gatt_char_value))) {
+		tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		read_destroy(params);
+
+		return BT_GATT_ITER_STOP;
+	}
+
+	if (!gatt_buf_add(data, length)) {
+		tester_rsp(BTP_SERVICE_ID_GATT, btp_opcode,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+		read_destroy(params);
+
+		return BT_GATT_ITER_STOP;
+	}
+
+	rp->values_count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void read_data(uint8_t *data, uint16_t len)
+{
+	const struct gatt_read_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+		goto fail;
+	}
+
+	read_params.handle_count = 1;
+	read_params.single.handle = sys_le16_to_cpu(cmd->handle);
+	read_params.single.offset = 0x0000;
+	read_params.func = read_cb;
+//	read_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	/* TODO should be handled as user_data via CONTAINER_OF macro */
+	btp_opcode = GATT_READ;
+
+	if (bt_gatt_read(conn, &read_params) < 0) {
+		read_destroy(&read_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void read_uuid(uint8_t *data, uint16_t len)
+{
+	const struct gatt_read_uuid_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (btp2bt_uuid(cmd->uuid, cmd->uuid_length, &uuid.uuid)) {
+		goto fail;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_read_uuid_rp))) {
+		goto fail;
+	}
+
+	read_params.by_uuid.uuid = &uuid.uuid;
+	read_params.handle_count = 0;
+	read_params.by_uuid.start_handle = sys_le16_to_cpu(cmd->start_handle);
+	read_params.by_uuid.end_handle = sys_le16_to_cpu(cmd->end_handle);
+	read_params.func = read_uuid_cb;
+//	read_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	btp_opcode = GATT_READ_UUID;
+
+	if (bt_gatt_read(conn, &read_params) < 0) {
+		read_destroy(&read_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_UUID, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void read_long(uint8_t *data, uint16_t len)
+{
+	const struct gatt_read_long_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+		goto fail;
+	}
+
+	read_params.handle_count = 1;
+	read_params.single.handle = sys_le16_to_cpu(cmd->handle);
+	read_params.single.offset = sys_le16_to_cpu(cmd->offset);
+	read_params.func = read_cb;
+//	read_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	/* TODO should be handled as user_data via CONTAINER_OF macro */
+	btp_opcode = GATT_READ_LONG;
+
+	if (bt_gatt_read(conn, &read_params) < 0) {
+		read_destroy(&read_params);
+
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_LONG, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void read_multiple(uint8_t *data, uint16_t len, uint8_t opcode)
+{
+	const struct gatt_read_multiple_cmd *cmd = (void *) data;
+	uint16_t handles[cmd->handles_count];
+	struct bt_conn *conn;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(handles); i++) {
+		handles[i] = sys_le16_to_cpu(cmd->handles[i]);
+	}
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail_conn;
+	}
+
+	if (!gatt_buf_reserve(sizeof(struct gatt_read_rp))) {
+		goto fail;
+	}
+
+	read_params.func = read_cb;
+	read_params.handle_count = i;
+	read_params.multiple.handles = handles; /* not used in read func */
+	read_params.multiple.variable = (opcode == GATT_READ_MULTIPLE_VAR);
+//	read_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	/* TODO should be handled as user_data via CONTAINER_OF macro */
+	btp_opcode = opcode;
+
+	if (bt_gatt_read(conn, &read_params) < 0) {
+		gatt_buf_clear();
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	bt_conn_unref(conn);
+
+fail_conn:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_READ_MULTIPLE, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void write_without_rsp(uint8_t *data, uint16_t len, uint8_t op,
+			      bool sign)
+{
+	const struct gatt_write_without_rsp_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	uint8_t status = BTP_STATUS_SUCCESS;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	if (bt_gatt_write_without_response(conn, sys_le16_to_cpu(cmd->handle),
+					   cmd->data,
+					   sys_le16_to_cpu(cmd->data_length),
+					   sign) < 0) {
+		status = BTP_STATUS_FAILED;
+	}
+
+	bt_conn_unref(conn);
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+}
+
+static void write_rsp(struct bt_conn *conn, uint8_t err,
+		      struct bt_gatt_write_params *params)
+{
+	tester_send(BTP_SERVICE_ID_GATT, GATT_WRITE, CONTROLLER_INDEX, &err,
+		    sizeof(err));
+}
+
+static struct bt_gatt_write_params write_params;
+
+static void write_data(uint8_t *data, uint16_t len)
+{
+	const struct gatt_write_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail;
+	}
+
+	write_params.handle = sys_le16_to_cpu(cmd->handle);
+	write_params.func = write_rsp;
+	write_params.offset = 0U;
+	write_params.data = cmd->data;
+	write_params.length = sys_le16_to_cpu(cmd->data_length);
+//	write_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	if (bt_gatt_write(conn, &write_params) < 0) {
+		bt_conn_unref(conn);
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void write_long_rsp(struct bt_conn *conn, uint8_t err,
+			   struct bt_gatt_write_params *params)
+{
+	tester_send(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX,
+		    &err, sizeof(err));
+}
+
+static void write_long(uint8_t *data, uint16_t len)
+{
+	const struct gatt_write_long_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail;
+	}
+
+	write_params.handle = sys_le16_to_cpu(cmd->handle);
+	write_params.func = write_long_rsp;
+	write_params.offset = cmd->offset;
+	write_params.data = cmd->data;
+	write_params.length = sys_le16_to_cpu(cmd->data_length);
+//	write_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	if (bt_gatt_write(conn, &write_params) < 0) {
+		bt_conn_unref(conn);
+		goto fail;
+	}
+
+	bt_conn_unref(conn);
+
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_WRITE_LONG, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static struct bt_gatt_subscribe_params subscriptions[MAX_SUBSCRIPTIONS];
+
+static struct bt_gatt_subscribe_params *find_subscription(uint16_t ccc_handle)
+{
+	for (int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
+		if (subscriptions[i].ccc_handle == ccc_handle) {
+			return &subscriptions[i];
+		}
+	}
+
+	return NULL;
+}
+
+/* TODO there should be better way of determining max supported MTU */
+#define MAX_NOTIF_DATA (MIN(BT_L2CAP_RX_MTU, BT_L2CAP_TX_MTU) - 3)
+
+static uint8_t ev_buf[sizeof(struct gatt_notification_ev) + MAX_NOTIF_DATA];
+
+static uint8_t notify_func(struct bt_conn *conn,
+			   struct bt_gatt_subscribe_params *params,
+			   const void *data, uint16_t length)
+{
+	struct gatt_notification_ev *ev = (void *) ev_buf;
+	const bt_addr_le_t *addr;
+
+	if (!conn || !data) {
+		BT_DBG("Unsubscribed");
+		(void)memset(params, 0, sizeof(*params));
+		return BT_GATT_ITER_STOP;
+	}
+	addr = bt_conn_get_dst(conn);
+	ev->type = (uint8_t)params->value;
+	ev->handle = sys_cpu_to_le16(params->value_handle);
+
+	length = MIN(length, MAX_NOTIF_DATA);
+
+	ev->data_length = sys_cpu_to_le16(length);
+	memcpy(ev->data, data, length);
+	memcpy(ev->address, addr->a.val, sizeof(ev->address));
+	ev->address_type = addr->type;
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_EV_NOTIFICATION,
+		    CONTROLLER_INDEX, ev_buf, sizeof(*ev) + length);
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void discover_complete(struct bt_conn *conn,
+			      struct bt_gatt_discover_params *params)
+{
+	struct bt_gatt_subscribe_params *subscription;
+	uint8_t op, status;
+
+	subscription = find_subscription(discover_params.end_handle);
+	__ASSERT_NO_MSG(subscription);
+
+	/* If no value handle it means that chrc has not been found */
+	if (!subscription->value_handle) {
+		status = BTP_STATUS_FAILED;
+		goto fail;
+	}
+
+//	subscription->chan_opt = BT_ATT_CHAN_OPT_NONE;
+	if (bt_gatt_subscribe(conn, subscription) < 0) {
+		status = BTP_STATUS_FAILED;
+		goto fail;
+	}
+
+	status = BTP_STATUS_SUCCESS;
+fail:
+	op = subscription->value == BT_GATT_CCC_NOTIFY ? GATT_CFG_NOTIFY :
+							 GATT_CFG_INDICATE;
+
+	if (status == BTP_STATUS_FAILED) {
+		(void)memset(subscription, 0, sizeof(*subscription));
+	}
+
+	tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+
+	(void)memset(params, 0, sizeof(*params));
+}
+
+static uint8_t discover_func(struct bt_conn *conn,
+			     const struct bt_gatt_attr *attr,
+			     struct bt_gatt_discover_params *params)
+{
+	struct bt_gatt_subscribe_params *subscription;
+
+	if (!attr) {
+		discover_complete(conn, params);
+		return BT_GATT_ITER_STOP;
+	}
+
+	subscription = find_subscription(discover_params.end_handle);
+	__ASSERT_NO_MSG(subscription);
+
+	/* Characteristic Value Handle is the next handle beyond declaration */
+	subscription->value_handle = attr->handle + 1;
+
+	/*
+	 * Continue characteristic discovery to get last characteristic
+	 * preceding this CCC descriptor
+	 */
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static int enable_subscription(struct bt_conn *conn, uint16_t ccc_handle,
+			       uint16_t value)
+{
+	struct bt_gatt_subscribe_params *subscription;
+
+	/* find unused subscription */
+	subscription = find_subscription(UNUSED_SUBSCRIBE_CCC_HANDLE);
+	if (!subscription) {
+		return -ENOMEM;
+	}
+
+	/* if discovery is busy fail */
+	if (discover_params.start_handle) {
+		return -EBUSY;
+	}
+
+	/* Discover Characteristic Value this CCC Descriptor refers to */
+	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
+	discover_params.end_handle = ccc_handle;
+	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
+	discover_params.func = discover_func;
+//	discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
+
+	subscription->ccc_handle = ccc_handle;
+	subscription->value = value;
+	subscription->notify = notify_func;
+
+	/* require security level from time of subscription */
+	subscription->min_security = bt_conn_get_security(conn);
+
+	return bt_gatt_discover(conn, &discover_params);
+}
+
+static int disable_subscription(struct bt_conn *conn, uint16_t ccc_handle)
+{
+	struct bt_gatt_subscribe_params *subscription;
+
+	/* Fail if CCC handle doesn't match */
+	subscription = find_subscription(ccc_handle);
+	if (!subscription) {
+		BT_ERR("CCC handle doesn't match");
+		return -EINVAL;
+	}
+
+	if (bt_gatt_unsubscribe(conn, subscription) < 0) {
+		return -EBUSY;
+	}
+
+	(void)memset(subscription, 0, sizeof(*subscription));
+
+	return 0;
+}
+
+static void config_subscription(uint8_t *data, uint16_t len, uint16_t op)
+{
+	const struct gatt_cfg_notify_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	uint16_t ccc_handle = sys_le16_to_cpu(cmd->ccc_handle);
+	uint8_t status;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX,
+			   BTP_STATUS_FAILED);
+		return;
+	}
+
+	if (cmd->enable) {
+		uint16_t value;
+
+		if (op == GATT_CFG_NOTIFY) {
+			value = BT_GATT_CCC_NOTIFY;
+		} else {
+			value = BT_GATT_CCC_INDICATE;
+		}
+
+		/* on success response will be sent from callback */
+		if (enable_subscription(conn, ccc_handle, value) == 0) {
+			bt_conn_unref(conn);
+			return;
+		}
+
+		status = BTP_STATUS_FAILED;
+	} else {
+		if (disable_subscription(conn, ccc_handle) < 0) {
+			status = BTP_STATUS_FAILED;
+		} else {
+			status = BTP_STATUS_SUCCESS;
+		}
+	}
+
+	BT_DBG("Config subscription (op %u) status %u", op, status);
+
+	bt_conn_unref(conn);
+	tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+}
+
+
+#if defined(CONFIG_BT_GATT_NOTIFY_MULTIPLE)
+static void notify_cb(struct bt_conn *conn, void *user_data)
+{
+	BT_DBG("Nofication sent");
+}
+
+static void notify_mult(uint8_t *data, uint16_t len, uint16_t op)
+{
+	const struct gatt_cfg_notify_mult_cmd *cmd = (void *) data;
+	const size_t max_cnt = CONFIG_BT_L2CAP_TX_BUF_COUNT;
+	struct bt_gatt_notify_params params[max_cnt];
+	struct bt_conn *conn;
+	const size_t min_cnt = 1U;
+	int err = 0;
+	uint8_t status = BTP_STATUS_SUCCESS;
+	uint16_t attr_data_len = 0;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	if (!IN_RANGE(cmd->cnt, min_cnt, max_cnt)) {
+		LOG_ERR("Invalid count value %d (range %zu to %zu)",
+			    cmd->cnt, min_cnt, max_cnt);
+
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	(void)memset(params, 0, sizeof(params));
+
+	for (uint16_t i = 0U; i < cmd->cnt; i++) {
+		struct bt_gatt_attr attr = server_db[cmd->attr_id[i] -
+			server_db[0].handle];
+
+		attr_data_len = strtoul(attr.user_data, NULL, 16);
+		params[i].uuid = 0;
+		params[i].attr = &attr;
+		params[i].data = &attr.user_data;
+		params[i].len = attr_data_len;
+		params[i].func = notify_cb;
+		params[i].user_data = NULL;
+	}
+
+	err = bt_gatt_notify_multiple(conn, cmd->cnt, params);
+	if (err != 0) {
+		LOG_ERR("bt_gatt_notify_multiple failed: %d", err);
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	} else {
+		BT_DBG("Send %u notifications", cmd->cnt);
+	}
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_GATT, op, CONTROLLER_INDEX, status);
+}
+#endif /* CONFIG_BT_GATT_NOTIFY_MULTIPLE */
+
+struct get_attrs_foreach_data {
+	struct net_buf_simple *buf;
+	struct bt_uuid *uuid;
+	uint8_t count;
+};
+
+static uint8_t get_attrs_rp(const struct bt_gatt_attr *attr, uint16_t handle,
+			    void *user_data)
+{
+	struct get_attrs_foreach_data *foreach = user_data;
+	struct gatt_attr *gatt_attr;
+
+	if (foreach->uuid && bt_uuid_cmp(foreach->uuid, attr->uuid)) {
+
+		return BT_GATT_ITER_CONTINUE;
+	}
+
+	gatt_attr = net_buf_simple_add(foreach->buf, sizeof(*gatt_attr));
+	gatt_attr->handle = sys_cpu_to_le16(handle);
+	gatt_attr->permission = attr->perm;
+
+	if (attr->uuid->type == BT_UUID_TYPE_16) {
+		gatt_attr->type_length = 2U;
+		net_buf_simple_add_le16(foreach->buf,
+					BT_UUID_16(attr->uuid)->val);
+	} else {
+		gatt_attr->type_length = 16U;
+		net_buf_simple_add_mem(foreach->buf,
+				       BT_UUID_128(attr->uuid)->val,
+				       gatt_attr->type_length);
+	}
+
+	foreach->count++;
+
+	return BT_GATT_ITER_CONTINUE;
+}
+
+static void get_attrs(uint8_t *data, uint16_t len)
+{
+	const struct gatt_get_attributes_cmd *cmd = (void *) data;
+	struct gatt_get_attributes_rp *rp;
+	struct net_buf_simple *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
+	struct get_attrs_foreach_data foreach;
+	uint16_t start_handle, end_handle;
+	union uuid uuid;
+
+	start_handle = sys_le16_to_cpu(cmd->start_handle);
+	end_handle = sys_le16_to_cpu(cmd->end_handle);
+
+	if (cmd->type_length) {
+		char uuid_str[BT_UUID_STR_LEN];
+
+		if (btp2bt_uuid(cmd->type, cmd->type_length, &uuid.uuid)) {
+			goto fail;
+		}
+
+		bt_uuid_to_str(&uuid.uuid, uuid_str, sizeof(uuid_str));
+		BT_DBG("start 0x%04x end 0x%04x, uuid %s", start_handle,
+			end_handle, uuid_str);
+
+		foreach.uuid = &uuid.uuid;
+	} else {
+		BT_DBG("start 0x%04x end 0x%04x", start_handle, end_handle);
+
+		foreach.uuid = NULL;
+	}
+
+	net_buf_simple_init(buf, sizeof(*rp));
+
+	foreach.buf = buf;
+	foreach.count = 0U;
+
+	bt_gatt_foreach_attr(start_handle, end_handle, get_attrs_rp, &foreach);
+
+	rp = net_buf_simple_push(buf, sizeof(*rp));
+	rp->attrs_count = foreach.count;
+
+	tester_send(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, CONTROLLER_INDEX,
+		    buf->data, buf->len);
+
+	return;
+fail:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTES, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static uint8_t err_to_att(int err)
+{
+	if (err < 0 && err >= -0xff) {
+		return -err;
+	}
+
+	return BT_ATT_ERR_UNLIKELY;
+}
+
+static uint8_t get_attr_val_rp(const struct bt_gatt_attr *attr, uint16_t handle,
+			       void *user_data)
+{
+	struct get_attr_data *u_data = user_data;
+	struct net_buf_simple *buf = u_data->buf;
+	struct bt_conn *conn = u_data->conn;
+	struct gatt_get_attribute_value_rp *rp;
+	ssize_t read, to_read;
+
+	rp = net_buf_simple_add(buf, sizeof(*rp));
+	rp->value_length = 0x0000;
+	rp->att_response = 0x00;
+
+	do {
+		to_read = net_buf_simple_tailroom(buf);
+
+		if (!attr->read) {
+			rp->att_response = BT_ATT_ERR_READ_NOT_PERMITTED;
+			break;
+		}
+
+		read = attr->read(conn, attr, buf->data + buf->len, to_read,
+				  rp->value_length);
+		if (read < 0) {
+			rp->att_response = err_to_att(read);
+			break;
+		}
+
+		rp->value_length += read;
+
+		net_buf_simple_add(buf, read);
+	} while (read == to_read);
+
+	return BT_GATT_ITER_STOP;
+}
+
+static void get_attr_val(uint8_t *data, uint16_t len)
+{
+	const struct gatt_get_attribute_value_cmd *cmd = (void *) data;
+	struct net_buf_simple *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
+	uint16_t handle = sys_le16_to_cpu(cmd->handle);
+	struct bt_conn *conn;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)cmd);
+
+	net_buf_simple_init(buf, 0);
+
+	struct get_attr_data cb_data = { .buf = buf, .conn = conn };
+
+	bt_gatt_foreach_attr(handle, handle, get_attr_val_rp, &cb_data);
+
+	if (buf->len) {
+		tester_send(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE,
+			    CONTROLLER_INDEX, buf->data, buf->len);
+	} else {
+		tester_rsp(BTP_SERVICE_ID_GATT, GATT_GET_ATTRIBUTE_VALUE,
+			   CONTROLLER_INDEX, BTP_STATUS_FAILED);
+	}
+}
+
+static void eatt_connect(uint8_t *data, uint16_t len)
+{
+	const struct gatt_eatt_connect_cmd *cmd = (void *)data;
+	struct bt_conn *conn;
+	uint8_t status = BTP_STATUS_SUCCESS;
+	int err;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)cmd);
+	if (!conn) {
+		status = BTP_STATUS_FAILED;
+		goto response;
+	}
+
+//	err = bt_eatt_connect(conn, cmd->num_channels);
+//	if (err) {
+//		status = BTP_STATUS_FAILED;
+//	}
+
+	bt_conn_unref(conn);
+
+response:
+	tester_rsp(BTP_SERVICE_ID_GATT, GATT_EATT_CONNECT, CONTROLLER_INDEX, status);
+}
+
+void tester_handle_gatt(uint8_t opcode, uint8_t index, uint8_t *data,
+			 uint16_t len)
+{
+	switch (opcode) {
+	case GATT_READ_SUPPORTED_COMMANDS:
+		supported_commands(data, len);
+		return;
+	case GATT_ADD_SERVICE:
+		add_service(data, len);
+		return;
+	case GATT_ADD_CHARACTERISTIC:
+		add_characteristic(data, len);
+		return;
+	case GATT_ADD_DESCRIPTOR:
+		add_descriptor(data, len);
+		return;
+	case GATT_ADD_INCLUDED_SERVICE:
+		add_included(data, len);
+		return;
+	case GATT_SET_VALUE:
+		set_value(data, len);
+		return;
+	case GATT_START_SERVER:
+		start_server(data, len);
+		return;
+	case GATT_SET_ENC_KEY_SIZE:
+		set_enc_key_size(data, len);
+		return;
+	case GATT_EXCHANGE_MTU:
+		exchange_mtu(data, len);
+		return;
+	case GATT_DISC_ALL_PRIM:
+		disc_all_prim(data, len);
+		return;
+	case GATT_DISC_PRIM_UUID:
+		disc_prim_uuid(data, len);
+		return;
+	case GATT_FIND_INCLUDED:
+		find_included(data, len);
+		return;
+	case GATT_DISC_ALL_CHRC:
+		disc_all_chrc(data, len);
+		return;
+	case GATT_DISC_CHRC_UUID:
+		disc_chrc_uuid(data, len);
+		return;
+	case GATT_DISC_ALL_DESC:
+		disc_all_desc(data, len);
+		return;
+	case GATT_READ:
+		read_data(data, len);
+		return;
+	case GATT_READ_UUID:
+		read_uuid(data, len);
+		return;
+	case GATT_READ_LONG:
+		read_long(data, len);
+		return;
+	case GATT_READ_MULTIPLE:
+	case GATT_READ_MULTIPLE_VAR:
+		read_multiple(data, len, opcode);
+		return;
+	case GATT_WRITE_WITHOUT_RSP:
+		write_without_rsp(data, len, opcode, false);
+		return;
+	case GATT_SIGNED_WRITE_WITHOUT_RSP:
+		write_without_rsp(data, len, opcode, true);
+		return;
+	case GATT_WRITE:
+		write_data(data, len);
+		return;
+	case GATT_WRITE_LONG:
+		write_long(data, len);
+		return;
+	case GATT_CFG_NOTIFY:
+	case GATT_CFG_INDICATE:
+		config_subscription(data, len, opcode);
+		return;
+	case GATT_GET_ATTRIBUTES:
+		get_attrs(data, len);
+		return;
+	case GATT_GET_ATTRIBUTE_VALUE:
+		get_attr_val(data, len);
+		return;
+	case GATT_EATT_CONNECT:
+		eatt_connect(data, len);
+		return;
+	case GATT_NOTIFY_MULTIPLE:
+//		notify_mult(data, len, opcode);
+		return;
+	default:
+		tester_rsp(BTP_SERVICE_ID_GATT, opcode, index,
+			   BTP_STATUS_UNKNOWN_CMD);
+		return;
+	}
+}
+
+uint8_t tester_init_gatt(void)
+{
+	SPOOL_INIT(server_pool, 1, SERVER_BUF_SIZE, 0);
+	server_buf = net_buf_alloc(&server_pool, K_NO_WAIT);
+	if (!server_buf) {
+		return BTP_STATUS_FAILED;
+	}
+
+	net_buf_reserve(server_buf, SERVER_BUF_SIZE);
+
+	return BTP_STATUS_SUCCESS;
+}
+
+uint8_t tester_unregister_gatt(void)
+{
+	return BTP_STATUS_SUCCESS;
+}

+ 617 - 0
example/tester/btp_l2cap.c

@@ -0,0 +1,617 @@
+/* l2cap.c - Bluetooth L2CAP Tester */
+
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <bluetooth/bluetooth.h>
+
+#include <errno.h>
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/att.h>
+#include <base/byteorder.h>
+
+#include <common/bt_buf.h>
+
+#define LOG_MODULE_NAME bttester_l2cap
+#include <logging/bt_log.h>
+
+#include "btp/btp.h"
+
+#define CONTROLLER_INDEX 0
+#define DATA_MTU_INITIAL 128
+#define DATA_MTU 256
+#define DATA_BUF_SIZE BT_L2CAP_SDU_BUF_SIZE(DATA_MTU)
+#define CHANNELS 2
+#define SERVERS 1
+
+SPOOL_DEFINE(data_pool, CHANNELS, DATA_BUF_SIZE, 8);
+
+static bool authorize_flag;
+static uint8_t req_keysize;
+
+static struct channel {
+	uint8_t chan_id; /* Internal number that identifies L2CAP channel. */
+	struct bt_l2cap_le_chan le;
+	bool in_use;
+	bool hold_credit;
+	struct net_buf *pending_credit;
+} channels[CHANNELS];
+
+/* TODO Extend to support multiple servers */
+static struct bt_l2cap_server servers[SERVERS];
+
+static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
+{
+	return net_buf_alloc(&data_pool, K_FOREVER);
+}
+
+static uint8_t recv_cb_buf[DATA_BUF_SIZE + sizeof(struct l2cap_data_received_ev)];
+
+static int recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
+{
+	struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
+	struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
+
+	ev->chan_id = chan->chan_id;
+	ev->data_length = sys_cpu_to_le16(buf->len);
+	memcpy(ev->data, buf->data, buf->len);
+
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
+		    CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->len);
+
+	if (chan->hold_credit && !chan->pending_credit) {
+		/* no need for extra ref, as when returning EINPROGRESS user
+		 * becomes owner of the netbuf
+		 */
+		chan->pending_credit = buf;
+		return -EINPROGRESS;
+	}
+
+	return 0;
+}
+
+static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
+{
+	struct l2cap_connected_ev ev;
+	struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
+	struct bt_conn_info info;
+
+	ev.chan_id = chan->chan_id;
+	/* TODO: ev.psm */
+	if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
+		switch (info.type) {
+		case BT_CONN_TYPE_LE:
+			ev.mtu_remote = sys_cpu_to_le16(chan->le.tx.mtu);
+			ev.mps_remote = sys_cpu_to_le16(chan->le.tx.mps);
+			ev.mtu_local = sys_cpu_to_le16(chan->le.rx.mtu);
+			ev.mps_local = sys_cpu_to_le16(chan->le.rx.mps);
+			ev.address_type = info.le.dst->type;
+			memcpy(ev.address, info.le.dst->a.val,
+			       sizeof(ev.address));
+			break;
+		case BT_CONN_TYPE_BR:
+			memcpy(ev.address, info.br.dst->val,
+			       sizeof(ev.address));
+			break;
+		}
+	}
+
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX,
+		    (uint8_t *) &ev, sizeof(ev));
+}
+
+static void disconnected_cb(struct bt_l2cap_chan *l2cap_chan)
+{
+	struct l2cap_disconnected_ev ev;
+	struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
+	struct bt_conn_info info;
+	BT_ERR("disconnected_cb 1");
+	/* release netbuf on premature disconnection */
+	if (chan->pending_credit) {
+		net_buf_unref(chan->pending_credit);
+		chan->pending_credit = NULL;
+	}
+
+	BT_ERR("disconnected_cb 2");
+	(void)memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
+
+	/* TODO: ev.result */
+	ev.chan_id = chan->chan_id;
+	/* TODO: ev.psm */
+	if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
+		switch (info.type) {
+		case BT_CONN_TYPE_LE:
+			ev.address_type = info.le.dst->type;
+			memcpy(ev.address, info.le.dst->a.val,
+			       sizeof(ev.address));
+			break;
+		case BT_CONN_TYPE_BR:
+			memcpy(ev.address, info.br.dst->val,
+			       sizeof(ev.address));
+			break;
+		}
+	}
+
+	BT_ERR("disconnected_cb 3");
+	chan->in_use = false;
+
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED,
+		    CONTROLLER_INDEX, (uint8_t *) &ev, sizeof(ev));
+	BT_ERR("disconnected_cb 4");
+}
+
+#if defined(CONFIG_BT_L2CAP_ECRED)
+static void reconfigured_cb(struct bt_l2cap_chan *l2cap_chan)
+{
+	struct l2cap_reconfigured_ev ev;
+	struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
+
+	(void)memset(&ev, 0, sizeof(ev));
+
+	ev.chan_id = chan->chan_id;
+	ev.mtu_remote = sys_cpu_to_le16(chan->le.tx.mtu);
+	ev.mps_remote = sys_cpu_to_le16(chan->le.tx.mps);
+	ev.mtu_local = sys_cpu_to_le16(chan->le.rx.mtu);
+	ev.mps_local = sys_cpu_to_le16(chan->le.rx.mps);
+
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_RECONFIGURED,
+		    CONTROLLER_INDEX, (uint8_t *)&ev, sizeof(ev));
+}
+#endif
+
+static const struct bt_l2cap_chan_ops l2cap_ops = {
+	.alloc_buf	= alloc_buf_cb,
+	.recv		= recv_cb,
+	.connected	= connected_cb,
+	.disconnected	= disconnected_cb,
+#if defined(CONFIG_BT_L2CAP_ECRED)
+	.reconfigured	= reconfigured_cb,
+#endif
+};
+
+static struct channel *get_free_channel()
+{
+	uint8_t i;
+	struct channel *chan;
+
+	for (i = 0U; i < CHANNELS; i++) {
+		if (channels[i].in_use) {
+			continue;
+		}
+
+		chan = &channels[i];
+
+		(void)memset(chan, 0, sizeof(*chan));
+		chan->chan_id = i;
+
+		channels[i].in_use = true;
+
+		return chan;
+	}
+
+	return NULL;
+}
+
+
+static void connect(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_connect_cmd *cmd = (void *) data;
+	struct l2cap_connect_rp *rp;
+	struct bt_conn *conn;
+	struct channel *chan = NULL;
+	struct bt_l2cap_chan *allocated_channels[5] = {};
+	uint16_t mtu = sys_le16_to_cpu(cmd->mtu);
+	uint8_t buf[sizeof(*rp) + CHANNELS];
+	uint8_t i = 0;
+	bool ecfc = cmd->options & L2CAP_CONNECT_OPT_ECFC;
+	int err;
+
+	if (cmd->num == 0 || cmd->num > CHANNELS || mtu > DATA_MTU_INITIAL) {
+		goto fail;
+	}
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		goto fail;
+	}
+
+	rp = (void *)buf;
+
+	for (i = 0U; i < cmd->num; i++) {
+		chan = get_free_channel();
+		if (!chan) {
+			goto fail;
+		}
+		chan->le.chan.ops = &l2cap_ops;
+		chan->le.rx.mtu = mtu;
+		rp->chan_id[i] = chan->chan_id;
+		allocated_channels[i] = &chan->le.chan;
+
+		chan->hold_credit = cmd->options & L2CAP_CONNECT_OPT_HOLD_CREDIT;
+	}
+
+	if (cmd->num == 1 && !ecfc) {
+		err = bt_l2cap_chan_connect(conn, &chan->le.chan, cmd->psm);
+		if (err < 0) {
+			goto fail;
+		}
+	} else if (ecfc) {
+#if defined(CONFIG_BT_L2CAP_ECRED)
+		err = bt_l2cap_ecred_chan_connect(conn, allocated_channels,
+							cmd->psm);
+		if (err < 0) {
+			goto fail;
+		}
+#else
+		goto fail;
+#endif
+	} else {
+		BT_ERR("Invalid 'num' parameter value");
+		goto fail;
+	}
+
+	rp->num = cmd->num;
+
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
+		    (uint8_t *)rp, sizeof(*rp) + rp->num);
+
+	return;
+
+fail:
+//	for (i = 0U; i < ARRAY_SIZE(allocated_channels); i++) {
+//		if (allocated_channels[i]) {
+//			channels[BT_L2CAP_LE_CHAN(allocated_channels[i])->ident].in_use = false;
+//		}
+//	}
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void disconnect(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_disconnect_cmd *cmd = (void *) data;
+	struct channel *chan = &channels[cmd->chan_id];
+	uint8_t status;
+	int err;
+
+	err = bt_l2cap_chan_disconnect(&chan->le.chan);
+	if (err) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX,
+		   status);
+}
+
+#if defined(CONFIG_BT_L2CAP_ECRED)
+static void reconfigure(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_reconfigure_cmd *cmd = (void *)data;
+	uint16_t mtu = sys_le16_to_cpu(cmd->mtu);
+	struct bt_conn *conn;
+	uint8_t status;
+	int err;
+
+	struct bt_l2cap_chan *reconf_channels[CHANNELS + 1] = {};
+
+	/* address is first in data */
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)cmd);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	if (cmd->num > CHANNELS) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	if (mtu > DATA_MTU) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	for (int i = 0; i < cmd->num; i++) {
+		if (cmd->chan_id[i] > CHANNELS) {
+			status = BTP_STATUS_FAILED;
+			goto rsp;
+		}
+
+		reconf_channels[i] = &channels[cmd->chan_id[i]].le.chan;
+	}
+
+	err = bt_l2cap_ecred_chan_reconfigure(reconf_channels, mtu);
+	if (err) {
+		status = BTP_STATUS_FAILED;
+		goto rsp;
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+rsp:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_RECONFIGURE, CONTROLLER_INDEX,
+		   status);
+}
+#endif
+
+#if defined(CONFIG_BT_EATT)
+void disconnect_eatt_chans(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_disconnect_eatt_chans_cmd *cmd = (void *) data;
+	struct bt_conn *conn;
+	int err;
+	int status;
+
+	conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
+	if (!conn) {
+		BT_ERR("Unknown connection");
+		status = BTP_STATUS_FAILED;
+		goto failed;
+	}
+
+	for (int i = 0; i < cmd->count; i++) {
+		err = bt_eatt_disconnect_one(conn);
+		if (err) {
+			status = BTP_STATUS_FAILED;
+			goto unref;
+		}
+	}
+
+	status = BTP_STATUS_SUCCESS;
+
+unref:
+	bt_conn_unref(conn);
+failed:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT_EATT_CHANS,
+		   CONTROLLER_INDEX, status);
+}
+#endif
+
+static void send_data(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_send_data_cmd *cmd = (void *) data;
+	struct channel *chan = &channels[cmd->chan_id];
+	struct net_buf *buf;
+	int ret;
+	uint16_t data_len = sys_le16_to_cpu(cmd->data_len);
+
+	/* FIXME: For now, fail if data length exceeds buffer length */
+	if (data_len > DATA_MTU) {
+		goto fail;
+	}
+
+	/* FIXME: For now, fail if data length exceeds remote's L2CAP SDU */
+	if (data_len > chan->le.tx.mtu) {
+		goto fail;
+	}
+
+	buf = net_buf_alloc(&data_pool, K_FOREVER);
+//	net_buf_reserve(buf, BT_L2CAP_SDU_CHAN_SEND_RESERVE);
+
+	net_buf_add_mem(buf, cmd->data, data_len);
+	ret = bt_l2cap_chan_send(&chan->le.chan, buf);
+	if (ret < 0) {
+		BT_ERR("Unable to send data: %d", -ret);
+		net_buf_unref(buf);
+		goto fail;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
+			BTP_STATUS_SUCCESS);
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
+			BTP_STATUS_FAILED);
+}
+
+static struct bt_l2cap_server *get_free_server(void)
+{
+	uint8_t i;
+
+	for (i = 0U; i < SERVERS ; i++) {
+		if (servers[i].psm) {
+			continue;
+		}
+
+		return &servers[i];
+	}
+
+	return NULL;
+}
+
+static bool is_free_psm(uint16_t psm)
+{
+	uint8_t i;
+
+	for (i = 0U; i < ARRAY_SIZE(servers); i++) {
+		if (servers[i].psm == psm) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int accept(struct bt_conn *conn, struct bt_l2cap_chan **l2cap_chan)
+{
+	struct channel *chan;
+
+	if (bt_conn_enc_key_size(conn) < req_keysize) {
+		return -EPERM;
+	}
+
+	if (authorize_flag) {
+		return -EACCES;
+	}
+
+	chan = get_free_channel();
+	if (!chan) {
+		return -ENOMEM;
+	}
+
+	chan->le.chan.ops = &l2cap_ops;
+	chan->le.rx.mtu = DATA_MTU_INITIAL;
+
+	*l2cap_chan = &chan->le.chan;
+
+	return 0;
+}
+
+static void listen(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_listen_cmd *cmd = (void *) data;
+	struct bt_l2cap_server *server;
+
+	/* TODO: Handle cmd->transport flag */
+
+	if (!is_free_psm(cmd->psm)) {
+		goto fail;
+	}
+
+	if (cmd->psm == 0) {
+		goto fail;
+	}
+
+	server = get_free_server();
+	if (!server) {
+		goto fail;
+	}
+
+	server->accept = accept;
+	server->psm = cmd->psm;
+
+	if (cmd->response == L2CAP_CONNECTION_RESPONSE_INSUFF_ENC_KEY) {
+		/* TSPX_psm_encryption_key_size_required */
+		req_keysize = 16;
+	} else if (cmd->response == L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHOR) {
+		authorize_flag = true;
+	} else if (cmd->response == L2CAP_CONNECTION_RESPONSE_INSUFF_AUTHEN) {
+		server->sec_level = BT_SECURITY_L3;
+	}
+
+	if (bt_l2cap_server_register(server) < 0) {
+		server->psm = 0U;
+		goto fail;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
+		   BTP_STATUS_SUCCESS);
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void credits(uint8_t *data, uint16_t len)
+{
+	const struct l2cap_credits_cmd *cmd = (void *)data;
+	struct channel *chan = &channels[cmd->chan_id];
+
+	if (!chan->in_use) {
+		goto fail;
+	}
+
+	if (chan->pending_credit) {
+		if (bt_l2cap_chan_recv_complete(&chan->le.chan,
+						chan->pending_credit) < 0) {
+			goto fail;
+		}
+
+		chan->pending_credit = NULL;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CREDITS, CONTROLLER_INDEX,
+		   BTP_STATUS_SUCCESS);
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CREDITS, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
+static void supported_commands(uint8_t *data, uint16_t len)
+{
+	uint8_t cmds[2];
+	struct l2cap_read_supported_commands_rp *rp = (void *) cmds;
+
+	(void)memset(cmds, 0, sizeof(cmds));
+
+	tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS);
+	tester_set_bit(cmds, L2CAP_CONNECT);
+	tester_set_bit(cmds, L2CAP_DISCONNECT);
+	tester_set_bit(cmds, L2CAP_LISTEN);
+	tester_set_bit(cmds, L2CAP_SEND_DATA);
+#if defined(CONFIG_BT_L2CAP_ECRED)
+	tester_set_bit(cmds, L2CAP_RECONFIGURE);
+#endif
+	tester_set_bit(cmds, L2CAP_CREDITS);
+#if defined(CONFIG_BT_EATT)
+	tester_set_bit(cmds, L2CAP_DISCONNECT_EATT_CHANS);
+#endif
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS,
+		    CONTROLLER_INDEX, (uint8_t *) rp, sizeof(cmds));
+}
+
+void tester_handle_l2cap(uint8_t opcode, uint8_t index, uint8_t *data,
+			 uint16_t len)
+{
+	switch (opcode) {
+	case L2CAP_READ_SUPPORTED_COMMANDS:
+		supported_commands(data, len);
+		return;
+	case L2CAP_CONNECT:
+		connect(data, len);
+		return;
+	case L2CAP_DISCONNECT:
+		disconnect(data, len);
+		return;
+	case L2CAP_SEND_DATA:
+		send_data(data, len);
+		return;
+	case L2CAP_LISTEN:
+		listen(data, len);
+		return;
+#if defined(CONFIG_BT_L2CAP_ECRED)
+	case L2CAP_RECONFIGURE:
+		reconfigure(data, len);
+		return;
+#endif
+	case L2CAP_CREDITS:
+		credits(data, len);
+		return;
+#if defined(CONFIG_BT_EATT)
+	case L2CAP_DISCONNECT_EATT_CHANS:
+		disconnect_eatt_chans(data, len);
+		return;
+#endif
+	default:
+		tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index,
+			   BTP_STATUS_UNKNOWN_CMD);
+		return;
+	}
+}
+
+uint8_t tester_init_l2cap(void)
+{
+	SPOOL_INIT(data_pool, CHANNELS, DATA_BUF_SIZE, 8);
+
+	return BTP_STATUS_SUCCESS;
+}
+
+uint8_t tester_unregister_l2cap(void)
+{
+	return BTP_STATUS_SUCCESS;
+}

+ 8 - 0
example/tester/build.mk

@@ -0,0 +1,8 @@
+# define source directory
+SRC		+= $(APP_PATH)
+
+# define include directory
+INCLUDE	+= $(APP_PATH)
+
+# define lib directory
+LIB		+=

+ 23 - 0
example/tester/prj.conf

@@ -0,0 +1,23 @@
+CONFIG_BT=y
+CONFIG_BT_CENTRAL=y
+CONFIG_BT_PERIPHERAL=y
+CONFIG_BT_PRIVACY=n
+CONFIG_BT_SMP=y
+CONFIG_BT_SMP_ENFORCE_MITM=n
+CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE=y
+CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y
+CONFIG_BT_SIGNING=y
+CONFIG_BT_BONDABLE=y
+CONFIG_BT_GATT_CLIENT=y
+CONFIG_BT_DEVICE_NAME="Tester"
+CONFIG_BT_DEVICE_NAME_MAX=32
+CONFIG_BT_DEVICE_NAME_DYNAMIC=y
+CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y
+CONFIG_BT_L2CAP_ECRED=y
+CONFIG_BT_FILTER_ACCEPT_LIST=y
+CONFIG_BT_EATT_AUTO_CONNECT=n
+CONFIG_BT_MAX_CONN=2
+CONFIG_BT_MAX_PAIRED=2
+CONFIG_BT_GATT_NOTIFY_MULTIPLE=y
+
+CONFIG_BT_TINYCRYPT_ECC=y