Bluetooth: L2CAP: Add initial implementation of ECRED mode
This adds the initial implementation of ECRED mode which can connect up to 5 channels simultaneously and is required by EATT. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
72d4f06bf0
commit
f7586b0b04
2 changed files with 508 additions and 51 deletions
|
@ -335,6 +335,21 @@ int bt_l2cap_server_register(struct bt_l2cap_server *server);
|
|||
*/
|
||||
int bt_l2cap_br_server_register(struct bt_l2cap_server *server);
|
||||
|
||||
/** @brief Connect Enhanced Credit Based L2CAP channels
|
||||
*
|
||||
* Connect up to 5 L2CAP channels by PSM, once the connection is completed
|
||||
* each channel connected() callback will be called. If the connection is
|
||||
* rejected disconnected() callback is called instead.
|
||||
*
|
||||
* @param conn Connection object.
|
||||
* @param chans Array of channel objects.
|
||||
* @param psm Channel PSM to connect to.
|
||||
*
|
||||
* @return 0 in case of success or negative value in case of error.
|
||||
*/
|
||||
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
|
||||
struct bt_l2cap_chan **chans, u16_t psm);
|
||||
|
||||
/** @brief Connect L2CAP channel
|
||||
*
|
||||
* Connect L2CAP channel by PSM, once the connection is completed channel
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define CHAN_RX(_w) CONTAINER_OF(_w, struct bt_l2cap_le_chan, rx_work)
|
||||
|
||||
#define L2CAP_LE_MIN_MTU 23
|
||||
#define L2CAP_ECRED_MIN_MTU 64
|
||||
|
||||
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
|
||||
#define L2CAP_LE_MAX_CREDITS (CONFIG_BT_ACL_RX_COUNT - 1)
|
||||
|
@ -65,6 +66,8 @@ NET_BUF_POOL_FIXED_DEFINE(disc_pool, 1,
|
|||
/* For now use MPS - SDU length to disable segmentation */
|
||||
#define L2CAP_MAX_LE_MTU (L2CAP_MAX_LE_MPS - 2)
|
||||
|
||||
#define L2CAP_ECRED_CHAN_MAX 5
|
||||
|
||||
#define l2cap_lookup_ident(conn, ident) __l2cap_lookup_ident(conn, ident, false)
|
||||
#define l2cap_remove_ident(conn, ident) __l2cap_lookup_ident(conn, ident, true)
|
||||
|
||||
|
@ -256,17 +259,24 @@ destroy:
|
|||
if (chan->destroy) {
|
||||
chan->destroy(chan);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void l2cap_rtx_timeout(struct k_work *work)
|
||||
{
|
||||
struct bt_l2cap_le_chan *chan = LE_CHAN_RTX(work);
|
||||
struct bt_conn *conn = chan->chan.conn;
|
||||
|
||||
BT_ERR("chan %p timeout", chan);
|
||||
|
||||
bt_l2cap_chan_remove(chan->chan.conn, &chan->chan);
|
||||
bt_l2cap_chan_remove(conn, &chan->chan);
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
/* Remove other channels if pending on the same ident */
|
||||
while ((chan = l2cap_remove_ident(conn, chan->chan.ident))) {
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
}
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
|
@ -410,7 +420,7 @@ static struct net_buf *l2cap_create_le_sig_pdu(struct net_buf *buf,
|
|||
}
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan,
|
||||
static void l2cap_chan_send_req(struct bt_l2cap_chan *chan,
|
||||
struct net_buf *buf, k_timeout_t timeout)
|
||||
{
|
||||
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part A] page 126:
|
||||
|
@ -422,9 +432,9 @@ static void l2cap_chan_send_req(struct bt_l2cap_le_chan *chan,
|
|||
* final expiration, when the response is received, or the physical
|
||||
* link is lost.
|
||||
*/
|
||||
k_delayed_work_submit(&chan->chan.rtx_work, timeout);
|
||||
k_delayed_work_submit(&chan->rtx_work, timeout);
|
||||
|
||||
bt_l2cap_send(chan->chan.conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||
bt_l2cap_send(chan->conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||
}
|
||||
|
||||
static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
|
||||
|
@ -447,15 +457,58 @@ static int l2cap_le_conn_req(struct bt_l2cap_le_chan *ch)
|
|||
req->mps = sys_cpu_to_le16(ch->rx.mps);
|
||||
req->credits = sys_cpu_to_le16(ch->rx.init_credits);
|
||||
|
||||
l2cap_chan_send_req(ch, buf, L2CAP_CONN_TIMEOUT);
|
||||
l2cap_chan_send_req(&ch->chan, buf, L2CAP_CONN_TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_ecred_conn_req(struct bt_l2cap_chan **chan, int channels)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
struct bt_l2cap_ecred_conn_req *req;
|
||||
struct bt_l2cap_le_chan *ch;
|
||||
int i;
|
||||
u8_t ident;
|
||||
|
||||
if (!chan || !channels) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ident = get_ident();
|
||||
|
||||
buf = l2cap_create_le_sig_pdu(NULL, BT_L2CAP_ECRED_CONN_REQ, ident,
|
||||
sizeof(*req) +
|
||||
(channels * sizeof(u16_t)));
|
||||
|
||||
req = net_buf_add(buf, sizeof(*req));
|
||||
|
||||
ch = BT_L2CAP_LE_CHAN(chan[0]);
|
||||
|
||||
/* Init common parameters */
|
||||
req->psm = sys_cpu_to_le16(ch->chan.psm);
|
||||
req->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
||||
req->mps = sys_cpu_to_le16(ch->rx.mps);
|
||||
req->credits = sys_cpu_to_le16(ch->rx.init_credits);
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
ch = BT_L2CAP_LE_CHAN(chan[i]);
|
||||
|
||||
ch->chan.ident = ident;
|
||||
|
||||
net_buf_add_le16(buf, ch->rx.cid);
|
||||
}
|
||||
|
||||
l2cap_chan_send_req(*chan, buf, L2CAP_CONN_TIMEOUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, u8_t status)
|
||||
{
|
||||
/* Skip channels already connected or with a pending request */
|
||||
if (chan->state != BT_L2CAP_CONNECT || chan->ident) {
|
||||
/* Skip channels already connected or there is an ongoing request */
|
||||
if (chan->state != BT_L2CAP_CONNECT ||
|
||||
k_work_pending(&chan->rtx_work.work) ||
|
||||
k_delayed_work_remaining_get(&chan->rtx_work)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -465,6 +518,20 @@ static void l2cap_le_encrypt_change(struct bt_l2cap_chan *chan, u8_t status)
|
|||
return;
|
||||
}
|
||||
|
||||
if (chan->ident) {
|
||||
struct bt_l2cap_chan *echan[L2CAP_ECRED_CHAN_MAX];
|
||||
struct bt_l2cap_le_chan *ch;
|
||||
int i = 0;
|
||||
|
||||
while ((ch = l2cap_remove_ident(chan->conn, chan->ident))) {
|
||||
echan[i++] = &ch->chan;
|
||||
}
|
||||
|
||||
/* Retry ecred connect */
|
||||
l2cap_ecred_conn_req(echan, i);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Retry to connect */
|
||||
l2cap_le_conn_req(BT_L2CAP_LE_CHAN(chan));
|
||||
}
|
||||
|
@ -848,16 +915,77 @@ static u16_t le_err_to_result(int err)
|
|||
}
|
||||
}
|
||||
|
||||
static u16_t l2cap_chan_accept(struct bt_conn *conn,
|
||||
struct bt_l2cap_server *server, u16_t scid,
|
||||
u16_t mtu, u16_t mps, u16_t credits,
|
||||
struct bt_l2cap_chan **chan)
|
||||
{
|
||||
struct bt_l2cap_le_chan *ch;
|
||||
int err;
|
||||
|
||||
BT_DBG("conn %p scid 0x%04x chan %p", conn, scid, chan);
|
||||
|
||||
if (!L2CAP_LE_CID_IS_DYN(scid)) {
|
||||
return BT_L2CAP_LE_ERR_INVALID_SCID;
|
||||
}
|
||||
|
||||
*chan = bt_l2cap_le_lookup_tx_cid(conn, scid);
|
||||
if (*chan) {
|
||||
return BT_L2CAP_LE_ERR_SCID_IN_USE;
|
||||
}
|
||||
|
||||
/* Request server to accept the new connection and allocate the
|
||||
* channel.
|
||||
*/
|
||||
err = server->accept(conn, chan);
|
||||
if (err < 0) {
|
||||
return le_err_to_result(err);
|
||||
}
|
||||
|
||||
(*chan)->required_sec_level = server->sec_level;
|
||||
|
||||
if (!l2cap_chan_add(conn, *chan, l2cap_chan_destroy)) {
|
||||
return BT_L2CAP_LE_ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
ch = BT_L2CAP_LE_CHAN(*chan);
|
||||
|
||||
/* Init TX parameters */
|
||||
l2cap_chan_tx_init(ch);
|
||||
ch->tx.cid = scid;
|
||||
ch->tx.mps = mps;
|
||||
ch->tx.mtu = mtu;
|
||||
ch->tx.init_credits = credits;
|
||||
l2cap_chan_tx_give_credits(ch, credits);
|
||||
|
||||
/* Init RX parameters */
|
||||
l2cap_chan_rx_init(ch);
|
||||
l2cap_chan_rx_give_credits(ch, ch->rx.init_credits);
|
||||
|
||||
/* Set channel PSM */
|
||||
(*chan)->psm = server->psm;
|
||||
|
||||
/* Update state */
|
||||
bt_l2cap_chan_set_state(*chan, BT_L2CAP_CONNECTED);
|
||||
|
||||
if ((*chan)->ops->connected) {
|
||||
(*chan)->ops->connected(*chan);
|
||||
}
|
||||
|
||||
return BT_L2CAP_LE_SUCCESS;
|
||||
}
|
||||
|
||||
static void le_conn_req(struct bt_l2cap *l2cap, u8_t ident,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = l2cap->chan.chan.conn;
|
||||
struct bt_l2cap_chan *chan;
|
||||
struct bt_l2cap_le_chan *ch;
|
||||
struct bt_l2cap_server *server;
|
||||
struct bt_l2cap_le_conn_req *req = (void *)buf->data;
|
||||
struct bt_l2cap_le_conn_rsp *rsp;
|
||||
u16_t psm, scid, mtu, mps, credits;
|
||||
int err;
|
||||
u16_t result;
|
||||
|
||||
if (buf->len < sizeof(*req)) {
|
||||
BT_ERR("Too small LE conn req packet size");
|
||||
|
@ -900,63 +1028,200 @@ static void le_conn_req(struct bt_l2cap *l2cap, u8_t ident,
|
|||
goto rsp;
|
||||
}
|
||||
|
||||
if (!L2CAP_LE_CID_IS_DYN(scid)) {
|
||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_INVALID_SCID);
|
||||
result = l2cap_chan_accept(conn, server, scid, mtu, mps, credits,
|
||||
&chan);
|
||||
if (result != BT_L2CAP_LE_SUCCESS) {
|
||||
rsp->result = sys_cpu_to_le16(result);
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
chan = bt_l2cap_le_lookup_tx_cid(conn, scid);
|
||||
if (chan) {
|
||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_SCID_IN_USE);
|
||||
goto rsp;
|
||||
ch = BT_L2CAP_LE_CHAN(chan);
|
||||
|
||||
/* Prepare response protocol data */
|
||||
rsp->dcid = sys_cpu_to_le16(ch->rx.cid);
|
||||
rsp->mps = sys_cpu_to_le16(ch->rx.mps);
|
||||
rsp->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
||||
rsp->credits = sys_cpu_to_le16(ch->rx.init_credits);
|
||||
rsp->result = BT_L2CAP_LE_SUCCESS;
|
||||
|
||||
rsp:
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||
}
|
||||
|
||||
static void le_ecred_conn_req(struct bt_l2cap *l2cap, u8_t ident,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = l2cap->chan.chan.conn;
|
||||
struct bt_l2cap_chan *chan[L2CAP_ECRED_CHAN_MAX];
|
||||
struct bt_l2cap_le_chan *ch = NULL;
|
||||
struct bt_l2cap_server *server;
|
||||
struct bt_l2cap_ecred_conn_req *req;
|
||||
struct bt_l2cap_ecred_conn_rsp *rsp;
|
||||
u16_t psm, mtu, mps, credits, result = BT_L2CAP_LE_ERR_INVALID_SCID;
|
||||
u16_t scid, dcid[L2CAP_ECRED_CHAN_MAX];
|
||||
int i = 0;
|
||||
|
||||
if (buf->len < sizeof(*req)) {
|
||||
BT_ERR("Too small LE conn req packet size");
|
||||
result = BT_L2CAP_LE_ERR_INVALID_PARAMS;
|
||||
goto response;
|
||||
}
|
||||
|
||||
/* Request server to accept the new connection and allocate the
|
||||
* channel.
|
||||
*/
|
||||
err = server->accept(conn, &chan);
|
||||
if (err < 0) {
|
||||
rsp->result = sys_cpu_to_le16(le_err_to_result(err));
|
||||
goto rsp;
|
||||
req = net_buf_pull_mem(buf, sizeof(*req));
|
||||
psm = sys_le16_to_cpu(req->psm);
|
||||
mtu = sys_le16_to_cpu(req->mtu);
|
||||
mps = sys_le16_to_cpu(req->mps);
|
||||
credits = sys_le16_to_cpu(req->credits);
|
||||
|
||||
BT_DBG("psm 0x%02x mtu %u mps %u credits %u", psm, mtu, mps, credits);
|
||||
|
||||
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MTU) {
|
||||
BT_ERR("Invalid ecred conn req params");
|
||||
result = BT_L2CAP_LE_ERR_UNACCEPT_PARAMS;
|
||||
goto response;
|
||||
}
|
||||
|
||||
chan->required_sec_level = server->sec_level;
|
||||
/* Check if there is a server registered */
|
||||
server = l2cap_server_lookup_psm(psm);
|
||||
if (!server) {
|
||||
result = BT_L2CAP_LE_ERR_PSM_NOT_SUPP;
|
||||
goto response;
|
||||
}
|
||||
|
||||
if (l2cap_chan_add(conn, chan, l2cap_chan_destroy)) {
|
||||
struct bt_l2cap_le_chan *ch = BT_L2CAP_LE_CHAN(chan);
|
||||
/* Check if connection has minimum required security level */
|
||||
if (conn->sec_level < server->sec_level) {
|
||||
result = BT_L2CAP_LE_ERR_AUTHENTICATION;
|
||||
goto response;
|
||||
}
|
||||
|
||||
/* Init TX parameters */
|
||||
l2cap_chan_tx_init(ch);
|
||||
ch->tx.cid = scid;
|
||||
ch->tx.mps = mps;
|
||||
ch->tx.mtu = mtu;
|
||||
ch->tx.init_credits = credits;
|
||||
l2cap_chan_tx_give_credits(ch, credits);
|
||||
while (buf->len >= sizeof(scid)) {
|
||||
scid = net_buf_pull_le16(buf);
|
||||
|
||||
/* Init RX parameters */
|
||||
l2cap_chan_rx_init(ch);
|
||||
l2cap_chan_rx_give_credits(ch, ch->rx.init_credits);
|
||||
|
||||
/* Set channel PSM */
|
||||
chan->psm = server->psm;
|
||||
|
||||
/* Update state */
|
||||
bt_l2cap_chan_set_state(chan, BT_L2CAP_CONNECTED);
|
||||
|
||||
if (chan->ops->connected) {
|
||||
chan->ops->connected(chan);
|
||||
result = l2cap_chan_accept(conn, server, scid, mtu, mps,
|
||||
credits, &chan[i]);
|
||||
switch (result) {
|
||||
case BT_L2CAP_LE_SUCCESS:
|
||||
ch = BT_L2CAP_LE_CHAN(chan[i]);
|
||||
dcid[i++] = sys_cpu_to_le16(ch->rx.cid);
|
||||
continue;
|
||||
/* Some connections refused – invalid Source CID */
|
||||
case BT_L2CAP_LE_ERR_INVALID_SCID:
|
||||
/* Some connections refused – Source CID already allocated */
|
||||
case BT_L2CAP_LE_ERR_SCID_IN_USE:
|
||||
/* If a Destination CID is 0x0000, the channel was not
|
||||
* established.
|
||||
*/
|
||||
dcid[i++] = 0x0000;
|
||||
continue;
|
||||
/* Some connections refused – not enough resources
|
||||
* available.
|
||||
*/
|
||||
case BT_L2CAP_LE_ERR_NO_RESOURCES:
|
||||
default:
|
||||
goto response;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare response protocol data */
|
||||
rsp->dcid = sys_cpu_to_le16(ch->rx.cid);
|
||||
response:
|
||||
if (!i) {
|
||||
i = buf->len / sizeof(scid);
|
||||
}
|
||||
|
||||
buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_ECRED_CONN_RSP, ident,
|
||||
sizeof(*rsp) + (sizeof(scid) * i));
|
||||
|
||||
rsp = net_buf_add(buf, sizeof(*rsp));
|
||||
(void)memset(rsp, 0, sizeof(*rsp));
|
||||
|
||||
if (result == BT_L2CAP_LE_ERR_UNACCEPT_PARAMS ||
|
||||
result == BT_L2CAP_LE_ERR_PSM_NOT_SUPP ||
|
||||
result == BT_L2CAP_LE_ERR_AUTHENTICATION) {
|
||||
memset(dcid, 0, sizeof(scid) * i);
|
||||
} else if (ch) {
|
||||
rsp->mps = sys_cpu_to_le16(ch->rx.mps);
|
||||
rsp->mtu = sys_cpu_to_le16(ch->rx.mtu);
|
||||
rsp->credits = sys_cpu_to_le16(ch->rx.init_credits);
|
||||
rsp->result = BT_L2CAP_LE_SUCCESS;
|
||||
} else {
|
||||
rsp->result = sys_cpu_to_le16(BT_L2CAP_LE_ERR_NO_RESOURCES);
|
||||
}
|
||||
rsp:
|
||||
|
||||
net_buf_add_mem(buf, dcid, sizeof(scid) * i);
|
||||
|
||||
rsp->result = sys_cpu_to_le16(result);
|
||||
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||
}
|
||||
|
||||
static void le_ecred_reconf_req(struct bt_l2cap *l2cap, u8_t ident,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = l2cap->chan.chan.conn;
|
||||
struct bt_l2cap_ecred_reconf_req *req;
|
||||
struct bt_l2cap_ecred_reconf_rsp *rsp;
|
||||
u16_t mtu, mps;
|
||||
u16_t scid, result;
|
||||
|
||||
if (buf->len < sizeof(*req)) {
|
||||
BT_ERR("Too small ecred reconf req packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
req = net_buf_pull_mem(buf, sizeof(*req));
|
||||
|
||||
mtu = sys_le16_to_cpu(req->mtu);
|
||||
mps = sys_le16_to_cpu(req->mps);
|
||||
|
||||
if (mtu < L2CAP_ECRED_MIN_MTU) {
|
||||
result = BT_L2CAP_RECONF_INVALID_MTU;
|
||||
goto response;
|
||||
}
|
||||
|
||||
if (mps < L2CAP_ECRED_MIN_MTU) {
|
||||
result = BT_L2CAP_RECONF_INVALID_MTU;
|
||||
goto response;
|
||||
}
|
||||
|
||||
while (buf->len >= sizeof(scid)) {
|
||||
struct bt_l2cap_chan *chan;
|
||||
|
||||
scid = net_buf_pull_le16(buf);
|
||||
|
||||
BT_DBG("scid 0x%04x", scid);
|
||||
|
||||
if (!scid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chan = bt_l2cap_le_lookup_tx_cid(conn, scid);
|
||||
if (!chan) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the MTU value is decreased for any of the included
|
||||
* channels, then the receiver shall disconnect all
|
||||
* included channels.
|
||||
*/
|
||||
if (BT_L2CAP_LE_CHAN(chan)->tx.mtu > mtu) {
|
||||
BT_ERR("chan %p decreased MTU %u -> %u", chan,
|
||||
BT_L2CAP_LE_CHAN(chan)->tx.mtu, mtu);
|
||||
result = BT_L2CAP_RECONF_INVALID_MTU;
|
||||
bt_l2cap_chan_disconnect(chan);
|
||||
goto response;
|
||||
}
|
||||
|
||||
BT_L2CAP_LE_CHAN(chan)->tx.mtu = mtu;
|
||||
BT_L2CAP_LE_CHAN(chan)->tx.mps = mps;
|
||||
}
|
||||
|
||||
BT_DBG("mtu %u mps %u", mtu, mps);
|
||||
|
||||
result = BT_L2CAP_RECONF_SUCCESS;
|
||||
|
||||
response:
|
||||
buf = l2cap_create_le_sig_pdu(buf, BT_L2CAP_ECRED_RECONF_RSP, ident,
|
||||
sizeof(*rsp));
|
||||
|
||||
rsp = net_buf_add(buf, sizeof(*rsp));
|
||||
rsp->result = sys_cpu_to_le16(result);
|
||||
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_LE_SIG, buf);
|
||||
}
|
||||
|
||||
|
@ -1056,6 +1321,113 @@ static int l2cap_change_security(struct bt_l2cap_le_chan *chan, u16_t err)
|
|||
chan->chan.required_sec_level);
|
||||
}
|
||||
|
||||
static void le_ecred_conn_rsp(struct bt_l2cap *l2cap, u8_t ident,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = l2cap->chan.chan.conn;
|
||||
struct bt_l2cap_le_chan *chan;
|
||||
struct bt_l2cap_ecred_conn_rsp *rsp;
|
||||
u16_t dcid, mtu, mps, credits, result;
|
||||
|
||||
if (buf->len < sizeof(*rsp)) {
|
||||
BT_ERR("Too small ecred conn rsp packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
rsp = net_buf_pull_mem(buf, sizeof(*rsp));
|
||||
mtu = sys_le16_to_cpu(rsp->mtu);
|
||||
mps = sys_le16_to_cpu(rsp->mps);
|
||||
credits = sys_le16_to_cpu(rsp->credits);
|
||||
result = sys_le16_to_cpu(rsp->result);
|
||||
|
||||
BT_DBG("mtu 0x%04x mps 0x%04x credits 0x%04x result %u", mtu,
|
||||
mps, credits, result);
|
||||
|
||||
switch (result) {
|
||||
case BT_L2CAP_LE_ERR_AUTHENTICATION:
|
||||
case BT_L2CAP_LE_ERR_ENCRYPTION:
|
||||
while ((chan = l2cap_lookup_ident(conn, ident))) {
|
||||
/* Cancel RTX work */
|
||||
k_delayed_work_cancel(&chan->chan.rtx_work);
|
||||
|
||||
/* If security needs changing wait it to be completed */
|
||||
if (!l2cap_change_security(chan, result)) {
|
||||
return;
|
||||
}
|
||||
bt_l2cap_chan_remove(conn, &chan->chan);
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
}
|
||||
break;
|
||||
case BT_L2CAP_LE_SUCCESS:
|
||||
/* Some connections refused – invalid Source CID */
|
||||
case BT_L2CAP_LE_ERR_INVALID_SCID:
|
||||
/* Some connections refused – Source CID already allocated */
|
||||
case BT_L2CAP_LE_ERR_SCID_IN_USE:
|
||||
/* Some connections refused – not enough resources available */
|
||||
case BT_L2CAP_LE_ERR_NO_RESOURCES:
|
||||
while ((chan = l2cap_lookup_ident(conn, ident))) {
|
||||
struct bt_l2cap_chan *c;
|
||||
|
||||
/* Cancel RTX work */
|
||||
k_delayed_work_cancel(&chan->chan.rtx_work);
|
||||
|
||||
dcid = net_buf_pull_le16(buf);
|
||||
|
||||
BT_DBG("dcid 0x%04x", dcid);
|
||||
|
||||
/* If a Destination CID is 0x0000, the channel was not
|
||||
* established.
|
||||
*/
|
||||
if (!dcid) {
|
||||
bt_l2cap_chan_remove(conn, &chan->chan);
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
c = bt_l2cap_le_lookup_tx_cid(conn, dcid);
|
||||
if (c) {
|
||||
/* If a device receives a
|
||||
* L2CAP_CREDIT_BASED_CONNECTION_RSP packet
|
||||
* with an already assigned Destination CID,
|
||||
* then both the original channel and the new
|
||||
* channel shall be immediately discarded and
|
||||
* not used.
|
||||
*/
|
||||
bt_l2cap_chan_remove(conn, &chan->chan);
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
bt_l2cap_chan_disconnect(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
chan->tx.cid = dcid;
|
||||
|
||||
chan->chan.ident = 0U;
|
||||
|
||||
chan->tx.mtu = mtu;
|
||||
chan->tx.mps = mps;
|
||||
|
||||
/* Update state */
|
||||
bt_l2cap_chan_set_state(&chan->chan,
|
||||
BT_L2CAP_CONNECTED);
|
||||
|
||||
if (chan->chan.ops->connected) {
|
||||
chan->chan.ops->connected(&chan->chan);
|
||||
}
|
||||
|
||||
/* Give credits */
|
||||
l2cap_chan_tx_give_credits(chan, credits);
|
||||
l2cap_chan_rx_give_credits(chan, chan->rx.init_credits);
|
||||
}
|
||||
break;
|
||||
case BT_L2CAP_LE_ERR_PSM_NOT_SUPP:
|
||||
default:
|
||||
while ((chan = l2cap_remove_ident(conn, ident))) {
|
||||
bt_l2cap_chan_del(&chan->chan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void le_conn_rsp(struct bt_l2cap *l2cap, u8_t ident,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
|
@ -1501,6 +1873,15 @@ static int l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
|||
case BT_L2CAP_CMD_REJECT:
|
||||
reject_cmd(l2cap, hdr->ident, buf);
|
||||
break;
|
||||
case BT_L2CAP_ECRED_CONN_REQ:
|
||||
le_ecred_conn_req(l2cap, hdr->ident, buf);
|
||||
break;
|
||||
case BT_L2CAP_ECRED_CONN_RSP:
|
||||
le_ecred_conn_rsp(l2cap, hdr->ident, buf);
|
||||
break;
|
||||
case BT_L2CAP_ECRED_RECONF_REQ:
|
||||
le_ecred_reconf_req(l2cap, hdr->ident, buf);
|
||||
break;
|
||||
#else
|
||||
case BT_L2CAP_CMD_REJECT:
|
||||
/* Ignored */
|
||||
|
@ -1945,6 +2326,67 @@ static int l2cap_le_connect(struct bt_conn *conn, struct bt_l2cap_le_chan *ch,
|
|||
return l2cap_le_conn_req(ch);
|
||||
}
|
||||
|
||||
static int l2cap_ecred_init(struct bt_conn *conn,
|
||||
struct bt_l2cap_le_chan *ch, u16_t psm)
|
||||
{
|
||||
|
||||
if (psm < L2CAP_LE_PSM_FIXED_START || psm > L2CAP_LE_PSM_DYN_END) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
l2cap_chan_tx_init(ch);
|
||||
l2cap_chan_rx_init(ch);
|
||||
|
||||
if (!l2cap_chan_add(conn, &ch->chan, l2cap_chan_destroy)) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ch->chan.psm = psm;
|
||||
|
||||
BT_DBG("ch %p psm 0x%02x mtu %u mps %u credits %u", ch, ch->chan.psm,
|
||||
ch->rx.mtu, ch->rx.mps, ch->rx.init_credits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_l2cap_ecred_chan_connect(struct bt_conn *conn,
|
||||
struct bt_l2cap_chan **chan, u16_t psm)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
BT_DBG("conn %p chan %p psm 0x%04x", conn, chan, psm);
|
||||
|
||||
if (!conn || !chan) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Init non-null channels */
|
||||
for (i = 0; i < L2CAP_ECRED_CHAN_MAX; i++) {
|
||||
if (!chan[i]) {
|
||||
break;
|
||||
}
|
||||
|
||||
err = l2cap_ecred_init(conn, BT_L2CAP_LE_CHAN(chan[i]), psm);
|
||||
if (err < 0) {
|
||||
i--;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return l2cap_ecred_conn_req(chan, i);
|
||||
fail:
|
||||
/* Remove channels added */
|
||||
for (; i >= 0; i--) {
|
||||
if (!chan[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bt_l2cap_chan_remove(conn, chan[i]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_l2cap_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
||||
u16_t psm)
|
||||
{
|
||||
|
@ -2005,7 +2447,7 @@ int bt_l2cap_chan_disconnect(struct bt_l2cap_chan *chan)
|
|||
req->dcid = sys_cpu_to_le16(ch->rx.cid);
|
||||
req->scid = sys_cpu_to_le16(ch->tx.cid);
|
||||
|
||||
l2cap_chan_send_req(ch, buf, L2CAP_DISC_TIMEOUT);
|
||||
l2cap_chan_send_req(chan, buf, L2CAP_DISC_TIMEOUT);
|
||||
bt_l2cap_chan_set_state(chan, BT_L2CAP_DISCONNECT);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue