You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by na...@apache.org on 2020/05/05 07:49:44 UTC

[mynewt-nimble] branch master updated: apps/bttester: Add support for Enhanced L2CAP COC

This is an automated email from the ASF dual-hosted git repository.

naraj pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-nimble.git


The following commit(s) were added to refs/heads/master by this push:
     new 760f239  apps/bttester: Add support for Enhanced L2CAP COC
760f239 is described below

commit 760f2391ae56c48093a7dffdbfffa49ebef4526e
Author: MichaƂ Narajowski <mi...@codecoup.pl>
AuthorDate: Mon Feb 17 14:01:31 2020 +0100

    apps/bttester: Add support for Enhanced L2CAP COC
---
 apps/bttester/pkg.yml        |   2 +-
 apps/bttester/src/bttester.h |  29 +++-
 apps/bttester/src/l2cap.c    | 345 ++++++++++++++++++++++++++++++++++---------
 apps/bttester/syscfg.yml     |   6 +
 4 files changed, 308 insertions(+), 74 deletions(-)

diff --git a/apps/bttester/pkg.yml b/apps/bttester/pkg.yml
index 00e7a76..ba2b7fb 100644
--- a/apps/bttester/pkg.yml
+++ b/apps/bttester/pkg.yml
@@ -37,7 +37,7 @@ pkg.deps:
     - "@apache-mynewt-nimble/nimble/host/services/gap"
     - "@apache-mynewt-nimble/nimble/host/services/gatt"
     - "@apache-mynewt-nimble/nimble/host/services/dis"
-    - "@apache-mynewt-nimble/nimble/host/store/ram"
+    - "@apache-mynewt-nimble/nimble/host/store/config"
     - "@apache-mynewt-nimble/nimble/transport/ram"
     - "@apache-mynewt-core/hw/drivers/uart"
     - "@apache-mynewt-core/hw/drivers/rtt"
diff --git a/apps/bttester/src/bttester.h b/apps/bttester/src/bttester.h
index f4e66a6..d77d032 100644
--- a/apps/bttester/src/bttester.h
+++ b/apps/bttester/src/bttester.h
@@ -747,10 +747,13 @@ struct l2cap_connect_cmd {
 	u8_t address_type;
 	u8_t address[6];
 	u16_t psm;
+	u16_t mtu;
+	u8_t num;
 } __packed;
 
 struct l2cap_connect_rp {
-	u8_t chan_id;
+	u8_t num;
+	u8_t chan_ids[0];
 } __packed;
 
 #define L2CAP_DISCONNECT		0x03
@@ -772,6 +775,8 @@ struct l2cap_send_data_cmd {
 struct l2cap_listen_cmd {
 	u16_t psm;
 	u8_t transport;
+	u16_t mtu;
+	u16_t response;
 } __packed;
 
 #define L2CAP_ACCEPT_CONNECTION		0x06
@@ -780,6 +785,15 @@ struct l2cap_accept_connection_cmd {
 	u16_t result;
 } __packed;
 
+#define L2CAP_RECONFIGURE		0x07
+struct l2cap_reconfigure_cmd {
+    u8_t address_type;
+    u8_t address[6];
+    u16_t mtu;
+    u8_t num;
+    u8_t idxs[];
+} __packed;
+
 /* events */
 #define L2CAP_EV_CONNECTION_REQ		0x80
 struct l2cap_connection_req_ev {
@@ -793,6 +807,10 @@ struct l2cap_connection_req_ev {
 struct l2cap_connected_ev {
 	u8_t chan_id;
 	u16_t psm;
+	u16_t peer_mtu;
+	u16_t peer_mps;
+	u16_t our_mtu;
+	u16_t our_mps;
 	u8_t address_type;
 	u8_t address[6];
 } __packed;
@@ -813,6 +831,15 @@ struct l2cap_data_received_ev {
 	u8_t data[0];
 } __packed;
 
+#define L2CAP_EV_RECONFIGURED		0x84
+struct l2cap_reconfigured_ev {
+	u8_t chan_id;
+	u16_t peer_mtu;
+	u16_t peer_mps;
+	u16_t our_mtu;
+	u16_t our_mps;
+} __packed;
+
 /* MESH Service */
 /* commands */
 #define MESH_READ_SUPPORTED_COMMANDS	0x01
diff --git a/apps/bttester/src/l2cap.c b/apps/bttester/src/l2cap.c
index 45b904a..77a2a8c 100644
--- a/apps/bttester/src/l2cap.c
+++ b/apps/bttester/src/l2cap.c
@@ -33,11 +33,13 @@
 #include "host/ble_gap.h"
 #include "host/ble_l2cap.h"
 
+#include "../../../nimble/host/src/ble_l2cap_priv.h"
+
 #include "bttester.h"
 
 #define CONTROLLER_INDEX             0
 #define CHANNELS                     MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
-#define TESTER_COC_MTU               (230)
+#define TESTER_COC_MTU               MYNEWT_VAL(BTTESTER_L2CAP_COC_MTU)
 #define TESTER_COC_BUF_COUNT         (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
 
 static os_membuf_t tester_sdu_coc_mem[
@@ -55,7 +57,27 @@ static struct channel {
 
 static u8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)];
 
-struct channel *find_channel(struct ble_l2cap_chan *chan) {
+static struct channel *get_free_channel(void)
+{
+	u8_t i;
+	struct channel *chan;
+
+	for (i = 0; i < CHANNELS; i++) {
+		if (channels[i].state) {
+			continue;
+		}
+
+		chan = &channels[i];
+		chan->chan_id = i;
+
+		return chan;
+	}
+
+	return NULL;
+}
+
+struct channel *find_channel(struct ble_l2cap_chan *chan)
+{
 	int i;
 
 	for (i = 0; i < CHANNELS; ++i) {
@@ -67,6 +89,15 @@ struct channel *find_channel(struct ble_l2cap_chan *chan) {
 	return NULL;
 }
 
+struct channel *get_channel(uint8_t chan_id)
+{
+	if (chan_id >= CHANNELS) {
+		return NULL;
+	}
+
+	return &channels[chan_id];
+}
+
 static void
 tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
 {
@@ -84,14 +115,20 @@ static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
 		    struct os_mbuf *buf, void *arg)
 {
 	struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
-	struct channel *channel = arg;
+	struct channel *channel = find_channel(chan);
+	assert(channel != NULL);
 
 	ev->chan_id = channel->chan_id;
-	ev->data_length = buf->om_len;
-	memcpy(ev->data, buf->om_data, buf->om_len);
+	ev->data_length = OS_MBUF_PKTLEN(buf);
+
+	if (ev->data_length > TESTER_COC_MTU) {
+		SYS_LOG_ERR("Too large sdu received, truncating data");
+		ev->data_length = TESTER_COC_MTU;
+	}
+	os_mbuf_copydata(buf, 0, ev->data_length, ev->data);
 
 	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
-		    CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->om_len);
+		    CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + ev->data_length);
 
 	tester_l2cap_coc_recv(chan, buf);
 }
@@ -108,44 +145,49 @@ static void unstalled_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
 	}
 }
 
-static struct channel *get_free_channel(void)
+static void reconfigured_ev(uint16_t conn_handle, struct ble_l2cap_chan *chan,
+			    struct ble_l2cap_chan_info *chan_info,
+			    int status)
 {
-	u8_t i;
-	struct channel *chan;
+	struct l2cap_reconfigured_ev ev;
+	struct channel *channel;
 
-	for (i = 0; i < CHANNELS; i++) {
-		if (channels[i].state) {
-			continue;
-		}
+	if (status != 0) {
+		return;
+	}
 
-		chan = &channels[i];
-		chan->chan_id = i;
+	channel = find_channel(chan);
+	assert(channel != NULL);
 
-		return chan;
-	}
+	ev.chan_id = channel->chan_id;
+	ev.peer_mtu = chan_info->peer_coc_mtu;
+	ev.peer_mps = chan_info->peer_l2cap_mtu;
+	ev.our_mtu = chan_info->our_coc_mtu;
+	ev.our_mps = chan_info->our_l2cap_mtu;
 
-	return NULL;
+	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_RECONFIGURED,
+		    CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
 }
 
 static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
-			 void *arg)
+			 struct ble_l2cap_chan_info *chan_info, void *arg)
 {
 	struct l2cap_connected_ev ev;
 	struct ble_gap_conn_desc desc;
-	struct channel *channel;
+	struct channel *channel = find_channel(chan);
 
-	channel = get_free_channel();
-	if (!channel) {
-		assert(0);
+	if (channel == NULL) {
+		channel = get_free_channel();
 	}
 
-	channel->chan = chan;
-	channel->state = 0;
-
 	ev.chan_id = channel->chan_id;
+	ev.psm = chan_info->psm;
+	ev.peer_mtu = chan_info->peer_coc_mtu;
+	ev.peer_mps = chan_info->peer_l2cap_mtu;
+	ev.our_mtu = chan_info->our_coc_mtu;
+	ev.our_mps = chan_info->our_l2cap_mtu;
 	channel->state = 1;
 	channel->chan = chan;
-	/* TODO: ev.psm */
 
 	if (!ble_gap_conn_find(conn_handle, &desc)) {
 		ev.address_type = desc.peer_ota_addr.type;
@@ -158,7 +200,7 @@ static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
 }
 
 static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
-			    void *arg)
+			    struct ble_l2cap_chan_info *chan_info, void *arg)
 {
 	struct l2cap_disconnected_ev ev;
 	struct ble_gap_conn_desc desc;
@@ -167,14 +209,12 @@ static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
 	memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
 
 	channel = find_channel(chan);
-	if (channel != NULL) {
-		channel->state = 0;
-		channel->chan = chan;
+	assert(channel != NULL);
 
-		ev.chan_id = channel->chan_id;
-		/* TODO: ev.result */
-		/* TODO: ev.psm */
-	}
+	channel->state = 0;
+	channel->chan = chan;
+	ev.chan_id = channel->chan_id;
+	ev.psm = chan_info->psm;
 
 	if (!ble_gap_conn_find(conn_handle, &desc)) {
 		ev.address_type = desc.peer_ota_addr.type;
@@ -208,39 +248,51 @@ static int
 tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 {
 	struct ble_l2cap_chan_info chan_info;
+	int accept_response;
 
 	switch (event->type) {
-	case BLE_L2CAP_EVENT_COC_CONNECTED:
+		case BLE_L2CAP_EVENT_COC_CONNECTED:
+		if (ble_l2cap_get_chan_info(event->connect.chan, &chan_info)) {
+			assert(0);
+		}
+
 		if (event->connect.status) {
 			console_printf("LE COC error: %d\n", event->connect.status);
 			disconnected_cb(event->connect.conn_handle,
-					event->connect.chan, arg);
+					event->connect.chan, &chan_info, arg);
 			return 0;
 		}
 
-		ble_l2cap_get_chan_info(event->connect.chan, &chan_info);
-
-		console_printf("LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
-			       "dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
-			       event->connect.conn_handle,
-			       (uint32_t) event->connect.chan,
-			       chan_info.scid,
-			       chan_info.dcid,
-			       chan_info.our_l2cap_mtu,
-			       chan_info.peer_l2cap_mtu);
+		console_printf("LE COC connected, conn: %d, chan: 0x%08lx, "
+			       "psm: 0x%02x, scid: 0x%04x, dcid: 0x%04x, "
+			       "our_mps: %d, our_mtu: %d, peer_mps: %d, "
+			       "peer_mtu: %d\n", event->connect.conn_handle,
+			       (uint32_t) event->connect.chan, chan_info.psm,
+			       chan_info.scid, chan_info.dcid,
+			       chan_info.our_l2cap_mtu, chan_info.our_coc_mtu,
+			       chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu);
 
 		connected_cb(event->connect.conn_handle,
-			     event->connect.chan, arg);
+			     event->connect.chan, &chan_info, arg);
 
 		return 0;
 	case BLE_L2CAP_EVENT_COC_DISCONNECTED:
+		if (ble_l2cap_get_chan_info(event->disconnect.chan,
+					    &chan_info)) {
+			assert(0);
+		}
 		console_printf("LE CoC disconnected, chan: 0x%08lx\n",
 			       (uint32_t) event->disconnect.chan);
 
 		disconnected_cb(event->disconnect.conn_handle,
-				event->disconnect.chan, arg);
+				event->disconnect.chan, &chan_info, arg);
 		return 0;
 	case BLE_L2CAP_EVENT_COC_ACCEPT:
+		accept_response = POINTER_TO_INT(arg);
+		if (accept_response) {
+			return accept_response;
+		}
+
 		console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n",
 			       (uint32_t) event->accept.chan,
 			       event->accept.conn_handle,
@@ -254,7 +306,8 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 		console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n",
 			       (uint32_t) event->receive.chan,
 			       event->receive.conn_handle,
-			       event->receive.sdu_rx->om_len);
+			       OS_MBUF_PKTLEN(event->receive.sdu_rx));
+
 		recv_cb(event->receive.conn_handle, event->receive.chan,
 			event->receive.sdu_rx, arg);
 		return 0;
@@ -263,10 +316,49 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 			       (uint32_t) event->tx_unstalled.chan,
 			       event->tx_unstalled.conn_handle,
 			       event->tx_unstalled.status);
+
 		unstalled_cb(event->tx_unstalled.conn_handle,
 			     event->tx_unstalled.chan,
 			     event->tx_unstalled.status, arg);
 		return 0;
+	case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED:
+		if (ble_l2cap_get_chan_info(event->reconfigured.chan,
+					    &chan_info)) {
+			assert(0);
+		}
+		console_printf("LE CoC reconfigure completed status 0x%02x, "
+			       "chan: 0x%08lx\n", event->reconfigured.status,
+			       (uint32_t) event->reconfigured.chan);
+
+		if (event->reconfigured.status == 0) {
+			console_printf("\t our_mps: %d our_mtu %d\n",
+				       chan_info.our_l2cap_mtu, chan_info.our_coc_mtu);
+		}
+
+		reconfigured_ev(event->reconfigured.conn_handle,
+				event->reconfigured.chan,
+				&chan_info,
+				event->reconfigured.status);
+		return 0;
+	case BLE_L2CAP_EVENT_COC_PEER_RECONFIGURED:
+		if (ble_l2cap_get_chan_info(event->reconfigured.chan,
+					    &chan_info)) {
+			assert(0);
+		}
+		console_printf("LE CoC peer reconfigured status 0x%02x, "
+			       "chan: 0x%08lx\n", event->reconfigured.status,
+			       (uint32_t) event->reconfigured.chan);
+
+		if (event->reconfigured.status == 0) {
+			console_printf("\t peer_mps: %d peer_mtu %d\n",
+				       chan_info.peer_l2cap_mtu, chan_info.peer_coc_mtu);
+		}
+
+		reconfigured_ev(event->reconfigured.conn_handle,
+				event->reconfigured.chan,
+				&chan_info,
+				event->reconfigured.status);
+		return 0;
 	default:
 		return 0;
 	}
@@ -275,45 +367,67 @@ tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
 static void connect(u8_t *data, u16_t len)
 {
 	const struct l2cap_connect_cmd *cmd = (void *) data;
-	struct l2cap_connect_rp rp;
+	u8_t rp_buf[sizeof(struct l2cap_connect_rp) + cmd->num];
+	struct l2cap_connect_rp *rp = (void *) rp_buf;
 	struct ble_gap_conn_desc desc;
 	struct channel *chan;
-	struct os_mbuf *sdu_rx;
+	struct os_mbuf *sdu_rx[cmd->num];
 	ble_addr_t *addr = (void *) data;
+	uint16_t mtu = htole16(cmd->mtu);
 	int rc;
+	int i;
 
 	SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6));
 
+	if (mtu == 0 || mtu > TESTER_COC_MTU) {
+		mtu = TESTER_COC_MTU;
+	}
+
 	rc = ble_gap_conn_find_by_addr(addr, &desc);
 	if (rc) {
 		SYS_LOG_ERR("GAP conn find failed");
 		goto fail;
 	}
 
-	chan = get_free_channel();
-	if (!chan) {
-		SYS_LOG_ERR("No free channels");
-		goto fail;
+	rp->num = cmd->num;
+
+	for (i = 0; i < cmd->num; i++) {
+		chan = get_free_channel();
+		if (!chan) {
+			SYS_LOG_ERR("No free channels");
+			goto fail;
+		}
+
+		rp->chan_ids[i] = chan->chan_id;
+
+		sdu_rx[i] = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
+		if (sdu_rx[i] == NULL) {
+			SYS_LOG_ERR("Failed to alloc buf");
+			goto fail;
+		}
 	}
 
-	sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
-	if (sdu_rx == NULL) {
-		SYS_LOG_ERR("Failed to alloc buf");
+	if (cmd->num == 1) {
+		rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
+				       mtu, sdu_rx[0],
+				       tester_l2cap_event, NULL);
+	} else if (cmd->num > 1) {
+		rc = ble_l2cap_enhanced_connect(desc.conn_handle,
+						htole16(cmd->psm), mtu,
+						cmd->num, sdu_rx,
+						tester_l2cap_event, NULL);
+	} else {
+		SYS_LOG_ERR("Invalid 'num' parameter value");
 		goto fail;
 	}
 
-	rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
-			       TESTER_COC_MTU, sdu_rx,
-			       tester_l2cap_event, chan);
 	if (rc) {
 		SYS_LOG_ERR("L2CAP connect failed\n");
 		goto fail;
 	}
 
-	rp.chan_id = chan->chan_id;
-
 	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
-		    (u8_t *) &rp, sizeof(rp));
+		    (u8_t *) rp, sizeof(rp_buf));
 
 	return;
 
@@ -322,7 +436,7 @@ fail:
 		   BTP_STATUS_FAILED);
 }
 
-static void disconnect(u8_t *data, u16_t len)
+static void disconnect(const u8_t *data, u16_t len)
 {
 	const struct l2cap_disconnect_cmd *cmd = (void *) data;
 	struct channel *chan;
@@ -331,7 +445,8 @@ static void disconnect(u8_t *data, u16_t len)
 
 	SYS_LOG_DBG("");
 
-	chan = &channels[cmd->chan_id];
+	chan = get_channel(cmd->chan_id);
+	assert(chan != NULL);
 
 	err = ble_l2cap_disconnect(chan->chan);
 	if (err) {
@@ -346,16 +461,21 @@ rsp:
 		   status);
 }
 
-static void send_data(u8_t *data, u16_t len)
+static void send_data(const u8_t *data, u16_t len)
 {
 	const struct l2cap_send_data_cmd *cmd = (void *) data;
-	struct channel *chan = &channels[cmd->chan_id];
 	struct os_mbuf *sdu_tx = NULL;
 	int rc;
 	u16_t data_len = sys_le16_to_cpu(cmd->data_len);
+	struct channel *chan = get_channel(cmd->chan_id);
 
 	SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id);
 
+	if (!chan) {
+		SYS_LOG_ERR("Invalid channel\n");
+		goto fail;
+	}
+
 	/* FIXME: For now, fail if data length exceeds buffer length */
 	if (data_len > TESTER_COC_MTU) {
 		SYS_LOG_ERR("Data length exceeds buffer length");
@@ -386,16 +506,48 @@ fail:
 		   BTP_STATUS_FAILED);
 }
 
-static void listen(u8_t *data, u16_t len)
+static int
+l2cap_coc_err2hs_err(uint16_t coc_err)
+{
+	switch (coc_err) {
+		case BLE_L2CAP_COC_ERR_UNKNOWN_LE_PSM:
+			return BLE_HS_ENOTSUP;
+		case BLE_L2CAP_COC_ERR_NO_RESOURCES:
+			return BLE_HS_ENOMEM;
+		case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHEN:
+			return BLE_HS_EAUTHEN;
+		case BLE_L2CAP_COC_ERR_INSUFFICIENT_AUTHOR:
+			return BLE_HS_EAUTHOR;
+		case BLE_L2CAP_COC_ERR_INSUFFICIENT_ENC:
+			return BLE_HS_EENCRYPT;
+		case BLE_L2CAP_COC_ERR_INSUFFICIENT_KEY_SZ:
+			return BLE_HS_EENCRYPT_KEY_SZ;
+		case BLE_L2CAP_COC_ERR_UNACCEPTABLE_PARAMETERS:
+			return BLE_HS_EINVAL;
+		default:
+			return 0;
+	}
+}
+
+
+static void listen(const u8_t *data, u16_t len)
 {
 	const struct l2cap_listen_cmd *cmd = (void *) data;
+	uint16_t mtu = htole16(cmd->mtu);
+	uint16_t rsp = htole16(cmd->response);
 	int rc;
 
 	SYS_LOG_DBG("");
 
+	if (mtu == 0 || mtu > TESTER_COC_MTU) {
+		mtu = TESTER_COC_MTU;
+	}
+
+	rsp = l2cap_coc_err2hs_err(rsp);
+
 	/* TODO: Handle cmd->transport flag */
-	rc = ble_l2cap_create_server(cmd->psm, TESTER_COC_MTU,
-				     tester_l2cap_event, NULL);
+	rc = ble_l2cap_create_server(cmd->psm, mtu, tester_l2cap_event,
+				     INT_TO_POINTER(rsp));
 	if (rc) {
 		goto fail;
 	}
@@ -409,6 +561,51 @@ fail:
 		   BTP_STATUS_FAILED);
 }
 
+static void reconfigure(const u8_t *data, u16_t len)
+{
+	const struct l2cap_reconfigure_cmd *cmd = (void *) data;
+	uint16_t mtu = htole16(cmd->mtu);
+	struct ble_gap_conn_desc desc;
+	ble_addr_t *addr = (void *) data;
+	struct ble_l2cap_chan *chans[cmd->num];
+	struct channel *channel;
+	int rc;
+	int i;
+
+	SYS_LOG_DBG("");
+
+	if (mtu == 0 || mtu > TESTER_COC_MTU) {
+		mtu = TESTER_COC_MTU;
+	}
+
+	rc = ble_gap_conn_find_by_addr(addr, &desc);
+	if (rc) {
+		SYS_LOG_ERR("GAP conn find failed");
+		goto fail;
+	}
+
+	for (i = 0; i < cmd->num; ++i) {
+		channel = get_channel(cmd->idxs[i]);
+		if (channel == NULL) {
+			goto fail;
+		}
+		chans[i] = channel->chan;
+	}
+
+	rc = ble_l2cap_reconfig(chans, cmd->num, mtu);
+	if (rc) {
+		goto fail;
+	}
+
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_RECONFIGURE, CONTROLLER_INDEX,
+		   BTP_STATUS_SUCCESS);
+	return;
+
+fail:
+	tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_RECONFIGURE, CONTROLLER_INDEX,
+		   BTP_STATUS_FAILED);
+}
+
 static void supported_commands(u8_t *data, u16_t len)
 {
 	u8_t cmds[1];
@@ -421,6 +618,7 @@ static void supported_commands(u8_t *data, u16_t len)
 	tester_set_bit(cmds, L2CAP_DISCONNECT);
 	tester_set_bit(cmds, L2CAP_LISTEN);
 	tester_set_bit(cmds, L2CAP_SEND_DATA);
+	tester_set_bit(cmds, L2CAP_RECONFIGURE);
 
 	tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS,
 		    CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
@@ -445,6 +643,9 @@ void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
 	case L2CAP_LISTEN:
 		listen(data, len);
 		return;
+	case L2CAP_RECONFIGURE:
+		reconfigure(data, len);
+		return;
 	default:
 		tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index,
 			   BTP_STATUS_UNKNOWN_CMD);
diff --git a/apps/bttester/syscfg.yml b/apps/bttester/syscfg.yml
index d0fffe1..c9ba0df 100644
--- a/apps/bttester/syscfg.yml
+++ b/apps/bttester/syscfg.yml
@@ -71,6 +71,10 @@ syscfg.defs:
         description: Enable logging BTP traffic
         value: 0
 
+    BTTESTER_L2CAP_COC_MTU:
+        description: Maximum MTU size the application can handle
+        value: 230
+
 syscfg.vals:
     OS_MAIN_STACK_SIZE: 512
     SHELL_TASK: 0
@@ -86,6 +90,8 @@ syscfg.vals:
 
     BLE_L2CAP_COC_MAX_NUM: 2
     BLE_L2CAP_SIG_MAX_PROCS: 2
+    BLE_L2CAP_ENHANCED_COC: 1
+    BLE_VERSION: 52
     # Some testcases require MPS < MTU
     BLE_L2CAP_COC_MPS: 100
     BLE_RPA_TIMEOUT: 30