#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
int disable_ertm;
static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
/* ---- L2CAP channels ---- */
+
+static inline void chan_hold(struct l2cap_chan *c)
+{
+ atomic_inc(&c->refcnt);
+}
+
+static inline void chan_put(struct l2cap_chan *c)
+{
+ if (atomic_dec_and_test(&c->refcnt))
+ kfree(c);
+}
+
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
struct l2cap_chan *c;
return 0;
}
-static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout)
+static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
{
- BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state,
- timeout);
- if (!mod_timer(&chan->chan_timer, jiffies + timeout))
- sock_hold(chan->sk);
+ BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
+
+ if (!mod_timer(timer, jiffies + timeout))
+ chan_hold(chan);
}
-static void l2cap_chan_clear_timer(struct l2cap_chan *chan)
+static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
{
- BT_DBG("chan %p state %d", chan, chan->sk->sk_state);
+ BT_DBG("chan %p state %d", chan, chan->state);
+
+ if (timer_pending(timer) && del_timer(timer))
+ chan_put(chan);
+}
- if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer))
- __sock_put(chan->sk);
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
+{
+ chan->state = state;
+ chan->ops->state_change(chan->data, state);
}
static void l2cap_chan_timeout(unsigned long arg)
struct sock *sk = chan->sk;
int reason;
- BT_DBG("chan %p state %d", chan, sk->sk_state);
+ BT_DBG("chan %p state %d", chan, chan->state);
bh_lock_sock(sk);
if (sock_owned_by_user(sk)) {
/* sk is owned by user. Try again later */
- l2cap_chan_set_timer(chan, HZ / 5);
+ __set_chan_timer(chan, HZ / 5);
bh_unlock_sock(sk);
- sock_put(sk);
+ chan_put(chan);
return;
}
- if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
+ if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
- else if (sk->sk_state == BT_CONNECT &&
+ else if (chan->state == BT_CONNECT &&
chan->sec_level != BT_SECURITY_SDP)
reason = ECONNREFUSED;
else
bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
- sock_put(sk);
+ chan->ops->close(chan->data);
+ chan_put(chan);
}
struct l2cap_chan *l2cap_chan_create(struct sock *sk)
setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan);
+ chan->state = BT_OPEN;
+
+ atomic_set(&chan->refcnt, 1);
+
return chan;
}
list_del(&chan->global_l);
write_unlock_bh(&chan_list_lock);
- kfree(chan);
+ chan_put(chan);
}
static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
- struct sock *sk = chan->sk;
-
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid);
chan->omtu = L2CAP_DEFAULT_MTU;
}
- sock_hold(sk);
+ chan_hold(chan);
list_add(&chan->list, &conn->chan_l);
}
struct l2cap_conn *conn = chan->conn;
struct sock *parent = bt_sk(sk)->parent;
- l2cap_chan_clear_timer(chan);
+ __clear_chan_timer(chan);
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
write_lock_bh(&conn->chan_lock);
list_del(&chan->list);
write_unlock_bh(&conn->chan_lock);
- __sock_put(sk);
+ chan_put(chan);
chan->conn = NULL;
hci_conn_put(conn->hcon);
}
- sk->sk_state = BT_CLOSED;
+ l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
} else
sk->sk_state_change(sk);
- if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE &&
- chan->conf_state & L2CAP_CONF_INPUT_DONE))
+ if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) &&
+ test_bit(CONF_INPUT_DONE, &chan->conf_state)))
return;
skb_queue_purge(&chan->tx_q);
if (chan->mode == L2CAP_MODE_ERTM) {
struct srej_list *l, *tmp;
- del_timer(&chan->retrans_timer);
- del_timer(&chan->monitor_timer);
- del_timer(&chan->ack_timer);
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
skb_queue_purge(&chan->srej_q);
skb_queue_purge(&chan->busy_q);
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
- l2cap_chan_clear_timer(l2cap_pi(sk)->chan);
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ __clear_chan_timer(chan);
lock_sock(sk);
- l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET);
+ l2cap_chan_close(chan, ECONNRESET);
release_sock(sk);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
}
-
- parent->sk_state = BT_CLOSED;
- sock_set_flag(parent, SOCK_ZAPPED);
}
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
struct l2cap_conn *conn = chan->conn;
struct sock *sk = chan->sk;
- BT_DBG("chan %p state %d socket %p", chan, sk->sk_state, sk->sk_socket);
+ BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket);
- switch (sk->sk_state) {
+ switch (chan->state) {
case BT_LISTEN:
l2cap_chan_cleanup_listen(sk);
+
+ l2cap_state_change(chan, BT_CLOSED);
+ sock_set_flag(sk, SOCK_ZAPPED);
break;
case BT_CONNECTED:
case BT_CONFIG:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
conn->hcon->type == ACL_LINK) {
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
l2cap_send_disconn_req(conn, chan, reason);
} else
l2cap_chan_del(chan, reason);
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
- sk->sk_state = BT_DISCONN;
+ l2cap_state_change(chan, BT_DISCONN);
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
- struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
struct l2cap_conn *conn = chan->conn;
- struct sock *sk = (struct sock *)pi;
int count, hlen = L2CAP_HDR_SIZE + 2;
u8 flags;
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
return;
if (chan->fcs == L2CAP_FCS_CRC16)
count = min_t(unsigned int, conn->mtu, hlen);
control |= L2CAP_CTRL_FRAME_TYPE;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
- if (chan->conn_state & L2CAP_CONN_SEND_PBIT) {
+ if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state))
control |= L2CAP_CTRL_POLL;
- chan->conn_state &= ~L2CAP_CONN_SEND_PBIT;
- }
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb)
static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control)
{
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
} else
control |= L2CAP_SUPER_RCV_READY;
static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
{
- return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND);
+ return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
static void l2cap_do_start(struct l2cap_chan *chan)
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
sizeof(req), &req);
sk = chan->sk;
if (chan->mode == L2CAP_MODE_ERTM) {
- del_timer(&chan->retrans_timer);
- del_timer(&chan->monitor_timer);
- del_timer(&chan->ack_timer);
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
}
req.dcid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- sk->sk_state = BT_DISCONN;
+ l2cap_state_change(chan, BT_DISCONN);
sk->sk_err = err;
}
continue;
}
- if (sk->sk_state == BT_CONNECT) {
+ if (chan->state == BT_CONNECT) {
struct l2cap_conn_req req;
if (!l2cap_check_security(chan) ||
continue;
}
- if (!l2cap_mode_supported(chan->mode,
- conn->feat_mask)
- && chan->conf_state &
- L2CAP_CONF_STATE2_DEVICE) {
+ if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
+ && test_bit(CONF_STATE2_DEVICE,
+ &chan->conf_state)) {
/* l2cap_chan_close() calls list_del(chan)
* so release the lock */
read_unlock_bh(&conn->chan_lock);
- l2cap_chan_close(chan, ECONNRESET);
+ l2cap_chan_close(chan, ECONNRESET);
read_lock_bh(&conn->chan_lock);
bh_unlock_sock(sk);
continue;
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
sizeof(req), &req);
- } else if (sk->sk_state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
char buf[128];
rsp.scid = cpu_to_le16(chan->dcid);
parent->sk_data_ready(parent, 0);
} else {
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
sizeof(rsp), &rsp);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT ||
+ if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
bh_unlock_sock(sk);
continue;
}
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (state && sk->sk_state != state)
+ if (state && c->state != state)
continue;
if (c->scid == cid) {
goto clean;
}
- sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
- if (!sk)
- goto clean;
-
- chan = l2cap_chan_create(sk);
- if (!chan) {
- l2cap_sock_kill(sk);
+ chan = pchan->ops->new_connection(pchan->data);
+ if (!chan)
goto clean;
- }
- l2cap_pi(sk)->chan = chan;
+ sk = chan->sk;
write_lock_bh(&conn->chan_lock);
hci_conn_hold(conn->hcon);
- l2cap_sock_init(sk, parent);
-
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
__l2cap_chan_add(conn, chan);
- l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0);
write_unlock_bh(&conn->chan_lock);
bh_unlock_sock(parent);
}
+static void l2cap_chan_ready(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ chan->conf_state = 0;
+ __clear_chan_timer(chan);
+
+ l2cap_state_change(chan, BT_CONNECTED);
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
bh_lock_sock(sk);
if (conn->hcon->type == LE_LINK) {
- l2cap_chan_clear_timer(chan);
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
- }
+ if (smp_conn_security(conn, chan->sec_level))
+ l2cap_chan_ready(sk);
- if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- l2cap_chan_clear_timer(chan);
- sk->sk_state = BT_CONNECTED;
+ } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ __clear_chan_timer(chan);
+ l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);
- } else if (sk->sk_state == BT_CONNECT)
+
+ } else if (chan->state == BT_CONNECT)
l2cap_do_start(chan);
bh_unlock_sock(sk);
l2cap_conn_start(conn);
}
+static void l2cap_conn_del(struct hci_conn *hcon, int err)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan, *l;
+ struct sock *sk;
+
+ if (!conn)
+ return;
+
+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+ kfree_skb(conn->rx_skb);
+
+ /* Kill channels */
+ list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+ sk = chan->sk;
+ bh_lock_sock(sk);
+ l2cap_chan_del(chan, err);
+ bh_unlock_sock(sk);
+ chan->ops->close(chan->data);
+ }
+
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+ del_timer_sync(&conn->info_timer);
+
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ del_timer(&conn->security_timer);
+
+ hcon->l2cap_data = NULL;
+ kfree(conn);
+}
+
+static void security_timeout(unsigned long arg)
+{
+ struct l2cap_conn *conn = (void *) arg;
+
+ l2cap_conn_del(conn->hcon, ETIMEDOUT);
+}
+
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn = hcon->l2cap_data;
INIT_LIST_HEAD(&conn->chan_l);
- if (hcon->type != LE_LINK)
+ if (hcon->type == LE_LINK)
+ setup_timer(&conn->security_timer, security_timeout,
+ (unsigned long) conn);
+ else
setup_timer(&conn->info_timer, l2cap_info_timeout,
(unsigned long) conn);
return conn;
}
-static void l2cap_conn_del(struct hci_conn *hcon, int err)
-{
- struct l2cap_conn *conn = hcon->l2cap_data;
- struct l2cap_chan *chan, *l;
- struct sock *sk;
-
- if (!conn)
- return;
-
- BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
-
- kfree_skb(conn->rx_skb);
-
- /* Kill channels */
- list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
- sk = chan->sk;
- bh_lock_sock(sk);
- l2cap_chan_del(chan, err);
- bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
- }
-
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
- del_timer_sync(&conn->info_timer);
-
- hcon->l2cap_data = NULL;
- kfree(conn);
-}
-
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
write_lock_bh(&conn->chan_lock);
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (state && sk->sk_state != state)
+ if (state && c->state != state)
continue;
if (c->psm == psm) {
l2cap_chan_add(conn, chan);
- sk->sk_state = BT_CONNECT;
- l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+ l2cap_state_change(chan, BT_CONNECT);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- l2cap_chan_clear_timer(chan);
+ __clear_chan_timer(chan);
if (l2cap_check_security(chan))
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
} else
l2cap_do_start(chan);
}
}
chan->retry_count++;
- __mod_monitor_timer();
+ __set_monitor_timer(chan);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
bh_lock_sock(sk);
chan->retry_count = 1;
- __mod_monitor_timer();
+ __set_monitor_timer(chan);
- chan->conn_state |= L2CAP_CONN_WAIT_F;
+ set_bit(CONN_WAIT_F, &chan->conn_state);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
bh_unlock_sock(sk);
}
if (!chan->unacked_frames)
- del_timer(&chan->retrans_timer);
+ __clear_retrans_timer(chan);
}
void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
| (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
int l2cap_ertm_send(struct l2cap_chan *chan)
{
struct sk_buff *skb, *tx_skb;
- struct sock *sk = chan->sk;
u16 control, fcs;
int nsent = 0;
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
return -ENOTCONN;
while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) {
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
control &= L2CAP_CTRL_SAR;
- if (chan->conn_state & L2CAP_CONN_SEND_FBIT) {
+ if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control |= L2CAP_CTRL_FINAL;
- chan->conn_state &= ~L2CAP_CONN_SEND_FBIT;
- }
+
control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
| (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
l2cap_do_send(chan, tx_skb);
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
bt_cb(skb)->tx_seq = chan->next_tx_seq;
chan->next_tx_seq = (chan->next_tx_seq + 1) % 64;
control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
l2cap_send_sframe(chan, control);
return;
}
break;
}
- if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
- (chan->conn_state & L2CAP_CONN_WAIT_F)) {
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
err = len;
break;
}
return err;
}
-static void l2cap_chan_ready(struct sock *sk)
-{
- struct sock *parent = bt_sk(sk)->parent;
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
-
- BT_DBG("sk %p, parent %p", sk, parent);
-
- chan->conf_state = 0;
- l2cap_chan_clear_timer(chan);
-
- if (!parent) {
- /* Outgoing channel.
- * Wake up socket sleeping on connect.
- */
- sk->sk_state = BT_CONNECTED;
- sk->sk_state_change(sk);
- } else {
- /* Incoming channel.
- * Wake up socket sleeping on accept.
- */
- parent->sk_data_ready(parent, 0);
- }
-}
-
/* Copy frame to all raw sockets on that connection */
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
{
if (!nskb)
continue;
- if (sock_queue_rcv_skb(sk, nskb))
+ if (chan->ops->recv(chan->data, nskb))
kfree_skb(nskb);
}
read_unlock(&conn->chan_lock);
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE)
+ if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
break;
/* fall through */
break;
if (chan->fcs == L2CAP_FCS_NONE ||
- chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
break;
if (chan->fcs == L2CAP_FCS_NONE ||
- chan->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
chan->fcs = L2CAP_FCS_NONE;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
}
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
- chan->conf_state |= L2CAP_CONF_NO_FCS_RECV;
+ set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
break;
switch (chan->mode) {
case L2CAP_MODE_STREAMING:
case L2CAP_MODE_ERTM:
- if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) {
+ if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
chan->mode = l2cap_select_mode(rfc.mode,
chan->conn->feat_mask);
break;
result = L2CAP_CONF_UNACCEPT;
else {
chan->omtu = mtu;
- chan->conf_state |= L2CAP_CONF_MTU_DONE;
+ set_bit(CONF_MTU_DONE, &chan->conf_state);
}
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
switch (rfc.mode) {
case L2CAP_MODE_BASIC:
chan->fcs = L2CAP_FCS_NONE;
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
break;
case L2CAP_MODE_ERTM:
rfc.monitor_timeout =
le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO);
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
chan->remote_mps = le16_to_cpu(rfc.max_pdu_size);
- chan->conf_state |= L2CAP_CONF_MODE_DONE;
+ set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc);
}
if (result == L2CAP_CONF_SUCCESS)
- chan->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
}
rsp->scid = cpu_to_le16(chan->dcid);
rsp->result = cpu_to_le16(result);
if (olen == sizeof(rfc))
memcpy(&rfc, (void *)val, olen);
- if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
+ if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
rfc.mode != chan->mode)
return -ECONNREFUSED;
l2cap_send_cmd(conn, chan->ident,
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+ if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return;
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
goto response;
}
- sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
- if (!sk)
- goto response;
-
- chan = l2cap_chan_create(sk);
- if (!chan) {
- l2cap_sock_kill(sk);
+ chan = pchan->ops->new_connection(pchan->data);
+ if (!chan)
goto response;
- }
- l2cap_pi(sk)->chan = chan;
+ sk = chan->sk;
write_lock_bh(&conn->chan_lock);
if (__l2cap_get_chan_by_dcid(conn, scid)) {
write_unlock_bh(&conn->chan_lock);
sock_set_flag(sk, SOCK_ZAPPED);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
goto response;
}
hci_conn_hold(conn->hcon);
- l2cap_sock_init(sk, parent);
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
dcid = chan->scid;
- l2cap_chan_set_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, sk->sk_sndtimeo);
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
status = L2CAP_CS_NO_INFO;
}
} else {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- sk->sk_state = BT_CONNECT2;
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
L2CAP_INFO_REQ, sizeof(info), &info);
}
- if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) &&
+ if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
result == L2CAP_CR_SUCCESS) {
u8 buf[128];
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
switch (result) {
case L2CAP_CR_SUCCESS:
- sk->sk_state = BT_CONFIG;
+ l2cap_state_change(chan, BT_CONFIG);
chan->ident = 0;
chan->dcid = dcid;
- chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
+ clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
- if (chan->conf_state & L2CAP_CONF_REQ_SENT)
+ if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
break;
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
-
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, req), req);
chan->num_conf_req++;
break;
case L2CAP_CR_PEND:
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
break;
default:
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, HZ / 5);
+ l2cap_state_change(chan, BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
break;
}
static inline void set_default_fcs(struct l2cap_chan *chan)
{
- struct l2cap_pinfo *pi = l2cap_pi(chan->sk);
-
/* FCS is enabled only in ERTM or streaming mode, if one or both
* sides request it.
*/
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
chan->fcs = L2CAP_FCS_NONE;
- else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV))
+ else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state))
chan->fcs = L2CAP_FCS_CRC16;
}
sk = chan->sk;
- if (sk->sk_state != BT_CONFIG) {
+ if (chan->state != BT_CONFIG) {
struct l2cap_cmd_rej rej;
rej.reason = cpu_to_le16(0x0002);
/* Reset config buffer. */
chan->conf_len = 0;
- if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE))
+ if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state))
goto unlock;
- if (chan->conf_state & L2CAP_CONF_INPUT_DONE) {
+ if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
goto unlock;
}
- if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) {
+ if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
u8 buf[64];
- chan->conf_state |= L2CAP_CONF_REQ_SENT;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
default:
sk->sk_err = ECONNRESET;
- l2cap_chan_set_timer(chan, HZ * 5);
+ __set_chan_timer(chan, HZ * 5);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
}
if (flags & 0x01)
goto done;
- chan->conf_state |= L2CAP_CONF_INPUT_DONE;
+ set_bit(CONF_INPUT_DONE, &chan->conf_state);
- if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
set_default_fcs(chan);
- sk->sk_state = BT_CONNECTED;
+ l2cap_state_change(chan, BT_CONNECTED);
chan->next_tx_seq = 0;
chan->expected_tx_seq = 0;
skb_queue_head_init(&chan->tx_q);
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, HZ / 5);
+ l2cap_state_change(chan, BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
bh_unlock_sock(sk);
return 0;
}
l2cap_chan_del(chan, ECONNRESET);
bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
return 0;
}
/* don't delete l2cap channel if sk is owned by user */
if (sock_owned_by_user(sk)) {
- sk->sk_state = BT_DISCONN;
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, HZ / 5);
+ l2cap_state_change(chan,BT_DISCONN);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 5);
bh_unlock_sock(sk);
return 0;
}
l2cap_chan_del(chan, 0);
bh_unlock_sock(sk);
- l2cap_sock_kill(sk);
+ chan->ops->close(chan->data);
return 0;
}
control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
control |= L2CAP_SUPER_RCV_NOT_READY;
l2cap_send_sframe(chan, control);
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
}
- if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY)
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
l2cap_retransmit_frames(chan);
l2cap_ertm_send(chan);
- if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) &&
+ if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
chan->frames_sent == 0) {
control |= L2CAP_SUPER_RCV_READY;
l2cap_send_sframe(chan, control);
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state))
goto drop;
- return sock_queue_rcv_skb(chan->sk, skb);
+ return chan->ops->recv(chan->data, skb);
case L2CAP_SDU_START:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU)
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state))
goto drop;
chan->sdu_len = get_unaligned_le16(skb->data);
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ set_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len = skb->len;
break;
case L2CAP_SDU_CONTINUE:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
goto disconnect;
if (!chan->sdu)
break;
case L2CAP_SDU_END:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
goto disconnect;
if (!chan->sdu)
goto disconnect;
- if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) {
+ if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) {
chan->partial_sdu_len += skb->len;
if (chan->partial_sdu_len > chan->imtu)
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
if (!_skb) {
- chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+ set_bit(CONN_SAR_RETRY, &chan->conn_state);
return -ENOMEM;
}
- err = sock_queue_rcv_skb(chan->sk, _skb);
+ err = chan->ops->recv(chan->data, _skb);
if (err < 0) {
kfree_skb(_skb);
- chan->conn_state |= L2CAP_CONN_SAR_RETRY;
+ set_bit(CONN_SAR_RETRY, &chan->conn_state);
return err;
}
- chan->conn_state &= ~L2CAP_CONN_SAR_RETRY;
- chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ clear_bit(CONN_SAR_RETRY, &chan->conn_state);
+ clear_bit(CONN_SAR_SDU, &chan->conn_state);
kfree_skb(chan->sdu);
break;
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
}
- if (!(chan->conn_state & L2CAP_CONN_RNR_SENT))
+ if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
goto done;
control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
l2cap_send_sframe(chan, control);
chan->retry_count = 1;
- del_timer(&chan->retrans_timer);
- __mod_monitor_timer();
+ __clear_retrans_timer(chan);
+ __set_monitor_timer(chan);
- chan->conn_state |= L2CAP_CONN_WAIT_F;
+ set_bit(CONN_WAIT_F, &chan->conn_state);
done:
- chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY;
- chan->conn_state &= ~L2CAP_CONN_RNR_SENT;
+ clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
+ clear_bit(CONN_RNR_SENT, &chan->conn_state);
BT_DBG("chan %p, Exit local busy", chan);
{
int sctrl, err;
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) {
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
__skb_queue_tail(&chan->busy_q, skb);
return l2cap_try_push_rx_skb(chan);
/* Busy Condition */
BT_DBG("chan %p, Enter local busy", chan);
- chan->conn_state |= L2CAP_CONN_LOCAL_BUSY;
+ set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
__skb_queue_tail(&chan->busy_q, skb);
sctrl |= L2CAP_SUPER_RCV_NOT_READY;
l2cap_send_sframe(chan, sctrl);
- chan->conn_state |= L2CAP_CONN_RNR_SENT;
+ set_bit(CONN_RNR_SENT, &chan->conn_state);
- del_timer(&chan->ack_timer);
+ __clear_ack_timer(chan);
queue_work(_busy_wq, &chan->busy_work);
switch (control & L2CAP_CTRL_SAR) {
case L2CAP_SDU_UNSEGMENTED:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
kfree_skb(chan->sdu);
break;
}
- err = sock_queue_rcv_skb(chan->sk, skb);
+ err = chan->ops->recv(chan->data, skb);
if (!err)
return 0;
break;
case L2CAP_SDU_START:
- if (chan->conn_state & L2CAP_CONN_SAR_SDU) {
+ if (test_bit(CONN_SAR_SDU, &chan->conn_state)) {
kfree_skb(chan->sdu);
break;
}
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state |= L2CAP_CONN_SAR_SDU;
+ set_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len = skb->len;
err = 0;
break;
case L2CAP_SDU_CONTINUE:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
break;
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
break;
case L2CAP_SDU_END:
- if (!(chan->conn_state & L2CAP_CONN_SAR_SDU))
+ if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
break;
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
- chan->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ clear_bit(CONN_SAR_SDU, &chan->conn_state);
chan->partial_sdu_len += skb->len;
if (chan->partial_sdu_len > chan->imtu)
if (chan->partial_sdu_len == chan->sdu_len) {
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
- err = sock_queue_rcv_skb(chan->sk, _skb);
+ err = chan->ops->recv(chan->data, _skb);
if (err < 0)
kfree_skb(_skb);
}
tx_seq, rx_control);
if (L2CAP_CTRL_FINAL & rx_control &&
- chan->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&chan->monitor_timer);
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
+ __clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
- __mod_retrans_timer();
- chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+ __set_retrans_timer(chan);
+ clear_bit(CONN_WAIT_F, &chan->conn_state);
}
chan->expected_ack_seq = req_seq;
goto drop;
}
- if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY)
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
goto drop;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
struct srej_list *first;
first = list_first_entry(&chan->srej_l,
if (list_empty(&chan->srej_l)) {
chan->buffer_seq = chan->buffer_seq_srej;
- chan->conn_state &= ~L2CAP_CONN_SREJ_SENT;
+ clear_bit(CONN_SREJ_SENT, &chan->conn_state);
l2cap_send_ack(chan);
BT_DBG("chan %p, Exit SREJ_SENT", chan);
}
if (tx_seq_offset < expected_tx_seq_offset)
goto drop;
- chan->conn_state |= L2CAP_CONN_SREJ_SENT;
+ set_bit(CONN_SREJ_SENT, &chan->conn_state);
BT_DBG("chan %p, Enter SREJ", chan);
__skb_queue_head_init(&chan->busy_q);
l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
- chan->conn_state |= L2CAP_CONN_SEND_PBIT;
+ set_bit(CONN_SEND_PBIT, &chan->conn_state);
l2cap_send_srejframe(chan, tx_seq);
- del_timer(&chan->ack_timer);
+ __clear_ack_timer(chan);
}
return 0;
expected:
chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
bt_cb(skb)->tx_seq = tx_seq;
bt_cb(skb)->sar = sar;
__skb_queue_tail(&chan->srej_q, skb);
return 0;
if (rx_control & L2CAP_CTRL_FINAL) {
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
}
- __mod_ack_timer();
+ __set_ack_timer(chan);
chan->num_acked = (chan->num_acked + 1) % num_to_ack;
if (chan->num_acked == num_to_ack - 1)
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL) {
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT) {
- if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
(chan->unacked_frames > 0))
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
l2cap_send_srejtail(chan);
} else {
l2cap_send_i_or_rr_or_rnr(chan);
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
- if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) &&
+ if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
(chan->unacked_frames > 0))
- __mod_retrans_timer();
+ __set_retrans_timer(chan);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
- if (chan->conn_state & L2CAP_CONN_SREJ_SENT)
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+ if (test_bit(CONN_SREJ_SENT, &chan->conn_state))
l2cap_send_ack(chan);
else
l2cap_ertm_send(chan);
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_FINAL) {
- if (chan->conn_state & L2CAP_CONN_REJ_ACT)
- chan->conn_state &= ~L2CAP_CONN_REJ_ACT;
- else
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
l2cap_retransmit_frames(chan);
} else {
l2cap_retransmit_frames(chan);
- if (chan->conn_state & L2CAP_CONN_WAIT_F)
- chan->conn_state |= L2CAP_CONN_REJ_ACT;
+ if (test_bit(CONN_WAIT_F, &chan->conn_state))
+ set_bit(CONN_REJ_ACT, &chan->conn_state);
}
}
static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control)
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
if (rx_control & L2CAP_CTRL_POLL) {
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
l2cap_retransmit_one_frame(chan, tx_seq);
l2cap_ertm_send(chan);
- if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
chan->srej_save_reqseq = tx_seq;
- chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+ set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
} else if (rx_control & L2CAP_CTRL_FINAL) {
- if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) &&
+ if (test_bit(CONN_SREJ_ACT, &chan->conn_state) &&
chan->srej_save_reqseq == tx_seq)
- chan->conn_state &= ~L2CAP_CONN_SREJ_ACT;
+ clear_bit(CONN_SREJ_ACT, &chan->conn_state);
else
l2cap_retransmit_one_frame(chan, tx_seq);
} else {
l2cap_retransmit_one_frame(chan, tx_seq);
- if (chan->conn_state & L2CAP_CONN_WAIT_F) {
+ if (test_bit(CONN_WAIT_F, &chan->conn_state)) {
chan->srej_save_reqseq = tx_seq;
- chan->conn_state |= L2CAP_CONN_SREJ_ACT;
+ set_bit(CONN_SREJ_ACT, &chan->conn_state);
}
}
}
BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control);
- chan->conn_state |= L2CAP_CONN_REMOTE_BUSY;
+ set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
chan->expected_ack_seq = tx_seq;
l2cap_drop_acked_frames(chan);
if (rx_control & L2CAP_CTRL_POLL)
- chan->conn_state |= L2CAP_CONN_SEND_FBIT;
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
- if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) {
- del_timer(&chan->retrans_timer);
+ if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
+ __clear_retrans_timer(chan);
if (rx_control & L2CAP_CTRL_POLL)
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL);
return;
BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len);
if (L2CAP_CTRL_FINAL & rx_control &&
- chan->conn_state & L2CAP_CONN_WAIT_F) {
- del_timer(&chan->monitor_timer);
+ test_bit(CONN_WAIT_F, &chan->conn_state)) {
+ __clear_monitor_timer(chan);
if (chan->unacked_frames > 0)
- __mod_retrans_timer();
- chan->conn_state &= ~L2CAP_CONN_WAIT_F;
+ __set_retrans_timer(chan);
+ clear_bit(CONN_WAIT_F, &chan->conn_state);
}
switch (rx_control & L2CAP_CTRL_SUPERVISE) {
BT_DBG("chan %p, len %d", chan, skb->len);
- if (sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_CONNECTED)
goto drop;
switch (chan->mode) {
if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
break;
BT_DBG("sk %p, len %d", sk, skb->len);
- if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->chan->imtu < skb->len)
+ if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
drop:
BT_DBG("sk %p, len %d", sk, skb->len);
- if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED)
+ if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->chan->imtu < skb->len)
+ if (chan->imtu < skb->len)
goto drop;
- if (!sock_queue_rcv_skb(sk, skb))
+ if (!chan->ops->recv(chan->data, skb))
goto done;
drop:
l2cap_att_channel(conn, cid, skb);
break;
+ case L2CAP_CID_SMP:
+ if (smp_sig_channel(conn, skb))
+ l2cap_conn_del(conn->hcon, EACCES);
+ break;
+
default:
l2cap_data_channel(conn, cid, skb);
break;
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- if (sk->sk_state != BT_LISTEN)
+ if (c->state != BT_LISTEN)
continue;
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
BT_DBG("hcon %p", hcon);
- if (hcon->type != ACL_LINK || !conn)
+ if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
return 0x13;
return conn->disc_reason;
if (encrypt == 0x00) {
if (chan->sec_level == BT_SECURITY_MEDIUM) {
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, HZ * 5);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ * 5);
} else if (chan->sec_level == BT_SECURITY_HIGH)
l2cap_chan_close(chan, ECONNREFUSED);
} else {
if (chan->sec_level == BT_SECURITY_MEDIUM)
- l2cap_chan_clear_timer(chan);
+ __clear_chan_timer(chan);
}
}
bh_lock_sock(sk);
- if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) {
+ BT_DBG("chan->scid %d", chan->scid);
+
+ if (chan->scid == L2CAP_CID_LE_DATA) {
+ if (!status && encrypt) {
+ chan->sec_level = hcon->sec_level;
+ del_timer(&conn->security_timer);
+ l2cap_chan_ready(sk);
+ }
+
+ bh_unlock_sock(sk);
+ continue;
+ }
+
+ if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
bh_unlock_sock(sk);
continue;
}
- if (!status && (sk->sk_state == BT_CONNECTED ||
- sk->sk_state == BT_CONFIG)) {
+ if (!status && (chan->state == BT_CONNECTED ||
+ chan->state == BT_CONFIG)) {
l2cap_check_encryption(chan, encrypt);
bh_unlock_sock(sk);
continue;
}
- if (sk->sk_state == BT_CONNECT) {
+ if (chan->state == BT_CONNECT) {
if (!status) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(chan->scid);
req.psm = chan->psm;
chan->ident = l2cap_get_ident(conn);
- chan->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
l2cap_send_cmd(conn, chan->ident,
L2CAP_CONN_REQ, sizeof(req), &req);
} else {
- l2cap_chan_clear_timer(chan);
- l2cap_chan_set_timer(chan, HZ / 10);
+ __clear_chan_timer(chan);
+ __set_chan_timer(chan, HZ / 10);
}
- } else if (sk->sk_state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
- __u16 result;
+ __u16 res, stat;
if (!status) {
- sk->sk_state = BT_CONFIG;
- result = L2CAP_CR_SUCCESS;
+ if (bt_sk(sk)->defer_setup) {
+ struct sock *parent = bt_sk(sk)->parent;
+ res = L2CAP_CR_PEND;
+ stat = L2CAP_CS_AUTHOR_PEND;
+ parent->sk_data_ready(parent, 0);
+ } else {
+ l2cap_state_change(chan, BT_CONFIG);
+ res = L2CAP_CR_SUCCESS;
+ stat = L2CAP_CS_NO_INFO;
+ }
} else {
- sk->sk_state = BT_DISCONN;
- l2cap_chan_set_timer(chan, HZ / 10);
- result = L2CAP_CR_SEC_BLOCK;
+ l2cap_state_change(chan, BT_DISCONN);
+ __set_chan_timer(chan, HZ / 10);
+ res = L2CAP_CR_SEC_BLOCK;
+ stat = L2CAP_CS_NO_INFO;
}
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+ rsp.result = cpu_to_le16(res);
+ rsp.status = cpu_to_le16(stat);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
sizeof(rsp), &rsp);
}
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
batostr(&bt_sk(sk)->src),
batostr(&bt_sk(sk)->dst),
- sk->sk_state, __le16_to_cpu(c->psm),
+ c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
- }
+}
read_unlock_bh(&chan_list_lock);
--- /dev/null
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/smp.h>
+#include <linux/crypto.h>
+#include <crypto/b128ops.h>
+
+#define SMP_TIMEOUT 30000 /* 30 seconds */
+
+static inline void swap128(u8 src[16], u8 dst[16])
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ dst[15 - i] = src[i];
+}
+
+static inline void swap56(u8 src[7], u8 dst[7])
+{
+ int i;
+ for (i = 0; i < 7; i++)
+ dst[6 - i] = src[i];
+}
+
+static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+{
+ struct blkcipher_desc desc;
+ struct scatterlist sg;
+ int err, iv_len;
+ unsigned char iv[128];
+
+ if (tfm == NULL) {
+ BT_ERR("tfm %p", tfm);
+ return -EINVAL;
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ err = crypto_blkcipher_setkey(tfm, k, 16);
+ if (err) {
+ BT_ERR("cipher setkey failed: %d", err);
+ return err;
+ }
+
+ sg_init_one(&sg, r, 16);
+
+ iv_len = crypto_blkcipher_ivsize(tfm);
+ if (iv_len) {
+ memset(&iv, 0xff, iv_len);
+ crypto_blkcipher_set_iv(tfm, iv, iv_len);
+ }
+
+ err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+ if (err)
+ BT_ERR("Encrypt data error %d", err);
+
+ return err;
+}
+
+static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
+ u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
+ u8 _rat, bdaddr_t *ra, u8 res[16])
+{
+ u8 p1[16], p2[16];
+ int err;
+
+ memset(p1, 0, 16);
+
+ /* p1 = pres || preq || _rat || _iat */
+ swap56(pres, p1);
+ swap56(preq, p1 + 7);
+ p1[14] = _rat;
+ p1[15] = _iat;
+
+ memset(p2, 0, 16);
+
+ /* p2 = padding || ia || ra */
+ baswap((bdaddr_t *) (p2 + 4), ia);
+ baswap((bdaddr_t *) (p2 + 10), ra);
+
+ /* res = r XOR p1 */
+ u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
+
+ /* res = e(k, res) */
+ err = smp_e(tfm, k, res);
+ if (err) {
+ BT_ERR("Encrypt data error");
+ return err;
+ }
+
+ /* res = res XOR p2 */
+ u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
+
+ /* res = e(k, res) */
+ err = smp_e(tfm, k, res);
+ if (err)
+ BT_ERR("Encrypt data error");
+
+ return err;
+}
+
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
+ u8 r1[16], u8 r2[16], u8 _r[16])
+{
+ int err;
+
+ /* Just least significant octets from r1 and r2 are considered */
+ memcpy(_r, r1 + 8, 8);
+ memcpy(_r + 8, r2 + 8, 8);
+
+ err = smp_e(tfm, k, _r);
+ if (err)
+ BT_ERR("Encrypt data error");
+
+ return err;
+}
+
+static int smp_rand(u8 *buf)
+{
+ get_random_bytes(buf, 16);
+
+ return 0;
+}
+
+static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
+ u16 dlen, void *data)
+{
+ struct sk_buff *skb;
+ struct l2cap_hdr *lh;
+ int len;
+
+ len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
+
+ if (len > conn->mtu)
+ return NULL;
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->len = cpu_to_le16(sizeof(code) + dlen);
+ lh->cid = cpu_to_le16(L2CAP_CID_SMP);
+
+ memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
+
+ memcpy(skb_put(skb, dlen), data, dlen);
+
+ return skb;
+}
+
+static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
+{
+ struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
+
+ BT_DBG("code 0x%2.2x", code);
+
+ if (!skb)
+ return;
+
+ hci_send_acl(conn->hcon, skb, 0);
+}
+
+static __u8 seclevel_to_authreq(__u8 level)
+{
+ switch (level) {
+ case BT_SECURITY_HIGH:
+ /* Right now we don't support bonding */
+ return SMP_AUTH_MITM;
+
+ default:
+ return SMP_AUTH_NONE;
+ }
+}
+
+static void build_pairing_cmd(struct l2cap_conn *conn,
+ struct smp_cmd_pairing *cmd, __u8 authreq)
+{
+ cmd->io_capability = conn->hcon->io_capability;
+ cmd->oob_flag = SMP_OOB_NOT_PRESENT;
+ cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+ cmd->init_key_dist = 0x00;
+ cmd->resp_key_dist = 0x00;
+ cmd->auth_req = authreq;
+}
+
+static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
+{
+ if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
+ (max_key_size < SMP_MIN_ENC_KEY_SIZE))
+ return SMP_ENC_KEY_SIZE;
+
+ conn->smp_key_size = max_key_size;
+
+ return 0;
+}
+
+static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+ u8 key_size;
+
+ BT_DBG("conn %p", conn);
+
+ conn->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&conn->preq[1], req, sizeof(*req));
+ skb_pull(skb, sizeof(*req));
+
+ if (req->oob_flag)
+ return SMP_OOB_NOT_AVAIL;
+
+ /* We didn't start the pairing, so no requirements */
+ build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
+
+ key_size = min(req->max_key_size, rsp.max_key_size);
+ if (check_enc_key_size(conn, key_size))
+ return SMP_ENC_KEY_SIZE;
+
+ /* Just works */
+ memset(conn->tk, 0, sizeof(conn->tk));
+
+ conn->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
+
+ return 0;
+}
+
+static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
+ struct smp_cmd_pairing_confirm cp;
+ struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+ int ret;
+ u8 res[16], key_size;
+
+ BT_DBG("conn %p", conn);
+
+ skb_pull(skb, sizeof(*rsp));
+
+ req = (void *) &conn->preq[1];
+
+ key_size = min(req->max_key_size, rsp->max_key_size);
+ if (check_enc_key_size(conn, key_size))
+ return SMP_ENC_KEY_SIZE;
+
+ if (rsp->oob_flag)
+ return SMP_OOB_NOT_AVAIL;
+
+ /* Just works */
+ memset(conn->tk, 0, sizeof(conn->tk));
+
+ conn->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
+
+ ret = smp_rand(conn->prnd);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
+ conn->src, conn->hcon->dst_type, conn->dst, res);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ swap128(res, cp.confirm_val);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+
+ return 0;
+}
+
+static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
+
+ BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+
+ memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
+ skb_pull(skb, sizeof(conn->pcnf));
+
+ if (conn->hcon->out) {
+ u8 random[16];
+
+ swap128(conn->prnd, random);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
+ random);
+ } else {
+ struct smp_cmd_pairing_confirm cp;
+ int ret;
+ u8 res[16];
+
+ ret = smp_rand(conn->prnd);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
+ conn->hcon->dst_type, conn->dst,
+ 0, conn->src, res);
+ if (ret)
+ return SMP_CONFIRM_FAILED;
+
+ swap128(res, cp.confirm_val);
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+ }
+
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
+
+ return 0;
+}
+
+static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct hci_conn *hcon = conn->hcon;
+ struct crypto_blkcipher *tfm = hcon->hdev->tfm;
+ int ret;
+ u8 key[16], res[16], random[16], confirm[16];
+
+ swap128(skb->data, random);
+ skb_pull(skb, sizeof(random));
+
+ memset(hcon->ltk, 0, sizeof(hcon->ltk));
+
+ if (conn->hcon->out)
+ ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
+ conn->src, conn->hcon->dst_type, conn->dst,
+ res);
+ else
+ ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
+ conn->hcon->dst_type, conn->dst, 0, conn->src,
+ res);
+ if (ret)
+ return SMP_UNSPECIFIED;
+
+ BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
+
+ swap128(res, confirm);
+
+ if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
+ BT_ERR("Pairing failed (confirmation values mismatch)");
+ return SMP_CONFIRM_FAILED;
+ }
+
+ if (conn->hcon->out) {
+ __le16 ediv;
+ u8 rand[8];
+
+ smp_s1(tfm, conn->tk, random, conn->prnd, key);
+ swap128(key, hcon->ltk);
+
+ memset(hcon->ltk + conn->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+
+ memset(rand, 0, sizeof(rand));
+ ediv = 0;
+ hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
+ } else {
+ u8 r[16];
+
+ swap128(conn->prnd, r);
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
+
+ smp_s1(tfm, conn->tk, conn->prnd, random, key);
+ swap128(key, hcon->ltk);
+
+ memset(hcon->ltk + conn->smp_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
+ }
+
+ return 0;
+}
+
+static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_security_req *rp = (void *) skb->data;
+ struct smp_cmd_pairing cp;
+ struct hci_conn *hcon = conn->hcon;
+
+ BT_DBG("conn %p", conn);
+
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ return 0;
+
+ skb_pull(skb, sizeof(*rp));
+
+ memset(&cp, 0, sizeof(cp));
+ build_pairing_cmd(conn, &cp, rp->auth_req);
+
+ conn->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&conn->preq[1], &cp, sizeof(cp));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
+
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ return 0;
+}
+
+int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+{
+ struct hci_conn *hcon = conn->hcon;
+ __u8 authreq;
+
+ BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
+
+ if (IS_ERR(hcon->hdev->tfm))
+ return 1;
+
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
+ return 0;
+
+ if (sec_level == BT_SECURITY_LOW)
+ return 1;
+
+ if (hcon->sec_level >= sec_level)
+ return 1;
+
+ authreq = seclevel_to_authreq(sec_level);
+
+ if (hcon->link_mode & HCI_LM_MASTER) {
+ struct smp_cmd_pairing cp;
+
+ build_pairing_cmd(conn, &cp, authreq);
+ conn->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&conn->preq[1], &cp, sizeof(cp));
+
+ mod_timer(&conn->security_timer, jiffies +
+ msecs_to_jiffies(SMP_TIMEOUT));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ } else {
+ struct smp_cmd_security_req cp;
+ cp.auth_req = authreq;
+ smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
+ }
+
+ hcon->pending_sec_level = sec_level;
+ set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
+
+ return 0;
+}
+
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ __u8 code = skb->data[0];
+ __u8 reason;
+ int err = 0;
+
+ if (IS_ERR(conn->hcon->hdev->tfm)) {
+ err = PTR_ERR(conn->hcon->hdev->tfm);
+ reason = SMP_PAIRING_NOTSUPP;
+ goto done;
+ }
+
+ skb_pull(skb, sizeof(code));
+
+ switch (code) {
+ case SMP_CMD_PAIRING_REQ:
+ reason = smp_cmd_pairing_req(conn, skb);
+ break;
+
+ case SMP_CMD_PAIRING_FAIL:
+ reason = 0;
+ err = -EPERM;
+ break;
+
+ case SMP_CMD_PAIRING_RSP:
+ reason = smp_cmd_pairing_rsp(conn, skb);
+ break;
+
+ case SMP_CMD_SECURITY_REQ:
+ reason = smp_cmd_security_req(conn, skb);
+ break;
+
+ case SMP_CMD_PAIRING_CONFIRM:
+ reason = smp_cmd_pairing_confirm(conn, skb);
+ break;
+
+ case SMP_CMD_PAIRING_RANDOM:
+ reason = smp_cmd_pairing_random(conn, skb);
+ break;
+
+ case SMP_CMD_ENCRYPT_INFO:
+ case SMP_CMD_MASTER_IDENT:
+ case SMP_CMD_IDENT_INFO:
+ case SMP_CMD_IDENT_ADDR_INFO:
+ case SMP_CMD_SIGN_INFO:
+ default:
+ BT_DBG("Unknown command code 0x%2.2x", code);
+
+ reason = SMP_CMD_NOTSUPP;
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+done:
+ if (reason)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
+ &reason);
+
+ kfree_skb(skb);
+ return err;
+}