Skip to content

Commit c589994

Browse files
sjanccarlescufi
authored andcommitted
bluetooth: Add support for reconfiguring L2CAP channels
This allows application to increase channel's MTU and (in some cases) MPS. When channel gets reconfigured dedicated callback is called to inform application. Signed-off-by: Szymon Janc <[email protected]>
1 parent 12351cb commit c589994

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

include/bluetooth/l2cap.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ struct bt_l2cap_le_chan {
177177
* @ref BT_L2CAP_SDU_RX_MTU by the stack.
178178
*/
179179
struct bt_l2cap_le_endpoint rx;
180+
181+
/** Pending RX MTU on ECFC reconfigure, used internally by stack */
182+
uint16_t pending_rx_mtu;
183+
180184
/** Channel Transmission Endpoint */
181185
struct bt_l2cap_le_endpoint tx;
182186
/** Channel Transmission queue */
@@ -316,6 +320,16 @@ struct bt_l2cap_chan_ops {
316320
* references to the channel object.
317321
*/
318322
void (*released)(struct bt_l2cap_chan *chan);
323+
324+
/** @brief Channel reconfigured callback
325+
*
326+
* If this callback is provided it will be called whenever peer or
327+
* local device requested reconfiguration. Application may check
328+
* updated MTU and MPS values by inspecting chan->le endpoints.
329+
*
330+
* @param chan The channel which was reconfigured
331+
*/
332+
void (*reconfigured)(struct bt_l2cap_chan *chan);
319333
};
320334

321335
/** @def BT_L2CAP_CHAN_SEND_RESERVE
@@ -414,6 +428,20 @@ int bt_l2cap_br_server_register(struct bt_l2cap_server *server);
414428
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
415429
struct bt_l2cap_chan **chans, uint16_t psm);
416430

431+
/** @brief Reconfigure Enhanced Credit Based L2CAP channels
432+
*
433+
* Reconfigure up to 5 L2CAP channels. Channels must be from the same bt_conn.
434+
* Once reconfiguration is completed each channel reconfigured() callback will
435+
* be called. MTU cannot be decreased on any of provided channels.
436+
*
437+
* @param chans Array of channel objects. Null-terminated. Elements after the
438+
* first 5 are silently ignored.
439+
* @param mtu Channel MTU to reconfigure to.
440+
*
441+
* @return 0 in case of success or negative value in case of error.
442+
*/
443+
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu);
444+
417445
/** @brief Connect L2CAP channel
418446
*
419447
* Connect L2CAP channel by PSM, once the connection is completed channel

subsys/bluetooth/host/l2cap.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,10 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
12981298
for (int i = 0; i < chan_count; i++) {
12991299
BT_L2CAP_LE_CHAN(chans[i])->tx.mtu = mtu;
13001300
BT_L2CAP_LE_CHAN(chans[i])->tx.mps = mps;
1301+
1302+
if (chans[i]->ops->reconfigured) {
1303+
chans[i]->ops->reconfigured(chans[i]);
1304+
}
13011305
}
13021306

13031307
BT_DBG("mtu %u mps %u", mtu, mps);
@@ -1311,6 +1315,36 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
13111315

13121316
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
13131317
}
1318+
1319+
static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
1320+
struct net_buf *buf)
1321+
{
1322+
struct bt_conn *conn = l2cap->chan.chan.conn;
1323+
struct bt_l2cap_ecred_reconf_rsp *rsp;
1324+
struct bt_l2cap_le_chan *ch;
1325+
uint16_t result;
1326+
1327+
if (buf->len < sizeof(*rsp)) {
1328+
BT_ERR("Too small ecred reconf rsp packet size");
1329+
return;
1330+
}
1331+
1332+
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
1333+
result = sys_le16_to_cpu(rsp->result);
1334+
1335+
while ((ch = l2cap_lookup_ident(conn, ident))) {
1336+
if (result == BT_L2CAP_LE_SUCCESS) {
1337+
ch->rx.mtu = ch->pending_rx_mtu;
1338+
}
1339+
1340+
ch->pending_rx_mtu = 0;
1341+
ch->chan.ident = 0U;
1342+
1343+
if (ch->chan.ops->reconfigured) {
1344+
ch->chan.ops->reconfigured(&ch->chan);
1345+
}
1346+
}
1347+
}
13141348
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
13151349

13161350
static struct bt_l2cap_le_chan *l2cap_remove_rx_cid(struct bt_conn *conn,
@@ -2013,6 +2047,9 @@ static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
20132047
case BT_L2CAP_ECRED_RECONF_REQ:
20142048
le_ecred_reconf_req(l2cap, hdr->ident, buf);
20152049
break;
2050+
case BT_L2CAP_ECRED_RECONF_RSP:
2051+
le_ecred_reconf_rsp(l2cap, hdr->ident, buf);
2052+
break;
20162053
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
20172054
#else
20182055
case BT_L2CAP_CMD_REJECT:
@@ -2566,6 +2603,105 @@ int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
25662603

25672604
return err;
25682605
}
2606+
2607+
static struct bt_l2cap_le_chan *l2cap_find_pending_reconf(struct bt_conn *conn)
2608+
{
2609+
struct bt_l2cap_chan *chan;
2610+
2611+
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
2612+
if (BT_L2CAP_LE_CHAN(chan)->pending_rx_mtu) {
2613+
return BT_L2CAP_LE_CHAN(chan);
2614+
}
2615+
}
2616+
2617+
return NULL;
2618+
}
2619+
2620+
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
2621+
{
2622+
struct bt_l2cap_ecred_reconf_req *req;
2623+
struct bt_conn *conn = NULL;
2624+
struct bt_l2cap_le_chan *ch;
2625+
struct net_buf *buf;
2626+
uint8_t ident;
2627+
int i;
2628+
2629+
BT_DBG("chans %p mtu 0x%04x", chans, mtu);
2630+
2631+
if (!chans) {
2632+
return -EINVAL;
2633+
}
2634+
2635+
for (i = 0; i < L2CAP_ECRED_CHAN_MAX; i++) {
2636+
if (!chans[i]) {
2637+
break;
2638+
}
2639+
2640+
/* validate that all channels are from same connection */
2641+
if (conn) {
2642+
if (conn != chans[i]->conn) {
2643+
return -EINVAL;
2644+
}
2645+
} else {
2646+
conn = chans[i]->conn;
2647+
}
2648+
2649+
/* validate MTU is not decreased */
2650+
if (mtu < BT_L2CAP_LE_CHAN(chans[i])->rx.mtu) {
2651+
return -EINVAL;
2652+
}
2653+
}
2654+
2655+
if (i == 0) {
2656+
return -EINVAL;
2657+
}
2658+
2659+
if (!conn) {
2660+
return -ENOTCONN;
2661+
}
2662+
2663+
if (conn->type != BT_CONN_TYPE_LE) {
2664+
return -EINVAL;
2665+
}
2666+
2667+
/* allow only 1 request at time */
2668+
if (l2cap_find_pending_reconf(conn)) {
2669+
return -EBUSY;
2670+
}
2671+
2672+
ident = get_ident();
2673+
2674+
buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_ECRED_RECONF_REQ,
2675+
ident,
2676+
sizeof(*req) + (i * sizeof(uint16_t)));
2677+
if (!buf) {
2678+
return -ENOMEM;
2679+
}
2680+
2681+
req = net_buf_add(buf, sizeof(*req));
2682+
req->mtu = sys_cpu_to_le16(mtu);
2683+
2684+
/* MPS shall not be bigger than MTU + BT_L2CAP_SDU_HDR_SIZE
2685+
* as the remaining bytes cannot be used.
2686+
*/
2687+
req->mps = sys_cpu_to_le16(MIN(mtu + BT_L2CAP_SDU_HDR_SIZE,
2688+
BT_L2CAP_RX_MTU));
2689+
2690+
for (int j = 0; j < i; j++) {
2691+
ch = BT_L2CAP_LE_CHAN(chans[j]);
2692+
2693+
ch->chan.ident = ident;
2694+
ch->pending_rx_mtu = mtu;
2695+
2696+
net_buf_add_le16(buf, ch->rx.cid);
2697+
};
2698+
2699+
/* we use first channel for sending and timeouting */
2700+
l2cap_chan_send_req(chans[0], buf, L2CAP_CONN_TIMEOUT);
2701+
2702+
return 0;
2703+
}
2704+
25692705
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
25702706

25712707
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,

0 commit comments

Comments
 (0)