]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/bluetooth/l2cap_core.c
Bluetooth: Allow 3D profile to use security mode 4 level 0
[karo-tx-linux.git] / net / bluetooth / l2cap_core.c
index 8c3499bec89319289073b2f1e7f3f202cd179ca6..e932ffda5445f72f265e1dae90d90a86f5054d1b 100644 (file)
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "smp.h"
+#include "a2mp.h"
+#include "amp.h"
 
 bool disable_ertm;
 
@@ -676,7 +677,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 
 static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 {
-       if (chan->chan_type == L2CAP_CHAN_RAW) {
+       switch (chan->chan_type) {
+       case L2CAP_CHAN_RAW:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_DEDICATED_BONDING_MITM;
@@ -685,15 +687,29 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
-       } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
-               if (chan->sec_level == BT_SECURITY_LOW)
-                       chan->sec_level = BT_SECURITY_SDP;
-
+               break;
+       case L2CAP_CHAN_CONN_LESS:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+               }
                if (chan->sec_level == BT_SECURITY_HIGH)
                        return HCI_AT_NO_BONDING_MITM;
                else
                        return HCI_AT_NO_BONDING;
-       } else {
+               break;
+       case L2CAP_CHAN_CONN_ORIENTED:
+               if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
+                       if (chan->sec_level == BT_SECURITY_LOW)
+                               chan->sec_level = BT_SECURITY_SDP;
+
+                       if (chan->sec_level == BT_SECURITY_HIGH)
+                               return HCI_AT_NO_BONDING_MITM;
+                       else
+                               return HCI_AT_NO_BONDING;
+               }
+               /* fall through */
+       default:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
                        return HCI_AT_GENERAL_BONDING_MITM;
@@ -702,6 +718,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                default:
                        return HCI_AT_NO_BONDING;
                }
+               break;
        }
 }
 
@@ -1015,14 +1032,29 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
 static bool __amp_capable(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
+       struct hci_dev *hdev;
+       bool amp_available = false;
 
-       if (enable_hs &&
-           hci_amp_capable() &&
-           chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
-           conn->fixed_chan_mask & L2CAP_FC_A2MP)
-               return true;
-       else
+       if (!conn->hs_enabled)
+               return false;
+
+       if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
                return false;
+
+       read_lock(&hci_dev_list_lock);
+       list_for_each_entry(hdev, &hci_dev_list, list) {
+               if (hdev->amp_type != AMP_TYPE_BREDR &&
+                   test_bit(HCI_UP, &hdev->flags)) {
+                       amp_available = true;
+                       break;
+               }
+       }
+       read_unlock(&hci_dev_list_lock);
+
+       if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED)
+               return amp_available;
+
+       return false;
 }
 
 static bool l2cap_check_efs(struct l2cap_chan *chan)
@@ -1415,8 +1447,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
                        sk->sk_state_change(sk);
                        release_sock(sk);
 
-               } else if (chan->state == BT_CONNECT)
+               } else if (chan->state == BT_CONNECT) {
                        l2cap_do_start(chan);
+               }
 
                l2cap_chan_unlock(chan);
        }
@@ -1637,6 +1670,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 
        conn->feat_mask = 0;
 
+       if (hcon->type == ACL_LINK)
+               conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+                                           &hcon->hdev->dev_flags);
+
        spin_lock_init(&conn->lock);
        mutex_init(&conn->chan_lock);
 
@@ -2262,7 +2299,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
        struct l2cap_hdr *lh;
 
-       BT_DBG("chan %p len %zu priority %u", chan, len, priority);
+       BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
+              __le16_to_cpu(chan->psm), len, priority);
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
@@ -2277,7 +2315,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
        lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
-       put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE));
+       put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
 
        err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
        if (unlikely(err < 0)) {
@@ -3042,8 +3080,8 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
 
        skb_queue_head_init(&chan->tx_q);
 
-       chan->local_amp_id = 0;
-       chan->move_id = 0;
+       chan->local_amp_id = AMP_ID_BREDR;
+       chan->move_id = AMP_ID_BREDR;
        chan->move_state = L2CAP_MOVE_STABLE;
        chan->move_role = L2CAP_MOVE_ROLE_NONE;
 
@@ -3083,20 +3121,20 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
        }
 }
 
-static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
 {
-       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
 }
 
-static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
 {
-       return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+       return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
 }
 
 static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
                                      struct l2cap_conf_rfc *rfc)
 {
-       if (chan->local_amp_id && chan->hs_hcon) {
+       if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) {
                u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
 
                /* Class 1 devices have must have ERTM timeouts
@@ -3134,7 +3172,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
 static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
 {
        if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
-           __l2cap_ews_supported(chan)) {
+           __l2cap_ews_supported(chan->conn)) {
                /* use extended control field */
                set_bit(FLAG_EXT_CTRL, &chan->flags);
                chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
@@ -3164,7 +3202,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
                if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
                        break;
 
-               if (__l2cap_efs_supported(chan))
+               if (__l2cap_efs_supported(chan->conn))
                        set_bit(FLAG_EFS_ENABLE, &chan->flags);
 
                /* fall through */
@@ -3316,7 +3354,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                        break;
 
                case L2CAP_CONF_EWS:
-                       if (!enable_hs)
+                       if (!chan->conn->hs_enabled)
                                return -ECONNREFUSED;
 
                        set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -3348,7 +3386,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
                }
 
                if (remote_efs) {
-                       if (__l2cap_efs_supported(chan))
+                       if (__l2cap_efs_supported(chan->conn))
                                set_bit(FLAG_EFS_ENABLE, &chan->flags);
                        else
                                return -ECONNREFUSED;
@@ -3754,6 +3792,13 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
 
        sk = chan->sk;
 
+       /* For certain devices (ex: HID mouse), support for authentication,
+        * pairing and bonding is optional. For such devices, inorder to avoid
+        * the ACL alive for too long after L2CAP disconnection, reset the ACL
+        * disc_timeout back to HCI_DISCONN_TIMEOUT during L2CAP connect.
+        */
+       conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
+
        bacpy(&bt_sk(sk)->src, conn->src);
        bacpy(&bt_sk(sk)->dst, conn->dst);
        chan->psm  = psm;
@@ -3780,12 +3825,12 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
                                 * The connection will succeed after the
                                 * physical link is up.
                                 */
-                               if (amp_id) {
-                                       __l2cap_state_change(chan, BT_CONNECT2);
-                                       result = L2CAP_CR_PEND;
-                               } else {
+                               if (amp_id == AMP_ID_BREDR) {
                                        __l2cap_state_change(chan, BT_CONFIG);
                                        result = L2CAP_CR_SUCCESS;
+                               } else {
+                                       __l2cap_state_change(chan, BT_CONNECT2);
+                                       result = L2CAP_CR_PEND;
                                }
                                status = L2CAP_CS_NO_INFO;
                        }
@@ -3883,13 +3928,13 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
        if (scid) {
                chan = __l2cap_get_chan_by_scid(conn, scid);
                if (!chan) {
-                       err = -EFAULT;
+                       err = -EBADSLT;
                        goto unlock;
                }
        } else {
                chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
                if (!chan) {
-                       err = -EFAULT;
+                       err = -EBADSLT;
                        goto unlock;
                }
        }
@@ -3977,7 +4022,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
 
        chan = l2cap_get_chan_by_scid(conn, dcid);
        if (!chan)
-               return -ENOENT;
+               return -EBADSLT;
 
        if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
                struct l2cap_cmd_rej_cid rej;
@@ -4205,7 +4250,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
        chan = __l2cap_get_chan_by_scid(conn, dcid);
        if (!chan) {
                mutex_unlock(&conn->chan_lock);
-               return 0;
+               return -EBADSLT;
        }
 
        l2cap_chan_lock(chan);
@@ -4295,7 +4340,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                if (!disable_ertm)
                        feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
                                | L2CAP_FEAT_FCS;
-               if (enable_hs)
+               if (conn->hs_enabled)
                        feat_mask |= L2CAP_FEAT_EXT_FLOW
                                | L2CAP_FEAT_EXT_WINDOW;
 
@@ -4306,7 +4351,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
                u8 buf[12];
                struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
 
-               if (enable_hs)
+               if (conn->hs_enabled)
                        l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
                else
                        l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
@@ -4403,7 +4448,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        if (cmd_len != sizeof(*req))
                return -EPROTO;
 
-       if (!enable_hs)
+       if (!conn->hs_enabled)
                return -EINVAL;
 
        psm = le16_to_cpu(req->psm);
@@ -4412,7 +4457,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
        BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
 
        /* For controller id 0 make BR/EDR connection */
-       if (req->amp_id == HCI_BREDR_ID) {
+       if (req->amp_id == AMP_ID_BREDR) {
                l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
                              req->amp_id);
                return 0;
@@ -4437,7 +4482,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
                hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
                if (!hs_hcon) {
                        hci_dev_put(hdev);
-                       return -EFAULT;
+                       return -EBADSLT;
                }
 
                BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4461,7 +4506,7 @@ error:
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
                       sizeof(rsp), &rsp);
 
-       return -EFAULT;
+       return 0;
 }
 
 static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
@@ -4647,7 +4692,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
 
        if (chan->state != BT_CONNECTED) {
                /* Ignore logical link if channel is on BR/EDR */
-               if (chan->local_amp_id)
+               if (chan->local_amp_id != AMP_ID_BREDR)
                        l2cap_logical_finish_create(chan, hchan);
        } else {
                l2cap_logical_finish_move(chan, hchan);
@@ -4658,7 +4703,7 @@ void l2cap_move_start(struct l2cap_chan *chan)
 {
        BT_DBG("chan %p", chan);
 
-       if (chan->local_amp_id == HCI_BREDR_ID) {
+       if (chan->local_amp_id == AMP_ID_BREDR) {
                if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
                        return;
                chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
@@ -4830,7 +4875,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
 
        BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
 
-       if (!enable_hs)
+       if (!conn->hs_enabled)
                return -EINVAL;
 
        chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -4857,7 +4902,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
                goto send_move_response;
        }
 
-       if (req->dest_amp_id) {
+       if (req->dest_amp_id != AMP_ID_BREDR) {
                struct hci_dev *hdev;
                hdev = hci_dev_get(req->dest_amp_id);
                if (!hdev || hdev->dev_type != HCI_AMP ||
@@ -4887,7 +4932,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
        chan->move_id = req->dest_amp_id;
        icid = chan->dcid;
 
-       if (!req->dest_amp_id) {
+       if (req->dest_amp_id == AMP_ID_BREDR) {
                /* Moving to BR/EDR */
                if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
                        chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
@@ -5079,7 +5124,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
                if (result == L2CAP_MC_CONFIRMED) {
                        chan->local_amp_id = chan->move_id;
-                       if (!chan->local_amp_id)
+                       if (chan->local_amp_id == AMP_ID_BREDR)
                                __release_logical_link(chan);
                } else {
                        chan->move_id = chan->local_amp_id;
@@ -5119,7 +5164,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
        if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
                chan->local_amp_id = chan->move_id;
 
-               if (!chan->local_amp_id && chan->hs_hchan)
+               if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan)
                        __release_logical_link(chan);
 
                l2cap_move_done(chan);
@@ -5211,7 +5256,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
 
        case L2CAP_CONN_RSP:
        case L2CAP_CREATE_CHAN_RSP:
-               err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
+               l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CONF_REQ:
@@ -5219,7 +5264,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_CONF_RSP:
-               err = l2cap_config_rsp(conn, cmd, cmd_len, data);
+               l2cap_config_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_DISCONN_REQ:
@@ -5227,7 +5272,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_DISCONN_RSP:
-               err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
+               l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_ECHO_REQ:
@@ -5242,7 +5287,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_INFO_RSP:
-               err = l2cap_information_rsp(conn, cmd, cmd_len, data);
+               l2cap_information_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_CREATE_CHAN_REQ:
@@ -5254,7 +5299,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_MOVE_CHAN_RSP:
-               err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
+               l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
                break;
 
        case L2CAP_MOVE_CHAN_CFM:
@@ -5262,7 +5307,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
                break;
 
        case L2CAP_MOVE_CHAN_CFM_RSP:
-               err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
+               l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
                break;
 
        default:
@@ -5293,54 +5338,65 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
        }
 }
 
+static __le16 l2cap_err_to_reason(int err)
+{
+       switch (err) {
+       case -EBADSLT:
+               return __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+       case -EMSGSIZE:
+               return __constant_cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
+       case -EINVAL:
+       case -EPROTO:
+       default:
+               return __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+       }
+}
+
 static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
                                        struct sk_buff *skb)
 {
-       u8 *data = skb->data;
-       int len = skb->len;
-       struct l2cap_cmd_hdr cmd;
+       struct hci_conn *hcon = conn->hcon;
+       struct l2cap_cmd_hdr *cmd;
+       u16 len;
        int err;
 
-       l2cap_raw_recv(conn, skb);
+       if (hcon->type != LE_LINK)
+               goto drop;
 
-       while (len >= L2CAP_CMD_HDR_SIZE) {
-               u16 cmd_len;
-               memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
-               data += L2CAP_CMD_HDR_SIZE;
-               len  -= L2CAP_CMD_HDR_SIZE;
+       if (skb->len < L2CAP_CMD_HDR_SIZE)
+               goto drop;
 
-               cmd_len = le16_to_cpu(cmd.len);
+       cmd = (void *) skb->data;
+       skb_pull(skb, L2CAP_CMD_HDR_SIZE);
 
-               BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
-                      cmd.ident);
+       len = le16_to_cpu(cmd->len);
 
-               if (cmd_len > len || !cmd.ident) {
-                       BT_DBG("corrupted command");
-                       break;
-               }
+       BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
 
-               err = l2cap_le_sig_cmd(conn, &cmd, data);
-               if (err) {
-                       struct l2cap_cmd_rej_unk rej;
+       if (len != skb->len || !cmd->ident) {
+               BT_DBG("corrupted command");
+               goto drop;
+       }
 
-                       BT_ERR("Wrong link type (%d)", err);
+       err = l2cap_le_sig_cmd(conn, cmd, skb->data);
+       if (err) {
+               struct l2cap_cmd_rej_unk rej;
 
-                       /* FIXME: Map err to a valid reason */
-                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
-                       l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
-                                      sizeof(rej), &rej);
-               }
+               BT_ERR("Wrong link type (%d)", err);
 
-               data += cmd_len;
-               len  -= cmd_len;
+               rej.reason = l2cap_err_to_reason(err);
+               l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
+                              sizeof(rej), &rej);
        }
 
+drop:
        kfree_skb(skb);
 }
 
 static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                                     struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        u8 *data = skb->data;
        int len = skb->len;
        struct l2cap_cmd_hdr cmd;
@@ -5348,6 +5404,9 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
        l2cap_raw_recv(conn, skb);
 
+       if (hcon->type != ACL_LINK)
+               goto drop;
+
        while (len >= L2CAP_CMD_HDR_SIZE) {
                u16 cmd_len;
                memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
@@ -5370,8 +5429,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
 
                        BT_ERR("Wrong link type (%d)", err);
 
-                       /* FIXME: Map err to a valid reason */
-                       rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+                       rej.reason = l2cap_err_to_reason(err);
                        l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
                                       sizeof(rej), &rej);
                }
@@ -5380,6 +5438,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
                len  -= cmd_len;
        }
 
+drop:
        kfree_skb(skb);
 }
 
@@ -5776,7 +5835,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                               struct sk_buff *skb, u8 event)
 {
        int err = 0;
-       bool skb_in_use = 0;
+       bool skb_in_use = false;
 
        BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
               event);
@@ -5797,7 +5856,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                                                           control->txseq);
 
                        chan->buffer_seq = chan->expected_tx_seq;
-                       skb_in_use = 1;
+                       skb_in_use = true;
 
                        err = l2cap_reassemble_sdu(chan, skb, control);
                        if (err)
@@ -5833,7 +5892,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
                         * current frame is stored for later use.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5911,7 +5970,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
 {
        int err = 0;
        u16 txseq = control->txseq;
-       bool skb_in_use = 0;
+       bool skb_in_use = false;
 
        BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
               event);
@@ -5923,7 +5982,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                        /* Keep frame for reassembly later */
                        l2cap_pass_to_tx(chan, control);
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5934,7 +5993,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
 
                        l2cap_pass_to_tx(chan, control);
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5949,7 +6008,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                         * the missing frames.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -5963,7 +6022,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
                         * SREJ'd frames.
                         */
                        skb_queue_tail(&chan->srej_q, skb);
-                       skb_in_use = 1;
+                       skb_in_use = true;
                        BT_DBG("Queued %p (queue len %d)", skb,
                               skb_queue_len(&chan->srej_q));
 
@@ -6372,8 +6431,12 @@ done:
 static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
                                  struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan;
 
+       if (hcon->type != ACL_LINK)
+               goto drop;
+
        chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
        if (!chan)
                goto drop;
@@ -6396,8 +6459,12 @@ drop:
 static void l2cap_att_channel(struct l2cap_conn *conn,
                              struct sk_buff *skb)
 {
+       struct hci_conn *hcon = conn->hcon;
        struct l2cap_chan *chan;
 
+       if (hcon->type != LE_LINK)
+               goto drop;
+
        chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
                                         conn->src, conn->dst);
        if (!chan)
@@ -6433,9 +6500,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
        BT_DBG("len %d, cid 0x%4.4x", len, cid);
 
        switch (cid) {
-       case L2CAP_CID_LE_SIGNALING:
-               l2cap_le_sig_channel(conn, skb);
-               break;
        case L2CAP_CID_SIGNALING:
                l2cap_sig_channel(conn, skb);
                break;
@@ -6450,6 +6514,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                l2cap_att_channel(conn, skb);
                break;
 
+       case L2CAP_CID_LE_SIGNALING:
+               l2cap_le_sig_channel(conn, skb);
+               break;
+
        case L2CAP_CID_SMP:
                if (smp_sig_channel(conn, skb))
                        l2cap_conn_del(conn->hcon, EACCES);
@@ -6748,9 +6816,13 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
                conn->rx_len -= skb->len;
 
                if (!conn->rx_len) {
-                       /* Complete frame received */
-                       l2cap_recv_frame(conn, conn->rx_skb);
+                       /* Complete frame received. l2cap_recv_frame
+                        * takes ownership of the skb so set the global
+                        * rx_skb pointer to NULL first.
+                        */
+                       struct sk_buff *rx_skb = conn->rx_skb;
                        conn->rx_skb = NULL;
+                       l2cap_recv_frame(conn, rx_skb);
                }
                break;
        }