set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
-void l2cap_le_flowctl_init(struct l2cap_chan *chan)
+static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
{
- chan->imtu = L2CAP_DEFAULT_MTU;
- chan->omtu = L2CAP_LE_MIN_MTU;
- chan->mode = L2CAP_MODE_LE_FLOWCTL;
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
chan->tx_credits = 0;
chan->rx_credits = le_max_credits;
+ chan->mps = min_t(u16, chan->imtu, L2CAP_LE_DEFAULT_MPS);
- if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
- chan->mps = chan->imtu;
- else
- chan->mps = L2CAP_LE_DEFAULT_MPS;
+ skb_queue_head_init(&chan->tx_q);
}
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
}
}
-static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
-{
- chan->sdu = NULL;
- chan->sdu_last_frag = NULL;
- chan->sdu_len = 0;
-
- if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
- chan->mps = chan->imtu;
- else
- chan->mps = le_default_mps;
-
- skb_queue_head_init(&chan->tx_q);
-
- if (!chan->tx_credits)
- chan->ops->suspend(chan);
-}
-
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
chan->conf_state = 0;
__clear_chan_timer(chan);
- if (chan->mode == L2CAP_MODE_LE_FLOWCTL)
- l2cap_le_flowctl_start(chan);
+ if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits)
+ chan->ops->suspend(chan);
chan->state = BT_CONNECTED;
return false;
if (bdaddr_type_is_le(dst_type))
- return (psm < 0x00ff);
+ return (psm <= 0x00ff);
/* PSM must be odd and lsb of upper byte must be 0 */
return ((psm & 0x0101) == 0x0001);
switch (chan->mode) {
case L2CAP_MODE_BASIC:
+ break;
case L2CAP_MODE_LE_FLOWCTL:
+ l2cap_le_flowctl_init(chan);
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
goto response_unlock;
}
+ l2cap_le_flowctl_init(chan);
+
bacpy(&chan->src, &conn->hcon->src);
bacpy(&chan->dst, &conn->hcon->dst);
chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
return 0;
}
+static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
+ struct l2cap_chan *chan;
+
+ if (cmd_len < sizeof(*rej))
+ return -EPROTO;
+
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan)
+ goto done;
+
+ l2cap_chan_lock(chan);
+ l2cap_chan_del(chan, ECONNREFUSED);
+ l2cap_chan_unlock(chan);
+
+done:
+ mutex_unlock(&conn->chan_lock);
+ return 0;
+}
+
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
+ l2cap_le_command_rej(conn, cmd, cmd_len, data);
break;
case L2CAP_CONN_PARAM_UPDATE_REQ: