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 <szymon.janc@codecoup.pl>
This commit is contained in:
parent
12351cb391
commit
c589994dc0
2 changed files with 164 additions and 0 deletions
|
@ -177,6 +177,10 @@ struct bt_l2cap_le_chan {
|
||||||
* @ref BT_L2CAP_SDU_RX_MTU by the stack.
|
* @ref BT_L2CAP_SDU_RX_MTU by the stack.
|
||||||
*/
|
*/
|
||||||
struct bt_l2cap_le_endpoint rx;
|
struct bt_l2cap_le_endpoint rx;
|
||||||
|
|
||||||
|
/** Pending RX MTU on ECFC reconfigure, used internally by stack */
|
||||||
|
uint16_t pending_rx_mtu;
|
||||||
|
|
||||||
/** Channel Transmission Endpoint */
|
/** Channel Transmission Endpoint */
|
||||||
struct bt_l2cap_le_endpoint tx;
|
struct bt_l2cap_le_endpoint tx;
|
||||||
/** Channel Transmission queue */
|
/** Channel Transmission queue */
|
||||||
|
@ -316,6 +320,16 @@ struct bt_l2cap_chan_ops {
|
||||||
* references to the channel object.
|
* references to the channel object.
|
||||||
*/
|
*/
|
||||||
void (*released)(struct bt_l2cap_chan *chan);
|
void (*released)(struct bt_l2cap_chan *chan);
|
||||||
|
|
||||||
|
/** @brief Channel reconfigured callback
|
||||||
|
*
|
||||||
|
* If this callback is provided it will be called whenever peer or
|
||||||
|
* local device requested reconfiguration. Application may check
|
||||||
|
* updated MTU and MPS values by inspecting chan->le endpoints.
|
||||||
|
*
|
||||||
|
* @param chan The channel which was reconfigured
|
||||||
|
*/
|
||||||
|
void (*reconfigured)(struct bt_l2cap_chan *chan);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @def BT_L2CAP_CHAN_SEND_RESERVE
|
/** @def BT_L2CAP_CHAN_SEND_RESERVE
|
||||||
|
@ -414,6 +428,20 @@ int bt_l2cap_br_server_register(struct bt_l2cap_server *server);
|
||||||
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
|
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
|
||||||
struct bt_l2cap_chan **chans, uint16_t psm);
|
struct bt_l2cap_chan **chans, uint16_t psm);
|
||||||
|
|
||||||
|
/** @brief Reconfigure Enhanced Credit Based L2CAP channels
|
||||||
|
*
|
||||||
|
* Reconfigure up to 5 L2CAP channels. Channels must be from the same bt_conn.
|
||||||
|
* Once reconfiguration is completed each channel reconfigured() callback will
|
||||||
|
* be called. MTU cannot be decreased on any of provided channels.
|
||||||
|
*
|
||||||
|
* @param chans Array of channel objects. Null-terminated. Elements after the
|
||||||
|
* first 5 are silently ignored.
|
||||||
|
* @param mtu Channel MTU to reconfigure to.
|
||||||
|
*
|
||||||
|
* @return 0 in case of success or negative value in case of error.
|
||||||
|
*/
|
||||||
|
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu);
|
||||||
|
|
||||||
/** @brief Connect L2CAP channel
|
/** @brief Connect L2CAP channel
|
||||||
*
|
*
|
||||||
* Connect L2CAP channel by PSM, once the connection is completed channel
|
* Connect L2CAP channel by PSM, once the connection is completed channel
|
||||||
|
|
|
@ -1298,6 +1298,10 @@ static void le_ecred_reconf_req(struct bt_l2cap *l2cap, uint8_t ident,
|
||||||
for (int i = 0; i < chan_count; i++) {
|
for (int i = 0; i < chan_count; i++) {
|
||||||
BT_L2CAP_LE_CHAN(chans[i])->tx.mtu = mtu;
|
BT_L2CAP_LE_CHAN(chans[i])->tx.mtu = mtu;
|
||||||
BT_L2CAP_LE_CHAN(chans[i])->tx.mps = mps;
|
BT_L2CAP_LE_CHAN(chans[i])->tx.mps = mps;
|
||||||
|
|
||||||
|
if (chans[i]->ops->reconfigured) {
|
||||||
|
chans[i]->ops->reconfigured(chans[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("mtu %u mps %u", mtu, mps);
|
BT_DBG("mtu %u mps %u", mtu, mps);
|
||||||
|
@ -1311,6 +1315,36 @@ response:
|
||||||
|
|
||||||
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void le_ecred_reconf_rsp(struct bt_l2cap *l2cap, uint8_t ident,
|
||||||
|
struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_conn *conn = l2cap->chan.chan.conn;
|
||||||
|
struct bt_l2cap_ecred_reconf_rsp *rsp;
|
||||||
|
struct bt_l2cap_le_chan *ch;
|
||||||
|
uint16_t result;
|
||||||
|
|
||||||
|
if (buf->len < sizeof(*rsp)) {
|
||||||
|
BT_ERR("Too small ecred reconf rsp packet size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
|
||||||
|
result = sys_le16_to_cpu(rsp->result);
|
||||||
|
|
||||||
|
while ((ch = l2cap_lookup_ident(conn, ident))) {
|
||||||
|
if (result == BT_L2CAP_LE_SUCCESS) {
|
||||||
|
ch->rx.mtu = ch->pending_rx_mtu;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch->pending_rx_mtu = 0;
|
||||||
|
ch->chan.ident = 0U;
|
||||||
|
|
||||||
|
if (ch->chan.ops->reconfigured) {
|
||||||
|
ch->chan.ops->reconfigured(&ch->chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
||||||
|
|
||||||
static struct bt_l2cap_le_chan *l2cap_remove_rx_cid(struct bt_conn *conn,
|
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)
|
||||||
case BT_L2CAP_ECRED_RECONF_REQ:
|
case BT_L2CAP_ECRED_RECONF_REQ:
|
||||||
le_ecred_reconf_req(l2cap, hdr->ident, buf);
|
le_ecred_reconf_req(l2cap, hdr->ident, buf);
|
||||||
break;
|
break;
|
||||||
|
case BT_L2CAP_ECRED_RECONF_RSP:
|
||||||
|
le_ecred_reconf_rsp(l2cap, hdr->ident, buf);
|
||||||
|
break;
|
||||||
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
||||||
#else
|
#else
|
||||||
case BT_L2CAP_CMD_REJECT:
|
case BT_L2CAP_CMD_REJECT:
|
||||||
|
@ -2566,6 +2603,105 @@ fail:
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct bt_l2cap_le_chan *l2cap_find_pending_reconf(struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_chan *chan;
|
||||||
|
|
||||||
|
SYS_SLIST_FOR_EACH_CONTAINER(&conn->channels, chan, node) {
|
||||||
|
if (BT_L2CAP_LE_CHAN(chan)->pending_rx_mtu) {
|
||||||
|
return BT_L2CAP_LE_CHAN(chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_l2cap_ecred_chan_reconfigure(struct bt_l2cap_chan **chans, uint16_t mtu)
|
||||||
|
{
|
||||||
|
struct bt_l2cap_ecred_reconf_req *req;
|
||||||
|
struct bt_conn *conn = NULL;
|
||||||
|
struct bt_l2cap_le_chan *ch;
|
||||||
|
struct net_buf *buf;
|
||||||
|
uint8_t ident;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
BT_DBG("chans %p mtu 0x%04x", chans, mtu);
|
||||||
|
|
||||||
|
if (!chans) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < L2CAP_ECRED_CHAN_MAX; i++) {
|
||||||
|
if (!chans[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* validate that all channels are from same connection */
|
||||||
|
if (conn) {
|
||||||
|
if (conn != chans[i]->conn) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn = chans[i]->conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* validate MTU is not decreased */
|
||||||
|
if (mtu < BT_L2CAP_LE_CHAN(chans[i])->rx.mtu) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn) {
|
||||||
|
return -ENOTCONN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->type != BT_CONN_TYPE_LE) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allow only 1 request at time */
|
||||||
|
if (l2cap_find_pending_reconf(conn)) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
ident = get_ident();
|
||||||
|
|
||||||
|
buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_ECRED_RECONF_REQ,
|
||||||
|
ident,
|
||||||
|
sizeof(*req) + (i * sizeof(uint16_t)));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
req = net_buf_add(buf, sizeof(*req));
|
||||||
|
req->mtu = sys_cpu_to_le16(mtu);
|
||||||
|
|
||||||
|
/* MPS shall not be bigger than MTU + BT_L2CAP_SDU_HDR_SIZE
|
||||||
|
* as the remaining bytes cannot be used.
|
||||||
|
*/
|
||||||
|
req->mps = sys_cpu_to_le16(MIN(mtu + BT_L2CAP_SDU_HDR_SIZE,
|
||||||
|
BT_L2CAP_RX_MTU));
|
||||||
|
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
ch = BT_L2CAP_LE_CHAN(chans[j]);
|
||||||
|
|
||||||
|
ch->chan.ident = ident;
|
||||||
|
ch->pending_rx_mtu = mtu;
|
||||||
|
|
||||||
|
net_buf_add_le16(buf, ch->rx.cid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* we use first channel for sending and timeouting */
|
||||||
|
l2cap_chan_send_req(chans[0], buf, L2CAP_CONN_TIMEOUT);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
#endif /* defined(CONFIG_BT_L2CAP_ECRED) */
|
||||||
|
|
||||||
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue