summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-13 12:40:34 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-13 12:40:34 +0400
commit42a2d923cc349583ebf6fdd52a7d35e1c2f7e6bd (patch)
tree2b2b0c03b5389c1301800119333967efafd994ca /net/bluetooth
parent5cbb3d216e2041700231bcfc383ee5f8b7fc8b74 (diff)
parent75ecab1df14d90e86cebef9ec5c76befde46e65f (diff)
downloadlinux-42a2d923cc349583ebf6fdd52a7d35e1c2f7e6bd.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) The addition of nftables. No longer will we need protocol aware firewall filtering modules, it can all live in userspace. At the core of nftables is a, for lack of a better term, virtual machine that executes byte codes to inspect packet or metadata (arriving interface index, etc.) and make verdict decisions. Besides support for loading packet contents and comparing them, the interpreter supports lookups in various datastructures as fundamental operations. For example sets are supports, and therefore one could create a set of whitelist IP address entries which have ACCEPT verdicts attached to them, and use the appropriate byte codes to do such lookups. Since the interpreted code is composed in userspace, userspace can do things like optimize things before giving it to the kernel. Another major improvement is the capability of atomically updating portions of the ruleset. In the existing netfilter implementation, one has to update the entire rule set in order to make a change and this is very expensive. Userspace tools exist to create nftables rules using existing netfilter rule sets, but both kernel implementations will need to co-exist for quite some time as we transition from the old to the new stuff. Kudos to Patrick McHardy, Pablo Neira Ayuso, and others who have worked so hard on this. 2) Daniel Borkmann and Hannes Frederic Sowa made several improvements to our pseudo-random number generator, mostly used for things like UDP port randomization and netfitler, amongst other things. In particular the taus88 generater is updated to taus113, and test cases are added. 3) Support 64-bit rates in HTB and TBF schedulers, from Eric Dumazet and Yang Yingliang. 4) Add support for new 577xx tigon3 chips to tg3 driver, from Nithin Sujir. 5) Fix two fatal flaws in TCP dynamic right sizing, from Eric Dumazet, Neal Cardwell, and Yuchung Cheng. 6) Allow IP_TOS and IP_TTL to be specified in sendmsg() ancillary control message data, much like other socket option attributes. From Francesco Fusco. 7) Allow applications to specify a cap on the rate computed automatically by the kernel for pacing flows, via a new SO_MAX_PACING_RATE socket option. From Eric Dumazet. 8) Make the initial autotuned send buffer sizing in TCP more closely reflect actual needs, from Eric Dumazet. 9) Currently early socket demux only happens for TCP sockets, but we can do it for connected UDP sockets too. Implementation from Shawn Bohrer. 10) Refactor inet socket demux with the goal of improving hash demux performance for listening sockets. With the main goals being able to use RCU lookups on even request sockets, and eliminating the listening lock contention. From Eric Dumazet. 11) The bonding layer has many demuxes in it's fast path, and an RCU conversion was started back in 3.11, several changes here extend the RCU usage to even more locations. From Ding Tianhong and Wang Yufen, based upon suggestions by Nikolay Aleksandrov and Veaceslav Falico. 12) Allow stackability of segmentation offloads to, in particular, allow segmentation offloading over tunnels. From Eric Dumazet. 13) Significantly improve the handling of secret keys we input into the various hash functions in the inet hashtables, TCP fast open, as well as syncookies. From Hannes Frederic Sowa. The key fundamental operation is "net_get_random_once()" which uses static keys. Hannes even extended this to ipv4/ipv6 fragmentation handling and our generic flow dissector. 14) The generic driver layer takes care now to set the driver data to NULL on device removal, so it's no longer necessary for drivers to explicitly set it to NULL any more. Many drivers have been cleaned up in this way, from Jingoo Han. 15) Add a BPF based packet scheduler classifier, from Daniel Borkmann. 16) Improve CRC32 interfaces and generic SKB checksum iterators so that SCTP's checksumming can more cleanly be handled. Also from Daniel Borkmann. 17) Add a new PMTU discovery mode, IP_PMTUDISC_INTERFACE, which forces using the interface MTU value. This helps avoid PMTU attacks, particularly on DNS servers. From Hannes Frederic Sowa. 18) Use generic XPS for transmit queue steering rather than internal (re-)implementation in virtio-net. From Jason Wang. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1622 commits) random32: add test cases for taus113 implementation random32: upgrade taus88 generator to taus113 from errata paper random32: move rnd_state to linux/random.h random32: add prandom_reseed_late() and call when nonblocking pool becomes initialized random32: add periodic reseeding random32: fix off-by-one in seeding requirement PHY: Add RTL8201CP phy_driver to realtek xtsonic: add missing platform_set_drvdata() in xtsonic_probe() macmace: add missing platform_set_drvdata() in mace_probe() ethernet/arc/arc_emac: add missing platform_set_drvdata() in arc_emac_probe() ipv6: protect for_each_sk_fl_rcu in mem_check with rcu_read_lock_bh vlan: Implement vlan_dev_get_egress_qos_mask as an inline. ixgbe: add warning when max_vfs is out of range. igb: Update link modes display in ethtool netfilter: push reasm skb through instead of original frag skbs ip6_output: fragment outgoing reassembled skb properly MAINTAINERS: mv643xx_eth: take over maintainership from Lennart net_sched: tbf: support of 64bit rates ixgbe: deleting dfwd stations out of order can cause null ptr deref ixgbe: fix build err, num_rx_queues is only available with CONFIG_RPS ...
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/Makefile2
-rw-r--r--net/bluetooth/a2mp.c72
-rw-r--r--net/bluetooth/a2mp.h150
-rw-r--r--net/bluetooth/af_bluetooth.c73
-rw-r--r--net/bluetooth/amp.c10
-rw-r--r--net/bluetooth/amp.h54
-rw-r--r--net/bluetooth/bnep/core.c18
-rw-r--r--net/bluetooth/cmtp/core.c6
-rw-r--r--net/bluetooth/hci_conn.c194
-rw-r--r--net/bluetooth/hci_core.c1128
-rw-r--r--net/bluetooth/hci_event.c175
-rw-r--r--net/bluetooth/hci_sock.c210
-rw-r--r--net/bluetooth/hci_sysfs.c373
-rw-r--r--net/bluetooth/hidp/core.c18
-rw-r--r--net/bluetooth/hidp/hidp.h4
-rw-r--r--net/bluetooth/l2cap_core.c538
-rw-r--r--net/bluetooth/l2cap_sock.c222
-rw-r--r--net/bluetooth/mgmt.c1609
-rw-r--r--net/bluetooth/rfcomm/core.c33
-rw-r--r--net/bluetooth/rfcomm/sock.c49
-rw-r--r--net/bluetooth/sco.c57
-rw-r--r--net/bluetooth/smp.c98
-rw-r--r--net/bluetooth/smp.h146
23 files changed, 3613 insertions, 1626 deletions
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index dea6a287daca..6a791e73e39d 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
a2mp.o amp.o
+
+subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 17f33a62f6db..efcd108822c4 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -15,8 +15,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
/* Global AMP Manager list */
LIST_HEAD(amp_mgr_list);
@@ -75,33 +76,26 @@ u8 __next_ident(struct amp_mgr *mgr)
return mgr->ident;
}
-static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
-{
- cl->id = 0;
- cl->type = 0;
- cl->status = 1;
-}
-
/* hci_dev_list shall be locked */
-static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
{
- int i = 0;
struct hci_dev *hdev;
+ int i = 1;
- __a2mp_cl_bredr(cl);
+ cl[0].id = AMP_ID_BREDR;
+ cl[0].type = AMP_TYPE_BREDR;
+ cl[0].status = AMP_STATUS_BLUETOOTH_ONLY;
list_for_each_entry(hdev, &hci_dev_list, list) {
- /* Iterate through AMP controllers */
- if (hdev->id == HCI_BREDR_ID)
- continue;
-
- /* Starting from second entry */
- if (++i >= num_ctrl)
- return;
-
- cl[i].id = hdev->id;
- cl[i].type = hdev->amp_type;
- cl[i].status = hdev->amp_status;
+ if (hdev->dev_type == HCI_AMP) {
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ if (test_bit(HCI_UP, &hdev->flags))
+ cl[i].status = hdev->amp_status;
+ else
+ cl[i].status = AMP_STATUS_POWERED_DOWN;
+ i++;
+ }
}
}
@@ -129,6 +123,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_discov_rsp *rsp;
u16 ext_feat;
u8 num_ctrl;
+ struct hci_dev *hdev;
if (len < sizeof(*req))
return -EINVAL;
@@ -152,7 +147,14 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
read_lock(&hci_dev_list_lock);
- num_ctrl = __hci_num_ctrl();
+ /* at minimum the BR/EDR needs to be listed */
+ num_ctrl = 1;
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ if (hdev->dev_type == HCI_AMP)
+ num_ctrl++;
+ }
+
len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
rsp = kmalloc(len, GFP_ATOMIC);
if (!rsp) {
@@ -163,7 +165,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
rsp->ext_feat = 0;
- __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+ __a2mp_add_cl(mgr, rsp->cl);
read_unlock(&hci_dev_list_lock);
@@ -208,7 +210,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
cl->status);
- if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) {
+ if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) {
struct a2mp_info_req req;
found = true;
@@ -344,7 +346,7 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
hdev = hci_dev_get(req->id);
- if (!hdev || hdev->amp_type == HCI_BREDR || tmp) {
+ if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) {
struct a2mp_amp_assoc_rsp rsp;
rsp.id = req->id;
@@ -451,7 +453,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
rsp.remote_id = req->local_id;
hdev = hci_dev_get(req->remote_id);
- if (!hdev || hdev->amp_type != HCI_AMP) {
+ if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) {
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
goto send_rsp;
}
@@ -535,7 +537,8 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
goto send_rsp;
}
- hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst);
+ hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+ &mgr->l2cap_conn->hcon->dst);
if (!hcon) {
BT_ERR("No phys link exist");
rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
@@ -669,7 +672,8 @@ static void a2mp_chan_close_cb(struct l2cap_chan *chan)
l2cap_chan_put(chan);
}
-static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
+static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
+ int err)
{
struct amp_mgr *mgr = chan->data;
@@ -706,6 +710,9 @@ static struct l2cap_ops a2mp_chan_ops = {
.teardown = l2cap_chan_no_teardown,
.ready = l2cap_chan_no_ready,
.defer = l2cap_chan_no_defer,
+ .resume = l2cap_chan_no_resume,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
@@ -829,6 +836,9 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
{
struct amp_mgr *mgr;
+ if (conn->hcon->type != ACL_LINK)
+ return NULL;
+
mgr = amp_mgr_create(conn, false);
if (!mgr) {
BT_ERR("Could not create AMP manager");
@@ -871,7 +881,7 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
rsp.id = hdev->id;
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
- if (hdev->amp_type != HCI_BREDR) {
+ if (hdev->amp_type != AMP_TYPE_BREDR) {
rsp.status = 0;
rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h
new file mode 100644
index 000000000000..487b54c1308f
--- /dev/null
+++ b/net/bluetooth/a2mp.h
@@ -0,0 +1,150 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ Copyright (c) 2011,2012 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#include <net/bluetooth/l2cap.h>
+
+#define A2MP_FEAT_EXT 0x8000
+
+enum amp_mgr_state {
+ READ_LOC_AMP_INFO,
+ READ_LOC_AMP_ASSOC,
+ READ_LOC_AMP_ASSOC_FINAL,
+ WRITE_REMOTE_AMP_ASSOC,
+};
+
+struct amp_mgr {
+ struct list_head list;
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct l2cap_chan *bredr_chan;
+ struct kref kref;
+ __u8 ident;
+ __u8 handle;
+ unsigned long state;
+ unsigned long flags;
+
+ struct list_head amp_ctrls;
+ struct mutex amp_ctrls_lock;
+};
+
+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+ __u8 data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
+extern struct list_head amp_mgr_list;
+extern struct mutex amp_mgr_list_lock;
+
+struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+u8 __next_ident(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);
+struct amp_mgr *amp_mgr_lookup_by_state(u8 state);
+void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data);
+void a2mp_discover_amp(struct l2cap_chan *chan);
+void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
+void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
+
+#endif /* __A2MP_H */
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 9096137c889c..f6a1671ea2ff 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -25,12 +25,13 @@
/* Bluetooth address family and sockets. */
#include <linux/module.h>
+#include <linux/debugfs.h>
#include <asm/ioctls.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
-#define VERSION "2.16"
+#define VERSION "2.17"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
@@ -221,12 +222,12 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
- msg->msg_namelen = 0;
-
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb) {
- if (sk->sk_shutdown & RCV_SHUTDOWN)
+ if (sk->sk_shutdown & RCV_SHUTDOWN) {
+ msg->msg_namelen = 0;
return 0;
+ }
return err;
}
@@ -238,9 +239,16 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- if (err == 0)
+ if (err == 0) {
sock_recv_ts_and_drops(msg, sk, skb);
+ if (bt_sk(sk)->skb_msg_name)
+ bt_sk(sk)->skb_msg_name(skb, msg->msg_name,
+ &msg->msg_namelen);
+ else
+ msg->msg_namelen = 0;
+ }
+
skb_free_datagram(sk, skb);
return err ? : copied;
@@ -490,6 +498,7 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
EXPORT_SYMBOL(bt_sock_ioctl);
+/* This function expects the sk lock to be held when called */
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
{
DECLARE_WAITQUEUE(wait, current);
@@ -525,6 +534,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
}
EXPORT_SYMBOL(bt_sock_wait_state);
+/* This function expects the sk lock to be held when called */
+int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long timeo;
+ int err = 0;
+
+ BT_DBG("sk %p", sk);
+
+ timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+ add_wait_queue(sk_sleep(sk), &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) {
+ if (!timeo) {
+ err = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ err = sock_intr_errno(timeo);
+ break;
+ }
+
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = sock_error(sk);
+ if (err)
+ break;
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk_sleep(sk), &wait);
+
+ return err;
+}
+EXPORT_SYMBOL(bt_sock_wait_ready);
+
#ifdef CONFIG_PROC_FS
struct bt_seq_state {
struct bt_sock_list *l;
@@ -563,7 +612,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
struct bt_sock_list *l = s->l;
if (v == SEQ_START_TOKEN) {
- seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent");
+ seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Parent");
if (l->custom_seq_show) {
seq_putc(seq, ' ');
@@ -576,15 +625,13 @@ static int bt_seq_show(struct seq_file *seq, void *v)
struct bt_sock *bt = bt_sk(sk);
seq_printf(seq,
- "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu",
+ "%pK %-6d %-6u %-6u %-6u %-6lu %-6lu",
sk,
atomic_read(&sk->sk_refcnt),
sk_rmem_alloc_get(sk),
sk_wmem_alloc_get(sk),
from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
sock_i_ino(sk),
- &bt->src,
- &bt->dst,
bt->parent? sock_i_ino(bt->parent): 0LU);
if (l->custom_seq_show) {
@@ -662,12 +709,17 @@ static struct net_proto_family bt_sock_family_ops = {
.create = bt_sock_create,
};
+struct dentry *bt_debugfs;
+EXPORT_SYMBOL_GPL(bt_debugfs);
+
static int __init bt_init(void)
{
int err;
BT_INFO("Core ver %s", VERSION);
+ bt_debugfs = debugfs_create_dir("bluetooth", NULL);
+
err = bt_sysfs_init();
if (err < 0)
return err;
@@ -708,7 +760,6 @@ error:
static void __exit bt_exit(void)
{
-
sco_exit();
l2cap_exit();
@@ -718,6 +769,8 @@ static void __exit bt_exit(void)
sock_unregister(PF_BLUETOOTH);
bt_sysfs_cleanup();
+
+ debugfs_remove_recursive(bt_debugfs);
}
subsys_initcall(bt_init);
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index d459ed43c779..bb39509b3f06 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -14,10 +14,11 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
#include <crypto/hash.h>
+#include "a2mp.h"
+#include "amp.h"
+
/* Remote AMP Controllers interface */
void amp_ctrl_get(struct amp_ctrl *ctrl)
{
@@ -110,7 +111,7 @@ static u8 __next_handle(struct amp_mgr *mgr)
struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
u8 remote_id, bool out)
{
- bdaddr_t *dst = mgr->l2cap_conn->dst;
+ bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
struct hci_conn *hcon;
hcon = hci_conn_add(hdev, AMP_LINK, dst);
@@ -409,7 +410,8 @@ void amp_create_logical_link(struct l2cap_chan *chan)
struct hci_cp_create_accept_logical_link cp;
struct hci_dev *hdev;
- BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
+ BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon,
+ &chan->conn->hcon->dst);
if (!hs_hcon)
return;
diff --git a/net/bluetooth/amp.h b/net/bluetooth/amp.h
new file mode 100644
index 000000000000..7ea3db77ba89
--- /dev/null
+++ b/net/bluetooth/amp.h
@@ -0,0 +1,54 @@
+/*
+ Copyright (c) 2011,2012 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef __AMP_H
+#define __AMP_H
+
+struct amp_ctrl {
+ struct list_head list;
+ struct kref kref;
+ __u8 id;
+ __u16 assoc_len_so_far;
+ __u16 assoc_rem_len;
+ __u16 assoc_len;
+ __u8 *assoc;
+};
+
+int amp_ctrl_put(struct amp_ctrl *ctrl);
+void amp_ctrl_get(struct amp_ctrl *ctrl);
+struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id);
+struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id);
+void amp_ctrl_list_flush(struct amp_mgr *mgr);
+
+struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
+ u8 remote_id, bool out);
+
+int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type);
+
+void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle);
+void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr);
+void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
+ struct hci_conn *hcon);
+void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+ struct hci_conn *hcon);
+void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+ struct hci_conn *hcon);
+void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
+void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
+void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
+void amp_create_logical_link(struct l2cap_chan *chan);
+void amp_disconnect_logical_link(struct hci_chan *hchan);
+void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason);
+
+#endif /* __AMP_H */
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index e430b1abcd2f..a841d3e776c5 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -32,6 +32,7 @@
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/hci_core.h>
#include "bnep.h"
@@ -510,20 +511,13 @@ static int bnep_session(void *arg)
static struct device *bnep_get_device(struct bnep_session *session)
{
- bdaddr_t *src = &bt_sk(session->sock->sk)->src;
- bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
- struct hci_dev *hdev;
struct hci_conn *conn;
- hdev = hci_get_route(dst, src);
- if (!hdev)
+ conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
+ if (!conn)
return NULL;
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
-
- hci_dev_put(hdev);
-
- return conn ? &conn->dev : NULL;
+ return &conn->dev;
}
static struct device_type bnep_type = {
@@ -539,8 +533,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
BT_DBG("");
- baswap((void *) dst, &bt_sk(sock->sk)->dst);
- baswap((void *) src, &bt_sk(sock->sk)->src);
+ baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
+ baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
/* session struct allocated as private part of net_device */
dev = alloc_netdev(sizeof(struct bnep_session),
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index e0a6ebf2baa6..67fe5e84e68f 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -340,20 +340,20 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
down_write(&cmtp_session_sem);
- s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
+ s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
if (s && s->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
- bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
+ bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
l2cap_pi(sock->sk)->chan->imtu);
BT_DBG("mtu %d", session->mtu);
- sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
+ sprintf(session->name, "%pMR", &session->bdaddr);
session->sock = sock;
session->state = BT_CONFIG;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index f0817121ec5e..ba5366c320da 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -28,8 +28,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
+#include "a2mp.h"
struct sco_param {
u16 pkt_type;
@@ -49,30 +50,6 @@ static const struct sco_param sco_param_wideband[] = {
{ EDR_ESCO_MASK | ESCO_EV3, 0x0008 }, /* T1 */
};
-static void hci_le_create_connection(struct hci_conn *conn)
-{
- struct hci_dev *hdev = conn->hdev;
- struct hci_cp_le_create_conn cp;
-
- conn->state = BT_CONNECT;
- conn->out = true;
- conn->link_mode |= HCI_LM_MASTER;
- conn->sec_level = BT_SECURITY_LOW;
-
- memset(&cp, 0, sizeof(cp));
- cp.scan_interval = __constant_cpu_to_le16(0x0060);
- cp.scan_window = __constant_cpu_to_le16(0x0030);
- bacpy(&cp.peer_addr, &conn->dst);
- cp.peer_addr_type = conn->dst_type;
- cp.conn_interval_min = __constant_cpu_to_le16(0x0028);
- cp.conn_interval_max = __constant_cpu_to_le16(0x0038);
- cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
- cp.min_ce_len = __constant_cpu_to_le16(0x0000);
- cp.max_ce_len = __constant_cpu_to_le16(0x0000);
-
- hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
-}
-
static void hci_le_create_connection_cancel(struct hci_conn *conn)
{
hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
@@ -340,8 +317,10 @@ static void hci_conn_timeout(struct work_struct *work)
}
/* Enter sniff mode */
-static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+static void hci_conn_idle(struct work_struct *work)
{
+ struct hci_conn *conn = container_of(work, struct hci_conn,
+ idle_work.work);
struct hci_dev *hdev = conn->hdev;
BT_DBG("hcon %p mode %d", conn, conn->mode);
@@ -375,21 +354,12 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
}
}
-static void hci_conn_idle(unsigned long arg)
-{
- struct hci_conn *conn = (void *) arg;
-
- BT_DBG("hcon %p mode %d", conn, conn->mode);
-
- hci_conn_enter_sniff_mode(conn);
-}
-
-static void hci_conn_auto_accept(unsigned long arg)
+static void hci_conn_auto_accept(struct work_struct *work)
{
- struct hci_conn *conn = (void *) arg;
- struct hci_dev *hdev = conn->hdev;
+ struct hci_conn *conn = container_of(work, struct hci_conn,
+ auto_accept_work.work);
- hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+ hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
&conn->dst);
}
@@ -404,6 +374,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
return NULL;
bacpy(&conn->dst, dst);
+ bacpy(&conn->src, &hdev->bdaddr);
conn->hdev = hdev;
conn->type = type;
conn->mode = HCI_CM_ACTIVE;
@@ -437,9 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
INIT_LIST_HEAD(&conn->chan_list);
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
- setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
- setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
- (unsigned long) conn);
+ INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept);
+ INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
atomic_set(&conn->refcnt, 0);
@@ -460,11 +430,9 @@ int hci_conn_del(struct hci_conn *conn)
BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle);
- del_timer(&conn->idle_timer);
-
cancel_delayed_work_sync(&conn->disc_work);
-
- del_timer(&conn->auto_accept_timer);
+ cancel_delayed_work_sync(&conn->auto_accept_work);
+ cancel_delayed_work_sync(&conn->idle_work);
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
@@ -518,6 +486,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
list_for_each_entry(d, &hci_dev_list, list) {
if (!test_bit(HCI_UP, &d->flags) ||
test_bit(HCI_RAW, &d->flags) ||
+ test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
d->dev_type != HCI_BREDR)
continue;
@@ -545,34 +514,124 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(hci_get_route);
+static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
+{
+ struct hci_conn *conn;
+
+ if (status == 0)
+ return;
+
+ BT_ERR("HCI request failed to create LE connection: status 0x%2.2x",
+ status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (!conn)
+ goto done;
+
+ conn->state = BT_CLOSED;
+
+ mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ status);
+
+ hci_proto_connect_cfm(conn, status);
+
+ hci_conn_del(conn);
+
+done:
+ hci_dev_unlock(hdev);
+}
+
+static int hci_create_le_conn(struct hci_conn *conn)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_le_create_conn cp;
+ struct hci_request req;
+ int err;
+
+ hci_req_init(&req, hdev);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.scan_interval = cpu_to_le16(hdev->le_scan_interval);
+ cp.scan_window = cpu_to_le16(hdev->le_scan_window);
+ bacpy(&cp.peer_addr, &conn->dst);
+ cp.peer_addr_type = conn->dst_type;
+ cp.own_address_type = conn->src_type;
+ cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
+ cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
+ cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
+ cp.min_ce_len = __constant_cpu_to_le16(0x0000);
+ cp.max_ce_len = __constant_cpu_to_le16(0x0000);
+
+ hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
+
+ err = hci_req_run(&req, create_le_conn_complete);
+ if (err) {
+ hci_conn_del(conn);
+ return err;
+ }
+
+ return 0;
+}
+
static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u8 auth_type)
{
- struct hci_conn *le;
+ struct hci_conn *conn;
+ int err;
- if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
+ if (test_bit(HCI_ADVERTISING, &hdev->flags))
return ERR_PTR(-ENOTSUPP);
- le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
- if (!le) {
- le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
- if (le)
- return ERR_PTR(-EBUSY);
+ /* Some devices send ATT messages as soon as the physical link is
+ * established. To be able to handle these ATT messages, the user-
+ * space first establishes the connection and then starts the pairing
+ * process.
+ *
+ * So if a hci_conn object already exists for the following connection
+ * attempt, we simply update pending_sec_level and auth_type fields
+ * and return the object found.
+ */
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+ if (conn) {
+ conn->pending_sec_level = sec_level;
+ conn->auth_type = auth_type;
+ goto done;
+ }
- le = hci_conn_add(hdev, LE_LINK, dst);
- if (!le)
- return ERR_PTR(-ENOMEM);
+ /* Since the controller supports only one LE connection attempt at a
+ * time, we return -EBUSY if there is any connection attempt running.
+ */
+ conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+ if (conn)
+ return ERR_PTR(-EBUSY);
- le->dst_type = bdaddr_to_le(dst_type);
- hci_le_create_connection(le);
- }
+ conn = hci_conn_add(hdev, LE_LINK, dst);
+ if (!conn)
+ return ERR_PTR(-ENOMEM);
- le->pending_sec_level = sec_level;
- le->auth_type = auth_type;
+ if (dst_type == BDADDR_LE_PUBLIC)
+ conn->dst_type = ADDR_LE_DEV_PUBLIC;
+ else
+ conn->dst_type = ADDR_LE_DEV_RANDOM;
- hci_conn_hold(le);
+ conn->src_type = hdev->own_addr_type;
- return le;
+ conn->state = BT_CONNECT;
+ conn->out = true;
+ conn->link_mode |= HCI_LM_MASTER;
+ conn->sec_level = BT_SECURITY_LOW;
+ conn->pending_sec_level = sec_level;
+ conn->auth_type = auth_type;
+
+ err = hci_create_le_conn(conn);
+ if (err)
+ return ERR_PTR(err);
+
+done:
+ hci_conn_hold(conn);
+ return conn;
}
static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
@@ -580,6 +639,9 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
{
struct hci_conn *acl;
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return ERR_PTR(-ENOTSUPP);
+
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (!acl) {
acl = hci_conn_add(hdev, ACL_LINK, dst);
@@ -846,8 +908,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
timer:
if (hdev->idle_timeout > 0)
- mod_timer(&conn->idle_timer,
- jiffies + msecs_to_jiffies(hdev->idle_timeout));
+ queue_delayed_work(hdev->workqueue, &conn->idle_work,
+ msecs_to_jiffies(hdev->idle_timeout));
}
/* Drop all connection on the device */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index fb7356fcfe51..6ccc4eb9e55e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -27,8 +27,9 @@
#include <linux/export.h>
#include <linux/idr.h>
-
#include <linux/rfkill.h>
+#include <linux/debugfs.h>
+#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -55,6 +56,586 @@ static void hci_notify(struct hci_dev *hdev, int event)
hci_sock_dev_event(hdev, event);
}
+/* ---- HCI debugfs entries ---- */
+
+static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ struct sk_buff *skb;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf)-1));
+ bool enable;
+ int err;
+
+ if (!test_bit(HCI_UP, &hdev->flags))
+ return -ENETDOWN;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+ return -EALREADY;
+
+ hci_req_lock(hdev);
+ if (enable)
+ skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
+ HCI_CMD_TIMEOUT);
+ else
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
+ HCI_CMD_TIMEOUT);
+ hci_req_unlock(hdev);
+
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ err = -bt_to_errno(skb->data[0]);
+ kfree_skb(skb);
+
+ if (err < 0)
+ return err;
+
+ change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+
+ return count;
+}
+
+static const struct file_operations dut_mode_fops = {
+ .open = simple_open,
+ .read = dut_mode_read,
+ .write = dut_mode_write,
+ .llseek = default_llseek,
+};
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ u8 p;
+
+ hci_dev_lock(hdev);
+ for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+ seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+ hdev->features[p][0], hdev->features[p][1],
+ hdev->features[p][2], hdev->features[p][3],
+ hdev->features[p][4], hdev->features[p][5],
+ hdev->features[p][6], hdev->features[p][7]);
+ }
+ if (lmp_le_capable(hdev))
+ seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+ "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ hdev->le_features[0], hdev->le_features[1],
+ hdev->le_features[2], hdev->le_features[3],
+ hdev->le_features[4], hdev->le_features[5],
+ hdev->le_features[6], hdev->le_features[7]);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+ .open = features_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+ struct bdaddr_list *b;
+
+ hci_dev_lock(hdev);
+ list_for_each_entry(b, &hdev->blacklist, list)
+ seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+ .open = blacklist_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+ struct bt_uuid *uuid;
+
+ hci_dev_lock(hdev);
+ list_for_each_entry(uuid, &hdev->uuids, list) {
+ u8 i, val[16];
+
+ /* The Bluetooth UUID values are stored in big endian,
+ * but with reversed byte order. So convert them into
+ * the right order for the %pUb modifier.
+ */
+ for (i = 0; i < 16; i++)
+ val[i] = uuid->uuid[15 - i];
+
+ seq_printf(f, "%pUb\n", val);
+ }
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+ .open = uuids_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+ struct discovery_state *cache = &hdev->discovery;
+ struct inquiry_entry *e;
+
+ hci_dev_lock(hdev);
+
+ list_for_each_entry(e, &cache->all, all) {
+ struct inquiry_data *data = &e->data;
+ seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+ &data->bdaddr,
+ data->pscan_rep_mode, data->pscan_period_mode,
+ data->pscan_mode, data->dev_class[2],
+ data->dev_class[1], data->dev_class[0],
+ __le16_to_cpu(data->clock_offset),
+ data->rssi, data->ssp_mode, e->timestamp);
+ }
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+ .open = inquiry_cache_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct list_head *p, *n;
+
+ hci_dev_lock(hdev);
+ list_for_each_safe(p, n, &hdev->link_keys) {
+ struct link_key *key = list_entry(p, struct link_key, list);
+ seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+ HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+ }
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+ .open = link_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+ .open = simple_open,
+ .read = use_debug_keys_read,
+ .llseek = default_llseek,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+
+ hci_dev_lock(hdev);
+ seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+ hdev->dev_class[1], hdev->dev_class[0]);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+ .open = dev_class_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->voice_setting;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+ NULL, "0x%4.4llx\n");
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ hdev->auto_accept_delay = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->auto_accept_delay;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+ auto_accept_delay_set, "%llu\n");
+
+static int ssp_debug_mode_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+ struct sk_buff *skb;
+ __u8 mode;
+ int err;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ if (!test_bit(HCI_UP, &hdev->flags))
+ return -ENETDOWN;
+
+ hci_req_lock(hdev);
+ mode = val;
+ skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
+ &mode, HCI_CMD_TIMEOUT);
+ hci_req_unlock(hdev);
+
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ err = -bt_to_errno(skb->data[0]);
+ kfree_skb(skb);
+
+ if (err < 0)
+ return err;
+
+ hci_dev_lock(hdev);
+ hdev->ssp_debug_mode = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int ssp_debug_mode_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->ssp_debug_mode;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
+ ssp_debug_mode_set, "%llu\n");
+
+static int idle_timeout_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val != 0 && (val < 500 || val > 3600000))
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->idle_timeout = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->idle_timeout;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+ idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->sniff_min_interval = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->sniff_min_interval;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+ sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->sniff_max_interval = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->sniff_max_interval;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+ sniff_max_interval_set, "%llu\n");
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+ struct hci_dev *hdev = f->private;
+
+ hci_dev_lock(hdev);
+ seq_printf(f, "%pMR\n", &hdev->static_addr);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+ .open = static_address_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int own_address_type_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->own_addr_type = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int own_address_type_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->own_addr_type;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
+ own_address_type_set, "%llu\n");
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+ struct list_head *p, *n;
+
+ hci_dev_lock(hdev);
+ list_for_each_safe(p, n, &hdev->link_keys) {
+ struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+ seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+ &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+ ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+ 8, ltk->rand, 16, ltk->val);
+ }
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+ .open = long_term_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->le_conn_min_interval = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->le_conn_min_interval;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+ conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+ struct hci_dev *hdev = data;
+
+ if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+ return -EINVAL;
+
+ hci_dev_lock(hdev);
+ hdev->le_conn_max_interval = val;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+ struct hci_dev *hdev = data;
+
+ hci_dev_lock(hdev);
+ *val = hdev->le_conn_max_interval;
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+ conn_max_interval_set, "%llu\n");
+
/* ---- HCI requests ---- */
static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -307,11 +888,23 @@ static void amp_init(struct hci_request *req)
/* Read Local Version */
hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+ /* Read Local Supported Commands */
+ hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
+
+ /* Read Local Supported Features */
+ hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+
/* Read Local AMP Info */
hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
/* Read Data Blk size */
hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
+
+ /* Read Flow Control Mode */
+ hci_req_add(req, HCI_OP_READ_FLOW_CONTROL_MODE, 0, NULL);
+
+ /* Read Location Data */
+ hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
}
static void hci_init1_req(struct hci_request *req, unsigned long opt)
@@ -341,6 +934,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt)
static void bredr_setup(struct hci_request *req)
{
+ struct hci_dev *hdev = req->hdev;
+
__le16 param;
__u8 flt_type;
@@ -356,6 +951,12 @@ static void bredr_setup(struct hci_request *req)
/* Read Voice Setting */
hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+ /* Read Number of Supported IAC */
+ hci_req_add(req, HCI_OP_READ_NUM_SUPPORTED_IAC, 0, NULL);
+
+ /* Read Current IAC LAP */
+ hci_req_add(req, HCI_OP_READ_CURRENT_IAC_LAP, 0, NULL);
+
/* Clear Event Filters */
flt_type = HCI_FLT_CLEAR_ALL;
hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
@@ -364,8 +965,10 @@ static void bredr_setup(struct hci_request *req)
param = __constant_cpu_to_le16(0x7d00);
hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
- /* Read page scan parameters */
- if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) {
+ /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2,
+ * but it does not support page scan related HCI commands.
+ */
+ if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) {
hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
}
@@ -519,6 +1122,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
if (lmp_bredr_capable(hdev))
bredr_setup(req);
+ else
+ clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
if (lmp_le_capable(hdev))
le_setup(req);
@@ -532,6 +1137,14 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (lmp_ssp_capable(hdev)) {
+ /* When SSP is available, then the host features page
+ * should also be available as well. However some
+ * controllers list the max_page as 0 as long as SSP
+ * has not been enabled. To achieve proper debugging
+ * output, force the minimum max_page to 1 at least.
+ */
+ hdev->max_page = 0x01;
+
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
u8 mode = 0x01;
hci_req_add(req, HCI_OP_WRITE_SSP_MODE,
@@ -607,6 +1220,34 @@ static void hci_set_le_support(struct hci_request *req)
&cp);
}
+static void hci_set_event_mask_page_2(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 events[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ /* If Connectionless Slave Broadcast master role is supported
+ * enable all necessary events for it.
+ */
+ if (hdev->features[2][0] & 0x01) {
+ events[1] |= 0x40; /* Triggered Clock Capture */
+ events[1] |= 0x80; /* Synchronization Train Complete */
+ events[2] |= 0x10; /* Slave Page Response Timeout */
+ events[2] |= 0x20; /* CSB Channel Map Change */
+ }
+
+ /* If Connectionless Slave Broadcast slave role is supported
+ * enable all necessary events for it.
+ */
+ if (hdev->features[2][0] & 0x02) {
+ events[2] |= 0x01; /* Synchronization Train Received */
+ events[2] |= 0x02; /* CSB Receive */
+ events[2] |= 0x04; /* CSB Timeout */
+ events[2] |= 0x08; /* Truncated Page Complete */
+ }
+
+ hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
+}
+
static void hci_init3_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
@@ -634,8 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
hci_setup_link_policy(req);
if (lmp_le_capable(hdev)) {
+ /* If the controller has a public BD_ADDR, then by
+ * default use that one. If this is a LE only
+ * controller without one, default to the random
+ * address.
+ */
+ if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+ hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+
hci_set_le_support(req);
- hci_update_ad(req);
}
/* Read features beyond page 1 if available */
@@ -648,6 +1298,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
}
}
+static void hci_init4_req(struct hci_request *req, unsigned long opt)
+{
+ struct hci_dev *hdev = req->hdev;
+
+ /* Set event mask page 2 if the HCI command for it is supported */
+ if (hdev->commands[22] & 0x04)
+ hci_set_event_mask_page_2(req);
+
+ /* Check for Synchronization Train support */
+ if (hdev->features[2][0] & 0x04)
+ hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
+}
+
static int __hci_init(struct hci_dev *hdev)
{
int err;
@@ -656,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev)
if (err < 0)
return err;
+ /* The Device Under Test (DUT) mode is special and available for
+ * all controller types. So just create it early on.
+ */
+ if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+ &dut_mode_fops);
+ }
+
/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
* BR/EDR/LE type controllers. AMP controllers only need the
* first stage init.
@@ -667,7 +1338,75 @@ static int __hci_init(struct hci_dev *hdev)
if (err < 0)
return err;
- return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+ err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
+ if (err < 0)
+ return err;
+
+ /* Only create debugfs entries during the initial setup
+ * phase and not every time the controller gets powered on.
+ */
+ if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+ return 0;
+
+ debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+ &features_fops);
+ debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+ &hdev->manufacturer);
+ debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+ debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+ debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+ &blacklist_fops);
+ debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+
+ if (lmp_bredr_capable(hdev)) {
+ debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
+ hdev, &inquiry_cache_fops);
+ debugfs_create_file("link_keys", 0400, hdev->debugfs,
+ hdev, &link_keys_fops);
+ debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+ hdev, &use_debug_keys_fops);
+ debugfs_create_file("dev_class", 0444, hdev->debugfs,
+ hdev, &dev_class_fops);
+ debugfs_create_file("voice_setting", 0444, hdev->debugfs,
+ hdev, &voice_setting_fops);
+ }
+
+ if (lmp_ssp_capable(hdev)) {
+ debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+ hdev, &auto_accept_delay_fops);
+ debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
+ hdev, &ssp_debug_mode_fops);
+ }
+
+ if (lmp_sniff_capable(hdev)) {
+ debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+ hdev, &idle_timeout_fops);
+ debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+ hdev, &sniff_min_interval_fops);
+ debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+ hdev, &sniff_max_interval_fops);
+ }
+
+ if (lmp_le_capable(hdev)) {
+ debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+ &hdev->le_white_list_size);
+ debugfs_create_file("static_address", 0444, hdev->debugfs,
+ hdev, &static_address_fops);
+ debugfs_create_file("own_address_type", 0644, hdev->debugfs,
+ hdev, &own_address_type_fops);
+ debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
+ hdev, &long_term_keys_fops);
+ debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
+ hdev, &conn_min_interval_fops);
+ debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
+ hdev, &conn_max_interval_fops);
+ }
+
+ return 0;
}
static void hci_scan_req(struct hci_request *req, unsigned long opt)
@@ -984,6 +1723,21 @@ int hci_inquiry(void __user *arg)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
+ if (hdev->dev_type != HCI_BREDR) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
hci_dev_lock(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
@@ -1043,100 +1797,10 @@ done:
return err;
}
-static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
+static int hci_dev_do_open(struct hci_dev *hdev)
{
- u8 ad_len = 0, flags = 0;
- size_t name_len;
-
- if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
- flags |= LE_AD_GENERAL;
-
- if (!lmp_bredr_capable(hdev))
- flags |= LE_AD_NO_BREDR;
-
- if (lmp_le_br_capable(hdev))
- flags |= LE_AD_SIM_LE_BREDR_CTRL;
-
- if (lmp_host_le_br_capable(hdev))
- flags |= LE_AD_SIM_LE_BREDR_HOST;
-
- if (flags) {
- BT_DBG("adv flags 0x%02x", flags);
-
- ptr[0] = 2;
- ptr[1] = EIR_FLAGS;
- ptr[2] = flags;
-
- ad_len += 3;
- ptr += 3;
- }
-
- if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
- ptr[0] = 2;
- ptr[1] = EIR_TX_POWER;
- ptr[2] = (u8) hdev->adv_tx_power;
-
- ad_len += 3;
- ptr += 3;
- }
-
- name_len = strlen(hdev->dev_name);
- if (name_len > 0) {
- size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
-
- if (name_len > max_len) {
- name_len = max_len;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, hdev->dev_name, name_len);
-
- ad_len += (name_len + 2);
- ptr += (name_len + 2);
- }
-
- return ad_len;
-}
-
-void hci_update_ad(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- struct hci_cp_le_set_adv_data cp;
- u8 len;
-
- if (!lmp_le_capable(hdev))
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- len = create_ad(hdev, cp.data);
-
- if (hdev->adv_data_len == len &&
- memcmp(cp.data, hdev->adv_data, len) == 0)
- return;
-
- memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
- hdev->adv_data_len = len;
-
- cp.length = len;
-
- hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
-}
-
-/* ---- HCI ioctl helpers ---- */
-
-int hci_dev_open(__u16 dev)
-{
- struct hci_dev *hdev;
int ret = 0;
- hdev = hci_dev_get(dev);
- if (!hdev)
- return -ENODEV;
-
BT_DBG("%s %p", hdev->name, hdev);
hci_req_lock(hdev);
@@ -1146,13 +1810,29 @@ int hci_dev_open(__u16 dev)
goto done;
}
- /* Check for rfkill but allow the HCI setup stage to proceed
- * (which in itself doesn't cause any RF activity).
- */
- if (test_bit(HCI_RFKILLED, &hdev->dev_flags) &&
- !test_bit(HCI_SETUP, &hdev->dev_flags)) {
- ret = -ERFKILL;
- goto done;
+ if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ /* Check for rfkill but allow the HCI setup stage to
+ * proceed (which in itself doesn't cause any RF activity).
+ */
+ if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+ ret = -ERFKILL;
+ goto done;
+ }
+
+ /* Check for valid public address or a configured static
+ * random adddress, but let the HCI setup proceed to
+ * be able to determine if there is a public address
+ * or not.
+ *
+ * This check is only valid for BR/EDR controllers
+ * since AMP controllers do not have an address.
+ */
+ if (hdev->dev_type == HCI_BREDR &&
+ !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+ !bacmp(&hdev->static_addr, BDADDR_ANY)) {
+ ret = -EADDRNOTAVAIL;
+ goto done;
+ }
}
if (test_bit(HCI_UP, &hdev->flags)) {
@@ -1172,16 +1852,11 @@ int hci_dev_open(__u16 dev)
ret = hdev->setup(hdev);
if (!ret) {
- /* Treat all non BR/EDR controllers as raw devices if
- * enable_hs is not set.
- */
- if (hdev->dev_type != HCI_BREDR && !enable_hs)
- set_bit(HCI_RAW, &hdev->flags);
-
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
set_bit(HCI_RAW, &hdev->flags);
- if (!test_bit(HCI_RAW, &hdev->flags))
+ if (!test_bit(HCI_RAW, &hdev->flags) &&
+ !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
ret = __hci_init(hdev);
}
@@ -1192,7 +1867,8 @@ int hci_dev_open(__u16 dev)
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
- mgmt_valid_hdev(hdev)) {
+ !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+ hdev->dev_type == HCI_BREDR) {
hci_dev_lock(hdev);
mgmt_powered(hdev, 1);
hci_dev_unlock(hdev);
@@ -1220,10 +1896,41 @@ int hci_dev_open(__u16 dev)
done:
hci_req_unlock(hdev);
- hci_dev_put(hdev);
return ret;
}
+/* ---- HCI ioctl helpers ---- */
+
+int hci_dev_open(__u16 dev)
+{
+ struct hci_dev *hdev;
+ int err;
+
+ hdev = hci_dev_get(dev);
+ if (!hdev)
+ return -ENODEV;
+
+ /* We need to ensure that no other power on/off work is pending
+ * before proceeding to call hci_dev_do_open. This is
+ * particularly important if the setup procedure has not yet
+ * completed.
+ */
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+ cancel_delayed_work(&hdev->power_off);
+
+ /* After this call it is guaranteed that the setup procedure
+ * has finished. This means that error conditions like RFKILL
+ * or no valid public or static random address apply.
+ */
+ flush_workqueue(hdev->req_workqueue);
+
+ err = hci_dev_do_open(hdev);
+
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int hci_dev_do_close(struct hci_dev *hdev)
{
BT_DBG("%s %p", hdev->name, hdev);
@@ -1247,6 +1954,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = 0;
clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
}
if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
@@ -1268,6 +1976,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
skb_queue_purge(&hdev->cmd_q);
atomic_set(&hdev->cmd_cnt, 1);
if (!test_bit(HCI_RAW, &hdev->flags) &&
+ !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
set_bit(HCI_INIT, &hdev->flags);
__hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
@@ -1300,15 +2009,16 @@ static int hci_dev_do_close(struct hci_dev *hdev)
hdev->flags = 0;
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
- if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
- mgmt_valid_hdev(hdev)) {
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 0);
- hci_dev_unlock(hdev);
+ if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+ if (hdev->dev_type == HCI_BREDR) {
+ hci_dev_lock(hdev);
+ mgmt_powered(hdev, 0);
+ hci_dev_unlock(hdev);
+ }
}
/* Controller radio is available but is currently powered down */
- hdev->amp_status = 0;
+ hdev->amp_status = AMP_STATUS_POWERED_DOWN;
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@@ -1328,11 +2038,17 @@ int hci_dev_close(__u16 dev)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work(&hdev->power_off);
err = hci_dev_do_close(hdev);
+done:
hci_dev_put(hdev);
return err;
}
@@ -1348,8 +2064,15 @@ int hci_dev_reset(__u16 dev)
hci_req_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags))
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ ret = -ENETDOWN;
goto done;
+ }
+
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ ret = -EBUSY;
+ goto done;
+ }
/* Drop queues */
skb_queue_purge(&hdev->rx_q);
@@ -1384,10 +2107,15 @@ int hci_dev_reset_stat(__u16 dev)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+done:
hci_dev_put(hdev);
-
return ret;
}
@@ -1404,6 +2132,21 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
if (!hdev)
return -ENODEV;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EBUSY;
+ goto done;
+ }
+
+ if (hdev->dev_type != HCI_BREDR) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
switch (cmd) {
case HCISETAUTH:
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
@@ -1462,6 +2205,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
break;
}
+done:
hci_dev_put(hdev);
return err;
}
@@ -1534,7 +2278,7 @@ int hci_get_dev_info(void __user *arg)
strcpy(di.name, hdev->name);
di.bdaddr = hdev->bdaddr;
- di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
+ di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
di.flags = hdev->flags;
di.pkt_type = hdev->pkt_type;
if (lmp_bredr_capable(hdev)) {
@@ -1570,6 +2314,9 @@ static int hci_rfkill_set_block(void *data, bool blocked)
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+ return -EBUSY;
+
if (blocked) {
set_bit(HCI_RFKILLED, &hdev->dev_flags);
if (!test_bit(HCI_SETUP, &hdev->dev_flags))
@@ -1592,13 +2339,20 @@ static void hci_power_on(struct work_struct *work)
BT_DBG("%s", hdev->name);
- err = hci_dev_open(hdev->id);
+ err = hci_dev_do_open(hdev);
if (err < 0) {
mgmt_set_powered_failed(hdev, err);
return;
}
- if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
+ /* During the HCI setup phase, a few error conditions are
+ * ignored and they need to be checked now. If they are still
+ * valid, it is important to turn the device back off.
+ */
+ if (test_bit(HCI_RFKILLED, &hdev->dev_flags) ||
+ (hdev->dev_type == HCI_BREDR &&
+ !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
+ !bacmp(&hdev->static_addr, BDADDR_ANY))) {
clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
hci_dev_do_close(hdev);
} else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
@@ -1623,19 +2377,12 @@ static void hci_power_off(struct work_struct *work)
static void hci_discov_off(struct work_struct *work)
{
struct hci_dev *hdev;
- u8 scan = SCAN_PAGE;
hdev = container_of(work, struct hci_dev, discov_off.work);
BT_DBG("%s", hdev->name);
- hci_dev_lock(hdev);
-
- hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
-
- hdev->discov_timeout = 0;
-
- hci_dev_unlock(hdev);
+ mgmt_discoverable_timeout(hdev);
}
int hci_uuids_clear(struct hci_dev *hdev)
@@ -1958,13 +2705,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
return 0;
}
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *b;
- list_for_each_entry(b, &hdev->blacklist, list)
- if (bacmp(bdaddr, &b->bdaddr) == 0)
+ list_for_each_entry(b, &hdev->blacklist, list) {
+ if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
return b;
+ }
return NULL;
}
@@ -1974,9 +2723,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
struct list_head *p, *n;
list_for_each_safe(p, n, &hdev->blacklist) {
- struct bdaddr_list *b;
-
- b = list_entry(p, struct bdaddr_list, list);
+ struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
list_del(p);
kfree(b);
@@ -1989,10 +2736,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
- if (bacmp(bdaddr, BDADDR_ANY) == 0)
+ if (!bacmp(bdaddr, BDADDR_ANY))
return -EBADF;
- if (hci_blacklist_lookup(hdev, bdaddr))
+ if (hci_blacklist_lookup(hdev, bdaddr, type))
return -EEXIST;
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
@@ -2000,6 +2747,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
return -ENOMEM;
bacpy(&entry->bdaddr, bdaddr);
+ entry->bdaddr_type = type;
list_add(&entry->list, &hdev->blacklist);
@@ -2010,10 +2758,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
- if (bacmp(bdaddr, BDADDR_ANY) == 0)
+ if (!bacmp(bdaddr, BDADDR_ANY))
return hci_blacklist_clear(hdev);
- entry = hci_blacklist_lookup(hdev, bdaddr);
+ entry = hci_blacklist_lookup(hdev, bdaddr, type);
if (!entry)
return -ENOENT;
@@ -2111,13 +2859,19 @@ struct hci_dev *hci_alloc_dev(void)
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
- hdev->io_capability = 0x03; /* No Input No Output */
+ hdev->num_iac = 0x01; /* One IAC support is mandatory */
+ hdev->io_capability = 0x03; /* No Input No Output */
hdev->inq_tx_power = HCI_TX_POWER_INVALID;
hdev->adv_tx_power = HCI_TX_POWER_INVALID;
hdev->sniff_max_interval = 800;
hdev->sniff_min_interval = 80;
+ hdev->le_scan_interval = 0x0060;
+ hdev->le_scan_window = 0x0030;
+ hdev->le_conn_min_interval = 0x0028;
+ hdev->le_conn_max_interval = 0x0038;
+
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
@@ -2206,7 +2960,12 @@ int hci_register_dev(struct hci_dev *hdev)
goto err;
}
- error = hci_add_sysfs(hdev);
+ if (!IS_ERR_OR_NULL(bt_debugfs))
+ hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
+
+ dev_set_name(&hdev->dev, "%s", hdev->name);
+
+ error = device_add(&hdev->dev);
if (error < 0)
goto err_wqueue;
@@ -2224,9 +2983,14 @@ int hci_register_dev(struct hci_dev *hdev)
set_bit(HCI_RFKILLED, &hdev->dev_flags);
set_bit(HCI_SETUP, &hdev->dev_flags);
+ set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
- if (hdev->dev_type != HCI_AMP)
- set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+ if (hdev->dev_type == HCI_BREDR) {
+ /* Assume BR/EDR support until proven otherwise (such as
+ * through reading supported features during init.
+ */
+ set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+ }
write_lock(&hci_dev_list_lock);
list_add(&hdev->list, &hci_dev_list);
@@ -2289,7 +3053,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
rfkill_destroy(hdev->rfkill);
}
- hci_del_sysfs(hdev);
+ device_del(&hdev->dev);
+
+ debugfs_remove_recursive(hdev->debugfs);
destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue);
@@ -2325,9 +3091,8 @@ int hci_resume_dev(struct hci_dev *hdev)
EXPORT_SYMBOL(hci_resume_dev);
/* Receive frame from HCI drivers */
-int hci_recv_frame(struct sk_buff *skb)
+int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
&& !test_bit(HCI_INIT, &hdev->flags))) {
kfree_skb(skb);
@@ -2386,7 +3151,6 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
scb->expect = hlen;
scb->pkt_type = type;
- skb->dev = (void *) hdev;
hdev->reassembly[index] = skb;
}
@@ -2446,7 +3210,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
/* Complete frame */
bt_cb(skb)->pkt_type = type;
- hci_recv_frame(skb);
+ hci_recv_frame(hdev, skb);
hdev->reassembly[index] = NULL;
return remain;
@@ -2537,15 +3301,8 @@ int hci_unregister_cb(struct hci_cb *cb)
}
EXPORT_SYMBOL(hci_unregister_cb);
-static int hci_send_frame(struct sk_buff *skb)
+static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
- if (!hdev) {
- kfree_skb(skb);
- return -ENODEV;
- }
-
BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
/* Time stamp */
@@ -2562,7 +3319,8 @@ static int hci_send_frame(struct sk_buff *skb)
/* Get rid of skb owner, prior to sending to the driver. */
skb_orphan(skb);
- return hdev->send(skb);
+ if (hdev->send(hdev, skb) < 0)
+ BT_ERR("%s sending frame failed", hdev->name);
}
void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
@@ -2625,7 +3383,6 @@ static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
BT_DBG("skb len %d", skb->len);
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
- skb->dev = (void *) hdev;
return skb;
}
@@ -2769,7 +3526,6 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
do {
skb = list; list = list->next;
- skb->dev = (void *) hdev;
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags);
@@ -2788,8 +3544,6 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
- skb->dev = (void *) hdev;
-
hci_queue_acl(chan, &chan->data_q, skb, flags);
queue_work(hdev->workqueue, &hdev->tx_work);
@@ -2810,7 +3564,6 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
skb_reset_transport_header(skb);
memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE);
- skb->dev = (void *) hdev;
bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb);
@@ -3075,7 +3828,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
hci_conn_enter_active_mode(chan->conn,
bt_cb(skb)->force_active);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
hdev->acl_last_tx = jiffies;
hdev->acl_cnt--;
@@ -3127,7 +3880,7 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
hci_conn_enter_active_mode(chan->conn,
bt_cb(skb)->force_active);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
hdev->acl_last_tx = jiffies;
hdev->block_cnt -= blocks;
@@ -3180,7 +3933,7 @@ static void hci_sched_sco(struct hci_dev *hdev)
while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
conn->sent++;
if (conn->sent == ~0)
@@ -3204,7 +3957,7 @@ static void hci_sched_esco(struct hci_dev *hdev)
&quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
conn->sent++;
if (conn->sent == ~0)
@@ -3246,7 +3999,7 @@ static void hci_sched_le(struct hci_dev *hdev)
skb = skb_dequeue(&chan->data_q);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
hdev->le_last_tx = jiffies;
cnt--;
@@ -3272,19 +4025,17 @@ static void hci_tx_work(struct work_struct *work)
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
hdev->sco_cnt, hdev->le_cnt);
- /* Schedule queues and send stuff to HCI driver */
-
- hci_sched_acl(hdev);
-
- hci_sched_sco(hdev);
-
- hci_sched_esco(hdev);
-
- hci_sched_le(hdev);
+ if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ /* Schedule queues and send stuff to HCI driver */
+ hci_sched_acl(hdev);
+ hci_sched_sco(hdev);
+ hci_sched_esco(hdev);
+ hci_sched_le(hdev);
+ }
/* Send next queued raw (unknown type) packet */
while ((skb = skb_dequeue(&hdev->raw_q)))
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
}
/* ----- HCI RX task (incoming data processing) ----- */
@@ -3471,7 +4222,8 @@ static void hci_rx_work(struct work_struct *work)
hci_send_to_sock(hdev, skb);
}
- if (test_bit(HCI_RAW, &hdev->flags)) {
+ if (test_bit(HCI_RAW, &hdev->flags) ||
+ test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
kfree_skb(skb);
continue;
}
@@ -3526,10 +4278,10 @@ static void hci_cmd_work(struct work_struct *work)
kfree_skb(hdev->sent_cmd);
- hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
+ hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
if (hdev->sent_cmd) {
atomic_dec(&hdev->cmd_cnt);
- hci_send_frame(skb);
+ hci_send_frame(hdev, skb);
if (test_bit(HCI_RESET, &hdev->flags))
del_timer(&hdev->cmd_timer);
else
@@ -3541,15 +4293,3 @@ static void hci_cmd_work(struct work_struct *work)
}
}
}
-
-u8 bdaddr_to_le(u8 bdaddr_type)
-{
- switch (bdaddr_type) {
- case BDADDR_LE_PUBLIC:
- return ADDR_LE_DEV_PUBLIC;
-
- default:
- /* Fallback to LE Random address type */
- return ADDR_LE_DEV_RANDOM;
- }
-}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 8db3e89fae35..5935f748c0f9 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -29,8 +29,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "a2mp.h"
+#include "amp.h"
/* Handle HCI Event packets */
@@ -194,6 +195,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
hdev->adv_data_len = 0;
+
+ memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data));
+ hdev->scan_rsp_data_len = 0;
+
+ hdev->ssp_debug_mode = 0;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -297,6 +303,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
goto done;
}
+ /* We need to ensure that we set this back on if someone changed
+ * the scan mode through a raw HCI socket.
+ */
+ set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
@@ -304,11 +315,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
set_bit(HCI_ISCAN, &hdev->flags);
if (!old_iscan)
mgmt_discoverable(hdev, 1);
- if (hdev->discov_timeout > 0) {
- int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
- queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- to);
- }
} else if (old_iscan)
mgmt_discoverable(hdev, 0);
@@ -412,6 +418,21 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev,
hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
}
+static void hci_cc_read_num_supported_iac(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_num_supported_iac *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (rp->status)
+ return;
+
+ hdev->num_iac = rp->num_iac;
+
+ BT_DBG("%s num iac %d", hdev->name, hdev->num_iac);
+}
+
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -449,14 +470,13 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
if (rp->status)
return;
- hdev->hci_ver = rp->hci_ver;
- hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
- hdev->lmp_ver = rp->lmp_ver;
- hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
- hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
-
- BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name,
- hdev->manufacturer, hdev->hci_ver, hdev->hci_rev);
+ if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ hdev->hci_ver = rp->hci_ver;
+ hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
+ hdev->lmp_ver = rp->lmp_ver;
+ hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
+ hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
+ }
}
static void hci_cc_read_local_commands(struct hci_dev *hdev,
@@ -536,7 +556,8 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
if (rp->status)
return;
- hdev->max_page = rp->max_page;
+ if (hdev->max_page < rp->max_page)
+ hdev->max_page = rp->max_page;
if (rp->page < HCI_MAX_PAGES)
memcpy(hdev->features[rp->page], rp->features, 8);
@@ -913,17 +934,9 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
if (!status) {
if (*sent)
- set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+ set_bit(HCI_ADVERTISING, &hdev->dev_flags);
else
- clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
- }
-
- if (!test_bit(HCI_INIT, &hdev->flags)) {
- struct hci_request req;
-
- hci_req_init(&req, hdev);
- hci_update_ad(&req);
- hci_req_run(&req, NULL);
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
}
hci_dev_unlock(hdev);
@@ -994,20 +1007,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
return;
if (!status) {
- if (sent->le)
+ if (sent->le) {
hdev->features[1][0] |= LMP_HOST_LE;
- else
+ set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+ } else {
hdev->features[1][0] &= ~LMP_HOST_LE;
+ clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ }
if (sent->simul)
hdev->features[1][0] |= LMP_HOST_LE_BREDR;
else
hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
}
-
- if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
- !test_bit(HCI_INIT, &hdev->flags))
- mgmt_le_enable_complete(hdev, sent->le, status);
}
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
@@ -1291,9 +1304,11 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
goto unlock;
if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
- struct hci_cp_auth_requested cp;
- cp.handle = __cpu_to_le16(conn->handle);
- hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
+ struct hci_cp_auth_requested auth_cp;
+
+ auth_cp.handle = __cpu_to_le16(conn->handle);
+ hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
+ sizeof(auth_cp), &auth_cp);
}
unlock:
@@ -1465,33 +1480,6 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
hci_dev_unlock(hdev);
}
-static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
-{
- struct hci_conn *conn;
-
- BT_DBG("%s status 0x%2.2x", hdev->name, status);
-
- if (status) {
- hci_dev_lock(hdev);
-
- conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
- if (!conn) {
- hci_dev_unlock(hdev);
- return;
- }
-
- BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn);
-
- conn->state = BT_CLOSED;
- mgmt_connect_failed(hdev, &conn->dst, conn->type,
- conn->dst_type, status);
- hci_proto_connect_cfm(conn, status);
- hci_conn_del(conn);
-
- hci_dev_unlock(hdev);
- }
-}
-
static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
{
struct hci_cp_create_phy_link *cp;
@@ -1706,7 +1694,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
&flags);
if ((mask & HCI_LM_ACCEPT) &&
- !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
+ !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) {
/* Connection accepted */
struct inquiry_entry *ie;
struct hci_conn *conn;
@@ -1821,10 +1809,25 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
if (ev->status == 0) {
- if (conn->type == ACL_LINK && conn->flush_key)
+ u8 type = conn->type;
+
+ if (type == ACL_LINK && conn->flush_key)
hci_remove_link_key(hdev, &conn->dst);
hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
+
+ /* Re-enable advertising if necessary, since it might
+ * have been disabled by the connection. From the
+ * HCI_LE_Set_Advertise_Enable command description in
+ * the core specification (v4.0):
+ * "The Controller shall continue advertising until the Host
+ * issues an LE_Set_Advertise_Enable command with
+ * Advertising_Enable set to 0x00 (Advertising is disabled)
+ * or until a connection is created or until the Advertising
+ * is timed out due to Directed Advertising."
+ */
+ if (type == LE_LINK)
+ mgmt_reenable_advertising(hdev);
}
unlock:
@@ -2139,6 +2142,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_write_voice_setting(hdev, skb);
break;
+ case HCI_OP_READ_NUM_SUPPORTED_IAC:
+ hci_cc_read_num_supported_iac(hdev, skb);
+ break;
+
case HCI_OP_WRITE_SSP_MODE:
hci_cc_write_ssp_mode(hdev, skb);
break;
@@ -2342,10 +2349,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_disconnect(hdev, ev->status);
break;
- case HCI_OP_LE_CREATE_CONN:
- hci_cs_le_create_conn(hdev, ev->status);
- break;
-
case HCI_OP_CREATE_PHY_LINK:
hci_cs_create_phylink(hdev, ev->status);
break;
@@ -2548,7 +2551,6 @@ static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
if (conn) {
conn->mode = ev->mode;
- conn->interval = __le16_to_cpu(ev->interval);
if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND,
&conn->flags)) {
@@ -2930,6 +2932,23 @@ unlock:
hci_dev_unlock(hdev);
}
+static inline size_t eir_get_length(u8 *eir, size_t eir_len)
+{
+ size_t parsed = 0;
+
+ while (parsed < eir_len) {
+ u8 field_len = eir[0];
+
+ if (field_len == 0)
+ return parsed;
+
+ parsed += field_len + 1;
+ eir += field_len + 1;
+ }
+
+ return eir_len;
+}
+
static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -3170,7 +3189,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
if (hdev->auto_accept_delay > 0) {
int delay = msecs_to_jiffies(hdev->auto_accept_delay);
- mod_timer(&conn->auto_accept_timer, jiffies + delay);
+ queue_delayed_work(conn->hdev->workqueue,
+ &conn->auto_accept_work, delay);
goto unlock;
}
@@ -3485,6 +3505,17 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->dst_type = ev->bdaddr_type;
+ /* The advertising parameters for own address type
+ * define which source address and source address
+ * type this connections has.
+ */
+ if (bacmp(&conn->src, BDADDR_ANY)) {
+ conn->src_type = ADDR_LE_DEV_PUBLIC;
+ } else {
+ bacpy(&conn->src, &hdev->static_addr);
+ conn->src_type = ADDR_LE_DEV_RANDOM;
+ }
+
if (ev->role == LE_CONN_ROLE_MASTER) {
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
@@ -3640,8 +3671,8 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
skb_pull(skb, HCI_EVENT_HDR_SIZE);
if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
- struct hci_command_hdr *hdr = (void *) hdev->sent_cmd->data;
- u16 opcode = __le16_to_cpu(hdr->opcode);
+ struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
+ u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
hci_req_cmd_complete(hdev, opcode, 0);
}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 9bd7d959e384..71f0be173080 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -66,6 +66,46 @@ static struct bt_sock_list hci_sk_list = {
.lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock)
};
+static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
+{
+ struct hci_filter *flt;
+ int flt_type, flt_event;
+
+ /* Apply filter */
+ flt = &hci_pi(sk)->filter;
+
+ if (bt_cb(skb)->pkt_type == HCI_VENDOR_PKT)
+ flt_type = 0;
+ else
+ flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
+
+ if (!test_bit(flt_type, &flt->type_mask))
+ return true;
+
+ /* Extra filter for event packets only */
+ if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT)
+ return false;
+
+ flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
+
+ if (!hci_test_bit(flt_event, &flt->event_mask))
+ return true;
+
+ /* Check filter only when opcode is set */
+ if (!flt->opcode)
+ return false;
+
+ if (flt_event == HCI_EV_CMD_COMPLETE &&
+ flt->opcode != get_unaligned((__le16 *)(skb->data + 3)))
+ return true;
+
+ if (flt_event == HCI_EV_CMD_STATUS &&
+ flt->opcode != get_unaligned((__le16 *)(skb->data + 4)))
+ return true;
+
+ return false;
+}
+
/* Send frame to RAW socket */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -77,7 +117,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
read_lock(&hci_sk_list.lock);
sk_for_each(sk, &hci_sk_list.head) {
- struct hci_filter *flt;
struct sk_buff *nskb;
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
@@ -87,31 +126,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
if (skb->sk == sk)
continue;
- if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
- continue;
-
- /* Apply filter */
- flt = &hci_pi(sk)->filter;
-
- if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ?
- 0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS),
- &flt->type_mask))
- continue;
-
- if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) {
- int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
-
- if (!hci_test_bit(evt, &flt->event_mask))
+ if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
+ if (is_filtered_packet(sk, skb))
continue;
-
- if (flt->opcode &&
- ((evt == HCI_EV_CMD_COMPLETE &&
- flt->opcode !=
- get_unaligned((__le16 *)(skb->data + 3))) ||
- (evt == HCI_EV_CMD_STATUS &&
- flt->opcode !=
- get_unaligned((__le16 *)(skb->data + 4)))))
+ } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+ if (!bt_cb(skb)->incoming)
+ continue;
+ if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+ bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+ bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
continue;
+ } else {
+ /* Don't send frame to other channel types */
+ continue;
}
if (!skb_copy) {
@@ -360,7 +387,6 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
__net_timestamp(skb);
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- skb->dev = (void *) hdev;
hci_send_to_sock(hdev, skb);
kfree_skb(skb);
}
@@ -426,6 +452,12 @@ static int hci_sock_release(struct socket *sock)
bt_sock_unlink(&hci_sk_list, sk);
if (hdev) {
+ if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+ mgmt_index_added(hdev);
+ clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+ hci_dev_close(hdev->id);
+ }
+
atomic_dec(&hdev->promisc);
hci_dev_put(hdev);
}
@@ -449,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_add(hdev, &bdaddr, 0);
+ err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
hci_dev_unlock(hdev);
@@ -466,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_del(hdev, &bdaddr, 0);
+ err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
hci_dev_unlock(hdev);
@@ -482,6 +514,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!hdev)
return -EBADFD;
+ if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+ return -EBUSY;
+
+ if (hdev->dev_type != HCI_BREDR)
+ return -EOPNOTSUPP;
+
switch (cmd) {
case HCISETRAW:
if (!capable(CAP_NET_ADMIN))
@@ -512,23 +550,29 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return hci_sock_blacklist_del(hdev, (void __user *) arg);
-
- default:
- if (hdev->ioctl)
- return hdev->ioctl(hdev, cmd, arg);
- return -EINVAL;
}
+
+ return -ENOIOCTLCMD;
}
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
- struct sock *sk = sock->sk;
void __user *argp = (void __user *) arg;
+ struct sock *sk = sock->sk;
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
+ lock_sock(sk);
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
+ err = -EBADFD;
+ goto done;
+ }
+
+ release_sock(sk);
+
switch (cmd) {
case HCIGETDEVLIST:
return hci_get_dev_list(argp);
@@ -573,13 +617,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCIINQUIRY:
return hci_inquiry(argp);
-
- default:
- lock_sock(sk);
- err = hci_sock_bound_ioctl(sk, cmd, arg);
- release_sock(sk);
- return err;
}
+
+ lock_sock(sk);
+
+ err = hci_sock_bound_ioctl(sk, cmd, arg);
+
+done:
+ release_sock(sk);
+ return err;
}
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
@@ -629,6 +675,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
hci_pi(sk)->hdev = hdev;
break;
+ case HCI_CHANNEL_USER:
+ if (hci_pi(sk)->hdev) {
+ err = -EALREADY;
+ goto done;
+ }
+
+ if (haddr.hci_dev == HCI_DEV_NONE) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto done;
+ }
+
+ hdev = hci_dev_get(haddr.hci_dev);
+ if (!hdev) {
+ err = -ENODEV;
+ goto done;
+ }
+
+ if (test_bit(HCI_UP, &hdev->flags) ||
+ test_bit(HCI_INIT, &hdev->flags) ||
+ test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ err = -EBUSY;
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = -EUSERS;
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ mgmt_index_removed(hdev);
+
+ err = hci_dev_open(hdev->id);
+ if (err) {
+ clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+ hci_dev_put(hdev);
+ goto done;
+ }
+
+ atomic_inc(&hdev->promisc);
+
+ hci_pi(sk)->hdev = hdev;
+ break;
+
case HCI_CHANNEL_CONTROL:
if (haddr.hci_dev != HCI_DEV_NONE) {
err = -EINVAL;
@@ -677,22 +773,30 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
{
struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
struct sock *sk = sock->sk;
- struct hci_dev *hdev = hci_pi(sk)->hdev;
+ struct hci_dev *hdev;
+ int err = 0;
BT_DBG("sock %p sk %p", sock, sk);
- if (!hdev)
- return -EBADFD;
+ if (peer)
+ return -EOPNOTSUPP;
lock_sock(sk);
+ hdev = hci_pi(sk)->hdev;
+ if (!hdev) {
+ err = -EBADFD;
+ goto done;
+ }
+
*addr_len = sizeof(*haddr);
haddr->hci_family = AF_BLUETOOTH;
haddr->hci_dev = hdev->id;
- haddr->hci_channel= 0;
+ haddr->hci_channel= hci_pi(sk)->channel;
+done:
release_sock(sk);
- return 0;
+ return err;
}
static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
@@ -767,6 +871,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
case HCI_CHANNEL_RAW:
hci_sock_cmsg(sk, msg, skb);
break;
+ case HCI_CHANNEL_USER:
case HCI_CHANNEL_CONTROL:
case HCI_CHANNEL_MONITOR:
sock_recv_timestamp(msg, sk, skb);
@@ -801,6 +906,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
+ case HCI_CHANNEL_USER:
break;
case HCI_CHANNEL_CONTROL:
err = mgmt_control(sk, msg, len);
@@ -835,9 +941,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
skb_pull(skb, 1);
- skb->dev = (void *) hdev;
- if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+ if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
+ bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
u16 opcode = get_unaligned_le16(skb->data);
u16 ogf = hci_opcode_ogf(opcode);
u16 ocf = hci_opcode_ocf(opcode);
@@ -868,6 +974,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
goto drop;
}
+ if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
+ bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
+ bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+ bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+ err = -EINVAL;
+ goto drop;
+ }
+
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
}
@@ -895,7 +1009,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
lock_sock(sk);
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
+ err = -EBADFD;
goto done;
}
@@ -981,7 +1095,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
lock_sock(sk);
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
+ err = -EBADFD;
goto done;
}
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index edf623a29043..0b61250cfdf9 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -1,17 +1,12 @@
/* Bluetooth HCI driver model support. */
-#include <linux/debugfs.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
static struct class *bt_class;
-struct dentry *bt_debugfs;
-EXPORT_SYMBOL_GPL(bt_debugfs);
-
static inline char *link_typetostr(int type)
{
switch (type) {
@@ -42,29 +37,15 @@ static ssize_t show_link_address(struct device *dev,
return sprintf(buf, "%pMR\n", &conn->dst);
}
-static ssize_t show_link_features(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_conn *conn = to_hci_conn(dev);
-
- return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
- conn->features[0][0], conn->features[0][1],
- conn->features[0][2], conn->features[0][3],
- conn->features[0][4], conn->features[0][5],
- conn->features[0][6], conn->features[0][7]);
-}
-
#define LINK_ATTR(_name, _mode, _show, _store) \
struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
-static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);
static struct attribute *bt_link_attrs[] = {
&link_attr_type.attr,
&link_attr_address.attr,
- &link_attr_features.attr,
NULL
};
@@ -150,28 +131,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
hci_dev_put(hdev);
}
-static inline char *host_bustostr(int bus)
-{
- switch (bus) {
- case HCI_VIRTUAL:
- return "VIRTUAL";
- case HCI_USB:
- return "USB";
- case HCI_PCCARD:
- return "PCCARD";
- case HCI_UART:
- return "UART";
- case HCI_RS232:
- return "RS232";
- case HCI_PCI:
- return "PCI";
- case HCI_SDIO:
- return "SDIO";
- default:
- return "UNKNOWN";
- }
-}
-
static inline char *host_typetostr(int type)
{
switch (type) {
@@ -184,13 +143,6 @@ static inline char *host_typetostr(int type)
}
}
-static ssize_t show_bus(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%s\n", host_bustostr(hdev->bus));
-}
-
static ssize_t show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -212,14 +164,6 @@ static ssize_t show_name(struct device *dev,
return sprintf(buf, "%s\n", name);
}
-static ssize_t show_class(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
- hdev->dev_class[1], hdev->dev_class[0]);
-}
-
static ssize_t show_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -227,150 +171,14 @@ static ssize_t show_address(struct device *dev,
return sprintf(buf, "%pMR\n", &hdev->bdaddr);
}
-static ssize_t show_features(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
-
- return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
- hdev->features[0][0], hdev->features[0][1],
- hdev->features[0][2], hdev->features[0][3],
- hdev->features[0][4], hdev->features[0][5],
- hdev->features[0][6], hdev->features[0][7]);
-}
-
-static ssize_t show_manufacturer(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->manufacturer);
-}
-
-static ssize_t show_hci_version(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->hci_ver);
-}
-
-static ssize_t show_hci_revision(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->hci_rev);
-}
-
-static ssize_t show_idle_timeout(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->idle_timeout);
-}
-
-static ssize_t store_idle_timeout(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- unsigned int val;
- int rv;
-
- rv = kstrtouint(buf, 0, &val);
- if (rv < 0)
- return rv;
-
- if (val != 0 && (val < 500 || val > 3600000))
- return -EINVAL;
-
- hdev->idle_timeout = val;
-
- return count;
-}
-
-static ssize_t show_sniff_max_interval(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->sniff_max_interval);
-}
-
-static ssize_t store_sniff_max_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- u16 val;
- int rv;
-
- rv = kstrtou16(buf, 0, &val);
- if (rv < 0)
- return rv;
-
- if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
- return -EINVAL;
-
- hdev->sniff_max_interval = val;
-
- return count;
-}
-
-static ssize_t show_sniff_min_interval(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%d\n", hdev->sniff_min_interval);
-}
-
-static ssize_t store_sniff_min_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hci_dev *hdev = to_hci_dev(dev);
- u16 val;
- int rv;
-
- rv = kstrtou16(buf, 0, &val);
- if (rv < 0)
- return rv;
-
- if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
- return -EINVAL;
-
- hdev->sniff_min_interval = val;
-
- return count;
-}
-
-static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL);
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
-static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
-static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
-static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
-static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
-
-static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
- show_idle_timeout, store_idle_timeout);
-static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
- show_sniff_max_interval, store_sniff_max_interval);
-static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
- show_sniff_min_interval, store_sniff_min_interval);
static struct attribute *bt_host_attrs[] = {
- &dev_attr_bus.attr,
&dev_attr_type.attr,
&dev_attr_name.attr,
- &dev_attr_class.attr,
&dev_attr_address.attr,
- &dev_attr_features.attr,
- &dev_attr_manufacturer.attr,
- &dev_attr_hci_version.attr,
- &dev_attr_hci_revision.attr,
- &dev_attr_idle_timeout.attr,
- &dev_attr_sniff_max_interval.attr,
- &dev_attr_sniff_min_interval.attr,
NULL
};
@@ -396,141 +204,6 @@ static struct device_type bt_host = {
.release = bt_host_release,
};
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct discovery_state *cache = &hdev->discovery;
- struct inquiry_entry *e;
-
- hci_dev_lock(hdev);
-
- list_for_each_entry(e, &cache->all, all) {
- struct inquiry_data *data = &e->data;
- seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
- &data->bdaddr,
- data->pscan_rep_mode, data->pscan_period_mode,
- data->pscan_mode, data->dev_class[2],
- data->dev_class[1], data->dev_class[0],
- __le16_to_cpu(data->clock_offset),
- data->rssi, data->ssp_mode, e->timestamp);
- }
-
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
- return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
- .open = inquiry_cache_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
-
- hci_dev_lock(hdev);
-
- list_for_each_entry(b, &hdev->blacklist, list)
- seq_printf(f, "%pMR\n", &b->bdaddr);
-
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
- return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
- .open = blacklist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void print_bt_uuid(struct seq_file *f, u8 *uuid)
-{
- u32 data0, data5;
- u16 data1, data2, data3, data4;
-
- data5 = get_unaligned_le32(uuid);
- data4 = get_unaligned_le16(uuid + 4);
- data3 = get_unaligned_le16(uuid + 6);
- data2 = get_unaligned_le16(uuid + 8);
- data1 = get_unaligned_le16(uuid + 10);
- data0 = get_unaligned_le32(uuid + 12);
-
- seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
- data0, data1, data2, data3, data4, data5);
-}
-
-static int uuids_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct bt_uuid *uuid;
-
- hci_dev_lock(hdev);
-
- list_for_each_entry(uuid, &hdev->uuids, list)
- print_bt_uuid(f, uuid->uuid);
-
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
- return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
- .open = uuids_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
- struct hci_dev *hdev = data;
-
- hci_dev_lock(hdev);
-
- hdev->auto_accept_delay = val;
-
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
- struct hci_dev *hdev = data;
-
- hci_dev_lock(hdev);
-
- *val = hdev->auto_accept_delay;
-
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
- auto_accept_delay_set, "%llu\n");
-
void hci_init_sysfs(struct hci_dev *hdev)
{
struct device *dev = &hdev->dev;
@@ -542,52 +215,8 @@ void hci_init_sysfs(struct hci_dev *hdev)
device_initialize(dev);
}
-int hci_add_sysfs(struct hci_dev *hdev)
-{
- struct device *dev = &hdev->dev;
- int err;
-
- BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
- dev_set_name(dev, "%s", hdev->name);
-
- err = device_add(dev);
- if (err < 0)
- return err;
-
- if (!bt_debugfs)
- return 0;
-
- hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
- if (!hdev->debugfs)
- return 0;
-
- debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
- hdev, &inquiry_cache_fops);
-
- debugfs_create_file("blacklist", 0444, hdev->debugfs,
- hdev, &blacklist_fops);
-
- debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
-
- debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev,
- &auto_accept_delay_fops);
- return 0;
-}
-
-void hci_del_sysfs(struct hci_dev *hdev)
-{
- BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
-
- debugfs_remove_recursive(hdev->debugfs);
-
- device_del(&hdev->dev);
-}
-
int __init bt_sysfs_init(void)
{
- bt_debugfs = debugfs_create_dir("bluetooth", NULL);
-
bt_class = class_create(THIS_MODULE, "bluetooth");
return PTR_ERR_OR_ZERO(bt_class);
@@ -596,6 +225,4 @@ int __init bt_sysfs_init(void)
void bt_sysfs_cleanup(void)
{
class_destroy(bt_class);
-
- debugfs_remove_recursive(bt_debugfs);
}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index bdc35a7a7fee..292e619db896 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -767,10 +767,10 @@ static int hidp_setup_hid(struct hidp_session *session,
strncpy(hid->name, req->name, sizeof(req->name) - 1);
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
- &bt_sk(session->ctrl_sock->sk)->src);
+ &l2cap_pi(session->ctrl_sock->sk)->chan->src);
snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
- &bt_sk(session->ctrl_sock->sk)->dst);
+ &l2cap_pi(session->ctrl_sock->sk)->chan->dst);
hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver;
@@ -1283,23 +1283,29 @@ static int hidp_session_thread(void *arg)
static int hidp_verify_sockets(struct socket *ctrl_sock,
struct socket *intr_sock)
{
+ struct l2cap_chan *ctrl_chan, *intr_chan;
struct bt_sock *ctrl, *intr;
struct hidp_session *session;
if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock))
return -EINVAL;
+ ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan;
+ intr_chan = l2cap_pi(intr_sock->sk)->chan;
+
+ if (bacmp(&ctrl_chan->src, &intr_chan->src) ||
+ bacmp(&ctrl_chan->dst, &intr_chan->dst))
+ return -ENOTUNIQ;
+
ctrl = bt_sk(ctrl_sock->sk);
intr = bt_sk(intr_sock->sk);
- if (bacmp(&ctrl->src, &intr->src) || bacmp(&ctrl->dst, &intr->dst))
- return -ENOTUNIQ;
if (ctrl->sk.sk_state != BT_CONNECTED ||
intr->sk.sk_state != BT_CONNECTED)
return -EBADFD;
/* early session check, we check again during session registration */
- session = hidp_session_find(&ctrl->dst);
+ session = hidp_session_find(&ctrl_chan->dst);
if (session) {
hidp_session_put(session);
return -EEXIST;
@@ -1332,7 +1338,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
if (!conn)
return -EBADFD;
- ret = hidp_session_new(&session, &bt_sk(ctrl_sock->sk)->dst, ctrl_sock,
+ ret = hidp_session_new(&session, &chan->dst, ctrl_sock,
intr_sock, req, conn);
if (ret)
goto out_conn;
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index 9e6cc3553105..ab5241400cf7 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -182,7 +182,7 @@ struct hidp_session {
};
/* HIDP init defines */
-extern int __init hidp_init_sockets(void);
-extern void __exit hidp_cleanup_sockets(void);
+int __init hidp_init_sockets(void);
+void __exit hidp_cleanup_sockets(void);
#endif /* __HIDP_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 63fa11109a1c..0cef67707838 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -36,14 +36,15 @@
#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;
-static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
+static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@@ -58,6 +59,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event);
+static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type)
+{
+ if (hcon->type == LE_LINK) {
+ if (type == ADDR_LE_DEV_PUBLIC)
+ return BDADDR_LE_PUBLIC;
+ else
+ return BDADDR_LE_RANDOM;
+ }
+
+ return BDADDR_BREDR;
+}
+
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -148,7 +161,7 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
struct l2cap_chan *c;
list_for_each_entry(c, &chan_list, global_l) {
- if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
+ if (c->sport == psm && !bacmp(&c->src, src))
return c;
}
return NULL;
@@ -210,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}
-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
- chan->ops->state_change(chan, state);
-}
-
-static void l2cap_state_change(struct l2cap_chan *chan, int state)
-{
- struct sock *sk = chan->sk;
-
- lock_sock(sk);
- __l2cap_state_change(chan, state);
- release_sock(sk);
+ chan->ops->state_change(chan, state, 0);
}
-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
+ int state, int err)
{
- struct sock *sk = chan->sk;
-
- sk->sk_err = err;
+ chan->state = state;
+ chan->ops->state_change(chan, chan->state, err);
}
static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
-
- lock_sock(sk);
- __l2cap_chan_set_err(chan, err);
- release_sock(sk);
+ chan->ops->state_change(chan, chan->state, err);
}
static void __set_retrans_timer(struct l2cap_chan *chan)
@@ -620,10 +620,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
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 %s sk %p", chan, state_to_string(chan->state),
- sk);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
switch (chan->state) {
case BT_LISTEN:
@@ -634,7 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONFIG:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
conn->hcon->type == ACL_LINK) {
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
l2cap_chan_del(chan, reason);
@@ -646,10 +644,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
struct l2cap_conn_rsp rsp;
__u16 result;
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
+
l2cap_state_change(chan, BT_DISCONN);
rsp.scid = cpu_to_le16(chan->dcid);
@@ -676,7 +675,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 +685,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 +716,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
default:
return HCI_AT_NO_BONDING;
}
+ break;
}
}
@@ -1015,14 +1030,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)
@@ -1186,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct l2cap_disconn_req req;
@@ -1209,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
sizeof(req), &req);
- lock_sock(sk);
- __l2cap_state_change(chan, BT_DISCONN);
- __l2cap_chan_set_err(chan, err);
- release_sock(sk);
+ l2cap_state_change_and_error(chan, BT_DISCONN, err);
}
/* ---- L2CAP connections ---- */
@@ -1225,8 +1251,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
mutex_lock(&conn->chan_lock);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
-
l2cap_chan_lock(chan);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1258,19 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_chan_check_security(chan)) {
- lock_sock(sk);
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
}
- release_sock(sk);
} else {
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -1309,8 +1330,6 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (state && c->state != state)
continue;
@@ -1319,16 +1338,16 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
int src_any, dst_any;
/* Exact match. */
- src_match = !bacmp(&bt_sk(sk)->src, src);
- dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+ src_match = !bacmp(&c->src, src);
+ dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
read_unlock(&chan_list_lock);
return c;
}
/* Closest match */
- src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
- dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+ src_any = !bacmp(&c->src, BDADDR_ANY);
+ dst_any = !bacmp(&c->dst, BDADDR_ANY);
if ((src_match && dst_any) || (src_any && dst_match) ||
(src_any && dst_any))
c1 = c;
@@ -1342,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
- struct sock *parent;
+ struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan, *pchan;
+ u8 dst_type;
BT_DBG("");
/* Check if we have socket listening on cid */
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
- conn->src, conn->dst);
+ &hcon->src, &hcon->dst);
if (!pchan)
return;
@@ -1357,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
return;
- parent = pchan->sk;
+ dst_type = bdaddr_type(hcon, hcon->dst_type);
+
+ /* If device is blocked, do not create a channel for it */
+ if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+ return;
- lock_sock(parent);
+ l2cap_chan_lock(pchan);
chan = pchan->ops->new_connection(pchan);
if (!chan)
@@ -1367,13 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
chan->dcid = L2CAP_CID_ATT;
- bacpy(&bt_sk(chan->sk)->src, conn->src);
- bacpy(&bt_sk(chan->sk)->dst, conn->dst);
+ bacpy(&chan->src, &hcon->src);
+ bacpy(&chan->dst, &hcon->dst);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
+ chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
clean:
- release_sock(parent);
+ l2cap_chan_unlock(pchan);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1408,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- struct sock *sk = chan->sk;
- __clear_chan_timer(chan);
- lock_sock(sk);
- __l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
- release_sock(sk);
+ l2cap_chan_ready(chan);
} else if (chan->state == BT_CONNECT) {
l2cap_do_start(chan);
@@ -1633,11 +1654,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
break;
}
- conn->src = &hcon->hdev->bdaddr;
- conn->dst = &hcon->dst;
-
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);
@@ -1688,8 +1710,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (state && c->state != state)
continue;
@@ -1698,16 +1718,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
int src_any, dst_any;
/* Exact match. */
- src_match = !bacmp(&bt_sk(sk)->src, src);
- dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+ src_match = !bacmp(&c->src, src);
+ dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
read_unlock(&chan_list_lock);
return c;
}
/* Closest match */
- src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
- dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+ src_any = !bacmp(&c->src, BDADDR_ANY);
+ dst_any = !bacmp(&c->dst, BDADDR_ANY);
if ((src_match && dst_any) || (src_any && dst_match) ||
(src_any && dst_any))
c1 = c;
@@ -1722,18 +1742,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type)
{
- struct sock *sk = chan->sk;
- bdaddr_t *src = &bt_sk(sk)->src;
struct l2cap_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
__u8 auth_type;
int err;
- BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst,
+ BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
dst_type, __le16_to_cpu(psm));
- hdev = hci_get_route(dst, src);
+ hdev = hci_get_route(dst, &chan->src);
if (!hdev)
return -EHOSTUNREACH;
@@ -1790,9 +1808,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
/* Set destination address and psm */
- lock_sock(sk);
- bacpy(&bt_sk(sk)->dst, dst);
- release_sock(sk);
+ bacpy(&chan->dst, dst);
+ chan->dst_type = dst_type;
chan->psm = psm;
chan->dcid = cid;
@@ -1825,7 +1842,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
/* Update source addr of the socket */
- bacpy(src, conn->src);
+ bacpy(&chan->src, &hcon->src);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
l2cap_chan_unlock(chan);
l2cap_chan_add(conn, chan);
@@ -1835,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
hci_conn_drop(hcon);
l2cap_state_change(chan, BT_CONNECT);
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1855,38 +1873,6 @@ done:
return err;
}
-int __l2cap_wait_ack(struct sock *sk)
-{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- DECLARE_WAITQUEUE(wait, current);
- int err = 0;
- int timeo = HZ/5;
-
- add_wait_queue(sk_sleep(sk), &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (chan->unacked_frames > 0 && chan->conn) {
- if (!timeo)
- timeo = HZ/5;
-
- if (signal_pending(current)) {
- err = sock_intr_errno(timeo);
- break;
- }
-
- release_sock(sk);
- timeo = schedule_timeout(timeo);
- lock_sock(sk);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = sock_error(sk);
- if (err)
- break;
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(sk_sleep(sk), &wait);
- return err;
-}
-
static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2263,7 +2249,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);
@@ -2278,7 +2265,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)) {
@@ -2826,17 +2813,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
- /* Don't send frame to the socket it came from */
- if (skb->sk == sk)
+ /* Don't send frame to the channel it came from */
+ if (bt_cb(skb)->chan == chan)
continue;
+
nskb = skb_clone(skb, GFP_KERNEL);
if (!nskb)
continue;
-
if (chan->ops->recv(chan, nskb))
kfree_skb(nskb);
}
@@ -3043,8 +3029,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;
@@ -3084,20 +3070,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
@@ -3135,7 +3121,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;
@@ -3165,7 +3151,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 */
@@ -3317,7 +3303,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);
@@ -3349,7 +3335,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;
@@ -3715,7 +3701,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
struct l2cap_chan *chan = NULL, *pchan;
- struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
@@ -3724,16 +3709,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
/* Check if we have socket listening on psm */
- pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst);
+ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
+ &conn->hcon->dst);
if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto sendresp;
}
- parent = pchan->sk;
-
mutex_lock(&conn->chan_lock);
- lock_sock(parent);
+ l2cap_chan_lock(pchan);
/* Check if the ACL is secure enough (if not SDP) */
if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
@@ -3753,8 +3737,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
if (!chan)
goto response;
- 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
@@ -3762,8 +3744,10 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
*/
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
- bacpy(&bt_sk(sk)->src, conn->src);
- bacpy(&bt_sk(sk)->dst, conn->dst);
+ bacpy(&chan->src, &conn->hcon->src);
+ bacpy(&chan->dst, &conn->hcon->dst);
+ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
chan->psm = psm;
chan->dcid = scid;
chan->local_amp_id = amp_id;
@@ -3772,14 +3756,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
dcid = chan->scid;
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
- __l2cap_state_change(chan, BT_CONNECT2);
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
@@ -3788,28 +3772,28 @@ 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 {
- __l2cap_state_change(chan, BT_CONFIG);
+ 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;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
response:
- release_sock(parent);
+ l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
sendresp:
@@ -3891,13 +3875,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;
}
}
@@ -3965,6 +3949,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
L2CAP_CONF_SUCCESS, flags), data);
}
+static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
+ u16 scid, u16 dcid)
+{
+ struct l2cap_cmd_rej_cid rej;
+
+ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+ rej.scid = __cpu_to_le16(scid);
+ rej.dcid = __cpu_to_le16(dcid);
+
+ l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+}
+
static inline int l2cap_config_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -3984,18 +3980,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
chan = l2cap_get_chan_by_scid(conn, dcid);
- if (!chan)
- return -ENOENT;
+ if (!chan) {
+ cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
+ return 0;
+ }
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
- struct l2cap_cmd_rej_cid rej;
-
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
- rej.scid = cpu_to_le16(chan->scid);
- rej.dcid = cpu_to_le16(chan->dcid);
-
- l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
- sizeof(rej), &rej);
+ cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+ chan->dcid);
goto unlock;
}
@@ -4198,7 +4190,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
struct l2cap_disconn_rsp rsp;
u16 dcid, scid;
struct l2cap_chan *chan;
- struct sock *sk;
if (cmd_len != sizeof(*req))
return -EPROTO;
@@ -4213,20 +4204,17 @@ 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);
+ cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
return 0;
}
l2cap_chan_lock(chan);
- sk = chan->sk;
-
rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
- lock_sock(sk);
- sk->sk_shutdown = SHUTDOWN_MASK;
- release_sock(sk);
+ chan->ops->set_shutdown(chan);
l2cap_chan_hold(chan);
l2cap_chan_del(chan, ECONNRESET);
@@ -4303,7 +4291,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;
@@ -4314,7 +4302,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;
@@ -4411,7 +4399,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);
@@ -4420,7 +4408,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;
@@ -4442,10 +4430,13 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
struct amp_mgr *mgr = conn->hcon->amp_mgr;
struct hci_conn *hs_hcon;
- hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+ hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+ &conn->hcon->dst);
if (!hs_hcon) {
hci_dev_put(hdev);
- return -EFAULT;
+ cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+ chan->dcid);
+ return 0;
}
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4469,7 +4460,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)
@@ -4655,7 +4646,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);
@@ -4666,7 +4657,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;
@@ -4723,7 +4714,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
sizeof(rsp), &rsp);
if (result == L2CAP_CR_SUCCESS) {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
L2CAP_CONF_REQ,
@@ -4838,7 +4829,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);
@@ -4865,7 +4856,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 ||
@@ -4885,7 +4876,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
*/
if ((__chan_is_moving(chan) ||
chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
- bacmp(conn->src, conn->dst) > 0) {
+ bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) {
result = L2CAP_MR_COLLISION;
goto send_move_response;
}
@@ -4895,7 +4886,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;
@@ -5087,7 +5078,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;
@@ -5127,7 +5118,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);
@@ -5219,7 +5210,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:
@@ -5227,7 +5218,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:
@@ -5235,7 +5226,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:
@@ -5250,7 +5241,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:
@@ -5262,7 +5253,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:
@@ -5270,7 +5261,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:
@@ -5304,51 +5295,48 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
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 = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+ 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;
@@ -5356,6 +5344,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);
@@ -5378,7 +5369,6 @@ 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);
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
@@ -5388,6 +5378,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
len -= cmd_len;
}
+drop:
kfree_skb(skb);
}
@@ -5784,7 +5775,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);
@@ -5805,7 +5796,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)
@@ -5841,7 +5832,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));
@@ -5919,7 +5910,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);
@@ -5931,7 +5922,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));
@@ -5942,7 +5933,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));
@@ -5957,7 +5948,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));
@@ -5971,7 +5962,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));
@@ -6380,9 +6371,13 @@ 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;
- chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
+ if (hcon->type != ACL_LINK)
+ goto drop;
+
+ chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
if (!chan)
goto drop;
@@ -6394,6 +6389,10 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
if (chan->imtu < skb->len)
goto drop;
+ /* Store remote BD_ADDR and PSM for msg_name */
+ bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
+ bt_cb(skb)->psm = psm;
+
if (!chan->ops->recv(chan, skb))
return;
@@ -6404,15 +6403,22 @@ 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);
+ &hcon->src, &hcon->dst);
if (!chan)
goto drop;
BT_DBG("chan %p, len %d", chan, skb->len);
+ if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+ goto drop;
+
if (chan->imtu < skb->len)
goto drop;
@@ -6441,9 +6447,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;
@@ -6458,6 +6461,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);
@@ -6481,17 +6488,15 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
/* Find listening sockets and check their link_mode */
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (c->state != BT_LISTEN)
continue;
- if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
+ if (!bacmp(&c->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm1 |= HCI_LM_MASTER;
exact++;
- } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ } else if (!bacmp(&c->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm2 |= HCI_LM_MASTER;
@@ -6597,11 +6602,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
- struct sock *sk = chan->sk;
-
- clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
- sk->sk_state_change(sk);
-
+ chan->ops->resume(chan);
l2cap_check_encryption(chan, encrypt);
l2cap_chan_unlock(chan);
continue;
@@ -6614,32 +6615,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
} else if (chan->state == BT_CONNECT2) {
- struct sock *sk = chan->sk;
struct l2cap_conn_rsp rsp;
__u16 res, stat;
- lock_sock(sk);
-
if (!status) {
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
- __l2cap_state_change(chan, BT_DISCONN);
+ l2cap_state_change(chan, BT_DISCONN);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
- release_sock(sk);
-
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(res);
@@ -6756,9 +6751,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;
}
@@ -6775,10 +6774,8 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
- &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ &c->src, &c->dst,
c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
@@ -6811,12 +6808,11 @@ int __init l2cap_init(void)
if (err < 0)
return err;
- if (bt_debugfs) {
- l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
- NULL, &l2cap_debugfs_fops);
- if (!l2cap_debugfs)
- BT_ERR("Failed to create L2CAP debug file");
- }
+ if (IS_ERR_OR_NULL(bt_debugfs))
+ return 0;
+
+ l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+ NULL, &l2cap_debugfs_fops);
return 0;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 0098af80b213..7cc24d263caa 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -32,7 +32,8 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
static struct bt_sock_list l2cap_sk_list = {
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
@@ -68,6 +69,18 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (la.l2_cid && la.l2_psm)
return -EINVAL;
+ if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+ return -EINVAL;
+
+ if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+ /* Connection oriented channels are not supported on LE */
+ if (la.l2_psm)
+ return -EINVAL;
+ /* We only allow ATT user space socket */
+ if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ return -EINVAL;
+ }
+
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
@@ -99,11 +112,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (err < 0)
goto done;
- if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
- __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
- chan->sec_level = BT_SECURITY_SDP;
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_CONN_LESS:
+ if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_3DSP)
+ chan->sec_level = BT_SECURITY_SDP;
+ break;
+ case L2CAP_CHAN_CONN_ORIENTED:
+ if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
+ __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
+ chan->sec_level = BT_SECURITY_SDP;
+ break;
+ }
- bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
+ bacpy(&chan->src, &la.l2_bdaddr);
+ chan->src_type = la.l2_bdaddr_type;
chan->state = BT_BOUND;
sk->sk_state = BT_BOUND;
@@ -134,6 +156,47 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
if (la.l2_cid && la.l2_psm)
return -EINVAL;
+ if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
+ return -EINVAL;
+
+ /* Check that the socket wasn't bound to something that
+ * conflicts with the address given to connect(). If chan->src
+ * is BDADDR_ANY it means bind() was never used, in which case
+ * chan->src_type and la.l2_bdaddr_type do not need to match.
+ */
+ if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
+ bdaddr_type_is_le(la.l2_bdaddr_type)) {
+ /* Old user space versions will try to incorrectly bind
+ * the ATT socket using BDADDR_BREDR. We need to accept
+ * this and fix up the source address type only when
+ * both the source CID and destination CID indicate
+ * ATT. Anything else is an invalid combination.
+ */
+ if (chan->scid != L2CAP_CID_ATT ||
+ la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ return -EINVAL;
+
+ /* We don't have the hdev available here to make a
+ * better decision on random vs public, but since all
+ * user space versions that exhibit this issue anyway do
+ * not support random local addresses assuming public
+ * here is good enough.
+ */
+ chan->src_type = BDADDR_LE_PUBLIC;
+ }
+
+ if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
+ return -EINVAL;
+
+ if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
+ /* Connection oriented channels are not supported on LE */
+ if (la.l2_psm)
+ return -EINVAL;
+ /* We only allow ATT user space socket */
+ if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ return -EINVAL;
+ }
+
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type);
if (err)
@@ -265,12 +328,14 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
if (peer) {
la->l2_psm = chan->psm;
- bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
+ bacpy(&la->l2_bdaddr, &chan->dst);
la->l2_cid = cpu_to_le16(chan->dcid);
+ la->l2_bdaddr_type = chan->dst_type;
} else {
la->l2_psm = chan->sport;
- bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
+ bacpy(&la->l2_bdaddr, &chan->src);
la->l2_cid = cpu_to_le16(chan->scid);
+ la->l2_bdaddr_type = chan->src_type;
}
return 0;
@@ -445,11 +510,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
break;
case BT_CHANNEL_POLICY:
- if (!enable_hs) {
- err = -ENOPROTOOPT;
- break;
- }
-
if (put_user(chan->chan_policy, (u32 __user *) optval))
err = -EFAULT;
break;
@@ -665,10 +725,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
- if (opt)
+ if (opt) {
set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
- else
+ set_bit(FLAG_DEFER_SETUP, &chan->flags);
+ } else {
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ clear_bit(FLAG_DEFER_SETUP, &chan->flags);
+ }
break;
case BT_FLUSHABLE:
@@ -683,7 +746,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
}
if (opt == BT_FLUSHABLE_OFF) {
- struct l2cap_conn *conn = chan->conn;
+ conn = chan->conn;
/* proceed further only when we have l2cap_conn and
No Flush support in the LM */
if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) {
@@ -720,11 +783,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
case BT_CHANNEL_POLICY:
- if (!enable_hs) {
- err = -ENOPROTOOPT;
- break;
- }
-
if (get_user(opt, (u32 __user *) optval)) {
err = -EFAULT;
break;
@@ -777,6 +835,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_state != BT_CONNECTED)
return -ENOTCONN;
+ lock_sock(sk);
+ err = bt_sock_wait_ready(sk, msg->msg_flags);
+ release_sock(sk);
+ if (err)
+ return err;
+
l2cap_chan_lock(chan);
err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
l2cap_chan_unlock(chan);
@@ -799,8 +863,8 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
pi->chan->state = BT_CONFIG;
__l2cap_connect_rsp_defer(pi->chan);
- release_sock(sk);
- return 0;
+ err = 0;
+ goto done;
}
release_sock(sk);
@@ -856,6 +920,38 @@ static void l2cap_sock_kill(struct sock *sk)
sock_put(sk);
}
+static int __l2cap_wait_ack(struct sock *sk)
+{
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ DECLARE_WAITQUEUE(wait, current);
+ int err = 0;
+ int timeo = HZ/5;
+
+ add_wait_queue(sk_sleep(sk), &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (chan->unacked_frames > 0 && chan->conn) {
+ if (!timeo)
+ timeo = HZ/5;
+
+ if (signal_pending(current)) {
+ err = sock_intr_errno(timeo);
+ break;
+ }
+
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ err = sock_error(sk);
+ if (err)
+ break;
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk_sleep(sk), &wait);
+ return err;
+}
+
static int l2cap_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
@@ -946,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
{
struct sock *sk, *parent = chan->data;
+ lock_sock(parent);
+
/* Check for backlog size */
if (sk_acceptq_is_full(parent)) {
BT_DBG("backlog full %d", parent->sk_ack_backlog);
@@ -963,18 +1061,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
bt_accept_enqueue(parent, sk);
+ release_sock(parent);
+
return l2cap_pi(sk)->chan;
}
static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
- int err;
struct sock *sk = chan->data;
- struct l2cap_pinfo *pi = l2cap_pi(sk);
+ int err;
lock_sock(sk);
- if (pi->rx_busy_skb) {
+ if (l2cap_pi(sk)->rx_busy_skb) {
err = -ENOMEM;
goto done;
}
@@ -990,9 +1089,9 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
* acked and reassembled until there is buffer space
* available.
*/
- if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) {
- pi->rx_busy_skb = skb;
- l2cap_chan_busy(pi->chan, 1);
+ if (err < 0 && chan->mode == L2CAP_MODE_ERTM) {
+ l2cap_pi(sk)->rx_busy_skb = skb;
+ l2cap_chan_busy(chan, 1);
err = 0;
}
@@ -1050,26 +1149,33 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
release_sock(sk);
}
-static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state)
+static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
+ int err)
{
struct sock *sk = chan->data;
sk->sk_state = state;
+
+ if (err)
+ sk->sk_err = err;
}
static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
unsigned long len, int nb)
{
+ struct sock *sk = chan->data;
struct sk_buff *skb;
int err;
l2cap_chan_unlock(chan);
- skb = bt_skb_send_alloc(chan->sk, len, nb, &err);
+ skb = bt_skb_send_alloc(sk, len, nb, &err);
l2cap_chan_lock(chan);
if (!skb)
return ERR_PTR(err);
+ bt_cb(skb)->chan = chan;
+
return skb;
}
@@ -1095,11 +1201,39 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
{
- struct sock *sk = chan->data;
- struct sock *parent = bt_sk(sk)->parent;
+ struct sock *parent, *sk = chan->data;
+
+ lock_sock(sk);
+ parent = bt_sk(sk)->parent;
if (parent)
parent->sk_data_ready(parent, 0);
+
+ release_sock(sk);
+}
+
+static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+ sk->sk_state_change(sk);
+}
+
+static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ lock_sock(sk);
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ release_sock(sk);
+}
+
+static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ return sk->sk_sndtimeo;
}
static struct l2cap_ops l2cap_chan_ops = {
@@ -1111,6 +1245,9 @@ static struct l2cap_ops l2cap_chan_ops = {
.state_change = l2cap_sock_state_change_cb,
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
+ .resume = l2cap_sock_resume_cb,
+ .set_shutdown = l2cap_sock_set_shutdown_cb,
+ .get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
};
@@ -1120,6 +1257,7 @@ static void l2cap_sock_destruct(struct sock *sk)
if (l2cap_pi(sk)->chan)
l2cap_chan_put(l2cap_pi(sk)->chan);
+
if (l2cap_pi(sk)->rx_busy_skb) {
kfree_skb(l2cap_pi(sk)->rx_busy_skb);
l2cap_pi(sk)->rx_busy_skb = NULL;
@@ -1129,10 +1267,22 @@ static void l2cap_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_write_queue);
}
+static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,
+ int *msg_namelen)
+{
+ struct sockaddr_l2 *la = (struct sockaddr_l2 *) msg_name;
+
+ memset(la, 0, sizeof(struct sockaddr_l2));
+ la->l2_family = AF_BLUETOOTH;
+ la->l2_psm = bt_cb(skb)->psm;
+ bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+
+ *msg_namelen = sizeof(struct sockaddr_l2);
+}
+
static void l2cap_sock_init(struct sock *sk, struct sock *parent)
{
- struct l2cap_pinfo *pi = l2cap_pi(sk);
- struct l2cap_chan *chan = pi->chan;
+ struct l2cap_chan *chan = l2cap_pi(sk)->chan;
BT_DBG("sk %p", sk);
@@ -1156,13 +1306,13 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
security_sk_clone(parent, sk);
} else {
-
switch (sk->sk_type) {
case SOCK_RAW:
chan->chan_type = L2CAP_CHAN_RAW;
break;
case SOCK_DGRAM:
chan->chan_type = L2CAP_CHAN_CONN_LESS;
+ bt_sk(sk)->skb_msg_name = l2cap_skb_msg_name;
break;
case SOCK_SEQPACKET:
case SOCK_STREAM:
@@ -1224,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
l2cap_chan_hold(chan);
- chan->sk = sk;
-
l2cap_pi(sk)->chan = chan;
return sk;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index fedc5399d465..074d83690a41 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -30,12 +30,11 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
-bool enable_hs;
+#include "smp.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 3
+#define MGMT_REVISION 4
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -76,6 +75,10 @@ static const u16 mgmt_commands[] = {
MGMT_OP_BLOCK_DEVICE,
MGMT_OP_UNBLOCK_DEVICE,
MGMT_OP_SET_DEVICE_ID,
+ MGMT_OP_SET_ADVERTISING,
+ MGMT_OP_SET_BREDR,
+ MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_OP_SET_SCAN_PARAMS,
};
static const u16 mgmt_events[] = {
@@ -181,11 +184,6 @@ static u8 mgmt_status_table[] = {
MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
};
-bool mgmt_valid_hdev(struct hci_dev *hdev)
-{
- return hdev->dev_type == HCI_BREDR;
-}
-
static u8 mgmt_status(u8 hci_status)
{
if (hci_status < ARRAY_SIZE(mgmt_status_table))
@@ -321,10 +319,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
count = 0;
list_for_each_entry(d, &hci_dev_list, list) {
- if (!mgmt_valid_hdev(d))
- continue;
-
- count++;
+ if (d->dev_type == HCI_BREDR)
+ count++;
}
rp_len = sizeof(*rp) + (2 * count);
@@ -339,11 +335,13 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
if (test_bit(HCI_SETUP, &d->dev_flags))
continue;
- if (!mgmt_valid_hdev(d))
+ if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
continue;
- rp->index[count++] = cpu_to_le16(d->id);
- BT_DBG("Added hci%u", d->id);
+ if (d->dev_type == HCI_BREDR) {
+ rp->index[count++] = cpu_to_le16(d->id);
+ BT_DBG("Added hci%u", d->id);
+ }
}
rp->num_controllers = cpu_to_le16(count);
@@ -366,9 +364,6 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_POWERED;
settings |= MGMT_SETTING_PAIRABLE;
- if (lmp_ssp_capable(hdev))
- settings |= MGMT_SETTING_SSP;
-
if (lmp_bredr_capable(hdev)) {
settings |= MGMT_SETTING_CONNECTABLE;
if (hdev->hci_ver >= BLUETOOTH_VER_1_2)
@@ -376,13 +371,17 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_BREDR;
settings |= MGMT_SETTING_LINK_SECURITY;
- }
- if (enable_hs)
- settings |= MGMT_SETTING_HS;
+ if (lmp_ssp_capable(hdev)) {
+ settings |= MGMT_SETTING_SSP;
+ settings |= MGMT_SETTING_HS;
+ }
+ }
- if (lmp_le_capable(hdev))
+ if (lmp_le_capable(hdev)) {
settings |= MGMT_SETTING_LE;
+ settings |= MGMT_SETTING_ADVERTISING;
+ }
return settings;
}
@@ -406,7 +405,7 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_PAIRABLE;
- if (lmp_bredr_capable(hdev))
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_BREDR;
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
@@ -421,6 +420,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_HS;
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ settings |= MGMT_SETTING_ADVERTISING;
+
return settings;
}
@@ -534,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
return ptr;
}
+static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+
+ list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+ if (cmd->opcode == opcode)
+ return cmd;
+ }
+
+ return NULL;
+}
+
+static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0;
+ size_t name_len;
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+static void update_scan_rsp_data(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_rsp_data cp;
+ u8 len;
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_scan_rsp_data(hdev, cp.data);
+
+ if (hdev->scan_rsp_data_len == len &&
+ memcmp(cp.data, hdev->scan_rsp_data, len) == 0)
+ return;
+
+ memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data));
+ hdev->scan_rsp_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp);
+}
+
+static u8 get_adv_discov_flags(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+
+ /* If there's a pending mgmt command the flags will not yet have
+ * their final values, so check for this first.
+ */
+ cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+ if (cmd) {
+ struct mgmt_mode *cp = cmd->param;
+ if (cp->val == 0x01)
+ return LE_AD_GENERAL;
+ else if (cp->val == 0x02)
+ return LE_AD_LIMITED;
+ } else {
+ if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+ return LE_AD_LIMITED;
+ else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ return LE_AD_GENERAL;
+ }
+
+ return 0;
+}
+
+static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0, flags = 0;
+
+ flags |= get_adv_discov_flags(hdev);
+
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ if (lmp_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_CTRL;
+ if (lmp_host_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_HOST;
+ } else {
+ flags |= LE_AD_NO_BREDR;
+ }
+
+ if (flags) {
+ BT_DBG("adv flags 0x%02x", flags);
+
+ ptr[0] = 2;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ return ad_len;
+}
+
+static void update_adv_data(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_adv_data(hdev, cp.data);
+
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0)
+ return;
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
static void create_eir(struct hci_dev *hdev, u8 *data)
{
u8 *ptr = data;
@@ -632,6 +784,9 @@ static void update_class(struct hci_request *req)
if (!hdev_is_powered(hdev))
return;
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return;
+
if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return;
@@ -639,6 +794,9 @@ static void update_class(struct hci_request *req)
cod[1] = hdev->major_class;
cod[2] = get_service_classes(hdev);
+ if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags))
+ cod[1] |= 0x20;
+
if (memcmp(cod, hdev->dev_class, 3) == 0)
return;
@@ -763,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
}
}
-static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
-{
- struct pending_cmd *cmd;
-
- list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
- if (cmd->opcode == opcode)
- return cmd;
- }
-
- return NULL;
-}
-
static void mgmt_pending_remove(struct pending_cmd *cmd)
{
list_del(&cmd->list);
@@ -804,6 +950,12 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
+ if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
cancel_delayed_work(&hdev->power_off);
@@ -820,12 +972,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
- MGMT_STATUS_BUSY);
- goto failed;
- }
-
cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
@@ -883,27 +1029,141 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip)
return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
}
+struct cmd_lookup {
+ struct sock *sk;
+ struct hci_dev *hdev;
+ u8 mgmt_status;
+};
+
+static void settings_rsp(struct pending_cmd *cmd, void *data)
+{
+ struct cmd_lookup *match = data;
+
+ send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
+
+ list_del(&cmd->list);
+
+ if (match->sk == NULL) {
+ match->sk = cmd->sk;
+ sock_hold(match->sk);
+ }
+
+ mgmt_pending_free(cmd);
+}
+
+static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
+{
+ u8 *status = data;
+
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
+ mgmt_pending_remove(cmd);
+}
+
+static u8 mgmt_bredr_support(struct hci_dev *hdev)
+{
+ if (!lmp_bredr_capable(hdev))
+ return MGMT_STATUS_NOT_SUPPORTED;
+ else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return MGMT_STATUS_REJECTED;
+ else
+ return MGMT_STATUS_SUCCESS;
+}
+
+static u8 mgmt_le_support(struct hci_dev *hdev)
+{
+ if (!lmp_le_capable(hdev))
+ return MGMT_STATUS_NOT_SUPPORTED;
+ else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return MGMT_STATUS_REJECTED;
+ else
+ return MGMT_STATUS_SUCCESS;
+}
+
+static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
+{
+ struct pending_cmd *cmd;
+ struct mgmt_mode *cp;
+ struct hci_request req;
+ bool changed;
+
+ BT_DBG("status 0x%02x", status);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev);
+ if (!cmd)
+ goto unlock;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ goto remove_cmd;
+ }
+
+ cp = cmd->param;
+ if (cp->val) {
+ changed = !test_and_set_bit(HCI_DISCOVERABLE,
+ &hdev->dev_flags);
+
+ if (hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
+ queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+ to);
+ }
+ } else {
+ changed = test_and_clear_bit(HCI_DISCOVERABLE,
+ &hdev->dev_flags);
+ }
+
+ send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev);
+
+ if (changed)
+ new_settings(hdev, cmd->sk);
+
+ /* When the discoverable mode gets changed, make sure
+ * that class of device has the limited discoverable
+ * bit correctly set.
+ */
+ hci_req_init(&req, hdev);
+ update_class(&req);
+ hci_req_run(&req, NULL);
+
+remove_cmd:
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
struct mgmt_cp_set_discoverable *cp = data;
struct pending_cmd *cmd;
+ struct hci_request req;
u16 timeout;
u8 scan;
int err;
BT_DBG("request for %s", hdev->name);
- if (!lmp_bredr_capable(hdev))
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_NOT_SUPPORTED);
+ MGMT_STATUS_REJECTED);
- if (cp->val != 0x00 && cp->val != 0x01)
+ if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_INVALID_PARAMS);
timeout = __le16_to_cpu(cp->timeout);
- if (!cp->val && timeout > 0)
+
+ /* Disabling discoverable requires that no timeout is set,
+ * and enabling limited discoverable requires a timeout.
+ */
+ if ((cp->val == 0x00 && timeout > 0) ||
+ (cp->val == 0x02 && timeout == 0))
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
MGMT_STATUS_INVALID_PARAMS);
@@ -931,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
if (!hdev_is_powered(hdev)) {
bool changed = false;
+ /* Setting limited discoverable when powered off is
+ * not a valid operation since it requires a timeout
+ * and so no need to check HCI_LIMITED_DISCOVERABLE.
+ */
if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
changed = true;
@@ -946,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
- if (hdev->discov_timeout > 0) {
- cancel_delayed_work(&hdev->discov_off);
- hdev->discov_timeout = 0;
- }
+ /* If the current mode is the same, then just update the timeout
+ * value with the new value. And if only the timeout gets updated,
+ * then no need for any HCI transactions.
+ */
+ if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) &&
+ (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE,
+ &hdev->dev_flags)) {
+ cancel_delayed_work(&hdev->discov_off);
+ hdev->discov_timeout = timeout;
- if (cp->val && timeout > 0) {
- hdev->discov_timeout = timeout;
+ if (cp->val && hdev->discov_timeout > 0) {
+ int to = msecs_to_jiffies(hdev->discov_timeout * 1000);
queue_delayed_work(hdev->workqueue, &hdev->discov_off,
- msecs_to_jiffies(hdev->discov_timeout * 1000));
+ to);
}
err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
@@ -968,20 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
+ /* Cancel any potential discoverable timeout that might be
+ * still active and store new timeout value. The arming of
+ * the timeout happens in the complete handler.
+ */
+ cancel_delayed_work(&hdev->discov_off);
+ hdev->discov_timeout = timeout;
+
+ /* Limited discoverable mode */
+ if (cp->val == 0x02)
+ set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ else
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+
+ hci_req_init(&req, hdev);
+
+ /* The procedure for LE-only controllers is much simpler - just
+ * update the advertising data.
+ */
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ goto update_ad;
+
scan = SCAN_PAGE;
- if (cp->val)
+ if (cp->val) {
+ struct hci_cp_write_current_iac_lap hci_cp;
+
+ if (cp->val == 0x02) {
+ /* Limited discoverable mode */
+ hci_cp.num_iac = 2;
+ hci_cp.iac_lap[0] = 0x00; /* LIAC */
+ hci_cp.iac_lap[1] = 0x8b;
+ hci_cp.iac_lap[2] = 0x9e;
+ hci_cp.iac_lap[3] = 0x33; /* GIAC */
+ hci_cp.iac_lap[4] = 0x8b;
+ hci_cp.iac_lap[5] = 0x9e;
+ } else {
+ /* General discoverable mode */
+ hci_cp.num_iac = 1;
+ hci_cp.iac_lap[0] = 0x33; /* GIAC */
+ hci_cp.iac_lap[1] = 0x8b;
+ hci_cp.iac_lap[2] = 0x9e;
+ }
+
+ hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ (hci_cp.num_iac * 3) + 1, &hci_cp);
+
scan |= SCAN_INQUIRY;
- else
- cancel_delayed_work(&hdev->discov_off);
+ } else {
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ }
+
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
- err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+update_ad:
+ update_adv_data(&req);
+
+ err = hci_req_run(&req, set_discoverable_complete);
if (err < 0)
mgmt_pending_remove(cmd);
- if (cp->val)
- hdev->discov_timeout = timeout;
-
failed:
hci_dev_unlock(hdev);
return err;
@@ -993,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
struct hci_cp_write_page_scan_activity acp;
u8 type;
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return;
+
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
@@ -1019,9 +1336,55 @@ static void write_fast_connectable(struct hci_request *req, bool enable)
hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
}
+static u8 get_adv_type(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+ bool connectable;
+
+ /* If there's a pending mgmt command the flag will not yet have
+ * it's final value, so check for this first.
+ */
+ cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+ if (cmd) {
+ struct mgmt_mode *cp = cmd->param;
+ connectable = !!cp->val;
+ } else {
+ connectable = test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ }
+
+ return connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+}
+
+static void enable_advertising(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_adv_param cp;
+ u8 enable = 0x01;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.min_interval = __constant_cpu_to_le16(0x0800);
+ cp.max_interval = __constant_cpu_to_le16(0x0800);
+ cp.type = get_adv_type(hdev);
+ cp.own_address_type = hdev->own_addr_type;
+ cp.channel_map = 0x07;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
+static void disable_advertising(struct hci_request *req)
+{
+ u8 enable = 0x00;
+
+ hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
static void set_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
+ struct mgmt_mode *cp;
+ bool changed;
BT_DBG("status 0x%02x", status);
@@ -1031,14 +1394,56 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
if (!cmd)
goto unlock;
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+ goto remove_cmd;
+ }
+
+ cp = cmd->param;
+ if (cp->val)
+ changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+
send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
+ if (changed)
+ new_settings(hdev, cmd->sk);
+
+remove_cmd:
mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
}
+static int set_connectable_update_settings(struct hci_dev *hdev,
+ struct sock *sk, u8 val)
+{
+ bool changed = false;
+ int err;
+
+ if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ changed = true;
+
+ if (val) {
+ set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ } else {
+ clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
+ if (err < 0)
+ return err;
+
+ if (changed)
+ return new_settings(hdev, sk);
+
+ return 0;
+}
+
static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
@@ -1050,9 +1455,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
- if (!lmp_bredr_capable(hdev))
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_NOT_SUPPORTED);
+ MGMT_STATUS_REJECTED);
if (cp->val != 0x00 && cp->val != 0x01)
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
@@ -1061,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- bool changed = false;
-
- if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
-
- if (cp->val) {
- set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
- } else {
- clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
- clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
- }
-
- err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
- if (err < 0)
- goto failed;
-
- if (changed)
- err = new_settings(hdev, sk);
-
+ err = set_connectable_update_settings(hdev, sk, cp->val);
goto failed;
}
@@ -1090,30 +1478,37 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
- err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
- goto failed;
- }
-
cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
}
- if (cp->val) {
- scan = SCAN_PAGE;
- } else {
- scan = 0;
+ hci_req_init(&req, hdev);
- if (test_bit(HCI_ISCAN, &hdev->flags) &&
- hdev->discov_timeout > 0)
- cancel_delayed_work(&hdev->discov_off);
- }
+ /* If BR/EDR is not enabled and we disable advertising as a
+ * by-product of disabling connectable, we need to update the
+ * advertising flags.
+ */
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ if (!cp->val) {
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ }
+ update_adv_data(&req);
+ } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) {
+ if (cp->val) {
+ scan = SCAN_PAGE;
+ } else {
+ scan = 0;
- hci_req_init(&req, hdev);
+ if (test_bit(HCI_ISCAN, &hdev->flags) &&
+ hdev->discov_timeout > 0)
+ cancel_delayed_work(&hdev->discov_off);
+ }
- hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ }
/* If we're going from non-connectable to connectable or
* vice-versa when fast connectable is enabled ensure that fast
@@ -1124,9 +1519,20 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
write_fast_connectable(&req, false);
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) &&
+ hci_conn_num(hdev, LE_LINK) == 0) {
+ disable_advertising(&req);
+ enable_advertising(&req);
+ }
+
err = hci_req_run(&req, set_connectable_complete);
- if (err < 0)
+ if (err < 0) {
mgmt_pending_remove(cmd);
+ if (err == -ENODATA)
+ err = set_connectable_update_settings(hdev, sk,
+ cp->val);
+ goto failed;
+ }
failed:
hci_dev_unlock(hdev);
@@ -1137,6 +1543,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
struct mgmt_mode *cp = data;
+ bool changed;
int err;
BT_DBG("request for %s", hdev->name);
@@ -1148,17 +1555,18 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
hci_dev_lock(hdev);
if (cp->val)
- set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+ changed = !test_and_set_bit(HCI_PAIRABLE, &hdev->dev_flags);
else
- clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
+ changed = test_and_clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
if (err < 0)
- goto failed;
+ goto unlock;
- err = new_settings(hdev, sk);
+ if (changed)
+ err = new_settings(hdev, sk);
-failed:
+unlock:
hci_dev_unlock(hdev);
return err;
}
@@ -1168,14 +1576,15 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- u8 val;
+ u8 val, status;
int err;
BT_DBG("request for %s", hdev->name);
- if (!lmp_bredr_capable(hdev))
+ status = mgmt_bredr_support(hdev);
+ if (status)
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
- MGMT_STATUS_NOT_SUPPORTED);
+ status);
if (cp->val != 0x00 && cp->val != 0x01)
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
@@ -1236,11 +1645,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
{
struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- u8 val;
+ u8 status;
int err;
BT_DBG("request for %s", hdev->name);
+ status = mgmt_bredr_support(hdev);
+ if (status)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status);
+
if (!lmp_ssp_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
MGMT_STATUS_NOT_SUPPORTED);
@@ -1251,14 +1664,20 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_dev_lock(hdev);
- val = !!cp->val;
-
if (!hdev_is_powered(hdev)) {
- bool changed = false;
+ bool changed;
- if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
- change_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
- changed = true;
+ if (cp->val) {
+ changed = !test_and_set_bit(HCI_SSP_ENABLED,
+ &hdev->dev_flags);
+ } else {
+ changed = test_and_clear_bit(HCI_SSP_ENABLED,
+ &hdev->dev_flags);
+ if (!changed)
+ changed = test_and_clear_bit(HCI_HS_ENABLED,
+ &hdev->dev_flags);
+ else
+ clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
}
err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
@@ -1271,13 +1690,14 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto failed;
}
- if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
+ if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) ||
+ mgmt_pending_find(MGMT_OP_SET_HS, hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
MGMT_STATUS_BUSY);
goto failed;
}
- if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
+ if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
goto failed;
}
@@ -1288,7 +1708,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto failed;
}
- err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val);
if (err < 0) {
mgmt_pending_remove(cmd);
goto failed;
@@ -1302,23 +1722,90 @@ failed:
static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
{
struct mgmt_mode *cp = data;
+ bool changed;
+ u8 status;
+ int err;
BT_DBG("request for %s", hdev->name);
- if (!enable_hs)
+ status = mgmt_bredr_support(hdev);
+ if (status)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
+
+ if (!lmp_ssp_capable(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
MGMT_STATUS_NOT_SUPPORTED);
+ if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+ MGMT_STATUS_REJECTED);
+
if (cp->val != 0x00 && cp->val != 0x01)
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
MGMT_STATUS_INVALID_PARAMS);
- if (cp->val)
- set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
- else
- clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ hci_dev_lock(hdev);
+
+ if (cp->val) {
+ changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ } else {
+ if (hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+ MGMT_STATUS_REJECTED);
+ goto unlock;
+ }
+
+ changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ }
- return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
+ err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static void le_enable_complete(struct hci_dev *hdev, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
+ &mgmt_err);
+ return;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
+
+ new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ /* Make sure the controller has a good default for
+ * advertising data. Restrict the update to when LE
+ * has actually been enabled. During power on, the
+ * update in powered_update_hci will take care of it.
+ */
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ struct hci_request req;
+
+ hci_dev_lock(hdev);
+
+ hci_req_init(&req, hdev);
+ update_adv_data(&req);
+ update_scan_rsp_data(&req);
+ hci_req_run(&req, NULL);
+
+ hci_dev_unlock(hdev);
+ }
}
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -1326,6 +1813,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
struct mgmt_mode *cp = data;
struct hci_cp_write_le_host_supported hci_cp;
struct pending_cmd *cmd;
+ struct hci_request req;
int err;
u8 val, enabled;
@@ -1340,7 +1828,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
MGMT_STATUS_INVALID_PARAMS);
/* LE-only devices do not allow toggling LE on/off */
- if (!lmp_bredr_capable(hdev))
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
MGMT_STATUS_REJECTED);
@@ -1357,6 +1845,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
changed = true;
}
+ if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ changed = true;
+ }
+
err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
if (err < 0)
goto unlock;
@@ -1367,7 +1860,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
- if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
+ if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
+ mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
MGMT_STATUS_BUSY);
goto unlock;
@@ -1379,15 +1873,22 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
goto unlock;
}
+ hci_req_init(&req, hdev);
+
memset(&hci_cp, 0, sizeof(hci_cp));
if (val) {
hci_cp.le = val;
hci_cp.simul = lmp_le_br_capable(hdev);
+ } else {
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ disable_advertising(&req);
}
- err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
- &hci_cp);
+ hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
+ &hci_cp);
+
+ err = hci_req_run(&req, le_enable_complete);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -1706,6 +2207,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
u16 key_count, expected_len;
int i;
+ BT_DBG("request for %s", hdev->name);
+
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
key_count = __le16_to_cpu(cp->key_count);
expected_len = sizeof(*cp) + key_count *
@@ -2515,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
update_eir(&req);
}
+ /* The name is stored in the scan response data and so
+ * no need to udpate the advertising data here.
+ */
if (lmp_le_capable(hdev))
- hci_update_ad(&req);
+ update_scan_rsp_data(&req);
err = hci_req_run(&req, set_name_complete);
if (err < 0)
@@ -2685,6 +3195,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
struct hci_request req;
/* General inquiry access code (GIAC) */
u8 lap[3] = { 0x33, 0x8b, 0x9e };
+ u8 status;
int err;
BT_DBG("%s", hdev->name);
@@ -2721,9 +3232,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
switch (hdev->discovery.type) {
case DISCOV_TYPE_BREDR:
- if (!lmp_bredr_capable(hdev)) {
+ status = mgmt_bredr_support(hdev);
+ if (status) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_SUPPORTED);
+ status);
mgmt_pending_remove(cmd);
goto failed;
}
@@ -2745,22 +3257,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
case DISCOV_TYPE_LE:
case DISCOV_TYPE_INTERLEAVED:
- if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ status = mgmt_le_support(hdev);
+ if (status) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_SUPPORTED);
+ status);
mgmt_pending_remove(cmd);
goto failed;
}
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
- !lmp_bredr_capable(hdev)) {
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
MGMT_STATUS_NOT_SUPPORTED);
mgmt_pending_remove(cmd);
goto failed;
}
- if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
MGMT_STATUS_REJECTED);
mgmt_pending_remove(cmd);
@@ -2778,6 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
param_cp.type = LE_SCAN_ACTIVE;
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+ param_cp.own_address_type = hdev->own_addr_type;
hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
&param_cp);
@@ -3065,6 +3579,186 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
return err;
}
+static void set_advertising_complete(struct hci_dev *hdev, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
+ cmd_status_rsp, &mgmt_err);
+ return;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
+ &match);
+
+ new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+}
+
+static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_mode *cp = data;
+ struct pending_cmd *cmd;
+ struct hci_request req;
+ u8 val, enabled, status;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ status = mgmt_le_support(hdev);
+ if (status)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ status);
+
+ if (cp->val != 0x00 && cp->val != 0x01)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ val = !!cp->val;
+ enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
+ /* The following conditions are ones which mean that we should
+ * not do any HCI communication but directly send a mgmt
+ * response to user space (after toggling the flag if
+ * necessary).
+ */
+ if (!hdev_is_powered(hdev) || val == enabled ||
+ hci_conn_num(hdev, LE_LINK) > 0) {
+ bool changed = false;
+
+ if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+ change_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ changed = true;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
+ mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ hci_req_init(&req, hdev);
+
+ if (val)
+ enable_advertising(&req);
+ else
+ disable_advertising(&req);
+
+ err = hci_req_run(&req, set_advertising_complete);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int set_static_address(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_static_address *cp = data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_REJECTED);
+
+ if (bacmp(&cp->bdaddr, BDADDR_ANY)) {
+ if (!bacmp(&cp->bdaddr, BDADDR_NONE))
+ return cmd_status(sk, hdev->id,
+ MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ /* Two most significant bits shall be set */
+ if ((cp->bdaddr.b[5] & 0xc0) != 0xc0)
+ return cmd_status(sk, hdev->id,
+ MGMT_OP_SET_STATIC_ADDRESS,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
+
+ hci_dev_lock(hdev);
+
+ bacpy(&hdev->static_addr, &cp->bdaddr);
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0);
+
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
+static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_scan_params *cp = data;
+ __u16 interval, window;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ interval = __le16_to_cpu(cp->interval);
+
+ if (interval < 0x0004 || interval > 0x4000)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ window = __le16_to_cpu(cp->window);
+
+ if (window < 0x0004 || window > 0x4000)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ if (window > interval)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ hdev->le_scan_interval = interval;
+ hdev->le_scan_window = window;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0);
+
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
@@ -3108,7 +3802,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name);
- if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2)
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) ||
+ hdev->hci_ver < BLUETOOTH_VER_1_2)
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_SUPPORTED);
@@ -3162,6 +3857,148 @@ unlock:
return err;
}
+static void set_bredr_scan(struct hci_request *req)
+{
+ struct hci_dev *hdev = req->hdev;
+ u8 scan = 0;
+
+ /* Ensure that fast connectable is disabled. This function will
+ * not do anything if the page scan parameters are already what
+ * they should be.
+ */
+ write_fast_connectable(req, false);
+
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ scan |= SCAN_PAGE;
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ scan |= SCAN_INQUIRY;
+
+ if (scan)
+ hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
+static void set_bredr_complete(struct hci_dev *hdev, u8 status)
+{
+ struct pending_cmd *cmd;
+
+ BT_DBG("status 0x%02x", status);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
+ if (!cmd)
+ goto unlock;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ /* We need to restore the flag if related HCI commands
+ * failed.
+ */
+ clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+ cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
+ } else {
+ send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev);
+ new_settings(hdev, cmd->sk);
+ }
+
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
+{
+ struct mgmt_mode *cp = data;
+ struct pending_cmd *cmd;
+ struct hci_request req;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+ MGMT_STATUS_REJECTED);
+
+ if (cp->val != 0x00 && cp->val != 0x01)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
+ goto unlock;
+ }
+
+ if (!hdev_is_powered(hdev)) {
+ if (!cp->val) {
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+ clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
+ clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ }
+
+ change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
+ if (err < 0)
+ goto unlock;
+
+ err = new_settings(hdev, sk);
+ goto unlock;
+ }
+
+ /* Reject disabling when powered on */
+ if (!cp->val) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+ MGMT_STATUS_REJECTED);
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ /* We need to flip the bit already here so that update_adv_data
+ * generates the correct flags.
+ */
+ set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+ hci_req_init(&req, hdev);
+
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ set_bredr_scan(&req);
+
+ /* Since only the advertising data flags will change, there
+ * is no need to update the scan response data.
+ */
+ update_adv_data(&req);
+
+ err = hci_req_run(&req, set_bredr_complete);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static bool ltk_is_valid(struct mgmt_ltk_info *key)
{
if (key->authenticated != 0x00 && key->authenticated != 0x01)
@@ -3180,6 +4017,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
u16 key_count, expected_len;
int i, err;
+ BT_DBG("request for %s", hdev->name);
+
+ if (!lmp_le_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
key_count = __le16_to_cpu(cp->key_count);
expected_len = sizeof(*cp) + key_count *
@@ -3208,15 +4051,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
for (i = 0; i < key_count; i++) {
struct mgmt_ltk_info *key = &cp->keys[i];
- u8 type;
+ u8 type, addr_type;
+
+ if (key->addr.type == BDADDR_LE_PUBLIC)
+ addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ addr_type = ADDR_LE_DEV_RANDOM;
if (key->master)
type = HCI_SMP_LTK;
else
type = HCI_SMP_LTK_SLAVE;
- hci_add_ltk(hdev, &key->addr.bdaddr,
- bdaddr_to_le(key->addr.type),
+ hci_add_ltk(hdev, &key->addr.bdaddr, addr_type,
type, 0, key->authenticated, key->val,
key->enc_size, key->ediv, key->rand);
}
@@ -3276,6 +4123,10 @@ static const struct mgmt_handler {
{ block_device, false, MGMT_BLOCK_DEVICE_SIZE },
{ unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
+ { set_advertising, false, MGMT_SETTING_SIZE },
+ { set_bredr, false, MGMT_SETTING_SIZE },
+ { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
+ { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE },
};
@@ -3320,6 +4171,13 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
MGMT_STATUS_INVALID_INDEX);
goto done;
}
+
+ if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
+ test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+ err = cmd_status(sk, index, opcode,
+ MGMT_STATUS_INVALID_INDEX);
+ goto done;
+ }
}
if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
@@ -3365,74 +4223,24 @@ done:
return err;
}
-static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
-{
- u8 *status = data;
-
- cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
- mgmt_pending_remove(cmd);
-}
-
-int mgmt_index_added(struct hci_dev *hdev)
+void mgmt_index_added(struct hci_dev *hdev)
{
- if (!mgmt_valid_hdev(hdev))
- return -ENOTSUPP;
+ if (hdev->dev_type != HCI_BREDR)
+ return;
- return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+ mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
}
-int mgmt_index_removed(struct hci_dev *hdev)
+void mgmt_index_removed(struct hci_dev *hdev)
{
u8 status = MGMT_STATUS_INVALID_INDEX;
- if (!mgmt_valid_hdev(hdev))
- return -ENOTSUPP;
+ if (hdev->dev_type != HCI_BREDR)
+ return;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
- return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
-}
-
-struct cmd_lookup {
- struct sock *sk;
- struct hci_dev *hdev;
- u8 mgmt_status;
-};
-
-static void settings_rsp(struct pending_cmd *cmd, void *data)
-{
- struct cmd_lookup *match = data;
-
- send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
-
- list_del(&cmd->list);
-
- if (match->sk == NULL) {
- match->sk = cmd->sk;
- sock_hold(match->sk);
- }
-
- mgmt_pending_free(cmd);
-}
-
-static void set_bredr_scan(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- u8 scan = 0;
-
- /* Ensure that fast connectable is disabled. This function will
- * not do anything if the page scan parameters are already what
- * they should be.
- */
- write_fast_connectable(req, false);
-
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- scan |= SCAN_PAGE;
- if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- scan |= SCAN_INQUIRY;
-
- if (scan)
- hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
}
static void powered_complete(struct hci_dev *hdev, u8 status)
@@ -3483,13 +4291,33 @@ static int powered_update_hci(struct hci_dev *hdev)
sizeof(cp), &cp);
}
+ if (lmp_le_capable(hdev)) {
+ /* Set random address to static address if configured */
+ if (bacmp(&hdev->static_addr, BDADDR_ANY))
+ hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
+ &hdev->static_addr);
+
+ /* Make sure the controller has a good default for
+ * advertising data. This also applies to the case
+ * where BR/EDR was toggled during the AUTO_OFF phase.
+ */
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ update_adv_data(&req);
+ update_scan_rsp_data(&req);
+ }
+
+ if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ enable_advertising(&req);
+ }
+
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE,
sizeof(link_sec), &link_sec);
if (lmp_bredr_capable(hdev)) {
- set_bredr_scan(&req);
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ set_bredr_scan(&req);
update_class(&req);
update_name(&req);
update_eir(&req);
@@ -3533,76 +4361,110 @@ new_settings:
return err;
}
-int mgmt_set_powered_failed(struct hci_dev *hdev, int err)
+void mgmt_set_powered_failed(struct hci_dev *hdev, int err)
{
struct pending_cmd *cmd;
u8 status;
cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
if (!cmd)
- return -ENOENT;
+ return;
if (err == -ERFKILL)
status = MGMT_STATUS_RFKILLED;
else
status = MGMT_STATUS_FAILED;
- err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
+ cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status);
mgmt_pending_remove(cmd);
+}
- return err;
+void mgmt_discoverable_timeout(struct hci_dev *hdev)
+{
+ struct hci_request req;
+
+ hci_dev_lock(hdev);
+
+ /* When discoverable timeout triggers, then just make sure
+ * the limited discoverable flag is cleared. Even in the case
+ * of a timeout triggered from general discoverable, it is
+ * safe to unconditionally clear the flag.
+ */
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+
+ hci_req_init(&req, hdev);
+ if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ u8 scan = SCAN_PAGE;
+ hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE,
+ sizeof(scan), &scan);
+ }
+ update_class(&req);
+ update_adv_data(&req);
+ hci_req_run(&req, NULL);
+
+ hdev->discov_timeout = 0;
+
+ new_settings(hdev, NULL);
+
+ hci_dev_unlock(hdev);
}
-int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
+void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
{
- struct cmd_lookup match = { NULL, hdev };
- bool changed = false;
- int err = 0;
+ bool changed;
+
+ /* Nothing needed here if there's a pending command since that
+ * commands request completion callback takes care of everything
+ * necessary.
+ */
+ if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
+ return;
if (discoverable) {
- if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- changed = true;
+ changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
} else {
- if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- changed = true;
+ clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+ changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
}
- mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
- &match);
+ if (changed) {
+ struct hci_request req;
- if (changed)
- err = new_settings(hdev, match.sk);
-
- if (match.sk)
- sock_put(match.sk);
+ /* In case this change in discoverable was triggered by
+ * a disabling of connectable there could be a need to
+ * update the advertising flags.
+ */
+ hci_req_init(&req, hdev);
+ update_adv_data(&req);
+ hci_req_run(&req, NULL);
- return err;
+ new_settings(hdev, NULL);
+ }
}
-int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
+void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{
- struct pending_cmd *cmd;
- bool changed = false;
- int err = 0;
+ bool changed;
- if (connectable) {
- if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
- } else {
- if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- changed = true;
- }
+ /* Nothing needed here if there's a pending command since that
+ * commands request completion callback takes care of everything
+ * necessary.
+ */
+ if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
+ return;
- cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev);
+ if (connectable)
+ changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
if (changed)
- err = new_settings(hdev, cmd ? cmd->sk : NULL);
-
- return err;
+ new_settings(hdev, NULL);
}
-int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
+void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
{
u8 mgmt_err = mgmt_status(status);
@@ -3613,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
cmd_status_rsp, &mgmt_err);
-
- return 0;
}
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
- bool persistent)
+void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
+ bool persistent)
{
struct mgmt_ev_new_link_key ev;
@@ -3631,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE);
ev.key.pin_len = key->pin_len;
- return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
{
struct mgmt_ev_new_long_term_key ev;
@@ -3653,13 +4513,23 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
memcpy(ev.key.rand, key->rand, sizeof(key->rand));
memcpy(ev.key.val, key->val, sizeof(key->val));
- return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev),
- NULL);
+ mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
+}
+
+static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
+ u8 data_len)
+{
+ eir[eir_len++] = sizeof(type) + data_len;
+ eir[eir_len++] = type;
+ memcpy(&eir[eir_len], data, data_len);
+ eir_len += data_len;
+
+ return eir_len;
}
-int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u32 flags, u8 *name, u8 name_len,
- u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u32 flags, u8 *name, u8 name_len,
+ u8 *dev_class)
{
char buf[512];
struct mgmt_ev_device_connected *ev = (void *) buf;
@@ -3680,8 +4550,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->eir_len = cpu_to_le16(eir_len);
- return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
- sizeof(*ev) + eir_len, NULL);
+ mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
+ sizeof(*ev) + eir_len, NULL);
}
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -3719,12 +4589,11 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
-int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, u8 reason)
+void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type, u8 reason)
{
struct mgmt_ev_device_disconnected ev;
struct sock *sk = NULL;
- int err;
mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
@@ -3732,45 +4601,39 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
ev.addr.type = link_to_bdaddr(link_type, addr_type);
ev.reason = reason;
- err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
- sk);
+ mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
if (sk)
sock_put(sk);
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
hdev);
-
- return err;
}
-int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 link_type, u8 addr_type, u8 status)
+void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type, u8 status)
{
struct mgmt_rp_disconnect rp;
struct pending_cmd *cmd;
- int err;
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
hdev);
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
- return -ENOENT;
+ return;
bacpy(&rp.addr.bdaddr, bdaddr);
rp.addr.type = link_to_bdaddr(link_type, addr_type);
- err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
- mgmt_status(status), &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
-
- return err;
}
-int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 status)
+void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u8 status)
{
struct mgmt_ev_connect_failed ev;
@@ -3778,10 +4641,10 @@ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev.addr.type = link_to_bdaddr(link_type, addr_type);
ev.status = mgmt_status(status);
- return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
+void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
@@ -3789,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
ev.addr.type = BDADDR_BREDR;
ev.secure = secure;
- return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
- NULL);
+ mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
- int err;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
- return -ENOENT;
+ return;
bacpy(&rp.addr.bdaddr, bdaddr);
rp.addr.type = BDADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
-
- return err;
}
-int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
- int err;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
- return -ENOENT;
+ return;
bacpy(&rp.addr.bdaddr, bdaddr);
rp.addr.type = BDADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
-
- return err;
}
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -3936,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u8 status)
{
struct mgmt_ev_auth_failed ev;
@@ -3945,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev.addr.type = link_to_bdaddr(link_type, addr_type);
ev.status = mgmt_status(status);
- return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
{
struct cmd_lookup match = { NULL, hdev };
- bool changed = false;
- int err = 0;
+ bool changed;
if (status) {
u8 mgmt_err = mgmt_status(status);
mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
cmd_status_rsp, &mgmt_err);
- return 0;
+ return;
}
- if (test_bit(HCI_AUTH, &hdev->flags)) {
- if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
- changed = true;
- } else {
- if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
- changed = true;
- }
+ if (test_bit(HCI_AUTH, &hdev->flags))
+ changed = !test_and_set_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags);
+ else
+ changed = test_and_clear_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags);
mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
&match);
if (changed)
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
static void clear_eir(struct hci_request *req)
@@ -3996,38 +4848,41 @@ static void clear_eir(struct hci_request *req)
hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
{
struct cmd_lookup match = { NULL, hdev };
struct hci_request req;
bool changed = false;
- int err = 0;
if (status) {
u8 mgmt_err = mgmt_status(status);
if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
- &hdev->dev_flags))
- err = new_settings(hdev, NULL);
+ &hdev->dev_flags)) {
+ clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ new_settings(hdev, NULL);
+ }
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
&mgmt_err);
-
- return err;
+ return;
}
if (enable) {
- if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
- changed = true;
+ changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
} else {
- if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
- changed = true;
+ changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ if (!changed)
+ changed = test_and_clear_bit(HCI_HS_ENABLED,
+ &hdev->dev_flags);
+ else
+ clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
}
mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
if (changed)
- err = new_settings(hdev, match.sk);
+ new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
@@ -4040,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
clear_eir(&req);
hci_req_run(&req, NULL);
-
- return err;
}
static void sk_lookup(struct pending_cmd *cmd, void *data)
@@ -4054,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data)
}
}
-int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
- u8 status)
+void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
+ u8 status)
{
struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
- int err = 0;
mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match);
mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match);
mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match);
if (!status)
- err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class,
- 3, NULL);
+ mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3,
+ NULL);
if (match.sk)
sock_put(match.sk);
-
- return err;
}
-int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
+void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
{
struct mgmt_cp_set_local_name ev;
struct pending_cmd *cmd;
if (status)
- return 0;
+ return;
memset(&ev, 0, sizeof(ev));
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
@@ -4094,96 +4944,54 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
* HCI dev don't send any mgmt signals.
*/
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
- return 0;
+ return;
}
- return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
}
-int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
+ u8 *randomizer, u8 status)
{
struct pending_cmd *cmd;
- int err;
BT_DBG("%s status %u", hdev->name, status);
cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
if (!cmd)
- return -ENOENT;
+ return;
if (status) {
- err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(status));
+ cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ mgmt_status(status));
} else {
struct mgmt_rp_read_local_oob_data rp;
memcpy(rp.hash, hash, sizeof(rp.hash));
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
- err = cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
- sizeof(rp));
+ cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ 0, &rp, sizeof(rp));
}
mgmt_pending_remove(cmd);
-
- return err;
}
-int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
-{
- struct cmd_lookup match = { NULL, hdev };
- bool changed = false;
- int err = 0;
-
- if (status) {
- u8 mgmt_err = mgmt_status(status);
-
- if (enable && test_and_clear_bit(HCI_LE_ENABLED,
- &hdev->dev_flags))
- err = new_settings(hdev, NULL);
-
- mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
- &mgmt_err);
-
- return err;
- }
-
- if (enable) {
- if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags))
- changed = true;
- } else {
- if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags))
- changed = true;
- }
-
- mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
-
- if (changed)
- err = new_settings(hdev, match.sk);
-
- if (match.sk)
- sock_put(match.sk);
-
- return err;
-}
-
-int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
- ssp, u8 *eir, u16 eir_len)
+void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
+ ssp, u8 *eir, u16 eir_len)
{
char buf[512];
struct mgmt_ev_device_found *ev = (void *) buf;
size_t ev_size;
if (!hci_discovery_active(hdev))
- return -EPERM;
+ return;
/* Leave 5 bytes for a potential CoD field */
if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
- return -EINVAL;
+ return;
memset(buf, 0, sizeof(buf));
@@ -4205,11 +5013,11 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->eir_len = cpu_to_le16(eir_len);
ev_size = sizeof(*ev) + eir_len;
- return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
+ mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
}
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, s8 rssi, u8 *name, u8 name_len)
+void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, s8 rssi, u8 *name, u8 name_len)
{
struct mgmt_ev_device_found *ev;
char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
@@ -4228,11 +5036,10 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->eir_len = cpu_to_le16(eir_len);
- return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
- sizeof(*ev) + eir_len, NULL);
+ mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL);
}
-int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
+void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
{
struct mgmt_ev_discovering ev;
struct pending_cmd *cmd;
@@ -4256,7 +5063,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
ev.type = hdev->discovery.type;
ev.discovering = discovering;
- return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
+ mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
}
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
@@ -4287,5 +5094,35 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
cmd ? cmd->sk : NULL);
}
-module_param(enable_hs, bool, 0644);
-MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
+static void adv_enable_complete(struct hci_dev *hdev, u8 status)
+{
+ BT_DBG("%s status %u", hdev->name, status);
+
+ /* Clear the advertising mgmt setting if we failed to re-enable it */
+ if (status) {
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ new_settings(hdev, NULL);
+ }
+}
+
+void mgmt_reenable_advertising(struct hci_dev *hdev)
+{
+ struct hci_request req;
+
+ if (hci_conn_num(hdev, LE_LINK) > 0)
+ return;
+
+ if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+ return;
+
+ hci_req_init(&req, hdev);
+ enable_advertising(&req);
+
+ /* If this fails we have no option but to let user space know
+ * that we've disabled advertising.
+ */
+ if (hci_req_run(&req, adv_enable_complete) < 0) {
+ clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+ new_settings(hdev, NULL);
+ }
+}
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index ca957d34b0c8..94d06cbfbc18 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -641,13 +641,13 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
{
struct rfcomm_session *s;
struct list_head *p, *n;
- struct bt_sock *sk;
+ struct l2cap_chan *chan;
list_for_each_safe(p, n, &session_list) {
s = list_entry(p, struct rfcomm_session, list);
- sk = bt_sk(s->sock->sk);
+ chan = l2cap_pi(s->sock->sk)->chan;
- if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
- !bacmp(&sk->dst, dst))
+ if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) &&
+ !bacmp(&chan->dst, dst))
return s;
}
return NULL;
@@ -732,11 +732,11 @@ failed:
void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
{
- struct sock *sk = s->sock->sk;
+ struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
if (src)
- bacpy(src, &bt_sk(sk)->src);
+ bacpy(src, &chan->src);
if (dst)
- bacpy(dst, &bt_sk(sk)->dst);
+ bacpy(dst, &chan->dst);
}
/* ---- RFCOMM frame sending ---- */
@@ -2112,12 +2112,11 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
rfcomm_lock();
list_for_each_entry(s, &session_list, list) {
+ struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan;
struct rfcomm_dlc *d;
list_for_each_entry(d, &s->dlcs, list) {
- struct sock *sk = s->sock->sk;
-
seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n",
- &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ &chan->src, &chan->dst,
d->state, d->dlci, d->mtu,
d->rx_credits, d->tx_credits);
}
@@ -2155,13 +2154,6 @@ static int __init rfcomm_init(void)
goto unregister;
}
- if (bt_debugfs) {
- rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
- bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
- if (!rfcomm_dlc_debugfs)
- BT_ERR("Failed to create RFCOMM debug file");
- }
-
err = rfcomm_init_ttys();
if (err < 0)
goto stop;
@@ -2172,6 +2164,13 @@ static int __init rfcomm_init(void)
BT_INFO("RFCOMM ver %s", VERSION);
+ if (IS_ERR_OR_NULL(bt_debugfs))
+ return 0;
+
+ rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
+ bt_debugfs, NULL,
+ &rfcomm_dlc_debugfs_fops);
+
return 0;
cleanup:
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 30b3721dc6d7..c4d3d423f89b 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -87,7 +87,8 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
parent->sk_data_ready(parent, 0);
} else {
if (d->state == BT_CONNECTED)
- rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL);
+ rfcomm_session_getaddr(d->session,
+ &rfcomm_pi(sk)->src, NULL);
sk->sk_state_change(sk);
}
@@ -110,7 +111,7 @@ static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
sk_for_each(sk, &rfcomm_sk_list.head) {
if (rfcomm_pi(sk)->channel == channel &&
- !bacmp(&bt_sk(sk)->src, src))
+ !bacmp(&rfcomm_pi(sk)->src, src))
break;
}
@@ -132,11 +133,11 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *
if (rfcomm_pi(sk)->channel == channel) {
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
+ if (!bacmp(&rfcomm_pi(sk)->src, src))
break;
/* Closest match */
- if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+ if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY))
sk1 = sk;
}
}
@@ -355,7 +356,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
err = -EADDRINUSE;
} else {
/* Save source address */
- bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr);
+ bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
rfcomm_pi(sk)->channel = sa->rc_channel;
sk->sk_state = BT_BOUND;
}
@@ -393,13 +394,14 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
}
sk->sk_state = BT_CONNECT;
- bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
+ bacpy(&rfcomm_pi(sk)->dst, &sa->rc_bdaddr);
rfcomm_pi(sk)->channel = sa->rc_channel;
d->sec_level = rfcomm_pi(sk)->sec_level;
d->role_switch = rfcomm_pi(sk)->role_switch;
- err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+ err = rfcomm_dlc_open(d, &rfcomm_pi(sk)->src, &sa->rc_bdaddr,
+ sa->rc_channel);
if (!err)
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
@@ -429,7 +431,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog)
}
if (!rfcomm_pi(sk)->channel) {
- bdaddr_t *src = &bt_sk(sk)->src;
+ bdaddr_t *src = &rfcomm_pi(sk)->src;
u8 channel;
err = -EINVAL;
@@ -530,9 +532,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
sa->rc_family = AF_BLUETOOTH;
sa->rc_channel = rfcomm_pi(sk)->channel;
if (peer)
- bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst);
+ bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->dst);
else
- bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src);
+ bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->src);
*len = sizeof(struct sockaddr_rc);
return 0;
@@ -544,7 +546,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sock *sk = sock->sk;
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
struct sk_buff *skb;
- int sent = 0;
+ int sent;
if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
return -ENOTCONN;
@@ -559,6 +561,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
lock_sock(sk);
+ sent = bt_sock_wait_ready(sk, msg->msg_flags);
+ if (sent)
+ goto done;
+
while (len) {
size_t size = min_t(size_t, len, d->mtu);
int err;
@@ -594,6 +600,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
len -= size;
}
+done:
release_sock(sk);
return sent;
@@ -946,8 +953,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM);
rfcomm_sock_init(sk, parent);
- bacpy(&bt_sk(sk)->src, &src);
- bacpy(&bt_sk(sk)->dst, &dst);
+ bacpy(&rfcomm_pi(sk)->src, &src);
+ bacpy(&rfcomm_pi(sk)->dst, &dst);
rfcomm_pi(sk)->channel = channel;
sk->sk_state = BT_CONFIG;
@@ -974,7 +981,7 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
sk_for_each(sk, &rfcomm_sk_list.head) {
seq_printf(f, "%pMR %pMR %d %d\n",
- &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ &rfcomm_pi(sk)->src, &rfcomm_pi(sk)->dst,
sk->sk_state, rfcomm_pi(sk)->channel);
}
@@ -1044,15 +1051,15 @@ int __init rfcomm_init_sockets(void)
goto error;
}
- if (bt_debugfs) {
- rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
- bt_debugfs, NULL, &rfcomm_sock_debugfs_fops);
- if (!rfcomm_sock_debugfs)
- BT_ERR("Failed to create RFCOMM debug file");
- }
-
BT_INFO("RFCOMM socket layer initialized");
+ if (IS_ERR_OR_NULL(bt_debugfs))
+ return 0;
+
+ rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
+ bt_debugfs, NULL,
+ &rfcomm_sock_debugfs_fops);
+
return 0;
error:
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 96bd388d93a4..12a0e51e21e1 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -92,9 +92,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
hcon->sco_data = conn;
conn->hcon = hcon;
- conn->src = &hdev->bdaddr;
- conn->dst = &hcon->dst;
-
if (hdev->sco_mtu > 0)
conn->mtu = hdev->sco_mtu;
else
@@ -156,16 +153,14 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
static int sco_connect(struct sock *sk)
{
- bdaddr_t *src = &bt_sk(sk)->src;
- bdaddr_t *dst = &bt_sk(sk)->dst;
struct sco_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
int err, type;
- BT_DBG("%pMR -> %pMR", src, dst);
+ BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst);
- hdev = hci_get_route(dst, src);
+ hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src);
if (!hdev)
return -EHOSTUNREACH;
@@ -182,7 +177,8 @@ static int sco_connect(struct sock *sk)
goto done;
}
- hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
+ hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst,
+ sco_pi(sk)->setting);
if (IS_ERR(hcon)) {
err = PTR_ERR(hcon);
goto done;
@@ -196,7 +192,7 @@ static int sco_connect(struct sock *sk)
}
/* Update source addr of the socket */
- bacpy(src, conn->src);
+ bacpy(&sco_pi(sk)->src, &hcon->src);
err = sco_chan_add(conn, sk, NULL);
if (err)
@@ -270,7 +266,7 @@ static struct sock *__sco_get_sock_listen_by_addr(bdaddr_t *ba)
if (sk->sk_state != BT_LISTEN)
continue;
- if (!bacmp(&bt_sk(sk)->src, ba))
+ if (!bacmp(&sco_pi(sk)->src, ba))
return sk;
}
@@ -291,11 +287,11 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src)
continue;
/* Exact match. */
- if (!bacmp(&bt_sk(sk)->src, src))
+ if (!bacmp(&sco_pi(sk)->src, src))
break;
/* Closest match */
- if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+ if (!bacmp(&sco_pi(sk)->src, BDADDR_ANY))
sk1 = sk;
}
@@ -475,7 +471,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
goto done;
}
- bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr);
+ bacpy(&sco_pi(sk)->src, &sa->sco_bdaddr);
sk->sk_state = BT_BOUND;
@@ -505,7 +501,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
lock_sock(sk);
/* Set destination address and psm */
- bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr);
+ bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
err = sco_connect(sk);
if (err)
@@ -522,7 +518,7 @@ done:
static int sco_sock_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
- bdaddr_t *src = &bt_sk(sk)->src;
+ bdaddr_t *src = &sco_pi(sk)->src;
int err = 0;
BT_DBG("sk %p backlog %d", sk, backlog);
@@ -626,9 +622,9 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len
*len = sizeof(struct sockaddr_sco);
if (peer)
- bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst);
+ bacpy(&sa->sco_bdaddr, &sco_pi(sk)->dst);
else
- bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src);
+ bacpy(&sa->sco_bdaddr, &sco_pi(sk)->src);
return 0;
}
@@ -999,7 +995,7 @@ static void sco_conn_ready(struct sco_conn *conn)
} else {
sco_conn_lock(conn);
- parent = sco_get_sock_listen(conn->src);
+ parent = sco_get_sock_listen(&conn->hcon->src);
if (!parent) {
sco_conn_unlock(conn);
return;
@@ -1017,8 +1013,8 @@ static void sco_conn_ready(struct sco_conn *conn)
sco_sock_init(sk, parent);
- bacpy(&bt_sk(sk)->src, conn->src);
- bacpy(&bt_sk(sk)->dst, conn->dst);
+ bacpy(&sco_pi(sk)->src, &conn->hcon->src);
+ bacpy(&sco_pi(sk)->dst, &conn->hcon->dst);
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
@@ -1051,8 +1047,8 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (sk->sk_state != BT_LISTEN)
continue;
- if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
- !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) ||
+ !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) {
lm |= HCI_LM_ACCEPT;
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
@@ -1111,8 +1107,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
read_lock(&sco_sk_list.lock);
sk_for_each(sk, &sco_sk_list.head) {
- seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src,
- &bt_sk(sk)->dst, sk->sk_state);
+ seq_printf(f, "%pMR %pMR %d\n", &sco_pi(sk)->src,
+ &sco_pi(sk)->dst, sk->sk_state);
}
read_unlock(&sco_sk_list.lock);
@@ -1181,15 +1177,14 @@ int __init sco_init(void)
goto error;
}
- if (bt_debugfs) {
- sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
- NULL, &sco_debugfs_fops);
- if (!sco_debugfs)
- BT_ERR("Failed to create SCO debug file");
- }
-
BT_INFO("SCO socket layer initialized");
+ if (IS_ERR_OR_NULL(bt_debugfs))
+ return 0;
+
+ sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs,
+ NULL, &sco_debugfs_fops);
+
return 0;
error:
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index b5562abdd6e0..85a2796cac61 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -28,7 +28,8 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h>
-#include <net/bluetooth/smp.h>
+
+#include "smp.h"
#define SMP_TIMEOUT msecs_to_jiffies(30000)
@@ -85,8 +86,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
}
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 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;
@@ -126,8 +127,8 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
return err;
}
-static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
- u8 r1[16], u8 r2[16], u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
+ u8 r2[16], u8 _r[16])
{
int err;
@@ -150,7 +151,7 @@ static int smp_rand(u8 *buf)
}
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
- u16 dlen, void *data)
+ u16 dlen, void *data)
{
struct sk_buff *skb;
struct l2cap_hdr *lh;
@@ -213,9 +214,8 @@ static __u8 seclevel_to_authreq(__u8 sec_level)
}
static void build_pairing_cmd(struct l2cap_conn *conn,
- struct smp_cmd_pairing *req,
- struct smp_cmd_pairing *rsp,
- __u8 authreq)
+ struct smp_cmd_pairing *req,
+ struct smp_cmd_pairing *rsp, __u8 authreq)
{
u8 dist_keys = 0;
@@ -249,7 +249,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
struct smp_chan *smp = conn->smp_chan;
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
- (max_key_size < SMP_MIN_ENC_KEY_SIZE))
+ (max_key_size < SMP_MIN_ENC_KEY_SIZE))
return SMP_ENC_KEY_SIZE;
smp->enc_key_size = max_key_size;
@@ -263,15 +263,15 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
if (send)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
- &reason);
+ &reason);
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
- mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
- hcon->dst_type, HCI_ERROR_AUTH_FAILURE);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
+ mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type,
+ HCI_ERROR_AUTH_FAILURE);
cancel_delayed_work_sync(&conn->security_timer);
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
smp_chan_destroy(conn);
}
@@ -309,8 +309,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
/* If either side has unknown io_caps, use JUST WORKS */
/* Otherwise, look up method from the table */
if (!(auth & SMP_AUTH_MITM) ||
- local_io > SMP_IO_KEYBOARD_DISPLAY ||
- remote_io > SMP_IO_KEYBOARD_DISPLAY)
+ local_io > SMP_IO_KEYBOARD_DISPLAY ||
+ remote_io > SMP_IO_KEYBOARD_DISPLAY)
method = JUST_WORKS;
else
method = gen_method[remote_io][local_io];
@@ -354,10 +354,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
hci_dev_lock(hcon->hdev);
if (method == REQ_PASSKEY)
- ret = mgmt_user_passkey_request(hcon->hdev, conn->dst,
+ ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type);
else
- ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
+ ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type,
cpu_to_le32(passkey), 0);
@@ -386,12 +386,13 @@ static void confirm_work(struct work_struct *work)
smp->tfm = tfm;
if (conn->hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0,
- conn->src, conn->hcon->dst_type, conn->dst, res);
+ ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ conn->hcon->src_type, &conn->hcon->src,
+ conn->hcon->dst_type, &conn->hcon->dst, res);
else
ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
- conn->hcon->dst_type, conn->dst, 0, conn->src,
- res);
+ conn->hcon->dst_type, &conn->hcon->dst,
+ conn->hcon->src_type, &conn->hcon->src, res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -425,11 +426,13 @@ static void random_work(struct work_struct *work)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
if (hcon->out)
- ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
- conn->src, hcon->dst_type, conn->dst, res);
+ ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ hcon->src_type, &hcon->src,
+ hcon->dst_type, &hcon->dst, res);
else
ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
- hcon->dst_type, conn->dst, 0, conn->src, res);
+ hcon->dst_type, &hcon->dst,
+ hcon->src_type, &hcon->src, res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -477,9 +480,9 @@ static void random_work(struct work_struct *work)
swap128(key, stk);
memset(stk + smp->enc_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
- hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type,
+ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
ediv, rand);
}
@@ -494,7 +497,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
{
struct smp_chan *smp;
- smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC);
+ smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
if (!smp)
return NULL;
@@ -649,7 +652,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
if ((req->auth_req & SMP_AUTH_BONDING) &&
- (rsp->auth_req & SMP_AUTH_BONDING))
+ (rsp->auth_req & SMP_AUTH_BONDING))
auth = SMP_AUTH_BONDING;
auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
@@ -684,7 +687,7 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
swap128(smp->prnd, random);
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
- random);
+ random);
} else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) {
queue_work(hdev->workqueue, &smp->confirm);
} else {
@@ -714,7 +717,7 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type);
+ key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type);
if (!key)
return 0;
@@ -728,8 +731,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
hcon->enc_key_size = key->enc_size;
return 1;
-
}
+
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_security_req *rp = (void *) skb->data;
@@ -835,9 +838,9 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
skb_pull(skb, sizeof(*rp));
hci_dev_lock(hdev);
- authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH);
- hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
- HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size,
+ authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
+ hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1,
+ authenticated, smp->tk, smp->enc_key_size,
rp->ediv, rp->rand);
smp_distribute_keys(conn, 1);
hci_dev_unlock(hdev);
@@ -847,16 +850,27 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
{
- __u8 code = skb->data[0];
- __u8 reason;
+ struct hci_conn *hcon = conn->hcon;
+ __u8 code, reason;
int err = 0;
- if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
+ if (hcon->type != LE_LINK) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (skb->len < 1) {
+ kfree_skb(skb);
+ return -EILSEQ;
+ }
+
+ if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
err = -ENOTSUPP;
reason = SMP_PAIRING_NOTSUPP;
goto done;
}
+ code = skb->data[0];
skb_pull(skb, sizeof(code));
/*
@@ -974,7 +988,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
authenticated = hcon->sec_level == BT_SECURITY_HIGH;
- hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
+ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
HCI_SMP_LTK_SLAVE, 1, authenticated,
enc.ltk, smp->enc_key_size, ediv, ident.rand);
@@ -996,10 +1010,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
/* Just public address */
memset(&addrinfo, 0, sizeof(addrinfo));
- bacpy(&addrinfo.bdaddr, conn->src);
+ bacpy(&addrinfo.bdaddr, &conn->hcon->src);
smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
- &addrinfo);
+ &addrinfo);
*keydist &= ~SMP_DIST_ID_KEY;
}
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
new file mode 100644
index 000000000000..f8ba07f3e5fa
--- /dev/null
+++ b/net/bluetooth/smp.h
@@ -0,0 +1,146 @@
+/*
+ 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.
+*/
+
+#ifndef __SMP_H
+#define __SMP_H
+
+struct smp_command_hdr {
+ __u8 code;
+} __packed;
+
+#define SMP_CMD_PAIRING_REQ 0x01
+#define SMP_CMD_PAIRING_RSP 0x02
+struct smp_cmd_pairing {
+ __u8 io_capability;
+ __u8 oob_flag;
+ __u8 auth_req;
+ __u8 max_key_size;
+ __u8 init_key_dist;
+ __u8 resp_key_dist;
+} __packed;
+
+#define SMP_IO_DISPLAY_ONLY 0x00
+#define SMP_IO_DISPLAY_YESNO 0x01
+#define SMP_IO_KEYBOARD_ONLY 0x02
+#define SMP_IO_NO_INPUT_OUTPUT 0x03
+#define SMP_IO_KEYBOARD_DISPLAY 0x04
+
+#define SMP_OOB_NOT_PRESENT 0x00
+#define SMP_OOB_PRESENT 0x01
+
+#define SMP_DIST_ENC_KEY 0x01
+#define SMP_DIST_ID_KEY 0x02
+#define SMP_DIST_SIGN 0x04
+
+#define SMP_AUTH_NONE 0x00
+#define SMP_AUTH_BONDING 0x01
+#define SMP_AUTH_MITM 0x04
+
+#define SMP_CMD_PAIRING_CONFIRM 0x03
+struct smp_cmd_pairing_confirm {
+ __u8 confirm_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_RANDOM 0x04
+struct smp_cmd_pairing_random {
+ __u8 rand_val[16];
+} __packed;
+
+#define SMP_CMD_PAIRING_FAIL 0x05
+struct smp_cmd_pairing_fail {
+ __u8 reason;
+} __packed;
+
+#define SMP_CMD_ENCRYPT_INFO 0x06
+struct smp_cmd_encrypt_info {
+ __u8 ltk[16];
+} __packed;
+
+#define SMP_CMD_MASTER_IDENT 0x07
+struct smp_cmd_master_ident {
+ __le16 ediv;
+ __u8 rand[8];
+} __packed;
+
+#define SMP_CMD_IDENT_INFO 0x08
+struct smp_cmd_ident_info {
+ __u8 irk[16];
+} __packed;
+
+#define SMP_CMD_IDENT_ADDR_INFO 0x09
+struct smp_cmd_ident_addr_info {
+ __u8 addr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define SMP_CMD_SIGN_INFO 0x0a
+struct smp_cmd_sign_info {
+ __u8 csrk[16];
+} __packed;
+
+#define SMP_CMD_SECURITY_REQ 0x0b
+struct smp_cmd_security_req {
+ __u8 auth_req;
+} __packed;
+
+#define SMP_PASSKEY_ENTRY_FAILED 0x01
+#define SMP_OOB_NOT_AVAIL 0x02
+#define SMP_AUTH_REQUIREMENTS 0x03
+#define SMP_CONFIRM_FAILED 0x04
+#define SMP_PAIRING_NOTSUPP 0x05
+#define SMP_ENC_KEY_SIZE 0x06
+#define SMP_CMD_NOTSUPP 0x07
+#define SMP_UNSPECIFIED 0x08
+#define SMP_REPEATED_ATTEMPTS 0x09
+
+#define SMP_MIN_ENC_KEY_SIZE 7
+#define SMP_MAX_ENC_KEY_SIZE 16
+
+#define SMP_FLAG_TK_VALID 1
+#define SMP_FLAG_CFM_PENDING 2
+#define SMP_FLAG_MITM_AUTH 3
+
+struct smp_chan {
+ struct l2cap_conn *conn;
+ u8 preq[7]; /* SMP Pairing Request */
+ u8 prsp[7]; /* SMP Pairing Response */
+ u8 prnd[16]; /* SMP Pairing Random (local) */
+ u8 rrnd[16]; /* SMP Pairing Random (remote) */
+ u8 pcnf[16]; /* SMP Pairing Confirm */
+ u8 tk[16]; /* SMP Temporary Key */
+ u8 enc_key_size;
+ unsigned long smp_flags;
+ struct crypto_blkcipher *tfm;
+ struct work_struct confirm;
+ struct work_struct random;
+
+};
+
+/* SMP Commands */
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
+int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
+int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
+int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
+
+void smp_chan_destroy(struct l2cap_conn *conn);
+
+#endif /* __SMP_H */