diff options
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/Kconfig | 1 | ||||
-rw-r--r-- | net/bluetooth/bnep/sock.c | 6 | ||||
-rw-r--r-- | net/bluetooth/cmtp/sock.c | 6 | ||||
-rw-r--r-- | net/bluetooth/hci_conn.c | 73 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 645 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 631 | ||||
-rw-r--r-- | net/bluetooth/hci_sock.c | 469 | ||||
-rw-r--r-- | net/bluetooth/hci_sysfs.c | 53 | ||||
-rw-r--r-- | net/bluetooth/hidp/sock.c | 6 | ||||
-rw-r--r-- | net/bluetooth/l2cap_core.c | 641 | ||||
-rw-r--r-- | net/bluetooth/l2cap_sock.c | 54 | ||||
-rw-r--r-- | net/bluetooth/lib.c | 27 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 2647 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 7 | ||||
-rw-r--r-- | net/bluetooth/smp.c | 108 |
15 files changed, 3585 insertions, 1789 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/bnep/sock.c b/net/bluetooth/bnep/sock.c index 17800b1d28ea..9f9c8dcd8af0 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -143,10 +143,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 +157,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..1230faaac29b 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -137,10 +137,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 +151,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 07bc69ed9498..947172bf1621 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -35,7 +35,6 @@ #include <linux/init.h> #include <linux/skbuff.h> #include <linux/interrupt.h> -#include <linux/notifier.h> #include <net/sock.h> #include <asm/system.h> @@ -51,7 +50,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 +79,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 +104,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 +151,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 +169,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 +279,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 +305,6 @@ static void hci_conn_timeout(struct work_struct *work) conn->state = BT_CLOSED; break; } - - hci_dev_unlock(hdev); } /* Enter sniff mode */ @@ -337,7 +332,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 +367,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 +381,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 +402,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 +550,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 +570,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 +581,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 +602,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,17 +627,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->pend); + 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; @@ -654,7 +648,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; @@ -674,8 +668,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. */ @@ -704,7 +697,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)) @@ -739,7 +732,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, @@ -758,7 +751,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; @@ -782,10 +775,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); @@ -801,11 +794,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); @@ -950,7 +943,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; @@ -981,10 +974,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 5aeb62491198..59ec99eb739b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -40,7 +40,6 @@ #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> @@ -55,8 +54,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 +66,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 +82,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 +359,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 +708,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 +744,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 +762,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); @@ -667,13 +810,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); @@ -688,7 +836,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; } @@ -847,11 +1000,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; @@ -883,11 +1036,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; @@ -967,11 +1120,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); } @@ -982,9 +1135,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) @@ -1037,6 +1188,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; @@ -1084,44 +1247,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; + struct smp_ltk *k; - list_for_each_entry(k, &hdev->link_keys, list) { - struct key_master_id *id; - - 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; @@ -1175,40 +1332,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; } @@ -1229,6 +1385,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) { @@ -1240,7 +1413,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; @@ -1280,7 +1453,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; @@ -1303,8 +1476,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; @@ -1331,7 +1503,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; @@ -1349,10 +1521,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; @@ -1366,13 +1538,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); @@ -1415,11 +1587,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 @@ -1427,7 +1595,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; @@ -1442,16 +1610,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) ¶m, + 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, @@ -1472,7 +1740,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; @@ -1503,7 +1770,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); @@ -1514,6 +1781,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); @@ -1529,6 +1797,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 | @@ -1551,11 +1823,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; @@ -1587,7 +1860,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); @@ -1614,11 +1887,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); @@ -1706,7 +1980,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); @@ -1862,11 +2136,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. */ @@ -2235,26 +2513,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, "e))) { @@ -2270,7 +2553,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; @@ -2285,6 +2568,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, "e))) { + 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) { @@ -2482,9 +2829,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)) { @@ -2568,6 +2918,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; @@ -2584,6 +2936,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..badb7851d116 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -35,7 +35,6 @@ #include <linux/init.h> #include <linux/skbuff.h> #include <linux/interrupt.h> -#include <linux/notifier.h> #include <net/sock.h> #include <asm/system.h> @@ -45,8 +44,6 @@ #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 +62,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 +192,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 +211,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 +230,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 +254,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 +354,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 +429,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 +436,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 +542,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 +553,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 +577,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 +595,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 +609,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 +628,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 +705,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 +729,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 +740,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 +914,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 +940,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 +975,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 +991,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 +1006,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 +1022,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 +1047,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 +1066,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 +1142,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 +1173,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 +1182,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 +1216,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 +1326,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 +1334,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 +1420,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 +1547,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 +1574,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 +1634,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 +1652,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 +1664,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 +1703,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 +1713,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 +1751,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 +1768,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 +1776,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 +1848,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 +1863,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 +1891,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 +1924,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 +1959,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 +1967,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 +1987,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 +2036,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 +2065,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 +2107,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 +2218,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 +2403,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 +2447,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 +2521,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 +2585,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 +2618,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 +2645,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 +2660,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 +2722,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 +2790,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 +2812,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 +2831,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 +2860,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 +2875,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 +2971,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 +2981,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 +3033,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 +3084,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 +3103,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 +3132,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 +3153,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 +3169,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 +3193,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 +3214,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 +3229,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 +3284,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 +3306,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 +3315,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 +3332,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 +3348,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(<k->list); + kfree(ltk); + } + hci_dev_unlock(hdev); return; @@ -3271,6 +3549,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 +3561,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 0dcc96266779..63afd234283e 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -48,8 +48,9 @@ #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 +86,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 +107,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 +133,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 +442,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 +471,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 +488,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 +621,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: @@ -461,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); @@ -495,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; @@ -574,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)) { @@ -636,6 +968,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char break; } +done: release_sock(sk); return err; } @@ -644,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) @@ -657,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: @@ -667,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: @@ -682,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 = { @@ -748,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; @@ -806,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; @@ -823,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 32d338c30e65..3e450f4a3125 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -73,42 +73,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 +103,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 +199,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 +255,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 +271,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 +299,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 +308,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 +320,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 +345,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 +367,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 +375,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 +389,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 +422,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 +437,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 +490,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 +667,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 +691,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 +700,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 +725,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 +742,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 +791,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 +800,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 +815,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 +825,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 +904,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 +946,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 +980,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 +1005,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,21 +1013,27 @@ 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_sync(&conn->info_timer); - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { + 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 +1085,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); @@ -1139,7 +1153,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d 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 +1180,21 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d 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 +1204,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d 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 +1240,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d /* 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 +1259,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *d err = 0; done: + l2cap_chan_unlock(chan); hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1276,14 +1301,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 +1316,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 +1476,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 +1506,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 +1526,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 +1546,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 +1565,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 +1589,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 +1612,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 +1624,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 +1646,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 +1658,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 +1680,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 +1697,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 +1843,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 +1861,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 +2029,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) @@ -2607,6 +2653,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 +2694,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 +2705,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 +2743,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 +2766,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 +2821,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 +2846,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 +2857,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 +2919,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 +2946,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 +2955,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 +2968,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,9 +3027,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr } default: - sk->sk_err = ECONNRESET; - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_DISC_REJ_TIMEOUT)); + l2cap_chan_set_err(chan, ECONNRESET); + + __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -2991,11 +3049,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; } @@ -3012,9 +3070,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; @@ -3022,12 +3086,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; } @@ -3036,23 +3106,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; } @@ -3132,7 +3209,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) { @@ -3149,11 +3227,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; @@ -3713,19 +3795,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) @@ -3865,8 +3939,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; @@ -3927,15 +4004,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; @@ -4135,9 +4212,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; @@ -4205,7 +4281,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; @@ -4213,10 +4288,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); @@ -4238,7 +4315,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; @@ -4287,26 +4364,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; @@ -4315,31 +4386,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; @@ -4348,14 +4411,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; } @@ -4479,8 +4539,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { __clear_chan_timer(chan); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_ENC_TIMEOUT)); + __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { @@ -4504,57 +4563,49 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) 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, - msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); + __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; @@ -4563,18 +4614,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); - __set_chan_timer(chan, - msecs_to_jiffies(L2CAP_DISC_TIMEOUT)); + __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); @@ -4583,10 +4635,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; } @@ -4647,6 +4699,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, " @@ -4717,7 +4770,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 401d9428ae4c..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; } @@ -862,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); @@ -882,6 +905,9 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) err = 0; } +done: + release_sock(sk); + return err; } @@ -899,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) @@ -1004,7 +1040,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); 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/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/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); } |