};
struct inquiry_entry {
- struct list_head list;
+ struct list_head all; /* inq_cache.all */
+ struct list_head list; /* unknown or resolve */
+ enum {
+ NAME_NOT_KNOWN,
+ NAME_NEEDED,
+ NAME_PENDING,
+ NAME_KNOWN,
+ } name_state;
__u32 timestamp;
struct inquiry_data data;
};
struct inquiry_cache {
- struct list_head list;
- __u32 timestamp;
+ struct list_head all; /* All devices found during inquiry */
+ struct list_head unknown; /* Name state not known */
+ struct list_head resolve; /* Name needs to be resolved */
+ __u32 timestamp;
};
struct hci_conn_hash {
static inline void inquiry_cache_init(struct hci_dev *hdev)
{
- INIT_LIST_HEAD(&hdev->inq_cache.list);
+ INIT_LIST_HEAD(&hdev->inq_cache.all);
+ INIT_LIST_HEAD(&hdev->inq_cache.unknown);
+ INIT_LIST_HEAD(&hdev->inq_cache.resolve);
}
static inline int inquiry_cache_empty(struct hci_dev *hdev)
{
- return list_empty(&hdev->inq_cache.list);
+ return list_empty(&hdev->inq_cache.all);
}
static inline long inquiry_cache_age(struct hci_dev *hdev)
struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev,
bdaddr_t *bdaddr);
-void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data);
+struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
+ bdaddr_t *bdaddr);
+void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+ bool name_known);
/* ----- HCI Connections ----- */
enum {
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
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 *eir);
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
/* ---- Inquiry support ---- */
static void inquiry_cache_flush(struct hci_dev *hdev)
{
+ struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *p, *n;
- list_for_each_entry_safe(p, n, &hdev->inq_cache.list, list) {
- list_del(&p->list);
+ 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)
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
- list_for_each_entry(e, &cache->list, list) {
+ list_for_each_entry(e, &cache->all, all) {
if (!bacmp(&e->data.bdaddr, bdaddr))
return e;
}
return NULL;
}
-void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data)
+struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
+ bdaddr_t *bdaddr)
+{
+ struct inquiry_cache *cache = &hdev->inq_cache;
+ 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;
+}
+
+void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+ bool name_known)
{
struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *ie;
BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
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)
+ goto update;
+
+ /* Entry not in the cache. Add new one. */
+ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC);
+ if (!ie)
+ return;
+
+ 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);
+ }
- list_add(&ie->list, &cache->list);
+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));
struct inquiry_entry *e;
int copied = 0;
- list_for_each_entry(e, &cache->list, list) {
+ list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
if (copied >= num)
data.clock_offset = info->clock_offset;
data.rssi = 0x00;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+ hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, NULL);
+ info->dev_class, 0, 1, NULL);
}
hci_dev_unlock(hdev);
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+ hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
- NULL);
+ 1, NULL);
}
} else {
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
data.clock_offset = info->clock_offset;
data.rssi = info->rssi;
data.ssp_mode = 0x00;
- hci_inquiry_cache_update(hdev, &data);
+ hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, info->rssi,
- NULL);
+ 1, NULL);
}
}
BT_DBG("%s status %d", hdev->name, ev->status);
}
+static inline bool eir_has_complete_name(u8 *data, size_t data_len)
+{
+ u8 field_len;
+ size_t parsed;
+
+ for (parsed = 0; parsed < data_len - 1; parsed += field_len) {
+ field_len = data[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > data_len)
+ break;
+
+ if (data[1] == EIR_NAME_COMPLETE)
+ return true;
+
+ data += field_len + 1;
+ }
+
+ return false;
+}
+
static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct inquiry_data data;
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
+ bool name_known;
+
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
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->flags))
+ name_known = eir_has_complete_name(info->data,
+ sizeof(info->data));
+ else
+ name_known = true;
+
+ hci_inquiry_cache_update(hdev, &data, name_known);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, info->rssi, info->data);
+ info->dev_class, info->rssi,
+ !name_known, info->data);
}
hci_dev_unlock(hdev);
hci_dev_lock(hdev);
- list_for_each_entry(e, &cache->list, list) {
+ 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),
return err;
}
+static int confirm_name(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct mgmt_cp_confirm_name *cp = (void *) data;
+ struct inquiry_entry *e;
+ struct hci_dev *hdev;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr);
+ if (!e) {
+ err = cmd_status (sk, index, 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;
+ list_move(&e->list, &hdev->inq_cache.resolve);
+ }
+
+ err = 0;
+
+failed:
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
static int block_device(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
case MGMT_OP_STOP_DISCOVERY:
err = stop_discovery(sk, index);
break;
+ case MGMT_OP_CONFIRM_NAME:
+ err = confirm_name(sk, index, buf + sizeof(*hdr), len);
+ break;
case MGMT_OP_BLOCK_DEVICE:
err = block_device(sk, index, buf + sizeof(*hdr), len);
break;
}
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 *eir)
{
struct mgmt_ev_device_found ev;
bacpy(&ev.addr.bdaddr, bdaddr);
ev.addr.type = link_to_mgmt(link_type, addr_type);
ev.rssi = rssi;
+ ev.confirm_name = cfm_name;
if (eir)
memcpy(ev.eir, eir, sizeof(ev.eir));