summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/Kconfig1
-rw-r--r--net/bluetooth/af_bluetooth.c12
-rw-r--r--net/bluetooth/bnep/sock.c7
-rw-r--r--net/bluetooth/cmtp/sock.c7
-rw-r--r--net/bluetooth/hci_conn.c76
-rw-r--r--net/bluetooth/hci_core.c649
-rw-r--r--net/bluetooth/hci_event.c632
-rw-r--r--net/bluetooth/hci_sock.c470
-rw-r--r--net/bluetooth/hci_sysfs.c53
-rw-r--r--net/bluetooth/hidp/sock.c6
-rw-r--r--net/bluetooth/l2cap_core.c642
-rw-r--r--net/bluetooth/l2cap_sock.c54
-rw-r--r--net/bluetooth/lib.c27
-rw-r--r--net/bluetooth/mgmt.c2647
-rw-r--r--net/bluetooth/rfcomm/core.c18
-rw-r--r--net/bluetooth/rfcomm/sock.c3
-rw-r--r--net/bluetooth/rfcomm/tty.c7
-rw-r--r--net/bluetooth/sco.c1
-rw-r--r--net/bluetooth/smp.c108
19 files changed, 3612 insertions, 1808 deletions
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 9ec85eb8853d..3537d385035e 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -29,7 +29,6 @@ menuconfig BT
BNEP Module (Bluetooth Network Encapsulation Protocol)
CMTP Module (CAPI Message Transport Protocol)
HIDP Module (Human Interface Device Protocol)
- SMP Module (Security Manager Protocol)
Say Y here to compile Bluetooth support into the kernel or say M to
compile it as module (bluetooth).
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index ef92864ac625..72eb187a5f60 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -71,19 +71,16 @@ static const char *const bt_slock_key_strings[BT_MAX_PROTO] = {
"slock-AF_BLUETOOTH-BTPROTO_AVDTP",
};
-static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+void bt_sock_reclassify_lock(struct sock *sk, int proto)
{
- struct sock *sk = sock->sk;
-
- if (!sk)
- return;
-
+ BUG_ON(!sk);
BUG_ON(sock_owned_by_user(sk));
sock_lock_init_class_and_name(sk,
bt_slock_key_strings[proto], &bt_slock_key[proto],
bt_key_strings[proto], &bt_lock_key[proto]);
}
+EXPORT_SYMBOL(bt_sock_reclassify_lock);
int bt_sock_register(int proto, const struct net_proto_family *ops)
{
@@ -145,7 +142,8 @@ static int bt_sock_create(struct net *net, struct socket *sock, int proto,
if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) {
err = bt_proto[proto]->create(net, sock, proto, kern);
- bt_sock_reclassify_lock(sock, proto);
+ if (!err)
+ bt_sock_reclassify_lock(sock->sk, proto);
module_put(bt_proto[proto]->owner);
}
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 17800b1d28ea..180bfc45810d 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -42,7 +42,6 @@
#include <linux/uaccess.h>
#include <net/sock.h>
-#include <asm/system.h>
#include "bnep.h"
@@ -143,10 +142,10 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == BNEPGETCONNLIST) {
struct bnep_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -157,7 +156,7 @@ static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = bnep_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 3f2dd5c25ae5..311668d14571 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -39,7 +39,6 @@
#include <linux/isdn/capilli.h>
-#include <asm/system.h>
#include "cmtp.h"
@@ -137,10 +136,10 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == CMTPGETCONNLIST) {
struct cmtp_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -151,7 +150,7 @@ static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = cmtp_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 3db432473ad5..5238b6b3ea6a 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -35,10 +35,8 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
@@ -51,7 +49,7 @@ static void hci_le_connect(struct hci_conn *conn)
struct hci_cp_le_create_conn cp;
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
conn->sec_level = BT_SECURITY_LOW;
@@ -80,10 +78,10 @@ void hci_acl_connect(struct hci_conn *conn)
struct inquiry_entry *ie;
struct hci_cp_create_conn cp;
- BT_DBG("%p", conn);
+ BT_DBG("hcon %p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->link_mode = HCI_LM_MASTER;
@@ -105,7 +103,8 @@ void hci_acl_connect(struct hci_conn *conn)
}
memcpy(conn->dev_class, ie->data.dev_class, 3);
- conn->ssp_mode = ie->data.ssp_mode;
+ if (ie->data.ssp_mode > 0)
+ set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
cp.pkt_type = cpu_to_le16(conn->pkt_type);
@@ -151,7 +150,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->attempt++;
@@ -169,7 +168,7 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
BT_DBG("%p", conn);
conn->state = BT_CONNECT;
- conn->out = 1;
+ conn->out = true;
conn->attempt++;
@@ -279,16 +278,13 @@ static void hci_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
disc_work.work);
- struct hci_dev *hdev = conn->hdev;
__u8 reason;
- BT_DBG("conn %p state %d", conn, conn->state);
+ BT_DBG("conn %p state %s", conn, state_to_string(conn->state));
if (atomic_read(&conn->refcnt))
return;
- hci_dev_lock(hdev);
-
switch (conn->state) {
case BT_CONNECT:
case BT_CONNECT2:
@@ -308,8 +304,6 @@ static void hci_conn_timeout(struct work_struct *work)
conn->state = BT_CLOSED;
break;
}
-
- hci_dev_unlock(hdev);
}
/* Enter sniff mode */
@@ -337,7 +331,7 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
}
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
struct hci_cp_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
@@ -372,7 +366,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
BT_DBG("%s dst %s", hdev->name, batostr(dst));
- conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC);
+ conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
if (!conn)
return NULL;
@@ -386,7 +380,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->remote_auth = 0xff;
conn->key_type = 0xff;
- conn->power_save = 1;
+ set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
switch (type) {
@@ -407,7 +401,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q);
- INIT_LIST_HEAD(&conn->chan_list);;
+ 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);
@@ -555,7 +549,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
if (!acl) {
acl = hci_conn_add(hdev, ACL_LINK, dst);
if (!acl)
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
hci_conn_hold(acl);
@@ -575,7 +569,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
sco = hci_conn_add(hdev, type, dst);
if (!sco) {
hci_conn_put(acl);
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
}
@@ -586,12 +580,12 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
- acl->power_save = 1;
+ set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON);
- if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) {
+ if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) {
/* defer SCO setup until mode change completed */
- set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->pend);
+ set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags);
return sco;
}
@@ -607,8 +601,7 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
- if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0 &&
- !(conn->link_mode & HCI_LM_ENCRYPT))
+ if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT))
return 0;
return 1;
@@ -633,13 +626,17 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
conn->auth_type = auth_type;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
struct hci_cp_auth_requested cp;
+
+ /* encrypt must be pending if auth is also pending */
+ set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
sizeof(cp), &cp);
if (conn->key_type != 0xff)
- set_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
+ set_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
}
return 0;
@@ -650,7 +647,7 @@ static void hci_conn_encrypt(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
- if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = cpu_to_le16(conn->handle);
cp.encrypt = 0x01;
@@ -670,8 +667,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
/* For non 2.1 devices and low security level we don't need the link
key. */
- if (sec_level == BT_SECURITY_LOW &&
- (!conn->ssp_mode || !conn->hdev->ssp_mode))
+ if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn))
return 1;
/* For other security levels we need the link key. */
@@ -700,7 +696,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
goto encrypt;
auth:
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
return 0;
if (!hci_conn_auth(conn, sec_level, auth_type))
@@ -735,7 +731,7 @@ int hci_conn_change_link_key(struct hci_conn *conn)
{
BT_DBG("conn %p", conn);
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
struct hci_cp_change_conn_link_key cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
@@ -754,7 +750,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
if (!role && conn->link_mode & HCI_LM_MASTER)
return 1;
- if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
struct hci_cp_switch_role cp;
bacpy(&cp.bdaddr, &conn->dst);
cp.role = role;
@@ -778,10 +774,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
if (conn->mode != HCI_CM_SNIFF)
goto timer;
- if (!conn->power_save && !force_active)
+ if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active)
goto timer;
- if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
struct hci_cp_exit_sniff_mode cp;
cp.handle = cpu_to_le16(conn->handle);
hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp);
@@ -797,11 +793,11 @@ timer:
void hci_conn_hash_flush(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
- struct hci_conn *c;
+ struct hci_conn *c, *n;
BT_DBG("hdev %s", hdev->name);
- list_for_each_entry_rcu(c, &h->list, list) {
+ list_for_each_entry_safe(c, n, &h->list, list) {
c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
@@ -946,7 +942,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
BT_DBG("%s conn %p", hdev->name, conn);
- chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+ chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL);
if (!chan)
return NULL;
@@ -977,10 +973,10 @@ int hci_chan_del(struct hci_chan *chan)
void hci_chan_list_flush(struct hci_conn *conn)
{
- struct hci_chan *chan;
+ struct hci_chan *chan, *n;
BT_DBG("conn %p", conn);
- list_for_each_entry_rcu(chan, &conn->chan_list, list)
+ list_for_each_entry_safe(chan, n, &conn->chan_list, list)
hci_chan_del(chan);
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 9de93714213a..e33af63a884a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -40,13 +40,11 @@
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <linux/rfkill.h>
#include <linux/timer.h>
#include <linux/crypto.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
@@ -55,8 +53,6 @@
#define AUTO_OFF_TIMEOUT 2000
-bool enable_hs;
-
static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
static void hci_tx_work(struct work_struct *work);
@@ -69,24 +65,11 @@ DEFINE_RWLOCK(hci_dev_list_lock);
LIST_HEAD(hci_cb_list);
DEFINE_RWLOCK(hci_cb_list_lock);
-/* HCI notifiers list */
-static ATOMIC_NOTIFIER_HEAD(hci_notifier);
-
/* ---- HCI notifications ---- */
-int hci_register_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&hci_notifier, nb);
-}
-
-int hci_unregister_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&hci_notifier, nb);
-}
-
static void hci_notify(struct hci_dev *hdev, int event)
{
- atomic_notifier_call_chain(&hci_notifier, event, hdev);
+ hci_sock_dev_event(hdev, event);
}
/* ---- HCI requests ---- */
@@ -98,8 +81,28 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result)
/* If this is the init phase check if the completed command matches
* the last init command, and if not just return.
*/
- if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd)
+ if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) {
+ struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
+ struct sk_buff *skb;
+
+ /* Some CSR based controllers generate a spontaneous
+ * reset complete event during init and any pending
+ * command will never be completed. In such a case we
+ * need to resend whatever was the last sent
+ * command.
+ */
+
+ if (cmd != HCI_OP_RESET || sent->opcode == HCI_OP_RESET)
+ return;
+
+ skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC);
+ if (skb) {
+ skb_queue_head(&hdev->cmd_q, skb);
+ queue_work(hdev->workqueue, &hdev->cmd_work);
+ }
+
return;
+ }
if (hdev->req_status == HCI_REQ_PEND) {
hdev->req_result = result;
@@ -355,72 +358,209 @@ struct hci_dev *hci_dev_get(int index)
}
/* ---- Inquiry support ---- */
-static void inquiry_cache_flush(struct hci_dev *hdev)
+
+bool hci_discovery_active(struct hci_dev *hdev)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
- struct inquiry_entry *next = cache->list, *e;
+ struct discovery_state *discov = &hdev->discovery;
+
+ switch (discov->state) {
+ case DISCOVERY_FINDING:
+ case DISCOVERY_RESOLVING:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void hci_discovery_set_state(struct hci_dev *hdev, int state)
+{
+ BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state);
+
+ if (hdev->discovery.state == state)
+ return;
+
+ switch (state) {
+ case DISCOVERY_STOPPED:
+ if (hdev->discovery.state != DISCOVERY_STARTING)
+ mgmt_discovering(hdev, 0);
+ hdev->discovery.type = 0;
+ break;
+ case DISCOVERY_STARTING:
+ break;
+ case DISCOVERY_FINDING:
+ mgmt_discovering(hdev, 1);
+ break;
+ case DISCOVERY_RESOLVING:
+ break;
+ case DISCOVERY_STOPPING:
+ break;
+ }
+
+ hdev->discovery.state = state;
+}
- BT_DBG("cache %p", cache);
+static void inquiry_cache_flush(struct hci_dev *hdev)
+{
+ struct discovery_state *cache = &hdev->discovery;
+ struct inquiry_entry *p, *n;
- cache->list = NULL;
- while ((e = next)) {
- next = e->next;
- kfree(e);
+ list_for_each_entry_safe(p, n, &cache->all, all) {
+ list_del(&p->all);
+ kfree(p);
}
+
+ INIT_LIST_HEAD(&cache->unknown);
+ INIT_LIST_HEAD(&cache->resolve);
}
struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
- for (e = cache->list; e; e = e->next)
+ list_for_each_entry(e, &cache->all, all) {
+ if (!bacmp(&e->data.bdaddr, bdaddr))
+ return e;
+ }
+
+ return NULL;
+}
+
+struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
+ bdaddr_t *bdaddr)
+{
+ struct discovery_state *cache = &hdev->discovery;
+ struct inquiry_entry *e;
+
+ BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+
+ list_for_each_entry(e, &cache->unknown, list) {
+ if (!bacmp(&e->data.bdaddr, bdaddr))
+ return e;
+ }
+
+ return NULL;
+}
+
+struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev,
+ bdaddr_t *bdaddr,
+ int state)
+{
+ struct discovery_state *cache = &hdev->discovery;
+ struct inquiry_entry *e;
+
+ BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state);
+
+ list_for_each_entry(e, &cache->resolve, list) {
+ if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state)
+ return e;
if (!bacmp(&e->data.bdaddr, bdaddr))
+ return e;
+ }
+
+ return NULL;
+}
+
+void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
+ struct inquiry_entry *ie)
+{
+ struct discovery_state *cache = &hdev->discovery;
+ struct list_head *pos = &cache->resolve;
+ struct inquiry_entry *p;
+
+ list_del(&ie->list);
+
+ list_for_each_entry(p, &cache->resolve, list) {
+ if (p->name_state != NAME_PENDING &&
+ abs(p->data.rssi) >= abs(ie->data.rssi))
break;
- return e;
+ pos = &p->list;
+ }
+
+ list_add(&ie->list, pos);
}
-void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data)
+bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+ bool name_known, bool *ssp)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *ie;
BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
+ if (ssp)
+ *ssp = data->ssp_mode;
+
ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr);
- if (!ie) {
- /* Entry not in the cache. Add new one. */
- ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC);
- if (!ie)
- return;
+ if (ie) {
+ if (ie->data.ssp_mode && ssp)
+ *ssp = true;
+
+ if (ie->name_state == NAME_NEEDED &&
+ data->rssi != ie->data.rssi) {
+ ie->data.rssi = data->rssi;
+ hci_inquiry_cache_update_resolve(hdev, ie);
+ }
- ie->next = cache->list;
- cache->list = ie;
+ goto update;
+ }
+
+ /* Entry not in the cache. Add new one. */
+ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC);
+ if (!ie)
+ return false;
+
+ list_add(&ie->all, &cache->all);
+
+ if (name_known) {
+ ie->name_state = NAME_KNOWN;
+ } else {
+ ie->name_state = NAME_NOT_KNOWN;
+ list_add(&ie->list, &cache->unknown);
+ }
+
+update:
+ if (name_known && ie->name_state != NAME_KNOWN &&
+ ie->name_state != NAME_PENDING) {
+ ie->name_state = NAME_KNOWN;
+ list_del(&ie->list);
}
memcpy(&ie->data, data, sizeof(*data));
ie->timestamp = jiffies;
cache->timestamp = jiffies;
+
+ if (ie->name_state == NAME_NOT_KNOWN)
+ return false;
+
+ return true;
}
static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
{
- struct inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_info *info = (struct inquiry_info *) buf;
struct inquiry_entry *e;
int copied = 0;
- for (e = cache->list; e && copied < num; e = e->next, copied++) {
+ list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
+
+ if (copied >= num)
+ break;
+
bacpy(&info->bdaddr, &data->bdaddr);
info->pscan_rep_mode = data->pscan_rep_mode;
info->pscan_period_mode = data->pscan_period_mode;
info->pscan_mode = data->pscan_mode;
memcpy(info->dev_class, data->dev_class, 3);
info->clock_offset = data->clock_offset;
+
info++;
+ copied++;
}
BT_DBG("cache %p, copied %d", cache, copied);
@@ -567,7 +707,7 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
- if (!test_bit(HCI_SETUP, &hdev->flags)) {
+ if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);
mgmt_powered(hdev, 1);
hci_dev_unlock(hdev);
@@ -603,6 +743,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
{
BT_DBG("%s %p", hdev->name, hdev);
+ cancel_work_sync(&hdev->le_scan);
+
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
@@ -619,14 +761,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
if (hdev->discov_timeout > 0) {
cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = 0;
+ clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
}
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
- cancel_delayed_work(&hdev->power_off);
-
- if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
cancel_delayed_work(&hdev->service_cache);
+ cancel_delayed_work_sync(&hdev->le_scan_disable);
+
hci_dev_lock(hdev);
inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
@@ -640,7 +782,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
/* Reset device */
skb_queue_purge(&hdev->cmd_q);
atomic_set(&hdev->cmd_cnt, 1);
- if (!test_bit(HCI_RAW, &hdev->flags)) {
+ if (!test_bit(HCI_RAW, &hdev->flags) &&
+ test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) {
set_bit(HCI_INIT, &hdev->flags);
__hci_request(hdev, hci_reset_req, 0,
msecs_to_jiffies(250));
@@ -666,13 +809,18 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */
hdev->close(hdev);
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 0);
- hci_dev_unlock(hdev);
+ if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+ hci_dev_lock(hdev);
+ mgmt_powered(hdev, 0);
+ hci_dev_unlock(hdev);
+ }
/* Clear flags */
hdev->flags = 0;
+ memset(hdev->eir, 0, sizeof(hdev->eir));
+ memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
+
hci_req_unlock(hdev);
hci_dev_put(hdev);
@@ -687,7 +835,12 @@ int hci_dev_close(__u16 dev)
hdev = hci_dev_get(dev);
if (!hdev)
return -ENODEV;
+
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+ cancel_delayed_work(&hdev->power_off);
+
err = hci_dev_do_close(hdev);
+
hci_dev_put(hdev);
return err;
}
@@ -846,11 +999,11 @@ int hci_get_dev_list(void __user *arg)
read_lock(&hci_dev_list_lock);
list_for_each_entry(hdev, &hci_dev_list, list) {
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work(&hdev->power_off);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- set_bit(HCI_PAIRABLE, &hdev->flags);
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ set_bit(HCI_PAIRABLE, &hdev->dev_flags);
(dr + n)->dev_id = hdev->id;
(dr + n)->dev_opt = hdev->flags;
@@ -882,11 +1035,11 @@ int hci_get_dev_info(void __user *arg)
if (!hdev)
return -ENODEV;
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
cancel_delayed_work_sync(&hdev->power_off);
- if (!test_bit(HCI_MGMT, &hdev->flags))
- set_bit(HCI_PAIRABLE, &hdev->flags);
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ set_bit(HCI_PAIRABLE, &hdev->dev_flags);
strcpy(di.name, hdev->name);
di.bdaddr = hdev->bdaddr;
@@ -966,11 +1119,11 @@ static void hci_power_on(struct work_struct *work)
if (hci_dev_open(hdev->id) < 0)
return;
- if (test_bit(HCI_AUTO_OFF, &hdev->flags))
+ if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
schedule_delayed_work(&hdev->power_off,
msecs_to_jiffies(AUTO_OFF_TIMEOUT));
- if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
+ if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
mgmt_index_added(hdev);
}
@@ -981,9 +1134,7 @@ static void hci_power_off(struct work_struct *work)
BT_DBG("%s", hdev->name);
- clear_bit(HCI_AUTO_OFF, &hdev->flags);
-
- hci_dev_close(hdev->id);
+ hci_dev_do_close(hdev);
}
static void hci_discov_off(struct work_struct *work)
@@ -1036,6 +1187,18 @@ int hci_link_keys_clear(struct hci_dev *hdev)
return 0;
}
+int hci_smp_ltks_clear(struct hci_dev *hdev)
+{
+ struct smp_ltk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+ list_del(&k->list);
+ kfree(k);
+ }
+
+ return 0;
+}
+
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct link_key *k;
@@ -1083,44 +1246,38 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
return 0;
}
-struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
{
- struct link_key *k;
-
- list_for_each_entry(k, &hdev->link_keys, list) {
- struct key_master_id *id;
+ struct smp_ltk *k;
- if (k->type != HCI_LK_SMP_LTK)
+ list_for_each_entry(k, &hdev->long_term_keys, list) {
+ if (k->ediv != ediv ||
+ memcmp(rand, k->rand, sizeof(k->rand)))
continue;
- if (k->dlen != sizeof(*id))
- continue;
-
- id = (void *) &k->data;
- if (id->ediv == ediv &&
- (memcmp(rand, id->rand, sizeof(id->rand)) == 0))
- return k;
+ return k;
}
return NULL;
}
EXPORT_SYMBOL(hci_find_ltk);
-struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 type)
+struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type)
{
- struct link_key *k;
+ struct smp_ltk *k;
- list_for_each_entry(k, &hdev->link_keys, list)
- if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0)
+ list_for_each_entry(k, &hdev->long_term_keys, list)
+ if (addr_type == k->bdaddr_type &&
+ bacmp(bdaddr, &k->bdaddr) == 0)
return k;
return NULL;
}
-EXPORT_SYMBOL(hci_find_link_key_type);
+EXPORT_SYMBOL(hci_find_ltk_by_addr);
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
- bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
+ bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
{
struct link_key *key, *old_key;
u8 old_key_type, persistent;
@@ -1174,40 +1331,39 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
return 0;
}
-int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16])
+int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
+ int new_key, u8 authenticated, u8 tk[16], u8 enc_size, u16
+ ediv, u8 rand[8])
{
- struct link_key *key, *old_key;
- struct key_master_id *id;
- u8 old_key_type;
+ struct smp_ltk *key, *old_key;
- BT_DBG("%s addr %s", hdev->name, batostr(bdaddr));
+ if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK))
+ return 0;
- old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK);
- if (old_key) {
+ old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type);
+ if (old_key)
key = old_key;
- old_key_type = old_key->type;
- } else {
- key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC);
+ else {
+ key = kzalloc(sizeof(*key), GFP_ATOMIC);
if (!key)
return -ENOMEM;
- list_add(&key->list, &hdev->link_keys);
- old_key_type = 0xff;
+ list_add(&key->list, &hdev->long_term_keys);
}
- key->dlen = sizeof(*id);
-
bacpy(&key->bdaddr, bdaddr);
- memcpy(key->val, ltk, sizeof(key->val));
- key->type = HCI_LK_SMP_LTK;
- key->pin_len = key_size;
+ key->bdaddr_type = addr_type;
+ memcpy(key->val, tk, sizeof(key->val));
+ key->authenticated = authenticated;
+ key->ediv = ediv;
+ key->enc_size = enc_size;
+ key->type = type;
+ memcpy(key->rand, rand, sizeof(key->rand));
- id = (void *) &key->data;
- id->ediv = ediv;
- memcpy(id->rand, rand, sizeof(id->rand));
+ if (!new_key)
+ return 0;
- if (new_key)
- mgmt_new_link_key(hdev, key, old_key_type);
+ if (type & HCI_SMP_LTK)
+ mgmt_new_ltk(hdev, key, 1);
return 0;
}
@@ -1228,6 +1384,23 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
return 0;
}
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct smp_ltk *k, *tmp;
+
+ list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+ if (bacmp(bdaddr, &k->bdaddr))
+ continue;
+
+ BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+
+ list_del(&k->list);
+ kfree(k);
+ }
+
+ return 0;
+}
+
/* HCI command timer function */
static void hci_cmd_timer(unsigned long arg)
{
@@ -1239,7 +1412,7 @@ static void hci_cmd_timer(unsigned long arg)
}
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr)
+ bdaddr_t *bdaddr)
{
struct oob_data *data;
@@ -1279,7 +1452,7 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev)
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
- u8 *randomizer)
+ u8 *randomizer)
{
struct oob_data *data;
@@ -1302,8 +1475,7 @@ 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)
{
struct bdaddr_list *b;
@@ -1330,7 +1502,7 @@ int hci_blacklist_clear(struct hci_dev *hdev)
return 0;
}
-int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
@@ -1348,10 +1520,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_add(&entry->list, &hdev->blacklist);
- return mgmt_device_blocked(hdev, bdaddr);
+ return mgmt_device_blocked(hdev, bdaddr, type);
}
-int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct bdaddr_list *entry;
@@ -1365,13 +1537,13 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_del(&entry->list);
kfree(entry);
- return mgmt_device_unblocked(hdev, bdaddr);
+ return mgmt_device_unblocked(hdev, bdaddr, type);
}
static void hci_clear_adv_cache(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
- adv_work.work);
+ adv_work.work);
hci_dev_lock(hdev);
@@ -1414,11 +1586,7 @@ static inline int is_connectable_adv(u8 evt_type)
}
int hci_add_adv_entry(struct hci_dev *hdev,
- struct hci_ev_le_advertising_info *ev)
-{
- struct adv_entry *entry;
-
- if (!is_connectable_adv(ev->evt_type))
+ struct hci_ev_le_advertising_info *ev) { struct adv_entry *entry; if (!is_connectable_adv(ev->evt_type))
return -EINVAL;
/* Only new entries should be added to adv_entries. So, if
@@ -1426,7 +1594,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
if (hci_find_adv_entry(hdev, &ev->bdaddr))
return 0;
- entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
@@ -1441,16 +1609,116 @@ int hci_add_adv_entry(struct hci_dev *hdev,
return 0;
}
+static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt)
+{
+ struct le_scan_params *param = (struct le_scan_params *) opt;
+ struct hci_cp_le_set_scan_param cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = param->type;
+ cp.interval = cpu_to_le16(param->interval);
+ cp.window = cpu_to_le16(param->window);
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp);
+}
+
+static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt)
+{
+ struct hci_cp_le_set_scan_enable cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.enable = 1;
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+}
+
+static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
+ u16 window, int timeout)
+{
+ long timeo = msecs_to_jiffies(3000);
+ struct le_scan_params param;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ return -EINPROGRESS;
+
+ param.type = type;
+ param.interval = interval;
+ param.window = window;
+
+ hci_req_lock(hdev);
+
+ err = __hci_request(hdev, le_scan_param_req, (unsigned long) &param,
+ timeo);
+ if (!err)
+ err = __hci_request(hdev, le_scan_enable_req, 0, timeo);
+
+ hci_req_unlock(hdev);
+
+ if (err < 0)
+ return err;
+
+ schedule_delayed_work(&hdev->le_scan_disable,
+ msecs_to_jiffies(timeout));
+
+ return 0;
+}
+
+static void le_scan_disable_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev,
+ le_scan_disable.work);
+ struct hci_cp_le_set_scan_enable cp;
+
+ BT_DBG("%s", hdev->name);
+
+ memset(&cp, 0, sizeof(cp));
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+}
+
+static void le_scan_work(struct work_struct *work)
+{
+ struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan);
+ struct le_scan_params *param = &hdev->le_scan_params;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_do_le_scan(hdev, param->type, param->interval, param->window,
+ param->timeout);
+}
+
+int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
+ int timeout)
+{
+ struct le_scan_params *param = &hdev->le_scan_params;
+
+ BT_DBG("%s", hdev->name);
+
+ if (work_busy(&hdev->le_scan))
+ return -EINPROGRESS;
+
+ param->type = type;
+ param->interval = interval;
+ param->window = window;
+ param->timeout = timeout;
+
+ queue_work(system_long_wq, &hdev->le_scan);
+
+ return 0;
+}
+
/* Register HCI device */
int hci_register_dev(struct hci_dev *hdev)
{
struct list_head *head = &hci_dev_list, *p;
int i, id, error;
- BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
- hdev->bus, hdev->owner);
+ BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
- if (!hdev->open || !hdev->close || !hdev->destruct)
+ if (!hdev->open || !hdev->close)
return -EINVAL;
/* Do not allow HCI_AMP devices to register at index 0,
@@ -1471,7 +1739,6 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->id = id;
list_add_tail(&hdev->list, head);
- atomic_set(&hdev->refcnt, 1);
mutex_init(&hdev->lock);
hdev->flags = 0;
@@ -1502,7 +1769,7 @@ int hci_register_dev(struct hci_dev *hdev)
init_waitqueue_head(&hdev->req_wait_q);
mutex_init(&hdev->req_lock);
- inquiry_cache_init(hdev);
+ discovery_init(hdev);
hci_conn_hash_init(hdev);
@@ -1513,6 +1780,7 @@ int hci_register_dev(struct hci_dev *hdev)
INIT_LIST_HEAD(&hdev->uuids);
INIT_LIST_HEAD(&hdev->link_keys);
+ INIT_LIST_HEAD(&hdev->long_term_keys);
INIT_LIST_HEAD(&hdev->remote_oob_data);
@@ -1528,6 +1796,10 @@ int hci_register_dev(struct hci_dev *hdev)
atomic_set(&hdev->promisc, 0);
+ INIT_WORK(&hdev->le_scan, le_scan_work);
+
+ INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
+
write_unlock(&hci_dev_list_lock);
hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND |
@@ -1550,11 +1822,12 @@ int hci_register_dev(struct hci_dev *hdev)
}
}
- set_bit(HCI_AUTO_OFF, &hdev->flags);
- set_bit(HCI_SETUP, &hdev->flags);
+ set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+ set_bit(HCI_SETUP, &hdev->dev_flags);
schedule_work(&hdev->power_on);
hci_notify(hdev, HCI_DEV_REG);
+ hci_dev_hold(hdev);
return id;
@@ -1586,7 +1859,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
kfree_skb(hdev->reassembly[i]);
if (!test_bit(HCI_INIT, &hdev->flags) &&
- !test_bit(HCI_SETUP, &hdev->flags)) {
+ !test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);
mgmt_index_removed(hdev);
hci_dev_unlock(hdev);
@@ -1613,11 +1886,12 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_blacklist_clear(hdev);
hci_uuids_clear(hdev);
hci_link_keys_clear(hdev);
+ hci_smp_ltks_clear(hdev);
hci_remote_oob_data_clear(hdev);
hci_adv_entries_clear(hdev);
hci_dev_unlock(hdev);
- __hci_dev_put(hdev);
+ hci_dev_put(hdev);
}
EXPORT_SYMBOL(hci_unregister_dev);
@@ -1705,7 +1979,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
while (count) {
scb = (void *) skb->cb;
- len = min(scb->expect, (__u16)count);
+ len = min_t(uint, scb->expect, count);
memcpy(skb_put(skb, len), data, len);
@@ -1861,11 +2135,15 @@ static int hci_send_frame(struct sk_buff *skb)
BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
- if (atomic_read(&hdev->promisc)) {
- /* Time stamp */
- __net_timestamp(skb);
+ /* Time stamp */
+ __net_timestamp(skb);
+
+ /* Send copy to monitor */
+ hci_send_to_monitor(hdev, skb);
- hci_send_to_sock(hdev, skb, NULL);
+ if (atomic_read(&hdev->promisc)) {
+ /* Send copy to the sockets */
+ hci_send_to_sock(hdev, skb);
}
/* Get rid of skb owner, prior to sending to the driver. */
@@ -2234,26 +2512,31 @@ static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type)
}
-static inline void hci_sched_acl(struct hci_dev *hdev)
+static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct hci_chan *chan;
- struct sk_buff *skb;
- int quote;
- unsigned int cnt;
-
- BT_DBG("%s", hdev->name);
-
- if (!hci_conn_num(hdev, ACL_LINK))
- return;
+ /* Calculate count of blocks used by this packet */
+ return DIV_ROUND_UP(skb->len - HCI_ACL_HDR_SIZE, hdev->block_len);
+}
+static inline void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
+{
if (!test_bit(HCI_RAW, &hdev->flags)) {
/* ACL tx timeout must be longer than maximum
* link supervision timeout (40.9 seconds) */
- if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45))
+ if (!cnt && time_after(jiffies, hdev->acl_last_tx +
+ msecs_to_jiffies(HCI_ACL_TX_TIMEOUT)))
hci_link_tx_to(hdev, ACL_LINK);
}
+}
+
+static inline void hci_sched_acl_pkt(struct hci_dev *hdev)
+{
+ unsigned int cnt = hdev->acl_cnt;
+ struct hci_chan *chan;
+ struct sk_buff *skb;
+ int quote;
- cnt = hdev->acl_cnt;
+ __check_timeout(hdev, cnt);
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
@@ -2269,7 +2552,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
skb = skb_dequeue(&chan->data_q);
hci_conn_enter_active_mode(chan->conn,
- bt_cb(skb)->force_active);
+ bt_cb(skb)->force_active);
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
@@ -2284,6 +2567,70 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
hci_prio_recalculate(hdev, ACL_LINK);
}
+static inline void hci_sched_acl_blk(struct hci_dev *hdev)
+{
+ unsigned int cnt = hdev->block_cnt;
+ struct hci_chan *chan;
+ struct sk_buff *skb;
+ int quote;
+
+ __check_timeout(hdev, cnt);
+
+ while (hdev->block_cnt > 0 &&
+ (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+ u32 priority = (skb_peek(&chan->data_q))->priority;
+ while (quote > 0 && (skb = skb_peek(&chan->data_q))) {
+ int blocks;
+
+ BT_DBG("chan %p skb %p len %d priority %u", chan, skb,
+ skb->len, skb->priority);
+
+ /* Stop if priority has changed */
+ if (skb->priority < priority)
+ break;
+
+ skb = skb_dequeue(&chan->data_q);
+
+ blocks = __get_blocks(hdev, skb);
+ if (blocks > hdev->block_cnt)
+ return;
+
+ hci_conn_enter_active_mode(chan->conn,
+ bt_cb(skb)->force_active);
+
+ hci_send_frame(skb);
+ hdev->acl_last_tx = jiffies;
+
+ hdev->block_cnt -= blocks;
+ quote -= blocks;
+
+ chan->sent += blocks;
+ chan->conn->sent += blocks;
+ }
+ }
+
+ if (cnt != hdev->block_cnt)
+ hci_prio_recalculate(hdev, ACL_LINK);
+}
+
+static inline void hci_sched_acl(struct hci_dev *hdev)
+{
+ BT_DBG("%s", hdev->name);
+
+ if (!hci_conn_num(hdev, ACL_LINK))
+ return;
+
+ switch (hdev->flow_ctl_mode) {
+ case HCI_FLOW_CTL_MODE_PACKET_BASED:
+ hci_sched_acl_pkt(hdev);
+ break;
+
+ case HCI_FLOW_CTL_MODE_BLOCK_BASED:
+ hci_sched_acl_blk(hdev);
+ break;
+ }
+}
+
/* Schedule SCO */
static inline void hci_sched_sco(struct hci_dev *hdev)
{
@@ -2481,9 +2828,12 @@ static void hci_rx_work(struct work_struct *work)
BT_DBG("%s", hdev->name);
while ((skb = skb_dequeue(&hdev->rx_q))) {
+ /* Send copy to monitor */
+ hci_send_to_monitor(hdev, skb);
+
if (atomic_read(&hdev->promisc)) {
/* Send copy to the sockets */
- hci_send_to_sock(hdev, skb, NULL);
+ hci_send_to_sock(hdev, skb);
}
if (test_bit(HCI_RAW, &hdev->flags)) {
@@ -2567,6 +2917,8 @@ int hci_do_inquiry(struct hci_dev *hdev, u8 length)
if (test_bit(HCI_INQUIRY, &hdev->flags))
return -EINPROGRESS;
+ inquiry_cache_flush(hdev);
+
memset(&cp, 0, sizeof(cp));
memcpy(&cp.lap, lap, sizeof(cp.lap));
cp.length = length;
@@ -2583,6 +2935,3 @@ int hci_cancel_inquiry(struct hci_dev *hdev)
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
}
-
-module_param(enable_hs, bool, 0644);
-MODULE_PARM_DESC(enable_hs, "Enable High Speed");
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 001307f81057..b37531094c49 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -35,18 +35,14 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static bool enable_le;
-
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
@@ -65,7 +61,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 0);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_dev_unlock(hdev);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@@ -195,7 +191,10 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_RESET, status);
- hdev->dev_flags = 0;
+ /* Reset all non-persistent flags */
+ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS));
+
+ hdev->discovery.state = DISCOVERY_STOPPED;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -211,13 +210,14 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_set_local_name_complete(hdev, sent, status);
-
- if (status == 0)
+ else if (!status)
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
hci_dev_unlock(hdev);
+
+ hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status);
}
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -229,7 +229,8 @@ static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (rp->status)
return;
- memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
+ if (test_bit(HCI_SETUP, &hdev->dev_flags))
+ memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
}
static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
@@ -252,6 +253,9 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_AUTH, &hdev->flags);
}
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_auth_enable_complete(hdev, status);
+
hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status);
}
@@ -349,14 +353,19 @@ static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV);
if (!sent)
return;
- memcpy(hdev->dev_class, sent, 3);
+ hci_dev_lock(hdev);
+
+ if (status == 0)
+ memcpy(hdev->dev_class, sent, 3);
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_set_class_of_dev_complete(hdev, sent, status);
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
@@ -419,18 +428,6 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
}
-static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
-
- BT_DBG("%s status 0x%x", hdev->name, rp->status);
-
- if (rp->status)
- return;
-
- hdev->ssp_mode = rp->mode;
-}
-
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -438,14 +435,18 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
if (!sent)
return;
- hdev->ssp_mode = *((__u8 *) sent);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status);
+ else if (!status) {
+ if (*((u8 *) sent))
+ set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ }
}
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
@@ -540,20 +541,6 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
}
-static void hci_set_le_support(struct hci_dev *hdev)
-{
- struct hci_cp_write_le_host_supported cp;
-
- memset(&cp, 0, sizeof(cp));
-
- if (enable_le) {
- cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
- }
-
- hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp);
-}
-
static void hci_setup(struct hci_dev *hdev)
{
if (hdev->dev_type != HCI_BREDR)
@@ -565,8 +552,18 @@ static void hci_setup(struct hci_dev *hdev)
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
- u8 mode = 0x01;
- hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ u8 mode = 0x01;
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
+ sizeof(mode), &mode);
+ } else {
+ struct hci_cp_write_eir cp;
+
+ memset(hdev->eir, 0, sizeof(hdev->eir));
+ memset(&cp, 0, sizeof(cp));
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+ }
}
if (hdev->features[3] & LMP_RSSI_INQ)
@@ -579,12 +576,15 @@ static void hci_setup(struct hci_dev *hdev)
struct hci_cp_read_local_ext_features cp;
cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp),
+ &cp);
}
- if (hdev->features[4] & LMP_LE)
- hci_set_le_support(hdev);
+ if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
+ u8 enable = 1;
+ hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable),
+ &enable);
+ }
}
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
@@ -594,7 +594,7 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
hdev->hci_ver = rp->hci_ver;
hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
@@ -608,6 +608,9 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
if (test_bit(HCI_INIT, &hdev->flags))
hci_setup(hdev);
+
+done:
+ hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status);
}
static void hci_setup_link_policy(struct hci_dev *hdev)
@@ -624,8 +627,8 @@ static void hci_setup_link_policy(struct hci_dev *hdev)
link_policy |= HCI_LP_PARK;
link_policy = cpu_to_le16(link_policy);
- hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY,
- sizeof(link_policy), &link_policy);
+ hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(link_policy),
+ &link_policy);
}
static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb)
@@ -701,6 +704,22 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
hdev->features[6], hdev->features[7]);
}
+static void hci_set_le_support(struct hci_dev *hdev)
+{
+ struct hci_cp_write_le_host_supported cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ cp.le = 1;
+ cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ }
+
+ if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE))
+ hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
+ &cp);
+}
+
static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
struct sk_buff *skb)
{
@@ -709,7 +728,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto done;
switch (rp->page) {
case 0:
@@ -720,6 +739,10 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
break;
}
+ if (test_bit(HCI_INIT, &hdev->flags) && hdev->features[4] & LMP_LE)
+ hci_set_le_support(hdev);
+
+done:
hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
}
@@ -890,7 +913,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
if (rp->status != 0)
@@ -916,7 +939,7 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
rp->status);
@@ -951,9 +974,9 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0,
+ rp->status);
hci_dev_unlock(hdev);
}
@@ -967,9 +990,9 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
@@ -982,9 +1005,9 @@ static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
+ 0, rp->status);
hci_dev_unlock(hdev);
}
@@ -998,9 +1021,9 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ ACL_LINK, 0, rp->status);
hci_dev_unlock(hdev);
}
@@ -1023,6 +1046,15 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status);
+
+ if (status) {
+ hci_dev_lock(hdev);
+ mgmt_start_discovery_failed(hdev, status);
+ hci_dev_unlock(hdev);
+ return;
+ }
}
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
@@ -1033,28 +1065,47 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
if (!cp)
return;
switch (cp->enable) {
case LE_SCANNING_ENABLED:
+ hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status);
+
+ if (status) {
+ hci_dev_lock(hdev);
+ mgmt_start_discovery_failed(hdev, status);
+ hci_dev_unlock(hdev);
+ return;
+ }
+
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
cancel_delayed_work_sync(&hdev->adv_work);
hci_dev_lock(hdev);
hci_adv_entries_clear(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
hci_dev_unlock(hdev);
break;
case LE_SCANNING_DISABLED:
+ if (status)
+ return;
+
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT);
+
+ if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED) {
+ mgmt_interleaved_discovery(hdev);
+ } else {
+ hci_dev_lock(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ hci_dev_unlock(hdev);
+ }
+
break;
default:
@@ -1090,16 +1141,27 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev,
struct sk_buff *skb)
{
- struct hci_cp_read_local_ext_features cp;
+ struct hci_cp_write_le_host_supported *sent;
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED);
+ if (!sent)
return;
- cp.page = 0x01;
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp);
+ if (!status) {
+ if (sent->le)
+ hdev->host_features[0] |= LMP_HOST_LE;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_LE;
+ }
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+ !test_bit(HCI_INIT, &hdev->flags))
+ mgmt_le_enable_complete(hdev, sent->le, status);
+
+ hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
}
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
@@ -1110,7 +1172,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev);
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_start_discovery_failed(hdev, status);
hci_dev_unlock(hdev);
return;
@@ -1119,7 +1181,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
set_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 1);
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
hci_dev_unlock(hdev);
}
@@ -1153,7 +1215,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
if (!conn) {
conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr);
if (conn) {
- conn->out = 1;
+ conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
} else
BT_ERR("No memory for new connection");
@@ -1263,7 +1325,7 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
/* Only request authentication for SSP connections or non-SSP
* devices with sec_level HIGH or if MITM protection is requested */
- if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) &&
+ if (!hci_conn_ssp_enabled(conn) &&
conn->pending_sec_level != BT_SECURITY_HIGH &&
!(conn->auth_type & 0x01))
return 0;
@@ -1271,6 +1333,73 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
return 1;
}
+static inline int hci_resolve_name(struct hci_dev *hdev,
+ struct inquiry_entry *e)
+{
+ struct hci_cp_remote_name_req cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ bacpy(&cp.bdaddr, &e->data.bdaddr);
+ cp.pscan_rep_mode = e->data.pscan_rep_mode;
+ cp.pscan_mode = e->data.pscan_mode;
+ cp.clock_offset = e->data.clock_offset;
+
+ return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
+}
+
+static bool hci_resolve_next_name(struct hci_dev *hdev)
+{
+ struct discovery_state *discov = &hdev->discovery;
+ struct inquiry_entry *e;
+
+ if (list_empty(&discov->resolve))
+ return false;
+
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED);
+ if (hci_resolve_name(hdev, e) == 0) {
+ e->name_state = NAME_PENDING;
+ return true;
+ }
+
+ return false;
+}
+
+static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
+ bdaddr_t *bdaddr, u8 *name, u8 name_len)
+{
+ struct discovery_state *discov = &hdev->discovery;
+ struct inquiry_entry *e;
+
+ if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
+ name_len, conn->dev_class);
+
+ if (discov->state == DISCOVERY_STOPPED)
+ return;
+
+ if (discov->state == DISCOVERY_STOPPING)
+ goto discov_complete;
+
+ if (discov->state != DISCOVERY_RESOLVING)
+ return;
+
+ e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING);
+ if (e) {
+ e->name_state = NAME_KNOWN;
+ list_del(&e->list);
+ if (name)
+ mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
+ e->data.rssi, name, name_len);
+ }
+
+ if (hci_resolve_next_name(hdev))
+ return;
+
+discov_complete:
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+}
+
static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_remote_name_req *cp;
@@ -1290,13 +1419,17 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0);
+
if (!conn)
goto unlock;
if (!hci_outgoing_auth_needed(hdev, conn))
goto unlock;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ 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);
@@ -1413,9 +1546,9 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
hci_sco_setup(conn, status);
}
@@ -1440,15 +1573,37 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
- clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
+ clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags);
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
hci_sco_setup(conn, status);
}
hci_dev_unlock(hdev);
}
+static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_disconnect *cp;
+ struct hci_conn *conn;
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn)
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, status);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
{
struct hci_cp_le_create_conn *cp;
@@ -1478,7 +1633,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
if (conn) {
conn->dst_type = cp->peer_addr_type;
- conn->out = 1;
+ conn->out = true;
} else {
BT_ERR("No memory for new connection");
}
@@ -1496,6 +1651,8 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
+ struct discovery_state *discov = &hdev->discovery;
+ struct inquiry_entry *e;
BT_DBG("%s status %d", hdev->name, status);
@@ -1506,8 +1663,28 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
return;
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ return;
+
hci_dev_lock(hdev);
- mgmt_discovering(hdev, 0);
+
+ if (discov->state != DISCOVERY_FINDING)
+ goto unlock;
+
+ if (list_empty(&discov->resolve)) {
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ goto unlock;
+ }
+
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED);
+ if (e && hci_resolve_name(hdev, e) == 0) {
+ e->name_state = NAME_PENDING;
+ hci_discovery_set_state(hdev, DISCOVERY_RESOLVING);
+ } else {
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ }
+
+unlock:
hci_dev_unlock(hdev);
}
@@ -1525,6 +1702,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
+ bool name_known, ssp;
+
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -1533,9 +1712,11 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.clock_offset = info->clock_offset;
data.rssi = 0x00;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+
+ name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, NULL);
+ info->dev_class, 0, !name_known, ssp, NULL,
+ 0);
}
hci_dev_unlock(hdev);
@@ -1569,8 +1750,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG;
hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- mgmt_connected(hdev, &ev->bdaddr, conn->type,
- conn->dst_type);
} else
conn->state = BT_CONNECTED;
@@ -1588,7 +1767,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
struct hci_cp_read_remote_features cp;
cp.handle = ev->handle;
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
/* Set packet type for incoming connection */
@@ -1596,14 +1775,14 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
struct hci_cp_change_conn_ptype cp;
cp.handle = ev->handle;
cp.pkt_type = cpu_to_le16(conn->pkt_type);
- hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp),
+ &cp);
}
} else {
conn->state = BT_CLOSED;
if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
- conn->dst_type, ev->status);
+ conn->dst_type, ev->status);
}
if (conn->type == ACL_LINK)
@@ -1668,8 +1847,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
else
cp.role = 0x01; /* Remain slave */
- hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ,
- sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
+ &cp);
} else {
struct hci_cp_accept_sync_conn_req cp;
@@ -1683,7 +1862,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
cp.retrans_effort = 0xff;
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
- sizeof(cp), &cp);
+ sizeof(cp), &cp);
}
} else {
/* Connection rejected */
@@ -1711,12 +1890,14 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
if (ev->status == 0)
conn->state = BT_CLOSED;
- if (conn->type == ACL_LINK || conn->type == LE_LINK) {
+ if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
+ (conn->type == ACL_LINK || conn->type == LE_LINK)) {
if (ev->status != 0)
- mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, ev->status);
else
- mgmt_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type);
+ mgmt_device_disconnected(hdev, &conn->dst, conn->type,
+ conn->dst_type);
}
if (ev->status == 0) {
@@ -1742,22 +1923,23 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
goto unlock;
if (!ev->status) {
- if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) &&
- test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) {
+ if (!hci_conn_ssp_enabled(conn) &&
+ test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) {
BT_INFO("re-auth of legacy device is not possible.");
} else {
conn->link_mode |= HCI_LM_AUTH;
conn->sec_level = conn->pending_sec_level;
}
} else {
- mgmt_auth_failed(hdev, &conn->dst, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ ev->status);
}
- clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
- clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
+ clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
if (conn->state == BT_CONFIG) {
- if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) {
+ if (!ev->status && hci_conn_ssp_enabled(conn)) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = ev->handle;
cp.encrypt = 0x01;
@@ -1776,7 +1958,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_conn_put(conn);
}
- if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) {
if (!ev->status) {
struct hci_cp_set_conn_encrypt cp;
cp.handle = ev->handle;
@@ -1784,7 +1966,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
&cp);
} else {
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
hci_encrypt_cfm(conn, ev->status, 0x00);
}
}
@@ -1804,17 +1986,25 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev);
- if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
- mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
-
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ goto check_auth;
+
+ if (ev->status == 0)
+ hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name,
+ strnlen(ev->name, HCI_MAX_NAME_LENGTH));
+ else
+ hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0);
+
+check_auth:
if (!conn)
goto unlock;
if (!hci_outgoing_auth_needed(hdev, conn))
goto unlock;
- if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ 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);
@@ -1845,7 +2035,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
conn->link_mode &= ~HCI_LM_ENCRYPT;
}
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
if (conn->state == BT_CONFIG) {
if (!ev->status)
@@ -1874,7 +2064,7 @@ static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct
if (!ev->status)
conn->link_mode |= HCI_LM_SECURE;
- clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
hci_key_change_cfm(conn, ev->status);
}
@@ -1916,7 +2106,10 @@ static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
- }
+ } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, &conn->dst, conn->type,
+ conn->dst_type, 0, NULL, 0,
+ conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -2024,10 +2217,6 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_host_buffer_size(hdev, skb);
break;
- case HCI_OP_READ_SSP_MODE:
- hci_cc_read_ssp_mode(hdev, skb);
- break;
-
case HCI_OP_WRITE_SSP_MODE:
hci_cc_write_ssp_mode(hdev, skb);
break;
@@ -2213,8 +2402,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_OP_DISCONNECT:
- if (ev->status != 0)
- mgmt_disconnect_failed(hdev, NULL, ev->status);
+ hci_cs_disconnect(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2258,7 +2446,7 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb
conn->link_mode |= HCI_LM_MASTER;
}
- clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend);
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
hci_role_switch_cfm(conn, ev->status, ev->role);
}
@@ -2332,6 +2520,56 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
queue_work(hdev->workqueue, &hdev->tx_work);
}
+static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
+ int i;
+
+ if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) {
+ BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode);
+ return;
+ }
+
+ if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) +
+ ev->num_hndl * sizeof(struct hci_comp_blocks_info)) {
+ BT_DBG("%s bad parameters", hdev->name);
+ return;
+ }
+
+ BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks,
+ ev->num_hndl);
+
+ for (i = 0; i < ev->num_hndl; i++) {
+ struct hci_comp_blocks_info *info = &ev->handles[i];
+ struct hci_conn *conn;
+ __u16 handle, block_count;
+
+ handle = __le16_to_cpu(info->handle);
+ block_count = __le16_to_cpu(info->blocks);
+
+ conn = hci_conn_hash_lookup_handle(hdev, handle);
+ if (!conn)
+ continue;
+
+ conn->sent -= block_count;
+
+ switch (conn->type) {
+ case ACL_LINK:
+ hdev->block_cnt += block_count;
+ if (hdev->block_cnt > hdev->num_blocks)
+ hdev->block_cnt = hdev->num_blocks;
+ break;
+
+ default:
+ BT_ERR("Unknown type %d conn %p", conn->type, conn);
+ break;
+ }
+ }
+
+ queue_work(hdev->workqueue, &hdev->tx_work);
+}
+
static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_mode_change *ev = (void *) skb->data;
@@ -2346,14 +2584,14 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb
conn->mode = ev->mode;
conn->interval = __le16_to_cpu(ev->interval);
- if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+ if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) {
if (conn->mode == HCI_CM_ACTIVE)
- conn->power_save = 1;
+ set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
else
- conn->power_save = 0;
+ clear_bit(HCI_CONN_POWER_SAVE, &conn->flags);
}
- if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
+ if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags))
hci_sco_setup(conn, ev->status);
}
@@ -2379,10 +2617,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_put(conn);
}
- if (!test_bit(HCI_PAIRABLE, &hdev->flags))
+ if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags))
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
sizeof(ev->bdaddr), &ev->bdaddr);
- else if (test_bit(HCI_MGMT, &hdev->flags)) {
+ else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
u8 secure;
if (conn->pending_sec_level == BT_SECURITY_HIGH)
@@ -2406,7 +2644,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s", hdev->name);
- if (!test_bit(HCI_LINK_KEYS, &hdev->flags))
+ if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
return;
hci_dev_lock(hdev);
@@ -2421,7 +2659,7 @@ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s found key type %u for %s", hdev->name, key->type,
batostr(&ev->bdaddr));
- if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) &&
+ if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) &&
key->type == HCI_LK_DEBUG_COMBINATION) {
BT_DBG("%s ignoring debug key", hdev->name);
goto not_found;
@@ -2483,7 +2721,7 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_put(conn);
}
- if (test_bit(HCI_LINK_KEYS, &hdev->flags))
+ if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
ev->key_type, pin_len);
@@ -2551,6 +2789,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
{
struct inquiry_data data;
int num_rsp = *((__u8 *) skb->data);
+ bool name_known, ssp;
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
@@ -2572,10 +2811,12 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+
+ name_known = hci_inquiry_cache_update(hdev, &data,
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- NULL);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
@@ -2589,10 +2830,11 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+ name_known = hci_inquiry_cache_update(hdev, &data,
+ false, &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi,
- NULL);
+ info->dev_class, info->rssi,
+ !name_known, ssp, NULL, 0);
}
}
@@ -2617,9 +2859,10 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
- conn->ssp_mode = (ev->features[0] & 0x01);
+ if (ev->features[0] & LMP_HOST_SSP)
+ set_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
}
if (conn->state != BT_CONFIG)
@@ -2631,7 +2874,10 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
- }
+ } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, &conn->dst, conn->type,
+ conn->dst_type, 0, NULL, 0,
+ conn->dev_class);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -2724,6 +2970,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
+ bool name_known, ssp;
+
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
@@ -2732,9 +2980,19 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x01;
- hci_inquiry_cache_update(hdev, &data);
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ name_known = eir_has_data_type(info->data,
+ sizeof(info->data),
+ EIR_NAME_COMPLETE);
+ else
+ name_known = true;
+
+ name_known = hci_inquiry_cache_update(hdev, &data, name_known,
+ &ssp);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi, info->data);
+ info->dev_class, info->rssi, !name_known,
+ ssp, info->data, sizeof(info->data));
}
hci_dev_unlock(hdev);
@@ -2774,19 +3032,22 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
hci_conn_hold(conn);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
- if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
+ if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) ||
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
struct hci_cp_io_capability_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.capability = conn->io_capability;
+ /* Change the IO capability from KeyboardDisplay
+ * to DisplayYesNo as it is not supported by BT spec. */
+ cp.capability = (conn->io_capability == 0x04) ?
+ 0x01 : conn->io_capability;
conn->auth_type = hci_get_auth_req(conn);
cp.authentication = conn->auth_type;
- if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
+ if ((conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) &&
hci_find_remote_oob_data(hdev, &conn->dst))
cp.oob_data = 0x01;
else
@@ -2822,8 +3083,9 @@ static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *s
goto unlock;
conn->remote_cap = ev->capability;
- conn->remote_oob = ev->oob_data;
conn->remote_auth = ev->authentication;
+ if (ev->oob_data)
+ set_bit(HCI_CONN_REMOTE_OOB, &conn->flags);
unlock:
hci_dev_unlock(hdev);
@@ -2840,7 +3102,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
@@ -2869,7 +3131,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
/* If we're not the initiators request authorization to
* proceed from user space (mgmt_user_confirm with
* confirm_hint set to 1). */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
BT_DBG("Confirming auto-accept as acceptor");
confirm_hint = 1;
goto confirm;
@@ -2890,8 +3152,8 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
}
confirm:
- mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
- confirm_hint);
+ mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey,
+ confirm_hint);
unlock:
hci_dev_unlock(hdev);
@@ -2906,8 +3168,8 @@ static inline void hci_user_passkey_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->flags))
- mgmt_user_passkey_request(hdev, &ev->bdaddr);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
hci_dev_unlock(hdev);
}
@@ -2930,8 +3192,9 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
* initiated the authentication. A traditional auth_complete
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
- if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
- mgmt_auth_failed(hdev, &conn->dst, ev->status);
+ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0)
+ mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ ev->status);
hci_conn_put(conn);
@@ -2950,13 +3213,13 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
if (ie)
- ie->data.ssp_mode = (ev->features[0] & 0x01);
+ ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP);
hci_dev_unlock(hdev);
}
static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
struct oob_data *data;
@@ -2965,7 +3228,7 @@ static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (!test_bit(HCI_MGMT, &hdev->flags))
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
@@ -3020,7 +3283,9 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
goto unlock;
}
- mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type);
+ if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, &ev->bdaddr, conn->type,
+ conn->dst_type, 0, NULL, 0, NULL);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@@ -3040,6 +3305,7 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
{
u8 num_reports = skb->data[0];
void *ptr = &skb->data[1];
+ s8 rssi;
hci_dev_lock(hdev);
@@ -3048,6 +3314,10 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
hci_add_adv_entry(hdev, ev);
+ rssi = ev->data[ev->length];
+ mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
+ NULL, rssi, 0, 1, ev->data, ev->length);
+
ptr += sizeof(*ev) + ev->length + 1;
}
@@ -3061,7 +3331,7 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
struct hci_cp_le_ltk_reply cp;
struct hci_cp_le_ltk_neg_reply neg;
struct hci_conn *conn;
- struct link_key *ltk;
+ struct smp_ltk *ltk;
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
@@ -3077,10 +3347,17 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
- conn->pin_length = ltk->pin_len;
+
+ if (ltk->authenticated)
+ conn->sec_level = BT_SECURITY_HIGH;
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+ if (ltk->type & HCI_SMP_STK) {
+ list_del(&ltk->list);
+ kfree(ltk);
+ }
+
hci_dev_unlock(hdev);
return;
@@ -3271,6 +3548,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_remote_oob_data_request_evt(hdev, skb);
break;
+ case HCI_EV_NUM_COMP_BLOCKS:
+ hci_num_comp_blocks_evt(hdev, skb);
+ break;
+
default:
BT_DBG("%s event 0x%x", hdev->name, event);
break;
@@ -3279,34 +3560,3 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb);
hdev->stat.evt_rx++;
}
-
-/* Generate internal stack event */
-void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
-{
- struct hci_event_hdr *hdr;
- struct hci_ev_stack_internal *ev;
- struct sk_buff *skb;
-
- skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC);
- if (!skb)
- return;
-
- hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
- hdr->evt = HCI_EV_STACK_INTERNAL;
- hdr->plen = sizeof(*ev) + dlen;
-
- ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
- ev->type = type;
- memcpy(ev->data, data, dlen);
-
- bt_cb(skb)->incoming = 1;
- __net_timestamp(skb);
-
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- skb->dev = (void *) hdev;
- hci_send_to_sock(hdev, skb, NULL);
- kfree_skb(skb);
-}
-
-module_param(enable_le, bool, 0644);
-MODULE_PARM_DESC(enable_le, "Enable LE support");
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index b2eb2b93580f..5914623f426a 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -42,14 +42,14 @@
#include <linux/ioctl.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_mon.h>
-static bool enable_mgmt;
+static atomic_t monitor_promisc = ATOMIC_INIT(0);
/* ----- HCI socket interface ----- */
@@ -85,22 +85,20 @@ static struct bt_sock_list hci_sk_list = {
};
/* Send frame to RAW socket */
-void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
- struct sock *skip_sk)
+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
{
struct sock *sk;
struct hlist_node *node;
+ struct sk_buff *skb_copy = NULL;
BT_DBG("hdev %p len %d", hdev, skb->len);
read_lock(&hci_sk_list.lock);
+
sk_for_each(sk, node, &hci_sk_list.head) {
struct hci_filter *flt;
struct sk_buff *nskb;
- if (sk == skip_sk)
- continue;
-
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
continue;
@@ -108,12 +106,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
if (skb->sk == sk)
continue;
- if (bt_cb(skb)->channel != hci_pi(sk)->channel)
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
continue;
- if (bt_cb(skb)->channel == HCI_CHANNEL_CONTROL)
- goto clone;
-
/* Apply filter */
flt = &hci_pi(sk)->filter;
@@ -137,21 +132,303 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
continue;
}
-clone:
+ if (!skb_copy) {
+ /* Create a private copy with headroom */
+ skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC);
+ if (!skb_copy)
+ continue;
+
+ /* Put type byte before the data */
+ memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
+ }
+
+ nskb = skb_clone(skb_copy, GFP_ATOMIC);
+ if (!nskb)
+ continue;
+
+ if (sock_queue_rcv_skb(sk, nskb))
+ kfree_skb(nskb);
+ }
+
+ read_unlock(&hci_sk_list.lock);
+
+ kfree_skb(skb_copy);
+}
+
+/* Send frame to control socket */
+void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+
+ BT_DBG("len %d", skb->len);
+
+ read_lock(&hci_sk_list.lock);
+
+ sk_for_each(sk, node, &hci_sk_list.head) {
+ struct sk_buff *nskb;
+
+ /* Skip the original socket */
+ if (sk == skip_sk)
+ continue;
+
+ if (sk->sk_state != BT_BOUND)
+ continue;
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
+ continue;
+
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
continue;
- /* Put type byte before the data */
- if (bt_cb(skb)->channel == HCI_CHANNEL_RAW)
- memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1);
+ if (sock_queue_rcv_skb(sk, nskb))
+ kfree_skb(nskb);
+ }
+
+ read_unlock(&hci_sk_list.lock);
+}
+
+/* Send frame to monitor socket */
+void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+ struct sk_buff *skb_copy = NULL;
+ __le16 opcode;
+
+ if (!atomic_read(&monitor_promisc))
+ return;
+
+ BT_DBG("hdev %p len %d", hdev, skb->len);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT);
+ break;
+ case HCI_EVENT_PKT:
+ opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT);
+ break;
+ case HCI_ACLDATA_PKT:
+ if (bt_cb(skb)->incoming)
+ opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT);
+ else
+ opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT);
+ break;
+ case HCI_SCODATA_PKT:
+ if (bt_cb(skb)->incoming)
+ opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT);
+ else
+ opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT);
+ break;
+ default:
+ return;
+ }
+
+ read_lock(&hci_sk_list.lock);
+
+ sk_for_each(sk, node, &hci_sk_list.head) {
+ struct sk_buff *nskb;
+
+ if (sk->sk_state != BT_BOUND)
+ continue;
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
+ continue;
+
+ if (!skb_copy) {
+ struct hci_mon_hdr *hdr;
+
+ /* Create a private copy with headroom */
+ skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC);
+ if (!skb_copy)
+ continue;
+
+ /* Put header before the data */
+ hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
+ hdr->opcode = opcode;
+ hdr->index = cpu_to_le16(hdev->id);
+ hdr->len = cpu_to_le16(skb->len);
+ }
+
+ nskb = skb_clone(skb_copy, GFP_ATOMIC);
+ if (!nskb)
+ continue;
+
+ if (sock_queue_rcv_skb(sk, nskb))
+ kfree_skb(nskb);
+ }
+
+ read_unlock(&hci_sk_list.lock);
+
+ kfree_skb(skb_copy);
+}
+
+static void send_monitor_event(struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct hlist_node *node;
+
+ BT_DBG("len %d", skb->len);
+
+ read_lock(&hci_sk_list.lock);
+
+ sk_for_each(sk, node, &hci_sk_list.head) {
+ struct sk_buff *nskb;
+
+ if (sk->sk_state != BT_BOUND)
+ continue;
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
+ continue;
+
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ continue;
if (sock_queue_rcv_skb(sk, nskb))
kfree_skb(nskb);
}
+
read_unlock(&hci_sk_list.lock);
}
+static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
+{
+ struct hci_mon_hdr *hdr;
+ struct hci_mon_new_index *ni;
+ struct sk_buff *skb;
+ __le16 opcode;
+
+ switch (event) {
+ case HCI_DEV_REG:
+ skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
+ ni->type = hdev->dev_type;
+ ni->bus = hdev->bus;
+ bacpy(&ni->bdaddr, &hdev->bdaddr);
+ memcpy(ni->name, hdev->name, 8);
+
+ opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX);
+ break;
+
+ case HCI_DEV_UNREG:
+ skb = bt_skb_alloc(0, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+
+ opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ __net_timestamp(skb);
+
+ hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE);
+ hdr->opcode = opcode;
+ hdr->index = cpu_to_le16(hdev->id);
+ hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
+
+ return skb;
+}
+
+static void send_monitor_replay(struct sock *sk)
+{
+ struct hci_dev *hdev;
+
+ read_lock(&hci_dev_list_lock);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ struct sk_buff *skb;
+
+ skb = create_monitor_event(hdev, HCI_DEV_REG);
+ if (!skb)
+ continue;
+
+ if (sock_queue_rcv_skb(sk, skb))
+ kfree_skb(skb);
+ }
+
+ read_unlock(&hci_dev_list_lock);
+}
+
+/* Generate internal stack event */
+static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
+{
+ struct hci_event_hdr *hdr;
+ struct hci_ev_stack_internal *ev;
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
+ hdr->evt = HCI_EV_STACK_INTERNAL;
+ hdr->plen = sizeof(*ev) + dlen;
+
+ ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
+ ev->type = type;
+ memcpy(ev->data, data, dlen);
+
+ bt_cb(skb)->incoming = 1;
+ __net_timestamp(skb);
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ skb->dev = (void *) hdev;
+ hci_send_to_sock(hdev, skb);
+ kfree_skb(skb);
+}
+
+void hci_sock_dev_event(struct hci_dev *hdev, int event)
+{
+ struct hci_ev_si_device ev;
+
+ BT_DBG("hdev %s event %d", hdev->name, event);
+
+ /* Send event to monitor */
+ if (atomic_read(&monitor_promisc)) {
+ struct sk_buff *skb;
+
+ skb = create_monitor_event(hdev, event);
+ if (skb) {
+ send_monitor_event(skb);
+ kfree_skb(skb);
+ }
+ }
+
+ /* Send event to sockets */
+ ev.event = event;
+ ev.dev_id = hdev->id;
+ hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+
+ if (event == HCI_DEV_UNREG) {
+ struct sock *sk;
+ struct hlist_node *node;
+
+ /* Detach sockets from device */
+ read_lock(&hci_sk_list.lock);
+ sk_for_each(sk, node, &hci_sk_list.head) {
+ bh_lock_sock_nested(sk);
+ if (hci_pi(sk)->hdev == hdev) {
+ hci_pi(sk)->hdev = NULL;
+ sk->sk_err = EPIPE;
+ sk->sk_state = BT_OPEN;
+ sk->sk_state_change(sk);
+
+ hci_dev_put(hdev);
+ }
+ bh_unlock_sock(sk);
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+}
+
static int hci_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
@@ -164,6 +441,9 @@ static int hci_sock_release(struct socket *sock)
hdev = hci_pi(sk)->hdev;
+ if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR)
+ atomic_dec(&monitor_promisc);
+
bt_sock_unlink(&hci_sk_list, sk);
if (hdev) {
@@ -190,7 +470,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_add(hdev, &bdaddr);
+ err = hci_blacklist_add(hdev, &bdaddr, 0);
hci_dev_unlock(hdev);
@@ -207,7 +487,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
hci_dev_lock(hdev);
- err = hci_blacklist_del(hdev, &bdaddr);
+ err = hci_blacklist_del(hdev, &bdaddr, 0);
hci_dev_unlock(hdev);
@@ -340,34 +620,69 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
if (haddr.hci_family != AF_BLUETOOTH)
return -EINVAL;
- if (haddr.hci_channel > HCI_CHANNEL_CONTROL)
- return -EINVAL;
-
- if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
- if (!enable_mgmt)
- return -EINVAL;
- set_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags);
- }
-
lock_sock(sk);
- if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) {
+ if (sk->sk_state == BT_BOUND) {
err = -EALREADY;
goto done;
}
- if (haddr.hci_dev != HCI_DEV_NONE) {
- hdev = hci_dev_get(haddr.hci_dev);
- if (!hdev) {
- err = -ENODEV;
+ switch (haddr.hci_channel) {
+ case HCI_CHANNEL_RAW:
+ if (hci_pi(sk)->hdev) {
+ err = -EALREADY;
goto done;
}
- atomic_inc(&hdev->promisc);
+ if (haddr.hci_dev != HCI_DEV_NONE) {
+ hdev = hci_dev_get(haddr.hci_dev);
+ if (!hdev) {
+ err = -ENODEV;
+ 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;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto done;
+ }
+
+ break;
+
+ case HCI_CHANNEL_MONITOR:
+ if (haddr.hci_dev != HCI_DEV_NONE) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (!capable(CAP_NET_RAW)) {
+ err = -EPERM;
+ goto done;
+ }
+
+ send_monitor_replay(sk);
+
+ atomic_inc(&monitor_promisc);
+ break;
+
+ default:
+ err = -EINVAL;
+ goto done;
}
+
hci_pi(sk)->channel = haddr.hci_channel;
- hci_pi(sk)->hdev = hdev;
sk->sk_state = BT_BOUND;
done:
@@ -462,7 +777,15 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- hci_sock_cmsg(sk, msg, skb);
+ switch (hci_pi(sk)->channel) {
+ case HCI_CHANNEL_RAW:
+ hci_sock_cmsg(sk, msg, skb);
+ break;
+ case HCI_CHANNEL_CONTROL:
+ case HCI_CHANNEL_MONITOR:
+ sock_recv_timestamp(msg, sk, skb);
+ break;
+ }
skb_free_datagram(sk, skb);
@@ -496,6 +819,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
case HCI_CHANNEL_CONTROL:
err = mgmt_control(sk, msg, len);
goto done;
+ case HCI_CHANNEL_MONITOR:
+ err = -EOPNOTSUPP;
+ goto done;
default:
err = -EINVAL;
goto done;
@@ -575,6 +901,11 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
+ err = -EINVAL;
+ goto done;
+ }
+
switch (optname) {
case HCI_DATA_DIR:
if (get_user(opt, (int __user *)optval)) {
@@ -637,6 +968,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char
break;
}
+done:
release_sock(sk);
return err;
}
@@ -645,11 +977,20 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
- int len, opt;
+ int len, opt, err = 0;
+
+ BT_DBG("sk %p, opt %d", sk, optname);
if (get_user(len, optlen))
return -EFAULT;
+ lock_sock(sk);
+
+ if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
+ err = -EINVAL;
+ goto done;
+ }
+
switch (optname) {
case HCI_DATA_DIR:
if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR)
@@ -658,7 +999,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
opt = 0;
if (put_user(opt, optval))
- return -EFAULT;
+ err = -EFAULT;
break;
case HCI_TIME_STAMP:
@@ -668,7 +1009,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
opt = 0;
if (put_user(opt, optval))
- return -EFAULT;
+ err = -EFAULT;
break;
case HCI_FILTER:
@@ -683,15 +1024,17 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char
len = min_t(unsigned int, len, sizeof(uf));
if (copy_to_user(optval, &uf, len))
- return -EFAULT;
+ err = -EFAULT;
break;
default:
- return -ENOPROTOOPT;
+ err = -ENOPROTOOPT;
break;
}
- return 0;
+done:
+ release_sock(sk);
+ return err;
}
static const struct proto_ops hci_sock_ops = {
@@ -749,52 +1092,12 @@ static int hci_sock_create(struct net *net, struct socket *sock, int protocol,
return 0;
}
-static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct hci_dev *hdev = (struct hci_dev *) ptr;
- struct hci_ev_si_device ev;
-
- BT_DBG("hdev %s event %ld", hdev->name, event);
-
- /* Send event to sockets */
- ev.event = event;
- ev.dev_id = hdev->id;
- hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
-
- if (event == HCI_DEV_UNREG) {
- struct sock *sk;
- struct hlist_node *node;
-
- /* Detach sockets from device */
- read_lock(&hci_sk_list.lock);
- sk_for_each(sk, node, &hci_sk_list.head) {
- bh_lock_sock_nested(sk);
- if (hci_pi(sk)->hdev == hdev) {
- hci_pi(sk)->hdev = NULL;
- sk->sk_err = EPIPE;
- sk->sk_state = BT_OPEN;
- sk->sk_state_change(sk);
-
- hci_dev_put(hdev);
- }
- bh_unlock_sock(sk);
- }
- read_unlock(&hci_sk_list.lock);
- }
-
- return NOTIFY_DONE;
-}
-
static const struct net_proto_family hci_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = hci_sock_create,
};
-static struct notifier_block hci_sock_nblock = {
- .notifier_call = hci_sock_dev_event
-};
-
int __init hci_sock_init(void)
{
int err;
@@ -807,8 +1110,6 @@ int __init hci_sock_init(void)
if (err < 0)
goto error;
- hci_register_notifier(&hci_sock_nblock);
-
BT_INFO("HCI socket layer initialized");
return 0;
@@ -824,10 +1125,5 @@ void hci_sock_cleanup(void)
if (bt_sock_unregister(BTPROTO_HCI) < 0)
BT_ERR("HCI socket unregistration failed");
- hci_unregister_notifier(&hci_sock_nblock);
-
proto_unregister(&hci_sk_proto);
}
-
-module_param(enable_mgmt, bool, 0644);
-MODULE_PARM_DESC(enable_mgmt, "Enable Management interface");
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 521095614235..bc154298979a 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -33,19 +33,19 @@ static inline char *link_typetostr(int type)
static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_conn *conn = dev_get_drvdata(dev);
+ struct hci_conn *conn = to_hci_conn(dev);
return sprintf(buf, "%s\n", link_typetostr(conn->type));
}
static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_conn *conn = dev_get_drvdata(dev);
+ struct hci_conn *conn = to_hci_conn(dev);
return sprintf(buf, "%s\n", batostr(&conn->dst));
}
static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_conn *conn = dev_get_drvdata(dev);
+ struct hci_conn *conn = to_hci_conn(dev);
return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
conn->features[0], conn->features[1],
@@ -79,8 +79,8 @@ static const struct attribute_group *bt_link_groups[] = {
static void bt_link_release(struct device *dev)
{
- void *data = dev_get_drvdata(dev);
- kfree(data);
+ struct hci_conn *conn = to_hci_conn(dev);
+ kfree(conn);
}
static struct device_type bt_link = {
@@ -120,8 +120,6 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
- dev_set_drvdata(&conn->dev, conn);
-
if (device_add(&conn->dev) < 0) {
BT_ERR("Failed to register connection device");
return;
@@ -189,19 +187,19 @@ 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 = dev_get_drvdata(dev);
+ 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)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type));
}
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
char name[HCI_MAX_NAME_LENGTH + 1];
int i;
@@ -214,20 +212,20 @@ static ssize_t show_name(struct device *dev, struct device_attribute *attr, char
static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
return sprintf(buf, "%s\n", batostr(&hdev->bdaddr));
}
static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
hdev->features[0], hdev->features[1],
@@ -238,31 +236,31 @@ static ssize_t show_features(struct device *dev, struct device_attribute *attr,
static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
unsigned int val;
int rv;
@@ -280,13 +278,13 @@ static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *a
static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
u16 val;
int rv;
@@ -304,13 +302,13 @@ static ssize_t store_sniff_max_interval(struct device *dev, struct device_attrib
static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct hci_dev *hdev = dev_get_drvdata(dev);
+ 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 = dev_get_drvdata(dev);
+ struct hci_dev *hdev = to_hci_dev(dev);
u16 val;
int rv;
@@ -370,8 +368,9 @@ static const struct attribute_group *bt_host_groups[] = {
static void bt_host_release(struct device *dev)
{
- void *data = dev_get_drvdata(dev);
- kfree(data);
+ struct hci_dev *hdev = to_hci_dev(dev);
+ kfree(hdev);
+ module_put(THIS_MODULE);
}
static struct device_type bt_host = {
@@ -383,12 +382,12 @@ static struct device_type bt_host = {
static int inquiry_cache_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
- struct inquiry_cache *cache = &hdev->inq_cache;
+ struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
hci_dev_lock(hdev);
- for (e = cache->list; e; e = e->next) {
+ list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
batostr(&data->bdaddr),
@@ -523,7 +522,7 @@ void hci_init_sysfs(struct hci_dev *hdev)
dev->type = &bt_host;
dev->class = bt_class;
- dev_set_drvdata(dev, hdev);
+ __module_get(THIS_MODULE);
device_initialize(dev);
}
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index 178ac7f127ad..73a32d705c1f 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -160,10 +160,10 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
{
if (cmd == HIDPGETCONNLIST) {
struct hidp_connlist_req cl;
- uint32_t uci;
+ u32 uci;
int err;
- if (get_user(cl.cnum, (uint32_t __user *) arg) ||
+ if (get_user(cl.cnum, (u32 __user *) arg) ||
get_user(uci, (u32 __user *) (arg + 4)))
return -EFAULT;
@@ -174,7 +174,7 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
err = hidp_get_connlist(&cl);
- if (!err && put_user(cl.cnum, (uint32_t __user *) arg))
+ if (!err && put_user(cl.cnum, (u32 __user *) arg))
err = -EFAULT;
return err;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index faf0b11ac1d3..b8e17e4dac8b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -49,7 +49,6 @@
#include <linux/crc16.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
@@ -73,42 +72,28 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
static void l2cap_send_disconn_req(struct l2cap_conn *conn,
struct l2cap_chan *chan, int err);
-static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
-
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->dcid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->dcid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->scid == cid) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->scid == cid)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}
/* Find channel with given SCID.
@@ -117,36 +102,32 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
{
struct l2cap_chan *c;
+ mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid);
- if (c)
- lock_sock(c->sk);
+ mutex_unlock(&conn->chan_lock);
+
return c;
}
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
- struct l2cap_chan *c, *r = NULL;
-
- rcu_read_lock();
+ struct l2cap_chan *c;
- list_for_each_entry_rcu(c, &conn->chan_l, list) {
- if (c->ident == ident) {
- r = c;
- break;
- }
+ list_for_each_entry(c, &conn->chan_l, list) {
+ if (c->ident == ident)
+ return c;
}
-
- rcu_read_unlock();
- return r;
+ return NULL;
}
static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
{
struct l2cap_chan *c;
+ mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_ident(conn, ident);
- if (c)
- lock_sock(c->sk);
+ mutex_unlock(&conn->chan_lock);
+
return c;
}
@@ -217,51 +198,51 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}
-static char *state_to_string(int state)
+static void __l2cap_state_change(struct l2cap_chan *chan, int state)
{
- switch(state) {
- case BT_CONNECTED:
- return "BT_CONNECTED";
- case BT_OPEN:
- return "BT_OPEN";
- case BT_BOUND:
- return "BT_BOUND";
- case BT_LISTEN:
- return "BT_LISTEN";
- case BT_CONNECT:
- return "BT_CONNECT";
- case BT_CONNECT2:
- return "BT_CONNECT2";
- case BT_CONFIG:
- return "BT_CONFIG";
- case BT_DISCONN:
- return "BT_DISCONN";
- case BT_CLOSED:
- return "BT_CLOSED";
- }
+ BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
+ state_to_string(state));
- return "invalid state";
+ chan->state = state;
+ chan->ops->state_change(chan->data, state);
}
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
- BT_DBG("%p %s -> %s", chan, state_to_string(chan->state),
- state_to_string(state));
+ struct sock *sk = chan->sk;
- chan->state = state;
- chan->ops->state_change(chan->data, state);
+ lock_sock(sk);
+ __l2cap_state_change(chan, state);
+ release_sock(sk);
+}
+
+static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+{
+ struct sock *sk = chan->sk;
+
+ sk->sk_err = 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);
}
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
- struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
int reason;
- BT_DBG("chan %p state %d", chan, chan->state);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
- lock_sock(sk);
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(chan);
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
@@ -273,9 +254,11 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_close(chan, reason);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+ mutex_unlock(&conn->chan_lock);
+
l2cap_chan_put(chan);
}
@@ -287,6 +270,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk)
if (!chan)
return NULL;
+ mutex_init(&chan->lock);
+
chan->sk = sk;
write_lock(&chan_list_lock);
@@ -313,7 +298,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
l2cap_chan_put(chan);
}
-static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
chan->psm, chan->dcid);
@@ -322,7 +307,8 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->conn = conn;
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_CONN_ORIENTED:
if (conn->hcon->type == LE_LINK) {
/* LE connection */
chan->omtu = L2CAP_LE_DEFAULT_MTU;
@@ -333,12 +319,16 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->scid = l2cap_alloc_cid(conn);
chan->omtu = L2CAP_DEFAULT_MTU;
}
- } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
+ break;
+
+ case L2CAP_CHAN_CONN_LESS:
/* Connectionless socket */
chan->scid = L2CAP_CID_CONN_LESS;
chan->dcid = L2CAP_CID_CONN_LESS;
chan->omtu = L2CAP_DEFAULT_MTU;
- } else {
+ break;
+
+ default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
chan->dcid = L2CAP_CID_SIGNALING;
@@ -354,11 +344,16 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
l2cap_chan_hold(chan);
- list_add_rcu(&chan->list, &conn->chan_l);
+ list_add(&chan->list, &conn->chan_l);
+}
+
+void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+{
+ mutex_lock(&conn->chan_lock);
+ __l2cap_chan_add(conn, chan);
+ mutex_unlock(&conn->chan_lock);
}
-/* Delete channel.
- * Must be called on the locked socket. */
static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
@@ -371,8 +366,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (conn) {
/* Delete from channel list */
- list_del_rcu(&chan->list);
- synchronize_rcu();
+ list_del(&chan->list);
l2cap_chan_put(chan);
@@ -380,11 +374,13 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}
- l2cap_state_change(chan, BT_CLOSED);
+ lock_sock(sk);
+
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
if (err)
- sk->sk_err = err;
+ __l2cap_chan_set_err(chan, err);
if (parent) {
bt_accept_unlink(sk);
@@ -392,6 +388,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
} else
sk->sk_state_change(sk);
+ release_sock(sk);
+
if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) &&
test_bit(CONF_INPUT_DONE, &chan->conf_state)))
return;
@@ -423,10 +421,12 @@ static void l2cap_chan_cleanup_listen(struct sock *parent)
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+
+ l2cap_chan_lock(chan);
__clear_chan_timer(chan);
- lock_sock(sk);
l2cap_chan_close(chan, ECONNRESET);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
+
chan->ops->close(chan->data);
}
}
@@ -436,14 +436,17 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
struct l2cap_conn *conn = chan->conn;
struct sock *sk = chan->sk;
- BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket);
+ BT_DBG("chan %p state %s sk %p", chan,
+ state_to_string(chan->state), sk);
switch (chan->state) {
case BT_LISTEN:
+ lock_sock(sk);
l2cap_chan_cleanup_listen(sk);
- l2cap_state_change(chan, BT_CLOSED);
+ __l2cap_state_change(chan, BT_CLOSED);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;
case BT_CONNECTED:
@@ -486,7 +489,9 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
default:
+ lock_sock(sk);
sock_set_flag(sk, SOCK_ZAPPED);
+ release_sock(sk);
break;
}
}
@@ -661,6 +666,21 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
+static void l2cap_send_conn_req(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_conn_req req;
+
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
+
+ chan->ident = l2cap_get_ident(conn);
+
+ set_bit(CONF_CONNECT_PEND, &chan->conf_state);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -670,17 +690,8 @@ static void l2cap_do_start(struct l2cap_chan *chan)
return;
if (l2cap_chan_check_security(chan) &&
- __l2cap_no_conn_pending(chan)) {
- struct l2cap_conn_req req;
- req.scid = cpu_to_le16(chan->scid);
- req.psm = chan->psm;
-
- chan->ident = l2cap_get_ident(conn);
- set_bit(CONF_CONNECT_PEND, &chan->conf_state);
-
- l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
- sizeof(req), &req);
- }
+ __l2cap_no_conn_pending(chan))
+ l2cap_send_conn_req(chan);
} else {
struct l2cap_info_req req;
req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
@@ -688,8 +699,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
- schedule_delayed_work(&conn->info_timer,
- msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+ schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident,
L2CAP_INFO_REQ, sizeof(req), &req);
@@ -714,14 +724,12 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
{
- struct sock *sk;
+ struct sock *sk = chan->sk;
struct l2cap_disconn_req req;
if (!conn)
return;
- sk = chan->sk;
-
if (chan->mode == L2CAP_MODE_ERTM) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -733,56 +741,47 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_DISCONN_REQ, sizeof(req), &req);
- l2cap_state_change(chan, BT_DISCONN);
- sk->sk_err = err;
+ lock_sock(sk);
+ __l2cap_state_change(chan, BT_DISCONN);
+ __l2cap_chan_set_err(chan, err);
+ release_sock(sk);
}
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
- struct l2cap_chan *chan;
+ struct l2cap_chan *chan, *tmp;
BT_DBG("conn %p", conn);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
struct sock *sk = chan->sk;
- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
- struct l2cap_conn_req req;
-
if (!l2cap_chan_check_security(chan) ||
!__l2cap_no_conn_pending(chan)) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
&& test_bit(CONF_STATE2_DEVICE,
&chan->conf_state)) {
- /* l2cap_chan_close() calls list_del(chan)
- * so release the lock */
l2cap_chan_close(chan, ECONNRESET);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
- req.scid = cpu_to_le16(chan->scid);
- req.psm = chan->psm;
-
- chan->ident = l2cap_get_ident(conn);
- set_bit(CONF_CONNECT_PEND, &chan->conf_state);
-
- l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ,
- sizeof(req), &req);
+ l2cap_send_conn_req(chan);
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
@@ -791,6 +790,7 @@ 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 (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
@@ -799,10 +799,11 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG);
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
}
+ release_sock(sk);
} else {
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -813,7 +814,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
rsp.result != L2CAP_CR_SUCCESS) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
@@ -823,10 +824,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
chan->num_conf_req++;
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* Find socket with cid and source bdaddr.
@@ -902,28 +903,34 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
__set_chan_timer(chan, sk->sk_sndtimeo);
- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
parent->sk_data_ready(parent, 0);
clean:
release_sock(parent);
}
-static void l2cap_chan_ready(struct sock *sk)
+static void l2cap_chan_ready(struct l2cap_chan *chan)
{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- struct sock *parent = bt_sk(sk)->parent;
+ struct sock *sk = chan->sk;
+ struct sock *parent;
+
+ lock_sock(sk);
+
+ parent = bt_sk(sk)->parent;
BT_DBG("sk %p, parent %p", sk, parent);
chan->conf_state = 0;
__clear_chan_timer(chan);
- l2cap_state_change(chan, BT_CONNECTED);
+ __l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);
if (parent)
parent->sk_data_ready(parent, 0);
+
+ release_sock(sk);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -938,29 +945,31 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->out && conn->hcon->type == LE_LINK)
smp_conn_security(conn, conn->hcon->pending_sec_level);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ list_for_each_entry(chan, &conn->chan_l, list) {
- bh_lock_sock(sk);
+ l2cap_chan_lock(chan);
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ struct sock *sk = chan->sk;
__clear_chan_timer(chan);
- l2cap_state_change(chan, BT_CONNECTED);
+ lock_sock(sk);
+ __l2cap_state_change(chan, BT_CONNECTED);
sk->sk_state_change(sk);
+ release_sock(sk);
} else if (chan->state == BT_CONNECT)
l2cap_do_start(chan);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -970,16 +979,14 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
BT_DBG("conn %p", conn);
- rcu_read_lock();
-
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ mutex_lock(&conn->chan_lock);
+ list_for_each_entry(chan, &conn->chan_l, list) {
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
- sk->sk_err = err;
+ __l2cap_chan_set_err(chan, err);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
static void l2cap_info_timeout(struct work_struct *work)
@@ -997,7 +1004,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
{
struct l2cap_conn *conn = hcon->l2cap_data;
struct l2cap_chan *chan, *l;
- struct sock *sk;
if (!conn)
return;
@@ -1006,22 +1012,28 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree_skb(conn->rx_skb);
+ mutex_lock(&conn->chan_lock);
+
/* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
- sk = chan->sk;
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
l2cap_chan_del(chan, err);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
+
chan->ops->close(chan->data);
}
+ mutex_unlock(&conn->chan_lock);
+
hci_chan_del(conn->hchan);
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
- __cancel_delayed_work(&conn->info_timer);
+ cancel_delayed_work_sync(&conn->info_timer);
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) {
- __cancel_delayed_work(&conn->security_timer);
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) {
+ cancel_delayed_work_sync(&conn->security_timer);
smp_chan_destroy(conn);
}
@@ -1072,6 +1084,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
conn->feat_mask = 0;
spin_lock_init(&conn->lock);
+ mutex_init(&conn->chan_lock);
INIT_LIST_HEAD(&conn->chan_l);
@@ -1120,7 +1133,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr
return c1;
}
-inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst)
{
struct sock *sk = chan->sk;
bdaddr_t *src = &bt_sk(sk)->src;
@@ -1139,7 +1152,7 @@ inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdad
hci_dev_lock(hdev);
- lock_sock(sk);
+ l2cap_chan_lock(chan);
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
@@ -1166,17 +1179,21 @@ inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdad
goto done;
}
+ lock_sock(sk);
+
switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
case BT_CONFIG:
/* Already connecting */
err = 0;
+ release_sock(sk);
goto done;
case BT_CONNECTED:
/* Already connected */
err = -EISCONN;
+ release_sock(sk);
goto done;
case BT_OPEN:
@@ -1186,11 +1203,15 @@ inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdad
default:
err = -EBADFD;
+ release_sock(sk);
goto done;
}
/* Set destination address and psm */
bacpy(&bt_sk(sk)->dst, dst);
+
+ release_sock(sk);
+
chan->psm = psm;
chan->dcid = cid;
@@ -1218,7 +1239,9 @@ inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdad
/* Update source addr of the socket */
bacpy(src, conn->src);
+ l2cap_chan_unlock(chan);
l2cap_chan_add(conn, chan);
+ l2cap_chan_lock(chan);
l2cap_state_change(chan, BT_CONNECT);
__set_chan_timer(chan, sk->sk_sndtimeo);
@@ -1235,6 +1258,7 @@ inline int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdad
err = 0;
done:
+ l2cap_chan_unlock(chan);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1276,14 +1300,14 @@ static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
monitor_timer.work);
- struct sock *sk = chan->sk;
BT_DBG("chan %p", chan);
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
if (chan->retry_count >= chan->remote_max_tx) {
l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return;
}
@@ -1291,25 +1315,26 @@ static void l2cap_monitor_timeout(struct work_struct *work)
__set_monitor_timer(chan);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
- release_sock(sk);
+ l2cap_chan_unlock(chan);
}
static void l2cap_retrans_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
retrans_timer.work);
- struct sock *sk = chan->sk;
BT_DBG("chan %p", chan);
- lock_sock(sk);
+ l2cap_chan_lock(chan);
+
chan->retry_count = 1;
__set_monitor_timer(chan);
set_bit(CONN_WAIT_F, &chan->conn_state);
l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
}
static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
@@ -1450,17 +1475,19 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
- if (bt_cb(skb)->retries == 1)
+ if (bt_cb(skb)->retries == 1) {
chan->unacked_frames++;
+ if (!nsent++)
+ __clear_ack_timer(chan);
+ }
+
chan->frames_sent++;
if (skb_queue_is_last(&chan->tx_q, skb))
chan->tx_send_head = NULL;
else
chan->tx_send_head = skb_queue_next(&chan->tx_q, skb);
-
- nsent++;
}
return nsent;
@@ -1478,7 +1505,7 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan)
return ret;
}
-static void l2cap_send_ack(struct l2cap_chan *chan)
+static void __l2cap_send_ack(struct l2cap_chan *chan)
{
u32 control = 0;
@@ -1498,6 +1525,12 @@ static void l2cap_send_ack(struct l2cap_chan *chan)
l2cap_send_sframe(chan, control);
}
+static void l2cap_send_ack(struct l2cap_chan *chan)
+{
+ __clear_ack_timer(chan);
+ __l2cap_send_ack(chan);
+}
+
static void l2cap_send_srejtail(struct l2cap_chan *chan)
{
struct srej_list *tail;
@@ -1512,9 +1545,11 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan)
l2cap_send_sframe(chan, control);
}
-static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb)
+static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
+ struct msghdr *msg, int len,
+ int count, struct sk_buff *skb)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn;
+ struct l2cap_conn *conn = chan->conn;
struct sk_buff **frag;
int err, sent = 0;
@@ -1529,7 +1564,10 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
while (len) {
count = min_t(unsigned int, conn->mtu, len);
- *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+ *frag = chan->ops->alloc_skb(chan, count,
+ msg->msg_flags & MSG_DONTWAIT,
+ &err);
+
if (!*frag)
return err;
if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
@@ -1550,17 +1588,18 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d priority %u", sk, (int)len, priority);
+ BT_DBG("chan %p len %d priority %u", chan, (int)len, priority);
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1572,7 +1611,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
put_unaligned_le16(chan->psm, skb_put(skb, 2));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1584,17 +1623,18 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 priority)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen = L2CAP_HDR_SIZE;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1605,7 +1645,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1617,13 +1657,12 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
struct msghdr *msg, size_t len,
u32 control, u16 sdulen)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct sk_buff *skb;
int err, count, hlen;
struct l2cap_hdr *lh;
- BT_DBG("sk %p len %d", sk, (int)len);
+ BT_DBG("chan %p len %d", chan, (int)len);
if (!conn)
return ERR_PTR(-ENOTCONN);
@@ -1640,8 +1679,10 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
hlen += L2CAP_FCS_SIZE;
count = min_t(unsigned int, (conn->mtu - hlen), len);
- skb = bt_skb_send_alloc(sk, count + hlen,
- msg->msg_flags & MSG_DONTWAIT, &err);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+
if (!skb)
return ERR_PTR(err);
@@ -1655,7 +1696,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
if (sdulen)
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
- err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
kfree_skb(skb);
return ERR_PTR(err);
@@ -1801,9 +1842,9 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn);
- rcu_read_lock();
+ mutex_lock(&conn->chan_lock);
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
+ list_for_each_entry(chan, &conn->chan_l, list) {
struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
@@ -1819,7 +1860,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
kfree_skb(nskb);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
}
/* ---- L2CAP signalling commands ---- */
@@ -1987,9 +2028,13 @@ static void l2cap_ack_timeout(struct work_struct *work)
BT_DBG("chan %p", chan);
- lock_sock(chan->sk);
- l2cap_send_ack(chan);
- release_sock(chan->sk);
+ l2cap_chan_lock(chan);
+
+ __l2cap_send_ack(chan);
+
+ l2cap_chan_unlock(chan);
+
+ l2cap_chan_put(chan);
}
static inline void l2cap_ertm_init(struct l2cap_chan *chan)
@@ -2574,7 +2619,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
cmd->ident == conn->info_ident) {
- __cancel_delayed_work(&conn->info_timer);
+ cancel_delayed_work(&conn->info_timer);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -2607,6 +2652,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
parent = pchan->sk;
+ mutex_lock(&conn->chan_lock);
lock_sock(parent);
/* Check if the ACL is secure enough (if not SDP) */
@@ -2647,7 +2693,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
bt_accept_enqueue(parent, sk);
- l2cap_chan_add(conn, chan);
+ __l2cap_chan_add(conn, chan);
dcid = chan->scid;
@@ -2658,28 +2704,29 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
if (bt_sk(sk)->defer_setup) {
- l2cap_state_change(chan, BT_CONNECT2);
+ __l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
parent->sk_data_ready(parent, 0);
} else {
- l2cap_state_change(chan, BT_CONFIG);
+ __l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
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);
+ mutex_unlock(&conn->chan_lock);
sendresp:
rsp.scid = cpu_to_le16(scid);
@@ -2695,8 +2742,7 @@ sendresp:
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
conn->info_ident = l2cap_get_ident(conn);
- schedule_delayed_work(&conn->info_timer,
- msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
+ schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
l2cap_send_cmd(conn, conn->info_ident,
L2CAP_INFO_REQ, sizeof(info), &info);
@@ -2719,27 +2765,36 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
struct l2cap_chan *chan;
- struct sock *sk;
u8 req[128];
+ int err;
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
result = __le16_to_cpu(rsp->result);
status = __le16_to_cpu(rsp->status);
- BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
+ BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
+ dcid, scid, result, status);
+
+ mutex_lock(&conn->chan_lock);
if (scid) {
- chan = l2cap_get_chan_by_scid(conn, scid);
- if (!chan)
- return -EFAULT;
+ chan = __l2cap_get_chan_by_scid(conn, scid);
+ if (!chan) {
+ err = -EFAULT;
+ goto unlock;
+ }
} else {
- chan = l2cap_get_chan_by_ident(conn, cmd->ident);
- if (!chan)
- return -EFAULT;
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan) {
+ err = -EFAULT;
+ goto unlock;
+ }
}
- sk = chan->sk;
+ err = 0;
+
+ l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CR_SUCCESS:
@@ -2765,8 +2820,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break;
}
- release_sock(sk);
- return 0;
+ l2cap_chan_unlock(chan);
+
+unlock:
+ mutex_unlock(&conn->chan_lock);
+
+ return err;
}
static inline void set_default_fcs(struct l2cap_chan *chan)
@@ -2786,7 +2845,6 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
u16 dcid, flags;
u8 rsp[64];
struct l2cap_chan *chan;
- struct sock *sk;
int len;
dcid = __le16_to_cpu(req->dcid);
@@ -2798,7 +2856,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (!chan)
return -ENOENT;
- sk = chan->sk;
+ l2cap_chan_lock(chan);
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
struct l2cap_cmd_rej_cid rej;
@@ -2860,7 +2918,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
goto unlock;
}
@@ -2887,7 +2945,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}
unlock:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
@@ -2896,7 +2954,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
struct l2cap_chan *chan;
- struct sock *sk;
int len = cmd->len - sizeof(*rsp);
scid = __le16_to_cpu(rsp->scid);
@@ -2910,7 +2967,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (!chan)
return 0;
- sk = chan->sk;
+ l2cap_chan_lock(chan);
switch (result) {
case L2CAP_CONF_SUCCESS:
@@ -2969,7 +3026,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
}
default:
- sk->sk_err = ECONNRESET;
+ l2cap_chan_set_err(chan, ECONNRESET);
+
__set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
l2cap_send_disconn_req(conn, chan, ECONNRESET);
goto done;
@@ -2990,11 +3048,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (chan->mode == L2CAP_MODE_ERTM)
l2cap_ertm_init(chan);
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
}
done:
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
@@ -3011,9 +3069,15 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
- chan = l2cap_get_chan_by_scid(conn, dcid);
- if (!chan)
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_scid(conn, dcid);
+ if (!chan) {
+ mutex_unlock(&conn->chan_lock);
return 0;
+ }
+
+ l2cap_chan_lock(chan);
sk = chan->sk;
@@ -3021,12 +3085,18 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
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);
l2cap_chan_del(chan, ECONNRESET);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+
+ mutex_unlock(&conn->chan_lock);
+
return 0;
}
@@ -3035,23 +3105,30 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
struct l2cap_chan *chan;
- struct sock *sk;
scid = __le16_to_cpu(rsp->scid);
dcid = __le16_to_cpu(rsp->dcid);
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
- chan = l2cap_get_chan_by_scid(conn, scid);
- if (!chan)
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_scid(conn, scid);
+ if (!chan) {
+ mutex_unlock(&conn->chan_lock);
return 0;
+ }
- sk = chan->sk;
+ l2cap_chan_lock(chan);
l2cap_chan_del(chan, 0);
- release_sock(sk);
+
+ l2cap_chan_unlock(chan);
chan->ops->close(chan->data);
+
+ mutex_unlock(&conn->chan_lock);
+
return 0;
}
@@ -3120,7 +3197,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
return 0;
- __cancel_delayed_work(&conn->info_timer);
+ cancel_delayed_work(&conn->info_timer);
if (result != L2CAP_IR_SUCCESS) {
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
@@ -3131,7 +3208,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
- if (type == L2CAP_IT_FEAT_MASK) {
+ switch (type) {
+ case L2CAP_IT_FEAT_MASK:
conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
@@ -3148,11 +3226,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
l2cap_conn_start(conn);
}
- } else if (type == L2CAP_IT_FIXED_CHAN) {
+ break;
+
+ case L2CAP_IT_FIXED_CHAN:
+ conn->fixed_chan_mask = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
l2cap_conn_start(conn);
+ break;
}
return 0;
@@ -3712,19 +3794,11 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u3
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
{
- u32 control;
-
BT_DBG("chan %p, Enter local busy", chan);
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
- control = __set_reqseq(chan, chan->buffer_seq);
- control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR);
- l2cap_send_sframe(chan, control);
-
- set_bit(CONN_RNR_SENT, &chan->conn_state);
-
- __clear_ack_timer(chan);
+ __set_ack_timer(chan);
}
static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
@@ -3864,8 +3938,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
goto drop;
}
- if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
+ l2cap_send_ack(chan);
goto drop;
+ }
if (tx_seq == chan->expected_tx_seq)
goto expected;
@@ -3926,15 +4003,15 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont
__skb_queue_head_init(&chan->srej_q);
l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
- set_bit(CONN_SEND_PBIT, &chan->conn_state);
+ /* Set P-bit only if there are some I-frames to ack. */
+ if (__clear_ack_timer(chan))
+ set_bit(CONN_SEND_PBIT, &chan->conn_state);
err = l2cap_send_srejframe(chan, tx_seq);
if (err < 0) {
l2cap_send_disconn_req(chan->conn, chan, -err);
return err;
}
-
- __clear_ack_timer(chan);
}
return 0;
@@ -4134,9 +4211,8 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_cont
return 0;
}
-static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb)
+static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
u32 control;
u16 req_seq;
int len, next_tx_seq_offset, req_seq_offset;
@@ -4204,7 +4280,6 @@ drop:
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct l2cap_chan *chan;
- struct sock *sk = NULL;
u32 control;
u16 tx_seq;
int len;
@@ -4212,10 +4287,12 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
BT_DBG("unknown cid 0x%4.4x", cid);
- goto drop;
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
}
- sk = chan->sk;
+ l2cap_chan_lock(chan);
BT_DBG("chan %p, len %d", chan, skb->len);
@@ -4237,7 +4314,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
break;
case L2CAP_MODE_ERTM:
- l2cap_ertm_data_rcv(sk, skb);
+ l2cap_ertm_data_rcv(chan, skb);
goto done;
@@ -4286,26 +4363,20 @@ drop:
kfree_skb(skb);
done:
- if (sk)
- release_sock(sk);
+ l2cap_chan_unlock(chan);
return 0;
}
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb)
{
- struct sock *sk = NULL;
struct l2cap_chan *chan;
chan = l2cap_global_chan_by_psm(0, psm, conn->src);
if (!chan)
goto drop;
- sk = chan->sk;
-
- lock_sock(sk);
-
- BT_DBG("sk %p, len %d", sk, skb->len);
+ BT_DBG("chan %p, len %d", chan, skb->len);
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
@@ -4314,31 +4385,23 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str
goto drop;
if (!chan->ops->recv(chan->data, skb))
- goto done;
+ return 0;
drop:
kfree_skb(skb);
-done:
- if (sk)
- release_sock(sk);
return 0;
}
static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb)
{
- struct sock *sk = NULL;
struct l2cap_chan *chan;
chan = l2cap_global_chan_by_scid(0, cid, conn->src);
if (!chan)
goto drop;
- sk = chan->sk;
-
- lock_sock(sk);
-
- BT_DBG("sk %p, len %d", sk, skb->len);
+ BT_DBG("chan %p, len %d", chan, skb->len);
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
goto drop;
@@ -4347,14 +4410,11 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct
goto drop;
if (!chan->ops->recv(chan->data, skb))
- goto done;
+ return 0;
drop:
kfree_skb(skb);
-done:
- if (sk)
- release_sock(sk);
return 0;
}
@@ -4499,59 +4559,52 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (hcon->type == LE_LINK) {
smp_distribute_keys(conn, 0);
- __cancel_delayed_work(&conn->security_timer);
+ cancel_delayed_work(&conn->security_timer);
}
- rcu_read_lock();
-
- list_for_each_entry_rcu(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
+ mutex_lock(&conn->chan_lock);
- bh_lock_sock(sk);
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ l2cap_chan_lock(chan);
BT_DBG("chan->scid %d", chan->scid);
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!status && encrypt) {
chan->sec_level = hcon->sec_level;
- l2cap_chan_ready(sk);
+ l2cap_chan_ready(chan);
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
l2cap_check_encryption(chan, encrypt);
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
continue;
}
if (chan->state == BT_CONNECT) {
if (!status) {
- struct l2cap_conn_req req;
- req.scid = cpu_to_le16(chan->scid);
- req.psm = chan->psm;
-
- chan->ident = l2cap_get_ident(conn);
- set_bit(CONF_CONNECT_PEND, &chan->conf_state);
-
- l2cap_send_cmd(conn, chan->ident,
- L2CAP_CONN_REQ, sizeof(req), &req);
+ l2cap_send_conn_req(chan);
} else {
__clear_chan_timer(chan);
__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 (bt_sk(sk)->defer_setup) {
struct sock *parent = bt_sk(sk)->parent;
@@ -4560,17 +4613,19 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (parent)
parent->sk_data_ready(parent, 0);
} 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);
@@ -4579,10 +4634,10 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
sizeof(rsp), &rsp);
}
- bh_unlock_sock(sk);
+ l2cap_chan_unlock(chan);
}
- rcu_read_unlock();
+ mutex_unlock(&conn->chan_lock);
return 0;
}
@@ -4643,6 +4698,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
if (chan && chan->sk) {
struct sock *sk = chan->sk;
+ lock_sock(sk);
if (chan->imtu < len - L2CAP_HDR_SIZE) {
BT_ERR("Frame exceeding recv MTU (len %d, "
@@ -4713,7 +4769,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
-}
+ }
read_unlock(&chan_list_lock);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index c61d967012b2..c4fe583b0af6 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -125,13 +125,15 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
err = l2cap_chan_connect(chan, la.l2_psm, la.l2_cid, &la.l2_bdaddr);
if (err)
- goto done;
+ return err;
+
+ lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
-done:
- if (sock_owned_by_user(sk))
- release_sock(sk);
+
+ release_sock(sk);
+
return err;
}
@@ -783,7 +785,7 @@ static void l2cap_sock_kill(struct sock *sk)
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
return;
- BT_DBG("sk %p state %d", sk, sk->sk_state);
+ BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
/* Kill poor orphan */
@@ -795,7 +797,8 @@ static void l2cap_sock_kill(struct sock *sk)
static int l2cap_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ struct l2cap_chan *chan;
+ struct l2cap_conn *conn;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -803,13 +806,24 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
if (!sk)
return 0;
+ chan = l2cap_pi(sk)->chan;
+ conn = chan->conn;
+
+ if (conn)
+ mutex_lock(&conn->chan_lock);
+
+ l2cap_chan_lock(chan);
lock_sock(sk);
+
if (!sk->sk_shutdown) {
if (chan->mode == L2CAP_MODE_ERTM)
err = __l2cap_wait_ack(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
+
+ release_sock(sk);
l2cap_chan_close(chan, 0);
+ lock_sock(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED,
@@ -820,6 +834,11 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
err = -sk->sk_err;
release_sock(sk);
+ l2cap_chan_unlock(chan);
+
+ if (conn)
+ mutex_unlock(&conn->chan_lock);
+
return err;
}
@@ -849,6 +868,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
if (!sk)
return NULL;
+ bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
+
l2cap_sock_init(sk, parent);
return l2cap_pi(sk)->chan;
@@ -860,8 +881,12 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
struct sock *sk = data;
struct l2cap_pinfo *pi = l2cap_pi(sk);
- if (pi->rx_busy_skb)
- return -ENOMEM;
+ lock_sock(sk);
+
+ if (pi->rx_busy_skb) {
+ err = -ENOMEM;
+ goto done;
+ }
err = sock_queue_rcv_skb(sk, skb);
@@ -880,6 +905,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
err = 0;
}
+done:
+ release_sock(sk);
+
return err;
}
@@ -897,12 +925,22 @@ static void l2cap_sock_state_change_cb(void *data, int state)
sk->sk_state = state;
}
+static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb,
+ int *err)
+{
+ struct sock *sk = chan->sk;
+
+ return bt_skb_send_alloc(sk, len, nb, err);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
.recv = l2cap_sock_recv_cb,
.close = l2cap_sock_close_cb,
.state_change = l2cap_sock_state_change_cb,
+ .alloc_skb = l2cap_sock_alloc_skb_cb,
};
static void l2cap_sock_destruct(struct sock *sk)
diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c
index 86a6bed229df..506628876f36 100644
--- a/net/bluetooth/lib.c
+++ b/net/bluetooth/lib.c
@@ -24,6 +24,8 @@
/* Bluetooth kernel library. */
+#define pr_fmt(fmt) "Bluetooth: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
@@ -151,7 +153,26 @@ int bt_to_errno(__u16 code)
}
EXPORT_SYMBOL(bt_to_errno);
-int bt_printk(const char *level, const char *format, ...)
+int bt_info(const char *format, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, format);
+
+ vaf.fmt = format;
+ vaf.va = &args;
+
+ r = pr_info("%pV", &vaf);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(bt_info);
+
+int bt_err(const char *format, ...)
{
struct va_format vaf;
va_list args;
@@ -162,10 +183,10 @@ int bt_printk(const char *level, const char *format, ...)
vaf.fmt = format;
vaf.va = &args;
- r = printk("%sBluetooth: %pV\n", level, &vaf);
+ r = pr_err("%pV", &vaf);
va_end(args);
return r;
}
-EXPORT_SYMBOL(bt_printk);
+EXPORT_SYMBOL(bt_err);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index bc8e59dda78e..7fcff8887131 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1,6 +1,8 @@
/*
BlueZ - Bluetooth protocol stack for Linux
+
Copyright (C) 2010 Nokia Corporation
+ Copyright (C) 2011-2012 Intel Corporation
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
@@ -32,12 +34,92 @@
#include <net/bluetooth/mgmt.h>
#include <net/bluetooth/smp.h>
-#define MGMT_VERSION 0
-#define MGMT_REVISION 1
+bool enable_hs;
+bool enable_le;
+
+#define MGMT_VERSION 1
+#define MGMT_REVISION 0
+
+static const u16 mgmt_commands[] = {
+ MGMT_OP_READ_INDEX_LIST,
+ MGMT_OP_READ_INFO,
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_DISCOVERABLE,
+ MGMT_OP_SET_CONNECTABLE,
+ MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_OP_SET_PAIRABLE,
+ MGMT_OP_SET_LINK_SECURITY,
+ MGMT_OP_SET_SSP,
+ MGMT_OP_SET_HS,
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_DEV_CLASS,
+ MGMT_OP_SET_LOCAL_NAME,
+ MGMT_OP_ADD_UUID,
+ MGMT_OP_REMOVE_UUID,
+ MGMT_OP_LOAD_LINK_KEYS,
+ MGMT_OP_LOAD_LONG_TERM_KEYS,
+ MGMT_OP_DISCONNECT,
+ MGMT_OP_GET_CONNECTIONS,
+ MGMT_OP_PIN_CODE_REPLY,
+ MGMT_OP_PIN_CODE_NEG_REPLY,
+ MGMT_OP_SET_IO_CAPABILITY,
+ MGMT_OP_PAIR_DEVICE,
+ MGMT_OP_CANCEL_PAIR_DEVICE,
+ MGMT_OP_UNPAIR_DEVICE,
+ MGMT_OP_USER_CONFIRM_REPLY,
+ MGMT_OP_USER_CONFIRM_NEG_REPLY,
+ MGMT_OP_USER_PASSKEY_REPLY,
+ MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ MGMT_OP_START_DISCOVERY,
+ MGMT_OP_STOP_DISCOVERY,
+ MGMT_OP_CONFIRM_NAME,
+ MGMT_OP_BLOCK_DEVICE,
+ MGMT_OP_UNBLOCK_DEVICE,
+};
+
+static const u16 mgmt_events[] = {
+ MGMT_EV_CONTROLLER_ERROR,
+ MGMT_EV_INDEX_ADDED,
+ MGMT_EV_INDEX_REMOVED,
+ MGMT_EV_NEW_SETTINGS,
+ MGMT_EV_CLASS_OF_DEV_CHANGED,
+ MGMT_EV_LOCAL_NAME_CHANGED,
+ MGMT_EV_NEW_LINK_KEY,
+ MGMT_EV_NEW_LONG_TERM_KEY,
+ MGMT_EV_DEVICE_CONNECTED,
+ MGMT_EV_DEVICE_DISCONNECTED,
+ MGMT_EV_CONNECT_FAILED,
+ MGMT_EV_PIN_CODE_REQUEST,
+ MGMT_EV_USER_CONFIRM_REQUEST,
+ MGMT_EV_USER_PASSKEY_REQUEST,
+ MGMT_EV_AUTH_FAILED,
+ MGMT_EV_DEVICE_FOUND,
+ MGMT_EV_DISCOVERING,
+ MGMT_EV_DEVICE_BLOCKED,
+ MGMT_EV_DEVICE_UNBLOCKED,
+ MGMT_EV_DEVICE_UNPAIRED,
+};
+
+/*
+ * These LE scan and inquiry parameters were chosen according to LE General
+ * Discovery Procedure specification.
+ */
+#define LE_SCAN_TYPE 0x01
+#define LE_SCAN_WIN 0x12
+#define LE_SCAN_INT 0x12
+#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
+#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
+
+#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
+#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
-#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
+#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
-#define SERVICE_CACHE_TIMEOUT (5 * 1000)
+#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
+ !test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
struct pending_cmd {
struct list_head list;
@@ -151,8 +233,8 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
return err;
}
-static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
- size_t rp_len)
+static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
+ void *rp, size_t rp_len)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
@@ -173,6 +255,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
put_unaligned_le16(cmd, &ev->opcode);
+ ev->status = status;
if (rp)
memcpy(ev->data, rp, rp_len);
@@ -181,10 +264,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
if (err < 0)
kfree_skb(skb);
- return err;;
+ return err;
}
-static int read_version(struct sock *sk)
+static int read_version(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
{
struct mgmt_rp_read_version rp;
@@ -193,11 +277,46 @@ static int read_version(struct sock *sk)
rp.version = MGMT_VERSION;
put_unaligned_le16(MGMT_REVISION, &rp.revision);
- return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp,
- sizeof(rp));
+ return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
+ sizeof(rp));
+}
+
+static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct mgmt_rp_read_commands *rp;
+ u16 num_commands = ARRAY_SIZE(mgmt_commands);
+ u16 num_events = ARRAY_SIZE(mgmt_events);
+ u16 *opcode;
+ size_t rp_size;
+ int i, err;
+
+ BT_DBG("sock %p", sk);
+
+ rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
+
+ rp = kmalloc(rp_size, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ put_unaligned_le16(num_commands, &rp->num_commands);
+ put_unaligned_le16(num_events, &rp->num_events);
+
+ for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
+ put_unaligned_le16(mgmt_commands[i], opcode);
+
+ for (i = 0; i < num_events; i++, opcode++)
+ put_unaligned_le16(mgmt_events[i], opcode);
+
+ err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp,
+ rp_size);
+ kfree(rp);
+
+ return err;
}
-static int read_index_list(struct sock *sk)
+static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
{
struct mgmt_rp_read_index_list *rp;
struct list_head *p;
@@ -226,10 +345,7 @@ static int read_index_list(struct sock *sk)
i = 0;
list_for_each_entry(d, &hci_dev_list, list) {
- if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags))
- cancel_delayed_work(&d->power_off);
-
- if (test_bit(HCI_SETUP, &d->flags))
+ if (test_bit(HCI_SETUP, &d->dev_flags))
continue;
put_unaligned_le16(d->id, &rp->index[i++]);
@@ -238,8 +354,8 @@ static int read_index_list(struct sock *sk)
read_unlock(&hci_dev_list_lock);
- err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp,
- rp_len);
+ err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
+ rp_len);
kfree(rp);
@@ -264,8 +380,13 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_LINK_SECURITY;
}
- if (hdev->features[4] & LMP_LE)
- settings |= MGMT_SETTING_LE;
+ if (enable_hs)
+ settings |= MGMT_SETTING_HS;
+
+ if (enable_le) {
+ if (hdev->features[4] & LMP_LE)
+ settings |= MGMT_SETTING_LE;
+ }
return settings;
}
@@ -274,47 +395,36 @@ static u32 get_current_settings(struct hci_dev *hdev)
{
u32 settings = 0;
- if (test_bit(HCI_UP, &hdev->flags))
+ if (hdev_is_powered(hdev))
settings |= MGMT_SETTING_POWERED;
- else
- return settings;
- if (test_bit(HCI_PSCAN, &hdev->flags))
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_CONNECTABLE;
- if (test_bit(HCI_ISCAN, &hdev->flags))
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_DISCOVERABLE;
- if (test_bit(HCI_PAIRABLE, &hdev->flags))
+ if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
settings |= MGMT_SETTING_PAIRABLE;
if (!(hdev->features[4] & LMP_NO_BREDR))
settings |= MGMT_SETTING_BREDR;
- if (hdev->host_features[0] & LMP_HOST_LE)
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_LE;
- if (test_bit(HCI_AUTH, &hdev->flags))
+ if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
settings |= MGMT_SETTING_LINK_SECURITY;
- if (hdev->ssp_mode > 0)
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_SSP;
+ if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
+ settings |= MGMT_SETTING_HS;
+
return settings;
}
-#define EIR_FLAGS 0x01 /* flags */
-#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
-#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
-#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
-#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
-#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
-#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
-#define EIR_NAME_SHORT 0x08 /* shortened local name */
-#define EIR_NAME_COMPLETE 0x09 /* complete local name */
-#define EIR_TX_POWER 0x0A /* transmit power level */
-#define EIR_DEVICE_ID 0x10 /* device ID */
-
#define PNP_INFO_SVCLASS_ID 0x1200
static u8 bluetooth_base_uuid[] = {
@@ -425,13 +535,16 @@ static int update_eir(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
+ if (!hdev_is_powered(hdev))
+ return 0;
+
if (!(hdev->features[6] & LMP_EXT_INQ))
return 0;
- if (hdev->ssp_mode == 0)
+ if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
return 0;
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return 0;
memset(&cp, 0, sizeof(cp));
@@ -460,10 +573,14 @@ static u8 get_service_classes(struct hci_dev *hdev)
static int update_class(struct hci_dev *hdev)
{
u8 cod[3];
+ int err;
BT_DBG("%s", hdev->name);
- if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (!hdev_is_powered(hdev))
+ return 0;
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return 0;
cod[0] = hdev->minor_class;
@@ -473,15 +590,19 @@ static int update_class(struct hci_dev *hdev)
if (memcmp(cod, hdev->dev_class, 3) == 0)
return 0;
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+ if (err == 0)
+ set_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
+
+ return err;
}
static void service_cache_off(struct work_struct *work)
{
struct hci_dev *hdev = container_of(work, struct hci_dev,
- service_cache.work);
+ service_cache.work);
- if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
return;
hci_dev_lock(hdev);
@@ -492,36 +613,30 @@ static void service_cache_off(struct work_struct *work)
hci_dev_unlock(hdev);
}
-static void mgmt_init_hdev(struct hci_dev *hdev)
+static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
{
- if (!test_and_set_bit(HCI_MGMT, &hdev->flags))
- INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
+ if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
+ return;
+
+ INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
- if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->flags))
- schedule_delayed_work(&hdev->service_cache,
- msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
+ /* Non-mgmt controlled devices get this bit set
+ * implicitly so that pairing works for them, however
+ * for mgmt we require user-space to explicitly enable
+ * it
+ */
+ clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
}
-static int read_controller_info(struct sock *sk, u16 index)
+static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
{
struct mgmt_rp_read_info rp;
- struct hci_dev *hdev;
-
- BT_DBG("sock %p hci%u", sk, index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_READ_INFO,
- MGMT_STATUS_INVALID_PARAMS);
- if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
- cancel_delayed_work_sync(&hdev->power_off);
+ BT_DBG("sock %p %s", sk, hdev->name);
hci_dev_lock(hdev);
- if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
- mgmt_init_hdev(hdev);
-
memset(&rp, 0, sizeof(rp));
bacpy(&rp.bdaddr, &hdev->bdaddr);
@@ -536,11 +651,12 @@ static int read_controller_info(struct sock *sk, u16 index)
memcpy(rp.dev_class, hdev->dev_class, 3);
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
+ memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
- return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
+ return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp,
+ sizeof(rp));
}
static void mgmt_pending_free(struct pending_cmd *cmd)
@@ -551,8 +667,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd)
}
static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
- struct hci_dev *hdev,
- void *data, u16 len)
+ struct hci_dev *hdev, void *data,
+ u16 len)
{
struct pending_cmd *cmd;
@@ -581,8 +697,8 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
}
static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
- void (*cb)(struct pending_cmd *cmd, void *data),
- void *data)
+ void (*cb)(struct pending_cmd *cmd, void *data),
+ void *data)
{
struct list_head *p, *n;
@@ -620,40 +736,39 @@ static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
{
__le32 settings = cpu_to_le32(get_current_settings(hdev));
- return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings));
+ return cmd_complete(sk, hdev->id, opcode, 0, &settings,
+ sizeof(settings));
}
-static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
+ struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- int err, up;
-
- cp = (void *) data;
+ int err;
- BT_DBG("request for hci%u", index);
+ BT_DBG("request for %s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_INVALID_PARAMS);
+ if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+ cancel_delayed_work(&hdev->power_off);
- hci_dev_lock(hdev);
+ if (cp->val) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
+ mgmt_powered(hdev, 1);
+ goto failed;
+ }
+ }
- up = test_bit(HCI_UP, &hdev->flags);
- if ((cp->val && up) || (!cp->val && !up)) {
+ if (!!cp->val == hdev_is_powered(hdev)) {
err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
- err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+ MGMT_STATUS_BUSY);
goto failed;
}
@@ -672,49 +787,115 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
+ struct sock *skip_sk)
{
- struct mgmt_cp_set_discoverable *cp;
- struct hci_dev *hdev;
+ struct sk_buff *skb;
+ struct mgmt_hdr *hdr;
+
+ skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr = (void *) skb_put(skb, sizeof(*hdr));
+ hdr->opcode = cpu_to_le16(event);
+ if (hdev)
+ hdr->index = cpu_to_le16(hdev->id);
+ else
+ hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+ hdr->len = cpu_to_le16(data_len);
+
+ if (data)
+ memcpy(skb_put(skb, data_len), data, data_len);
+
+ /* Time stamp */
+ __net_timestamp(skb);
+
+ hci_send_to_control(skb, skip_sk);
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static int new_settings(struct hci_dev *hdev, struct sock *skip)
+{
+ __le32 ev;
+
+ ev = cpu_to_le32(get_current_settings(hdev));
+
+ return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
+}
+
+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;
+ u16 timeout;
u8 scan;
int err;
- cp = (void *) data;
-
- BT_DBG("request for hci%u", index);
+ BT_DBG("request for %s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ timeout = get_unaligned_le16(&cp->timeout);
+ if (!cp->val && timeout > 0)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_INVALID_PARAMS);
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev) && timeout > 0) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_NOT_POWERED);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
- err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_BUSY);
goto failed;
}
- if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
- test_bit(HCI_PSCAN, &hdev->flags)) {
+ if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_REJECTED);
+ goto failed;
+ }
+
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
+
+ if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
+ change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
+ changed = true;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
+ if (err < 0)
+ goto failed;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ 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 (cp->val && timeout > 0) {
+ hdev->discov_timeout = timeout;
+ queue_delayed_work(hdev->workqueue, &hdev->discov_off,
+ msecs_to_jiffies(hdev->discov_timeout * 1000));
+ }
+
err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
goto failed;
}
@@ -737,53 +918,56 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
if (cp->val)
- hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
+ hdev->discov_timeout = timeout;
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
+ struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
u8 scan;
int err;
- cp = (void *) data;
+ BT_DBG("request for %s", hdev->name);
- BT_DBG("request for hci%u", index);
+ hci_dev_lock(hdev);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ changed = true;
- hci_dev_lock(hdev);
+ 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);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_NOT_POWERED);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
- err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+ MGMT_STATUS_BUSY);
goto failed;
}
- if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
+ if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
goto failed;
}
@@ -794,116 +978,282 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- if (cp->val)
+ if (cp->val) {
scan = SCAN_PAGE;
- else
+ } else {
scan = 0;
+ if (test_bit(HCI_ISCAN, &hdev->flags) &&
+ hdev->discov_timeout > 0)
+ cancel_delayed_work(&hdev->discov_off);
+ }
+
err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
if (err < 0)
mgmt_pending_remove(cmd);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
- u16 data_len, struct sock *skip_sk)
+static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct sk_buff *skb;
- struct mgmt_hdr *hdr;
+ struct mgmt_mode *cp = data;
+ int err;
- skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
+ BT_DBG("request for %s", hdev->name);
- bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
+ hci_dev_lock(hdev);
- hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(event);
- if (hdev)
- hdr->index = cpu_to_le16(hdev->id);
+ if (cp->val)
+ set_bit(HCI_PAIRABLE, &hdev->dev_flags);
else
- hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
- hdr->len = cpu_to_le16(data_len);
+ clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
- if (data)
- memcpy(skb_put(skb, data_len), data, data_len);
+ err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
+ if (err < 0)
+ goto failed;
- hci_send_to_sock(NULL, skb, skip_sk);
- kfree_skb(skb);
+ err = new_settings(hdev, sk);
- return 0;
+failed:
+ hci_dev_unlock(hdev);
+ return err;
}
-static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_mode *cp;
- struct hci_dev *hdev;
- __le32 ev;
+ struct mgmt_mode *cp = data;
+ struct pending_cmd *cmd;
+ u8 val;
int err;
- cp = (void *) data;
+ BT_DBG("request for %s", hdev->name);
- BT_DBG("request for hci%u", index);
+ hci_dev_lock(hdev);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!!cp->val != test_bit(HCI_LINK_SECURITY,
+ &hdev->dev_flags)) {
+ change_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+ changed = true;
+ }
- hci_dev_lock(hdev);
+ err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
+ if (err < 0)
+ goto failed;
- if (cp->val)
- set_bit(HCI_PAIRABLE, &hdev->flags);
- else
- clear_bit(HCI_PAIRABLE, &hdev->flags);
+ if (changed)
+ err = new_settings(hdev, sk);
- err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
- if (err < 0)
goto failed;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
+ val = !!cp->val;
+
+ if (test_bit(HCI_AUTH, &hdev->flags) == val) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
- err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
+ return err;
+}
+
+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;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+ MGMT_STATUS_NOT_SUPPORTED);
+ goto failed;
+ }
+
+ val = !!cp->val;
+
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
+
+ if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ change_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ changed = true;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
+ if (err < 0)
+ goto failed;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+ goto failed;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_SSP, 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) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
+
+failed:
+ hci_dev_unlock(hdev);
return err;
}
-static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
{
- struct mgmt_cp_add_uuid *cp;
- struct hci_dev *hdev;
- struct bt_uuid *uuid;
+ struct mgmt_mode *cp = data;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (!enable_hs)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ if (cp->val)
+ set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
+
+ return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
+}
+
+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;
int err;
+ u8 val, enabled;
+
+ BT_DBG("request for %s", hdev->name);
- cp = (void *) data;
+ hci_dev_lock(hdev);
- BT_DBG("request for hci%u", index);
+ if (!enable_le || !(hdev->features[4] & LMP_LE)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+ MGMT_STATUS_NOT_SUPPORTED);
+ goto unlock;
+ }
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_ADD_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ val = !!cp->val;
+ enabled = !!(hdev->host_features[0] & LMP_HOST_LE);
+
+ if (!hdev_is_powered(hdev) || val == enabled) {
+ bool changed = false;
+
+ if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ change_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+ changed = true;
+ }
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_ADD_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ memset(&hci_cp, 0, sizeof(hci_cp));
+
+ if (val) {
+ hci_cp.le = val;
+ hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
+ &hci_cp);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto unlock;
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
+{
+ struct mgmt_cp_add_uuid *cp = data;
+ struct pending_cmd *cmd;
+ struct bt_uuid *uuid;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
hci_dev_lock(hdev);
+ if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
if (!uuid) {
err = -ENOMEM;
@@ -923,41 +1273,65 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto failed;
- err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
+ if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0,
+ hdev->dev_class, 3);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static bool enable_service_cache(struct hci_dev *hdev)
{
- struct list_head *p, *n;
- struct mgmt_cp_remove_uuid *cp;
- struct hci_dev *hdev;
- u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int err, found;
+ if (!hdev_is_powered(hdev))
+ return false;
- cp = (void *) data;
+ if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
+ schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT);
+ return true;
+ }
- BT_DBG("request for hci%u", index);
+ return false;
+}
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_remove_uuid *cp = data;
+ struct pending_cmd *cmd;
+ struct list_head *p, *n;
+ u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int err, found;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("request for %s", hdev->name);
hci_dev_lock(hdev);
+ if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
err = hci_uuids_clear(hdev);
- goto unlock;
+
+ if (enable_service_cache(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID,
+ 0, hdev->dev_class, 3);
+ goto unlock;
+ }
+
+ goto update_class;
}
found = 0;
@@ -973,11 +1347,12 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
}
if (found == 0) {
- err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
+ MGMT_STATUS_INVALID_PARAMS);
goto unlock;
}
+update_class:
err = update_class(hdev);
if (err < 0)
goto unlock;
@@ -986,41 +1361,50 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (err < 0)
goto unlock;
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
+ if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0,
+ hdev->dev_class, 3);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_set_dev_class *cp;
+ struct mgmt_cp_set_dev_class *cp = data;
+ struct pending_cmd *cmd;
int err;
- cp = (void *) data;
-
- BT_DBG("request for hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("request for %s", hdev->name);
hci_dev_lock(hdev);
+ if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
hdev->major_class = cp->major;
hdev->minor_class = cp->minor;
- if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->flags)) {
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
+ hdev->dev_class, 3);
+ goto unlock;
+ }
+
+ if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
hci_dev_unlock(hdev);
cancel_delayed_work_sync(&hdev->service_cache);
hci_dev_lock(hdev);
@@ -1028,30 +1412,33 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
}
err = update_class(hdev);
+ if (err < 0)
+ goto unlock;
- if (err == 0)
- err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
+ if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
+ hdev->dev_class, 3);
+ goto unlock;
+ }
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+unlock:
+ hci_dev_unlock(hdev);
return err;
}
-static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
+static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_load_link_keys *cp;
+ struct mgmt_cp_load_link_keys *cp = data;
u16 key_count, expected_len;
int i;
- cp = (void *) data;
-
- if (len < sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count *
@@ -1059,92 +1446,103 @@ static int load_link_keys(struct sock *sk, u16 index, unsigned char *data,
if (expected_len != len) {
BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
len, expected_len);
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+ MGMT_STATUS_INVALID_PARAMS);
}
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
- BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
+ BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
key_count);
hci_dev_lock(hdev);
hci_link_keys_clear(hdev);
- set_bit(HCI_LINK_KEYS, &hdev->flags);
+ set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
if (cp->debug_keys)
- set_bit(HCI_DEBUG_KEYS, &hdev->flags);
+ set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
else
- clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
+ clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
for (i = 0; i < key_count; i++) {
struct mgmt_link_key_info *key = &cp->keys[i];
- hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
- key->pin_len);
+ hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
+ key->type, key->pin_len);
}
- cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
+ cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return 0;
}
-static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, struct sock *skip_sk)
{
- struct hci_dev *hdev;
- struct mgmt_cp_remove_keys *cp;
- struct mgmt_rp_remove_keys rp;
+ struct mgmt_ev_device_unpaired ev;
+
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = addr_type;
+
+ return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
+ skip_sk);
+}
+
+static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_unpair_device *cp = data;
+ struct mgmt_rp_unpair_device rp;
struct hci_cp_disconnect dc;
struct pending_cmd *cmd;
struct hci_conn *conn;
int err;
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
memset(&rp, 0, sizeof(rp));
- bacpy(&rp.bdaddr, &cp->bdaddr);
- rp.status = MGMT_STATUS_FAILED;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- err = hci_remove_link_key(hdev, &cp->bdaddr);
- if (err < 0) {
- rp.status = MGMT_STATUS_NOT_PAIRED;
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+ MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
goto unlock;
}
- if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ if (cp->addr.type == MGMT_ADDR_BREDR)
+ err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
+ else
+ err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
+
+ if (err < 0) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+ MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp));
goto unlock;
}
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (cp->disconnect) {
+ if (cp->addr.type == MGMT_ADDR_BREDR)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+ &cp->addr.bdaddr);
+ else
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
+ &cp->addr.bdaddr);
+ } else {
+ conn = NULL;
+ }
+
if (!conn) {
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
+ &rp, sizeof(rp));
+ device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
goto unlock;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp));
+ cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
+ sizeof(*cp));
if (!cmd) {
err = -ENOMEM;
goto unlock;
@@ -1157,19 +1555,14 @@ static int remove_keys(struct sock *sk, u16 index, unsigned char *data,
mgmt_pending_remove(cmd);
unlock:
- if (err < 0)
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_disconnect *cp;
+ struct mgmt_cp_disconnect *cp = data;
struct hci_cp_disconnect dc;
struct pending_cmd *cmd;
struct hci_conn *conn;
@@ -1177,38 +1570,28 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_NOT_POWERED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
+ MGMT_STATUS_NOT_POWERED);
goto failed;
}
if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
- err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
+ MGMT_STATUS_BUSY);
goto failed;
}
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
- if (!conn)
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+ if (cp->addr.type == MGMT_ADDR_BREDR)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
+ else
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
if (!conn) {
- err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
- MGMT_STATUS_NOT_CONNECTED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
+ MGMT_STATUS_NOT_CONNECTED);
goto failed;
}
@@ -1227,8 +1610,6 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
@@ -1251,41 +1632,42 @@ static u8 link_to_mgmt(u8 link_type, u8 addr_type)
}
}
-static int get_connections(struct sock *sk, u16 index)
+static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
{
struct mgmt_rp_get_connections *rp;
- struct hci_dev *hdev;
struct hci_conn *c;
- struct list_head *p;
size_t rp_len;
- u16 count;
- int i, err;
+ int err;
+ u16 i;
BT_DBG("");
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- count = 0;
- list_for_each(p, &hdev->conn_hash.list) {
- count++;
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS,
+ MGMT_STATUS_NOT_POWERED);
+ goto unlock;
}
- rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
+ i = 0;
+ list_for_each_entry(c, &hdev->conn_hash.list, list) {
+ if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
+ i++;
+ }
+
+ rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
rp = kmalloc(rp_len, GFP_ATOMIC);
if (!rp) {
err = -ENOMEM;
goto unlock;
}
- put_unaligned_le16(count, &rp->conn_count);
-
i = 0;
list_for_each_entry(c, &hdev->conn_hash.list, list) {
+ if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
+ continue;
bacpy(&rp->addr[i].bdaddr, &c->dst);
rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
if (rp->addr[i].type == MGMT_ADDR_INVALID)
@@ -1293,85 +1675,77 @@ static int get_connections(struct sock *sk, u16 index)
i++;
}
+ put_unaligned_le16(i, &rp->conn_count);
+
/* Recalculate length in case of filtered SCO connections, etc */
rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
- err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp,
+ rp_len);
-unlock:
kfree(rp);
+
+unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int send_pin_code_neg_reply(struct sock *sk, u16 index,
- struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
+static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
+ struct mgmt_cp_pin_code_neg_reply *cp)
{
struct pending_cmd *cmd;
int err;
cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
- sizeof(*cp));
+ sizeof(*cp));
if (!cmd)
return -ENOMEM;
- err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
- &cp->bdaddr);
+ err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
+ sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
if (err < 0)
mgmt_pending_remove(cmd);
return err;
}
-static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
struct hci_conn *conn;
- struct mgmt_cp_pin_code_reply *cp;
- struct mgmt_cp_pin_code_neg_reply ncp;
+ struct mgmt_cp_pin_code_reply *cp = data;
struct hci_cp_pin_code_reply reply;
struct pending_cmd *cmd;
int err;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ MGMT_STATUS_NOT_POWERED);
goto failed;
}
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
if (!conn) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_NOT_CONNECTED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ MGMT_STATUS_NOT_CONNECTED);
goto failed;
}
if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
- bacpy(&ncp.bdaddr, &cp->bdaddr);
+ struct mgmt_cp_pin_code_neg_reply ncp;
+
+ memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
BT_ERR("PIN code is not 16 bytes long");
- err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
+ err = send_pin_code_neg_reply(sk, hdev, &ncp);
if (err >= 0)
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ MGMT_STATUS_INVALID_PARAMS);
goto failed;
}
@@ -1382,7 +1756,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- bacpy(&reply.bdaddr, &cp->bdaddr);
+ bacpy(&reply.bdaddr, &cp->addr.bdaddr);
reply.pin_len = cp->pin_len;
memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -1392,67 +1766,39 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_pin_code_neg_reply *cp;
+ struct mgmt_cp_pin_code_neg_reply *cp = data;
int err;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
- MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+ MGMT_STATUS_NOT_POWERED);
goto failed;
}
- err = send_pin_code_neg_reply(sk, index, hdev, cp);
+ err = send_pin_code_neg_reply(sk, hdev, cp);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_set_io_capability *cp;
+ struct mgmt_cp_set_io_capability *cp = data;
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
hdev->io_capability = cp->io_capability;
@@ -1461,9 +1807,9 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
hdev->io_capability);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
- return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
+ return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL,
+ 0);
}
static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
@@ -1491,9 +1837,9 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
bacpy(&rp.addr.bdaddr, &conn->dst);
rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
- rp.status = status;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
+ &rp, sizeof(rp));
/* So we don't get further callbacks for this connection */
conn->connect_cfm_cb = NULL;
@@ -1515,13 +1861,13 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
if (!cmd)
BT_DBG("Unable to find a pending command");
else
- pairing_complete(cmd, status);
+ pairing_complete(cmd, mgmt_status(status));
}
-static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
+static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_pair_device *cp;
+ struct mgmt_cp_pair_device *cp = data;
struct mgmt_rp_pair_device rp;
struct pending_cmd *cmd;
u8 sec_level, auth_type;
@@ -1530,19 +1876,14 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
BT_DBG("");
- cp = (void *) data;
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
hci_dev_lock(hdev);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+ MGMT_STATUS_NOT_POWERED);
+ goto unlock;
+ }
+
sec_level = BT_SECURITY_MEDIUM;
if (cp->io_cap == 0x03)
auth_type = HCI_AT_DEDICATED_BONDING;
@@ -1551,27 +1892,26 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (cp->addr.type == MGMT_ADDR_BREDR)
conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
- auth_type);
+ auth_type);
else
conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
- auth_type);
+ auth_type);
memset(&rp, 0, sizeof(rp));
bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
rp.addr.type = cp->addr.type;
if (IS_ERR(conn)) {
- rp.status = -PTR_ERR(conn);
- err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
- &rp, sizeof(rp));
+ err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+ MGMT_STATUS_CONNECT_FAILED, &rp,
+ sizeof(rp));
goto unlock;
}
if (conn->connect_cfm_cb) {
hci_conn_put(conn);
- rp.status = EBUSY;
- err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
- &rp, sizeof(rp));
+ err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+ MGMT_STATUS_BUSY, &rp, sizeof(rp));
goto unlock;
}
@@ -1599,58 +1939,88 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
- u16 mgmt_op, u16 hci_op, __le32 passkey)
+static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
+ struct mgmt_addr_info *addr = data;
struct pending_cmd *cmd;
- struct hci_dev *hdev;
struct hci_conn *conn;
int err;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("");
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+ MGMT_STATUS_NOT_POWERED);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
+ if (!cmd) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto unlock;
+ }
+
+ conn = cmd->user_data;
+
+ if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto unlock;
+ }
+
+ pairing_complete(cmd, MGMT_STATUS_CANCELLED);
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
+ addr, sizeof(*addr));
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 type, u16 mgmt_op,
+ u16 hci_op, __le32 passkey)
+{
+ struct pending_cmd *cmd;
+ struct hci_conn *conn;
+ int err;
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_NOT_POWERED);
goto done;
}
- /*
- * Check for an existing ACL link, if present pair via
- * HCI commands.
- *
- * If no ACL link is present, check for an LE link and if
- * present, pair via the SMP engine.
- *
- * If neither ACL nor LE links are present, fail with error.
- */
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
- if (!conn) {
+ if (type == MGMT_ADDR_BREDR)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
+ else
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
- if (!conn) {
- err = cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_NOT_CONNECTED);
- goto done;
- }
+ if (!conn) {
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_NOT_CONNECTED);
+ goto done;
+ }
+
+ if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
/* Continue with pairing via SMP */
err = smp_user_confirm_reply(conn, mgmt_op, passkey);
if (!err)
- err = cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_SUCCESS);
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_SUCCESS);
else
- err = cmd_status(sk, index, mgmt_op,
- MGMT_STATUS_FAILED);
+ err = cmd_status(sk, hdev->id, mgmt_op,
+ MGMT_STATUS_FAILED);
goto done;
}
@@ -1676,94 +2046,96 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
done:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
+static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_user_confirm_reply *cp = (void *) data;
+ struct mgmt_cp_user_confirm_reply *cp = data;
BT_DBG("");
if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
+ return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
+ MGMT_STATUS_INVALID_PARAMS);
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_CONFIRM_REPLY,
- HCI_OP_USER_CONFIRM_REPLY, 0);
+ return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+ MGMT_OP_USER_CONFIRM_REPLY,
+ HCI_OP_USER_CONFIRM_REPLY, 0);
}
-static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
- u16 len)
+static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
struct mgmt_cp_user_confirm_neg_reply *cp = data;
BT_DBG("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
- MGMT_STATUS_INVALID_PARAMS);
-
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_CONFIRM_NEG_REPLY,
- HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
+ return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+ MGMT_OP_USER_CONFIRM_NEG_REPLY,
+ HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
}
-static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
+static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_user_passkey_reply *cp = (void *) data;
+ struct mgmt_cp_user_passkey_reply *cp = data;
BT_DBG("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
- EINVAL);
-
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_PASSKEY_REPLY,
- HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
+ return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+ MGMT_OP_USER_PASSKEY_REPLY,
+ HCI_OP_USER_PASSKEY_REPLY, cp->passkey);
}
-static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
- u16 len)
+static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct mgmt_cp_user_passkey_neg_reply *cp = (void *) data;
+ struct mgmt_cp_user_passkey_neg_reply *cp = data;
BT_DBG("");
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
- EINVAL);
+ return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+ MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
+}
+
+static int update_name(struct hci_dev *hdev, const char *name)
+{
+ struct hci_cp_write_local_name cp;
- return user_pairing_resp(sk, index, &cp->bdaddr,
- MGMT_OP_USER_PASSKEY_NEG_REPLY,
- HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
+ memcpy(cp.name, name, sizeof(cp.name));
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp);
}
-static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct mgmt_cp_set_local_name *mgmt_cp = (void *) data;
- struct hci_cp_write_local_name hci_cp;
- struct hci_dev *hdev;
+ struct mgmt_cp_set_local_name *cp = data;
struct pending_cmd *cmd;
int err;
BT_DBG("");
- if (len != sizeof(*mgmt_cp))
- return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
- MGMT_STATUS_INVALID_PARAMS);
+ memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
- hci_dev_lock(hdev);
+ if (!hdev_is_powered(hdev)) {
+ memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name));
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
+ data, len);
+ if (err < 0)
+ goto failed;
+
+ err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len,
+ sk);
+
+ goto failed;
+ }
cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);
if (!cmd) {
@@ -1771,49 +2143,40 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
goto failed;
}
- memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
- err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
- &hci_cp);
+ err = update_name(hdev, cp->name);
if (err < 0)
mgmt_pending_remove(cmd);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int read_local_oob_data(struct sock *sk, u16 index)
+static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 data_len)
{
- struct hci_dev *hdev;
struct pending_cmd *cmd;
int err;
- BT_DBG("hci%u", index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_NOT_POWERED);
goto unlock;
}
if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
- err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_NOT_SUPPORTED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_NOT_SUPPORTED);
goto unlock;
}
if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
- err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
- MGMT_STATUS_BUSY);
+ err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_BUSY);
goto unlock;
}
@@ -1829,104 +2192,112 @@ static int read_local_oob_data(struct sock *sk, u16 index)
unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
+ struct mgmt_cp_add_remote_oob_data *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u ", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s ", hdev->name);
hci_dev_lock(hdev);
- err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
- cp->randomizer);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_NOT_POWERED, &cp->addr,
+ sizeof(cp->addr));
+ goto unlock;
+ }
+
+ err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
+ cp->randomizer);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
- MGMT_STATUS_FAILED);
+ status = MGMT_STATUS_FAILED;
else
- err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
- 0);
+ status = 0;
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
+ &cp->addr, sizeof(cp->addr));
+unlock:
+ hci_dev_unlock(hdev);
return err;
}
-static int remove_remote_oob_data(struct sock *sk, u16 index,
- unsigned char *data, u16 len)
+static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
+ struct mgmt_cp_remove_remote_oob_data *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u ", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ MGMT_STATUS_NOT_POWERED, &cp->addr,
+ sizeof(cp->addr));
+ goto unlock;
+ }
+
+ err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
+ if (err < 0)
+ status = MGMT_STATUS_INVALID_PARAMS;
+ else
+ status = 0;
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ status, &cp->addr, sizeof(cp->addr));
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+int mgmt_interleaved_discovery(struct hci_dev *hdev)
+{
+ int err;
+
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
+ err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- MGMT_STATUS_INVALID_PARAMS);
- else
- err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
- NULL, 0);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int start_discovery(struct sock *sk, u16 index,
- unsigned char *data, u16 len)
+static int start_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct mgmt_cp_start_discovery *cp = (void *) data;
+ struct mgmt_cp_start_discovery *cp = data;
struct pending_cmd *cmd;
- struct hci_dev *hdev;
int err;
- BT_DBG("hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- if (!test_bit(HCI_UP, &hdev->flags)) {
- err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_POWERED);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED);
+ goto failed;
+ }
+
+ if (hdev->discovery.state != DISCOVERY_STOPPED) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY);
goto failed;
}
@@ -1936,137 +2307,217 @@ static int start_discovery(struct sock *sk, u16 index,
goto failed;
}
- err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
+ hdev->discovery.type = cp->type;
+
+ switch (hdev->discovery.type) {
+ case DISCOV_TYPE_BREDR:
+ if (lmp_bredr_capable(hdev))
+ err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
+ else
+ err = -ENOTSUPP;
+ break;
+
+ case DISCOV_TYPE_LE:
+ if (lmp_host_le_capable(hdev))
+ err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
+ LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
+ else
+ err = -ENOTSUPP;
+ break;
+
+ case DISCOV_TYPE_INTERLEAVED:
+ if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
+ err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
+ LE_SCAN_WIN,
+ LE_SCAN_TIMEOUT_BREDR_LE);
+ else
+ err = -ENOTSUPP;
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
if (err < 0)
mgmt_pending_remove(cmd);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_STARTING);
failed:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int stop_discovery(struct sock *sk, u16 index)
+static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
+ struct mgmt_cp_stop_discovery *mgmt_cp = data;
struct pending_cmd *cmd;
+ struct hci_cp_remote_name_req_cancel cp;
+ struct inquiry_entry *e;
int err;
- BT_DBG("hci%u", index);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
+ if (!hci_discovery_active(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
+ MGMT_STATUS_REJECTED, &mgmt_cp->type,
+ sizeof(mgmt_cp->type));
+ goto unlock;
+ }
+
+ if (hdev->discovery.type != mgmt_cp->type) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type,
+ sizeof(mgmt_cp->type));
+ goto unlock;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
if (!cmd) {
err = -ENOMEM;
- goto failed;
+ goto unlock;
+ }
+
+ if (hdev->discovery.state == DISCOVERY_FINDING) {
+ err = hci_cancel_inquiry(hdev);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
+ goto unlock;
+ }
+
+ e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
+ if (!e) {
+ mgmt_pending_remove(cmd);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
+ &mgmt_cp->type, sizeof(mgmt_cp->type));
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ goto unlock;
}
- err = hci_cancel_inquiry(hdev);
+ bacpy(&cp.bdaddr, &e->data.bdaddr);
+ err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
+ &cp);
if (err < 0)
mgmt_pending_remove(cmd);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
-failed:
+unlock:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
-static int block_device(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_block_device *cp = (void *) data;
+ struct mgmt_cp_confirm_name *cp = data;
+ struct inquiry_entry *e;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ hci_dev_lock(hdev);
+
+ if (!hci_discovery_active(hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_FAILED);
+ goto failed;
+ }
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
+ if (!e) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto failed;
+ }
+
+ if (cp->name_known) {
+ e->name_state = NAME_KNOWN;
+ list_del(&e->list);
+ } else {
+ e->name_state = NAME_NEEDED;
+ hci_inquiry_cache_update_resolve(hdev, e);
+ }
+
+ err = 0;
+
+failed:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_block_device *cp = data;
+ u8 status;
+ int err;
+
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- err = hci_blacklist_add(hdev, &cp->bdaddr);
+ err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
- MGMT_STATUS_FAILED);
+ status = MGMT_STATUS_FAILED;
else
- err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
- NULL, 0);
+ status = 0;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
+ &cp->addr, sizeof(cp->addr));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
- u16 len)
+static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_cp_unblock_device *cp = (void *) data;
+ struct mgmt_cp_unblock_device *cp = data;
+ u8 status;
int err;
- BT_DBG("hci%u", index);
-
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
-
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
- err = hci_blacklist_del(hdev, &cp->bdaddr);
-
+ err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
- err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
+ status = MGMT_STATUS_INVALID_PARAMS;
else
- err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
- NULL, 0);
+ status = 0;
+
+ err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
+ &cp->addr, sizeof(cp->addr));
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
return err;
}
-static int set_fast_connectable(struct sock *sk, u16 index,
- unsigned char *data, u16 len)
+static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
{
- struct hci_dev *hdev;
- struct mgmt_mode *cp = (void *) data;
+ struct mgmt_mode *cp = data;
struct hci_cp_write_page_scan_activity acp;
u8 type;
int err;
- BT_DBG("hci%u", index);
+ BT_DBG("%s", hdev->name);
- if (len != sizeof(*cp))
- return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!hdev_is_powered(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_NOT_POWERED);
- hdev = hci_dev_get(index);
- if (!hdev)
- return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_REJECTED);
hci_dev_lock(hdev);
@@ -2080,35 +2531,128 @@ static int set_fast_connectable(struct sock *sk, u16 index,
acp.window = 0x0012; /* default 11.25 msec page scan window */
- err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
- sizeof(acp), &acp);
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp),
+ &acp);
if (err < 0) {
- err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_FAILED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_FAILED);
goto done;
}
err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
if (err < 0) {
- err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- MGMT_STATUS_FAILED);
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_FAILED);
goto done;
}
- err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
- NULL, 0);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0,
+ NULL, 0);
done:
hci_dev_unlock(hdev);
- hci_dev_put(hdev);
-
return err;
}
+static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
+ void *cp_data, u16 len)
+{
+ struct mgmt_cp_load_long_term_keys *cp = cp_data;
+ u16 key_count, expected_len;
+ int i;
+
+ key_count = get_unaligned_le16(&cp->key_count);
+
+ expected_len = sizeof(*cp) + key_count *
+ sizeof(struct mgmt_ltk_info);
+ if (expected_len != len) {
+ BT_ERR("load_keys: expected %u bytes, got %u bytes",
+ len, expected_len);
+ return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+ EINVAL);
+ }
+
+ BT_DBG("%s key_count %u", hdev->name, key_count);
+
+ hci_dev_lock(hdev);
+
+ hci_smp_ltks_clear(hdev);
+
+ for (i = 0; i < key_count; i++) {
+ struct mgmt_ltk_info *key = &cp->keys[i];
+ u8 type;
+
+ if (key->master)
+ type = HCI_SMP_LTK;
+ else
+ type = HCI_SMP_LTK_SLAVE;
+
+ hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
+ type, 0, key->authenticated, key->val,
+ key->enc_size, key->ediv, key->rand);
+ }
+
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+struct mgmt_handler {
+ int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len);
+ bool var_len;
+ size_t data_len;
+} mgmt_handlers[] = {
+ { NULL }, /* 0x0000 (no command) */
+ { read_version, false, MGMT_READ_VERSION_SIZE },
+ { read_commands, false, MGMT_READ_COMMANDS_SIZE },
+ { read_index_list, false, MGMT_READ_INDEX_LIST_SIZE },
+ { read_controller_info, false, MGMT_READ_INFO_SIZE },
+ { set_powered, false, MGMT_SETTING_SIZE },
+ { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE },
+ { set_connectable, false, MGMT_SETTING_SIZE },
+ { set_fast_connectable, false, MGMT_SETTING_SIZE },
+ { set_pairable, false, MGMT_SETTING_SIZE },
+ { set_link_security, false, MGMT_SETTING_SIZE },
+ { set_ssp, false, MGMT_SETTING_SIZE },
+ { set_hs, false, MGMT_SETTING_SIZE },
+ { set_le, false, MGMT_SETTING_SIZE },
+ { set_dev_class, false, MGMT_SET_DEV_CLASS_SIZE },
+ { set_local_name, false, MGMT_SET_LOCAL_NAME_SIZE },
+ { add_uuid, false, MGMT_ADD_UUID_SIZE },
+ { remove_uuid, false, MGMT_REMOVE_UUID_SIZE },
+ { load_link_keys, true, MGMT_LOAD_LINK_KEYS_SIZE },
+ { load_long_term_keys, true, MGMT_LOAD_LONG_TERM_KEYS_SIZE },
+ { disconnect, false, MGMT_DISCONNECT_SIZE },
+ { get_connections, false, MGMT_GET_CONNECTIONS_SIZE },
+ { pin_code_reply, false, MGMT_PIN_CODE_REPLY_SIZE },
+ { pin_code_neg_reply, false, MGMT_PIN_CODE_NEG_REPLY_SIZE },
+ { set_io_capability, false, MGMT_SET_IO_CAPABILITY_SIZE },
+ { pair_device, false, MGMT_PAIR_DEVICE_SIZE },
+ { cancel_pair_device, false, MGMT_CANCEL_PAIR_DEVICE_SIZE },
+ { unpair_device, false, MGMT_UNPAIR_DEVICE_SIZE },
+ { user_confirm_reply, false, MGMT_USER_CONFIRM_REPLY_SIZE },
+ { user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE },
+ { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE },
+ { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
+ { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE },
+ { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE },
+ { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
+ { start_discovery, false, MGMT_START_DISCOVERY_SIZE },
+ { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE },
+ { confirm_name, false, MGMT_CONFIRM_NAME_SIZE },
+ { block_device, false, MGMT_BLOCK_DEVICE_SIZE },
+ { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
+};
+
+
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
- unsigned char *buf;
+ void *buf;
+ u8 *cp;
struct mgmt_hdr *hdr;
u16 opcode, index, len;
+ struct hci_dev *hdev = NULL;
+ struct mgmt_handler *handler;
int err;
BT_DBG("got %zu bytes", msglen);
@@ -2125,7 +2669,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
goto done;
}
- hdr = (struct mgmt_hdr *) buf;
+ hdr = buf;
opcode = get_unaligned_le16(&hdr->opcode);
index = get_unaligned_le16(&hdr->index);
len = get_unaligned_le16(&hdr->len);
@@ -2135,117 +2679,54 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
goto done;
}
- switch (opcode) {
- case MGMT_OP_READ_VERSION:
- err = read_version(sk);
- break;
- case MGMT_OP_READ_INDEX_LIST:
- err = read_index_list(sk);
- break;
- case MGMT_OP_READ_INFO:
- err = read_controller_info(sk, index);
- break;
- case MGMT_OP_SET_POWERED:
- err = set_powered(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_DISCOVERABLE:
- err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_CONNECTABLE:
- err = set_connectable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_FAST_CONNECTABLE:
- err = set_fast_connectable(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_SET_PAIRABLE:
- err = set_pairable(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_ADD_UUID:
- err = add_uuid(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_UUID:
- err = remove_uuid(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_DEV_CLASS:
- err = set_dev_class(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_LOAD_LINK_KEYS:
- err = load_link_keys(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_KEYS:
- err = remove_keys(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_DISCONNECT:
- err = disconnect(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_GET_CONNECTIONS:
- err = get_connections(sk, index);
- break;
- case MGMT_OP_PIN_CODE_REPLY:
- err = pin_code_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_PIN_CODE_NEG_REPLY:
- err = pin_code_neg_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_SET_IO_CAPABILITY:
- err = set_io_capability(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_PAIR_DEVICE:
- err = pair_device(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_CONFIRM_REPLY:
- err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_USER_PASSKEY_REPLY:
- err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_USER_PASSKEY_NEG_REPLY:
- err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_SET_LOCAL_NAME:
- err = set_local_name(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_READ_LOCAL_OOB_DATA:
- err = read_local_oob_data(sk, index);
- break;
- case MGMT_OP_ADD_REMOTE_OOB_DATA:
- err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
- err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
- len);
- break;
- case MGMT_OP_START_DISCOVERY:
- err = start_discovery(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_STOP_DISCOVERY:
- err = stop_discovery(sk, index);
- break;
- case MGMT_OP_BLOCK_DEVICE:
- err = block_device(sk, index, buf + sizeof(*hdr), len);
- break;
- case MGMT_OP_UNBLOCK_DEVICE:
- err = unblock_device(sk, index, buf + sizeof(*hdr), len);
- break;
- default:
+ if (index != MGMT_INDEX_NONE) {
+ hdev = hci_dev_get(index);
+ if (!hdev) {
+ err = cmd_status(sk, index, opcode,
+ MGMT_STATUS_INVALID_INDEX);
+ goto done;
+ }
+ }
+
+ if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
+ mgmt_handlers[opcode].func == NULL) {
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode,
- MGMT_STATUS_UNKNOWN_COMMAND);
- break;
+ MGMT_STATUS_UNKNOWN_COMMAND);
+ goto done;
+ }
+
+ if ((hdev && opcode < MGMT_OP_READ_INFO) ||
+ (!hdev && opcode >= MGMT_OP_READ_INFO)) {
+ err = cmd_status(sk, index, opcode,
+ MGMT_STATUS_INVALID_INDEX);
+ goto done;
}
+ handler = &mgmt_handlers[opcode];
+
+ if ((handler->var_len && len < handler->data_len) ||
+ (!handler->var_len && len != handler->data_len)) {
+ err = cmd_status(sk, index, opcode,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto done;
+ }
+
+ if (hdev)
+ mgmt_init_hdev(sk, hdev);
+
+ cp = buf + sizeof(*hdr);
+
+ err = handler->func(sk, hdev, cp, len);
if (err < 0)
goto done;
err = msglen;
done:
+ if (hdev)
+ hci_dev_put(hdev);
+
kfree(buf);
return err;
}
@@ -2265,7 +2746,7 @@ int mgmt_index_added(struct hci_dev *hdev)
int mgmt_index_removed(struct hci_dev *hdev)
{
- u8 status = ENODEV;
+ u8 status = MGMT_STATUS_INVALID_INDEX;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
@@ -2273,9 +2754,9 @@ int mgmt_index_removed(struct hci_dev *hdev)
}
struct cmd_lookup {
- u8 val;
struct sock *sk;
struct hci_dev *hdev;
+ u8 mgmt_status;
};
static void settings_rsp(struct pending_cmd *cmd, void *data)
@@ -2296,63 +2777,91 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
- struct cmd_lookup match = { powered, NULL, hdev };
- __le32 ev;
- int ret;
+ struct cmd_lookup match = { NULL, hdev };
+ int err;
+
+ if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+ return 0;
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
- if (!powered) {
- u8 status = ENETDOWN;
+ if (powered) {
+ u8 scan = 0;
+
+ 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_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+
+ update_class(hdev);
+ update_name(hdev, hdev->dev_name);
+ update_eir(hdev);
+ } else {
+ u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
}
- ev = cpu_to_le32(get_current_settings(hdev));
-
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
- match.sk);
+ err = new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
{
- struct cmd_lookup match = { discoverable, NULL, hdev };
- __le32 ev;
- int ret;
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
- mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
+ if (discoverable) {
+ if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ changed = true;
+ } else {
+ if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ changed = true;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
+ &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
- match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
{
- __le32 ev;
- struct cmd_lookup match = { connectable, NULL, hdev };
- int ret;
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
- mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
- &match);
+ 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;
+ }
- ev = cpu_to_le32(get_current_settings(hdev));
+ mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
+ &match);
- ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
+ if (changed)
+ err = new_settings(hdev, match.sk);
if (match.sk)
sock_put(match.sk);
- return ret;
+ return err;
}
int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
@@ -2361,24 +2870,24 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
if (scan & SCAN_PAGE)
mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
- cmd_status_rsp, &mgmt_err);
+ cmd_status_rsp, &mgmt_err);
if (scan & SCAN_INQUIRY)
mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
- cmd_status_rsp, &mgmt_err);
+ cmd_status_rsp, &mgmt_err);
return 0;
}
-int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
- u8 persistent)
+int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, u8 persistent)
{
struct mgmt_ev_new_link_key ev;
memset(&ev, 0, sizeof(ev));
ev.store_hint = persistent;
- bacpy(&ev.key.bdaddr, &key->bdaddr);
+ bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
+ ev.key.addr.type = MGMT_ADDR_BREDR;
ev.key.type = key->type;
memcpy(ev.key.val, key->val, 16);
ev.key.pin_len = key->pin_len;
@@ -2386,15 +2895,54 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type)
+int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
{
- struct mgmt_addr_info ev;
+ struct mgmt_ev_new_long_term_key ev;
- bacpy(&ev.bdaddr, bdaddr);
- ev.type = link_to_mgmt(link_type, addr_type);
+ memset(&ev, 0, sizeof(ev));
- return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL);
+ ev.store_hint = persistent;
+ bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
+ ev.key.addr.type = key->bdaddr_type;
+ ev.key.authenticated = key->authenticated;
+ ev.key.enc_size = key->enc_size;
+ ev.key.ediv = key->ediv;
+
+ if (key->type == HCI_SMP_LTK)
+ ev.key.master = 1;
+
+ 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);
+}
+
+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)
+{
+ char buf[512];
+ struct mgmt_ev_device_connected *ev = (void *) buf;
+ u16 eir_len = 0;
+
+ bacpy(&ev->addr.bdaddr, bdaddr);
+ ev->addr.type = link_to_mgmt(link_type, addr_type);
+
+ ev->flags = __cpu_to_le32(flags);
+
+ if (name_len > 0)
+ eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
+ name, name_len);
+
+ if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
+ eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
+ EIR_CLASS_OF_DEV, dev_class, 3);
+
+ put_unaligned_le16(eir_len, &ev->eir_len);
+
+ return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
+ sizeof(*ev) + eir_len, NULL);
}
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
@@ -2403,10 +2951,11 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
struct sock **sk = data;
struct mgmt_rp_disconnect rp;
- bacpy(&rp.bdaddr, &cp->bdaddr);
- rp.status = 0;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
+ sizeof(rp));
*sk = cmd->sk;
sock_hold(*sk);
@@ -2414,25 +2963,25 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
-static void remove_keys_rsp(struct pending_cmd *cmd, void *data)
+static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
{
- u8 *status = data;
- struct mgmt_cp_remove_keys *cp = cmd->param;
- struct mgmt_rp_remove_keys rp;
+ struct hci_dev *hdev = data;
+ struct mgmt_cp_unpair_device *cp = cmd->param;
+ struct mgmt_rp_unpair_device rp;
memset(&rp, 0, sizeof(rp));
- bacpy(&rp.bdaddr, &cp->bdaddr);
- if (status != NULL)
- rp.status = *status;
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp,
- sizeof(rp));
+ device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
+
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
mgmt_pending_remove(cmd);
}
-int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type)
+int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type)
{
struct mgmt_addr_info ev;
struct sock *sk = NULL;
@@ -2443,45 +2992,44 @@ int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
bacpy(&ev.bdaddr, bdaddr);
ev.type = link_to_mgmt(link_type, addr_type);
- err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk);
+ err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
+ sk);
if (sk)
- sock_put(sk);
+ sock_put(sk);
- mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL);
+ 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 status)
+int 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;
- u8 mgmt_err = mgmt_status(status);
int err;
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
return -ENOENT;
- if (bdaddr) {
- struct mgmt_rp_disconnect rp;
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = link_to_mgmt(link_type, addr_type);
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = status;
-
- err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
- &rp, sizeof(rp));
- } else
- err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT,
- mgmt_err);
+ err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
+ mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
+ hdev);
return err;
}
int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 status)
+ u8 addr_type, u8 status)
{
struct mgmt_ev_connect_failed ev;
@@ -2496,15 +3044,16 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
{
struct mgmt_ev_pin_code_request ev;
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = MGMT_ADDR_BREDR;
ev.secure = secure;
return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
- NULL);
+ NULL);
}
int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
@@ -2514,11 +3063,11 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = MGMT_ADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
- sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2526,7 +3075,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 status)
{
struct pending_cmd *cmd;
struct mgmt_rp_pin_code_reply rp;
@@ -2536,11 +3085,11 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = MGMT_ADDR_BREDR;
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
- sizeof(rp));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
+ mgmt_status(status), &rp, sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2548,34 +3097,39 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
- __le32 value, u8 confirm_hint)
+ u8 link_type, u8 addr_type, __le32 value,
+ u8 confirm_hint)
{
struct mgmt_ev_user_confirm_request ev;
BT_DBG("%s", hdev->name);
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.confirm_hint = confirm_hint;
put_unaligned_le32(value, &ev.value);
return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
- NULL);
+ NULL);
}
-int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type)
{
struct mgmt_ev_user_passkey_request ev;
BT_DBG("%s", hdev->name);
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_mgmt(link_type, addr_type);
return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
- NULL);
+ NULL);
}
static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status, u8 opcode)
+ u8 link_type, u8 addr_type, u8 status,
+ u8 opcode)
{
struct pending_cmd *cmd;
struct mgmt_rp_user_confirm_reply rp;
@@ -2585,9 +3139,10 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return -ENOENT;
- bacpy(&rp.bdaddr, bdaddr);
- rp.status = mgmt_status(status);
- err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
+ bacpy(&rp.addr.bdaddr, bdaddr);
+ rp.addr.type = link_to_mgmt(link_type, addr_type);
+ err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
+ &rp, sizeof(rp));
mgmt_pending_remove(cmd);
@@ -2595,72 +3150,215 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
}
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_CONFIRM_REPLY);
+ return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
+ status, MGMT_OP_USER_CONFIRM_REPLY);
}
-int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 status)
+int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_CONFIRM_NEG_REPLY);
+ return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
+ status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
}
int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 status)
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_PASSKEY_REPLY);
+ return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
+ status, MGMT_OP_USER_PASSKEY_REPLY);
}
-int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev,
- bdaddr_t *bdaddr, u8 status)
+int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 link_type, u8 addr_type, u8 status)
{
- return user_pairing_resp_complete(hdev, bdaddr, status,
- MGMT_OP_USER_PASSKEY_NEG_REPLY);
+ return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
+ status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
}
-int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status)
+int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
+ u8 addr_type, u8 status)
{
struct mgmt_ev_auth_failed ev;
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.status = mgmt_status(status);
return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
}
+int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+ mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
+ cmd_status_rsp, &mgmt_err);
+ return 0;
+ }
+
+ 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;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
+ &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ return err;
+}
+
+static int clear_eir(struct hci_dev *hdev)
+{
+ struct hci_cp_write_eir cp;
+
+ if (!(hdev->features[6] & LMP_EXT_INQ))
+ return 0;
+
+ memset(hdev->eir, 0, sizeof(hdev->eir));
+
+ memset(&cp, 0, sizeof(cp));
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+}
+
+int mgmt_ssp_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_SSP_ENABLED,
+ &hdev->dev_flags))
+ err = new_settings(hdev, NULL);
+
+ mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp,
+ &mgmt_err);
+
+ return err;
+ }
+
+ if (enable) {
+ if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+ changed = true;
+ } else {
+ if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+ changed = true;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+ update_eir(hdev);
+ else
+ clear_eir(hdev);
+
+ return err;
+}
+
+static void class_rsp(struct pending_cmd *cmd, void *data)
+{
+ struct cmd_lookup *match = data;
+
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status,
+ match->hdev->dev_class, 3);
+
+ list_del(&cmd->list);
+
+ if (match->sk == NULL) {
+ match->sk = cmd->sk;
+ sock_hold(match->sk);
+ }
+
+ mgmt_pending_free(cmd);
+}
+
+int 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;
+
+ clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
+
+ mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match);
+ mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match);
+ mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match);
+
+ if (!status)
+ err = 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)
{
struct pending_cmd *cmd;
struct mgmt_cp_set_local_name ev;
- int err;
+ bool changed = false;
+ int err = 0;
+
+ if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) {
+ memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
+ changed = true;
+ }
memset(&ev, 0, sizeof(ev));
memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+ memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
if (!cmd)
goto send_event;
+ /* Always assume that either the short or the complete name has
+ * changed if there was a pending mgmt command */
+ changed = true;
+
if (status) {
err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
- mgmt_status(status));
+ mgmt_status(status));
goto failed;
}
- update_eir(hdev);
-
- err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
- sizeof(ev));
+ err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
+ sizeof(ev));
if (err < 0)
goto failed;
send_event:
- err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ if (changed)
+ err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
+ sizeof(ev), cmd ? cmd->sk : NULL);
+
+ update_eir(hdev);
failed:
if (cmd)
@@ -2669,7 +3367,7 @@ failed:
}
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
- u8 *randomizer, u8 status)
+ u8 *randomizer, u8 status)
{
struct pending_cmd *cmd;
int err;
@@ -2681,9 +3379,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
return -ENOENT;
if (status) {
- err = cmd_status(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(status));
+ err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ mgmt_status(status));
} else {
struct mgmt_rp_read_local_oob_data rp;
@@ -2691,8 +3388,8 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
err = cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA,
- &rp, sizeof(rp));
+ MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp,
+ sizeof(rp));
}
mgmt_pending_remove(cmd);
@@ -2700,48 +3397,120 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
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 *eir)
+ u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
+ ssp, u8 *eir, u16 eir_len)
{
- struct mgmt_ev_device_found ev;
+ char buf[512];
+ struct mgmt_ev_device_found *ev = (void *) buf;
+ size_t ev_size;
- memset(&ev, 0, sizeof(ev));
+ /* Leave 5 bytes for a potential CoD field */
+ if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
+ return -EINVAL;
- bacpy(&ev.addr.bdaddr, bdaddr);
- ev.addr.type = link_to_mgmt(link_type, addr_type);
- ev.rssi = rssi;
+ memset(buf, 0, sizeof(buf));
+
+ bacpy(&ev->addr.bdaddr, bdaddr);
+ ev->addr.type = link_to_mgmt(link_type, addr_type);
+ ev->rssi = rssi;
+ if (cfm_name)
+ ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME;
+ if (!ssp)
+ ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING;
+
+ if (eir_len > 0)
+ memcpy(ev->eir, eir, eir_len);
- if (eir)
- memcpy(ev.eir, eir, sizeof(ev.eir));
+ if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
+ eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
+ dev_class, 3);
- if (dev_class)
- memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class));
+ put_unaligned_le16(eir_len, &ev->eir_len);
- return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL);
+ ev_size = sizeof(*ev) + eir_len;
+
+ return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
}
-int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name)
+int 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_remote_name ev;
+ struct mgmt_ev_device_found *ev;
+ char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
+ u16 eir_len;
- memset(&ev, 0, sizeof(ev));
+ ev = (struct mgmt_ev_device_found *) buf;
- bacpy(&ev.bdaddr, bdaddr);
- memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
+ memset(buf, 0, sizeof(buf));
+
+ bacpy(&ev->addr.bdaddr, bdaddr);
+ ev->addr.type = link_to_mgmt(link_type, addr_type);
+ ev->rssi = rssi;
+
+ eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
+ name_len);
+
+ put_unaligned_le16(eir_len, &ev->eir_len);
- return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL);
+ return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
+ sizeof(*ev) + eir_len, NULL);
}
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
+ u8 type;
int err;
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+
cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
if (!cmd)
return -ENOENT;
- err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
+ type = hdev->discovery.type;
+
+ err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
+ &type, sizeof(type));
mgmt_pending_remove(cmd);
return err;
@@ -2756,7 +3525,8 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
if (!cmd)
return -ENOENT;
- err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
+ err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
+ &hdev->discovery.type, sizeof(hdev->discovery.type));
mgmt_pending_remove(cmd);
return err;
@@ -2764,44 +3534,61 @@ int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
{
+ struct mgmt_ev_discovering ev;
struct pending_cmd *cmd;
+ BT_DBG("%s discovering %u", hdev->name, discovering);
+
if (discovering)
cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
else
cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
if (cmd != NULL) {
- cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
+ u8 type = hdev->discovery.type;
+
+ cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
+ sizeof(type));
mgmt_pending_remove(cmd);
}
- return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
- sizeof(discovering), NULL);
+ memset(&ev, 0, sizeof(ev));
+ ev.type = hdev->discovery.type;
+ ev.discovering = discovering;
+
+ return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
}
-int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct pending_cmd *cmd;
struct mgmt_ev_device_blocked ev;
cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = type;
return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ cmd ? cmd->sk : NULL);
}
-int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
{
struct pending_cmd *cmd;
struct mgmt_ev_device_unblocked ev;
cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
- bacpy(&ev.bdaddr, bdaddr);
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = type;
return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
- cmd ? cmd->sk : NULL);
+ cmd ? cmd->sk : NULL);
}
+
+module_param(enable_hs, bool, 0644);
+MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
+
+module_param(enable_le, bool, 0644);
+MODULE_PARM_DESC(enable_le, "Enable Low Energy support");
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 501649bf5596..8a602388f1e7 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1164,12 +1164,18 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
break;
case BT_DISCONN:
- /* When socket is closed and we are not RFCOMM
- * initiator rfcomm_process_rx already calls
- * rfcomm_session_put() */
- if (s->sock->sk->sk_state != BT_CLOSED)
- if (list_empty(&s->dlcs))
- rfcomm_session_put(s);
+ /* rfcomm_session_put is called later so don't do
+ * anything here otherwise we will mess up the session
+ * reference counter:
+ *
+ * (a) when we are the initiator dlc_unlink will drive
+ * the reference counter to 0 (there is no initial put
+ * after session_add)
+ *
+ * (b) when we are not the initiator rfcomm_rx_process
+ * will explicitly call put to balance the initial hold
+ * done after session add.
+ */
break;
}
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index f066678faeee..a55a43e9f70e 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -45,7 +45,6 @@
#include <linux/security.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <net/bluetooth/bluetooth.h>
@@ -956,6 +955,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
if (!sk)
goto done;
+ bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM);
+
rfcomm_sock_init(sk, parent);
bacpy(&bt_sk(sk)->src, &src);
bacpy(&bt_sk(sk)->dst, &dst);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index a2d4f5122a6a..4bf54b377255 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -196,7 +196,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
{
struct rfcomm_dev *dev, *entry;
- struct list_head *head = &rfcomm_dev_list, *p;
+ struct list_head *head = &rfcomm_dev_list;
int err = 0;
BT_DBG("id %d channel %d", req->dev_id, req->channel);
@@ -215,7 +215,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
break;
dev->id++;
- head = p;
+ head = &entry->list;
}
} else {
dev->id = req->dev_id;
@@ -229,7 +229,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
if (entry->id > dev->id - 1)
break;
- head = p;
+ head = &entry->list;
}
}
@@ -1157,7 +1157,6 @@ int __init rfcomm_init_ttys(void)
if (!rfcomm_tty_driver)
return -ENOMEM;
- rfcomm_tty_driver->owner = THIS_MODULE;
rfcomm_tty_driver->driver_name = "rfcomm";
rfcomm_tty_driver->name = "rfcomm";
rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 8bf26d1bc5c1..f6ab12907963 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -44,7 +44,6 @@
#include <linux/security.h>
#include <net/sock.h>
-#include <asm/system.h>
#include <linux/uaccess.h>
#include <net/bluetooth/bluetooth.h>
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 32c47de30344..deb119875fd9 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -29,7 +29,7 @@
#include <linux/scatterlist.h>
#include <crypto/b128ops.h>
-#define SMP_TIMEOUT 30000 /* 30 seconds */
+#define SMP_TIMEOUT msecs_to_jiffies(30000)
static inline void swap128(u8 src[16], u8 dst[16])
{
@@ -186,8 +186,7 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
hci_send_acl(conn->hchan, skb, 0);
cancel_delayed_work_sync(&conn->security_timer);
- schedule_delayed_work(&conn->security_timer,
- msecs_to_jiffies(SMP_TIMEOUT));
+ schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT);
}
static __u8 authreq_to_seclevel(__u8 authreq)
@@ -217,7 +216,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
{
u8 dist_keys = 0;
- if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
+ if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) {
dist_keys = SMP_DIST_ENC_KEY;
authreq |= SMP_AUTH_BONDING;
} else {
@@ -250,21 +249,27 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
return SMP_ENC_KEY_SIZE;
- smp->smp_key_size = max_key_size;
+ smp->enc_key_size = max_key_size;
return 0;
}
static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
{
+ struct hci_conn *hcon = conn->hcon;
+
if (send)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
- clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
- mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
- cancel_delayed_work_sync(&conn->security_timer);
- smp_chan_destroy(conn);
+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
+ mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
+ hcon->dst_type, reason);
+
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) {
+ cancel_delayed_work_sync(&conn->security_timer);
+ smp_chan_destroy(conn);
+ }
}
#define JUST_WORKS 0x00
@@ -305,7 +310,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
remote_io > SMP_IO_KEYBOARD_DISPLAY)
method = JUST_WORKS;
else
- method = gen_method[local_io][remote_io];
+ method = gen_method[remote_io][local_io];
/* If not bonding, don't ask user to confirm a Zero TK */
if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM)
@@ -346,9 +351,11 @@ 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, conn->dst,
+ hcon->type, hcon->dst_type);
else
ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
+ hcon->type, hcon->dst_type,
cpu_to_le32(passkey), 0);
hci_dev_unlock(hcon->hdev);
@@ -377,12 +384,11 @@ static void confirm_work(struct work_struct *work)
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);
+ conn->src, conn->hcon->dst_type, conn->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->dst, 0, conn->src,
+ res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -417,12 +423,10 @@ static void random_work(struct work_struct *work)
if (hcon->out)
ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0,
- conn->src, hcon->dst_type, conn->dst,
- res);
+ conn->src, hcon->dst_type, conn->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, conn->dst, 0, conn->src, res);
if (ret) {
reason = SMP_UNSPECIFIED;
goto error;
@@ -446,16 +450,16 @@ static void random_work(struct work_struct *work)
smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key);
swap128(key, stk);
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+ memset(stk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) {
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) {
reason = SMP_UNSPECIFIED;
goto error;
}
hci_le_start_enc(hcon, ediv, rand, stk);
- hcon->enc_key_size = smp->smp_key_size;
+ hcon->enc_key_size = smp->enc_key_size;
} else {
u8 stk[16], r[16], rand[8];
__le16 ediv;
@@ -469,11 +473,12 @@ static void random_work(struct work_struct *work)
smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key);
swap128(key, stk);
- memset(stk + smp->smp_key_size, 0,
- SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size);
+ memset(stk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
- hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size,
- ediv, rand, stk);
+ hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type,
+ HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
+ ediv, rand);
}
return;
@@ -506,7 +511,7 @@ void smp_chan_destroy(struct l2cap_conn *conn)
{
struct smp_chan *smp = conn->smp_chan;
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ BUG_ON(!smp);
if (smp->tfm)
crypto_free_blkcipher(smp->tfm);
@@ -571,7 +576,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (conn->hcon->link_mode & HCI_LM_MASTER)
return SMP_CMD_NOTSUPP;
- if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
smp = smp_chan_create(conn);
smp = conn->smp_chan;
@@ -584,6 +589,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (req->auth_req & SMP_AUTH_BONDING)
auth = req->auth_req;
+ conn->hcon->pending_sec_level = authreq_to_seclevel(auth);
+
build_pairing_cmd(conn, req, &rsp, auth);
key_size = min(req->max_key_size, rsp.max_key_size);
@@ -698,23 +705,18 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
static u8 smp_ltk_encrypt(struct l2cap_conn *conn)
{
- struct link_key *key;
- struct key_master_id *master;
+ struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_link_key_type(hcon->hdev, conn->dst,
- HCI_LK_SMP_LTK);
+ key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type);
if (!key)
return 0;
- if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND,
- &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
return 1;
- master = (void *) key->data;
- hci_le_start_enc(hcon, master->ediv, master->rand,
- key->val);
- hcon->enc_key_size = key->pin_len;
+ hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
+ hcon->enc_key_size = key->enc_size;
return 1;
@@ -733,7 +735,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (smp_ltk_encrypt(conn))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return 0;
smp = smp_chan_create(conn);
@@ -772,7 +774,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
if (smp_ltk_encrypt(conn))
goto done;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend))
+ if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
return 0;
smp = smp_chan_create(conn);
@@ -817,13 +819,19 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_master_ident *rp = (void *) skb->data;
struct smp_chan *smp = conn->smp_chan;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ struct hci_conn *hcon = conn->hcon;
+ u8 authenticated;
skb_pull(skb, sizeof(*rp));
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- rp->ediv, rp->rand, smp->tk);
-
+ 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,
+ rp->ediv, rp->rand);
smp_distribute_keys(conn, 1);
+ hci_dev_unlock(hdev);
return 0;
}
@@ -908,7 +916,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
BT_DBG("conn %p force %d", conn, force);
- if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
+ if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
return 0;
rsp = (void *) &smp->prsp[1];
@@ -933,6 +941,8 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
if (*keydist & SMP_DIST_ENC_KEY) {
struct smp_cmd_encrypt_info enc;
struct smp_cmd_master_ident ident;
+ struct hci_conn *hcon = conn->hcon;
+ u8 authenticated;
__le16 ediv;
get_random_bytes(enc.ltk, sizeof(enc.ltk));
@@ -941,8 +951,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
- hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
- ediv, ident.rand, enc.ltk);
+ authenticated = hcon->sec_level == BT_SECURITY_HIGH;
+ hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type,
+ HCI_SMP_LTK_SLAVE, 1, authenticated,
+ enc.ltk, smp->enc_key_size, ediv, ident.rand);
ident.ediv = cpu_to_le16(ediv);
@@ -982,7 +994,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
}
if (conn->hcon->out || force) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
+ clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
cancel_delayed_work_sync(&conn->security_timer);
smp_chan_destroy(conn);
}