From e7844ee599c3eb944c3bd0092d715ca86e76a1f3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 18 Mar 2015 16:15:07 -0700 Subject: [PATCH 01/27] Bluetooth: Gracefully response to enabling LE on LE only devices Currently the enabling of LE on LE only devices causes an error. This is a bit difference from other commands where trying to set the same existing settings causes a positive response. Fix this behavior for this single corner case. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f3a957905193..cee9bc9735af 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2209,10 +2209,22 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_INVALID_PARAMS); - /* LE-only devices do not allow toggling LE on/off */ - if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + /* Bluetooth single mode LE only controllers or dual-mode + * controllers configured as LE only devices, do not allow + * switching LE off. These have either LE enabled explicitly + * or BR/EDR has been previously switched off. + * + * When trying to enable an already enabled LE, then gracefully + * send a positive response. Trying to disable it however will + * result into rejection. + */ + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { + if (cp->val == 0x01) + return send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_REJECTED); + } hci_dev_lock(hdev); From dc5d82a9febb28aa9126f9a864eb212317e4ede6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 19 Mar 2015 17:22:25 -0700 Subject: [PATCH 02/27] Bluetooth: Use HCI_MAX_AD_LENGTH constant instead hardcoded value Using the HCI_MAX_AD_LENGTH for the max advertising data and max scan response data length makes more sense than hardcoding the value. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cee9bc9735af..8c771e79d67d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6312,8 +6312,8 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, } rp->supported_flags = cpu_to_le32(0); - rp->max_adv_data_len = 31; - rp->max_scan_rsp_len = 31; + rp->max_adv_data_len = HCI_MAX_AD_LENGTH; + rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH; rp->max_instances = 0; rp->num_instances = 0; From b55d1abf568cdf12d148ced8b82b217bff824885 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Fri, 20 Mar 2015 11:14:50 -0700 Subject: [PATCH 03/27] Bluetooth: Expose quirks through debugfs This patch expose controller quirks through debugfs. It would be useful for BlueZ tests using vhci. Currently there is no way to test quirk dependent behaviour. It might be also useful for manual testing. Signed-off-by: Jakub Pawlowski Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_debugfs.c | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c index 0818fabf346a..e6255833a258 100644 --- a/net/bluetooth/hci_debugfs.c +++ b/net/bluetooth/hci_debugfs.c @@ -28,6 +28,54 @@ #include "hci_debugfs.h" +#define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk) \ +static ssize_t __name ## _read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct hci_dev *hdev = file->private_data; \ + char buf[3]; \ + \ + buf[0] = test_bit(__quirk, &hdev->quirks) ? 'Y' : 'N'; \ + buf[1] = '\n'; \ + buf[2] = '\0'; \ + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); \ +} \ + \ +static ssize_t __name ## _write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct hci_dev *hdev = file->private_data; \ + char buf[32]; \ + size_t buf_size = min(count, (sizeof(buf) - 1)); \ + bool enable; \ + \ + if (test_bit(HCI_UP, &hdev->flags)) \ + return -EBUSY; \ + \ + if (copy_from_user(buf, user_buf, buf_size)) \ + return -EFAULT; \ + \ + buf[buf_size] = '\0'; \ + if (strtobool(buf, &enable)) \ + return -EINVAL; \ + \ + if (enable == test_bit(__quirk, &hdev->quirks)) \ + return -EALREADY; \ + \ + change_bit(__quirk, &hdev->quirks); \ + \ + return count; \ +} \ + \ +static const struct file_operations __name ## _fops = { \ + .open = simple_open, \ + .read = __name ## _read, \ + .write = __name ## _write, \ + .llseek = default_llseek, \ +} \ + static int features_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; @@ -997,6 +1045,11 @@ static int adv_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get, adv_max_interval_set, "%llu\n"); +DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter, + HCI_QUIRK_STRICT_DUPLICATE_FILTER); +DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery, + HCI_QUIRK_SIMULTANEOUS_DISCOVERY); + void hci_debugfs_create_le(struct hci_dev *hdev) { debugfs_create_file("identity", 0400, hdev->debugfs, hdev, @@ -1041,6 +1094,13 @@ void hci_debugfs_create_le(struct hci_dev *hdev) &adv_max_interval_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); + + debugfs_create_file("quirk_strict_duplicate_filter", 0644, + hdev->debugfs, hdev, + &quirk_strict_duplicate_filter_fops); + debugfs_create_file("quirk_simultaneous_discovery", 0644, + hdev->debugfs, hdev, + &quirk_simultaneous_discovery_fops); } void hci_debugfs_create_conn(struct hci_conn *conn) From 15c32c5ff2d41dcf368e02607639843905b6db1a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 20 Mar 2015 17:16:36 -0700 Subject: [PATCH 04/27] Bluetooth: Read Verbose Config Version Info for Broadcom devices The Broadcom devices expose their chip id via Read Verbose Config Version Info command. While this information is not used at the moment, it might be useful for debugging purposes and so read it before patching the firmware. This makes it show up in dmesg and HCI traces taken for the system. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 708b6574d805..1521dad3d460 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2458,6 +2458,25 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) subver = le16_to_cpu(ver->lmp_subver); kfree_skb(skb); + /* Read Verbose Config Version Info */ + skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: Read Verbose Version failed (%ld)", + hdev->name, ret); + return ret; + } + + if (skb->len != 7) { + BT_ERR("%s: BCM: Read Verbose Version event length mismatch", + hdev->name); + kfree_skb(skb); + return -EIO; + } + + BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); + kfree_skb(skb); + for (i = 0; bcm_subver_table[i].name; i++) { if (subver == bcm_subver_table[i].subver) { hw_name = bcm_subver_table[i].name; From baf880a96859cca79208122e555e7efeabd16e4d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 21 Mar 2015 08:02:23 +0200 Subject: [PATCH 05/27] Bluetooth: Fix memory leak in le_scan_disable_work_complete() The hci_request in le_scan_disable_work_complete() was being initialized in a general context but only used in a specific branch in the function (when simultaneous discovery is not supported). This patch moves the usage to be limited to the branch where hci_req_run() is actually called. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 773f2164d9a1..17f52a195ba8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2874,7 +2874,6 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, { /* General inquiry access code (GIAC) */ u8 lap[3] = { 0x33, 0x8b, 0x9e }; - struct hci_request req; struct hci_cp_inquiry cp; int err; @@ -2893,13 +2892,6 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, break; case DISCOV_TYPE_INTERLEAVED: - hci_req_init(&req, hdev); - - memset(&cp, 0, sizeof(cp)); - memcpy(&cp.lap, lap, sizeof(cp.lap)); - cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN; - hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp); - hci_dev_lock(hdev); if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, @@ -2914,8 +2906,17 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, hci_discovery_set_state(hdev, DISCOVERY_STOPPED); } else { + struct hci_request req; + hci_inquiry_cache_flush(hdev); + hci_req_init(&req, hdev); + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, sizeof(cp.lap)); + cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN; + hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp); + err = hci_req_run(&req, inquiry_complete); if (err) { BT_ERR("Inquiry request failed: err %d", err); From 17b2772b8fe3442b46c99fb91150a480acb7ebc4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 22 Mar 2015 15:52:38 +0100 Subject: [PATCH 06/27] Bluetooth: Read Broadcom chip info for Apple Bluetooth devices For the Apple Bluetooth devices that are Broadcom based, it makes sense to read the chip information. It is a single HCI command and might help gathering more information about these devices. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 1521dad3d460..9bf4d6ae6c6b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_NEW 0x2000 #define BTUSB_AMP 0x4000 #define BTUSB_QCA_ROME 0x8000 +#define BTUSB_BCM_APPLE 0x10000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -62,7 +64,8 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP }, /* Apple-specific (Broadcom) devices */ - { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_APPLE }, /* MediaTek MT76x0E */ { USB_DEVICE(0x0e8d, 0x763f) }, @@ -2634,6 +2637,34 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +static int btusb_setup_bcm_apple(struct hci_dev *hdev) +{ + struct sk_buff *skb; + int err; + + /* Read Verbose Config Version Info */ + skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: BCM: Read Verbose Version failed (%d)", + hdev->name, err); + return err; + } + + if (skb->len != 7) { + BT_ERR("%s: BCM: Read Verbose Version event length mismatch", + hdev->name); + kfree_skb(skb); + return -EIO; + } + + BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1], + get_unaligned_le16(skb->data + 5)); + kfree_skb(skb); + + return 0; +} + static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, const bdaddr_t *bdaddr) { @@ -3033,6 +3064,11 @@ static int btusb_probe(struct usb_interface *intf, set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); } + if (id->driver_info & BTUSB_BCM_APPLE) { + hdev->setup = btusb_setup_bcm_apple; + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + } + if (id->driver_info & BTUSB_INTEL) { hdev->setup = btusb_setup_intel; hdev->shutdown = btusb_shutdown_intel; From 841a6664f213f76a9bc1bfd07a466d3dbe281a88 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:09 -0700 Subject: [PATCH 07/27] Bluetooth: Add definitions for Add/Remove Advertising API This patch adds definitions for the Add Advertising and Remove Advertising MGMT commands and events. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index a1a68671bf88..68abd4b0c25d 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -539,6 +539,30 @@ struct mgmt_rp_read_adv_features { __u8 instance[0]; } __packed; +#define MGMT_OP_ADD_ADVERTISING 0x003E +struct mgmt_cp_add_advertising { + __u8 instance; + __le32 flags; + __le16 duration; + __le16 timeout; + __u8 adv_data_len; + __u8 scan_rsp_len; + __u8 data[0]; +} __packed; +#define MGMT_ADD_ADVERTISING_SIZE 11 +struct mgmt_rp_add_advertising { + __u8 instance; +} __packed; + +#define MGMT_OP_REMOVE_ADVERTISING 0x003F +struct mgmt_cp_remove_advertising { + __u8 instance; +} __packed; +#define MGMT_REMOVE_ADVERTISING_SIZE 1 +struct mgmt_rp_remove_advertising { + __u8 instance; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -742,3 +766,13 @@ struct mgmt_ev_local_oob_data_updated { __le16 eir_len; __u8 eir[0]; } __packed; + +#define MGMT_EV_ADVERTISING_ADDED 0x0023 +struct mgmt_ev_advertising_added { + __u8 instance; +} __packed; + +#define MGMT_EV_ADVERTISING_REMOVED 0x0024 +struct mgmt_ev_advertising_removed { + __u8 instance; +} __packed; From 4453b006538d02ada8294a195bb2dc2ada498436 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:10 -0700 Subject: [PATCH 08/27] Bluetooth: Introduce HCI_ADVERTISING_INSTANCE setting and add AD flags This patch introduces the HCI_ADVERTISING_INSTANCE setting, which is set when an at least one advertising instance has been added using the "Add Advertising" mgmt command. This patch also adds a macro definition for the EIR_APPEARANCE field type. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 06e7eee31ce4..3acecf35420b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -227,6 +227,7 @@ enum { HCI_LE_ENABLED, HCI_ADVERTISING, HCI_ADVERTISING_CONNECTABLE, + HCI_ADVERTISING_INSTANCE, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LIMITED_DISCOVERABLE, @@ -465,6 +466,7 @@ enum { #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +#define EIR_APPEARANCE 0x19 /* Device appearance */ #define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */ #define EIR_LE_ROLE 0x1C /* LE role */ #define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */ From 203fea0178d7e165dbe834d1bdd9d243018fd5bf Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:11 -0700 Subject: [PATCH 09/27] Bluetooth: Add data structure for advertising instance This patch introduces a new data structure to represent advertising instances that were added using the "Add Advertising" mgmt command. Initially an hci_dev structure will support only one of these instances at a time, so the current instance is simply stored as a direct member of hci_dev. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/hci_core.c | 1 + 2 files changed, 17 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b65c53de6a69..3a6d4e3d68fe 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -155,6 +155,15 @@ struct oob_data { u8 rand256[16]; }; +struct adv_info { + __u8 instance; + __u32 flags; + __u16 adv_data_len; + __u8 adv_data[HCI_MAX_AD_LENGTH]; + __u16 scan_rsp_len; + __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; +}; + #define HCI_MAX_SHORT_NAME_LENGTH 10 /* Default LE RPA expiry time, 15 minutes */ @@ -364,6 +373,8 @@ struct hci_dev { __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; __u8 scan_rsp_data_len; + struct adv_info adv_instance; + __u8 irk[16]; __u32 rpa_timeout; struct delayed_work rpa_expired; @@ -550,6 +561,11 @@ static inline void hci_discovery_filter_clear(struct hci_dev *hdev) hdev->discovery.scan_duration = 0; } +static inline void adv_info_init(struct hci_dev *hdev) +{ + memset(&hdev->adv_instance, 0, sizeof(struct adv_info)); +} + bool hci_discovery_active(struct hci_dev *hdev); void hci_discovery_set_state(struct hci_dev *hdev, int state); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 17f52a195ba8..e6bfeb7b4415 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3126,6 +3126,7 @@ struct hci_dev *hci_alloc_dev(void) hci_init_sysfs(hdev); discovery_init(hdev); + adv_info_init(hdev); return hdev; } From 24b4f38fc9ebf93af223c67169a946d6baf9db61 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:12 -0700 Subject: [PATCH 10/27] Bluetooth: Implement the Add Advertising command This patch adds the most basic implementation for the "Add Advertisement" command. All state updates between the various HCI settings (POWERED, ADVERTISING, ADVERTISING_INSTANCE, and LE_ENABLED) has been implemented. The command currently supports only setting the advertising data fields, with no flags and no scan response data. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 283 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 273 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8c771e79d67d..7b4c0b027e90 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -100,6 +100,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_LOCAL_OOB_EXT_DATA, MGMT_OP_READ_EXT_INDEX_LIST, MGMT_OP_READ_ADV_FEATURES, + MGMT_OP_ADD_ADVERTISING, }; static const u16 mgmt_events[] = { @@ -135,6 +136,8 @@ static const u16 mgmt_events[] = { MGMT_EV_EXT_INDEX_ADDED, MGMT_EV_EXT_INDEX_REMOVED, MGMT_EV_LOCAL_OOB_DATA_UPDATED, + MGMT_EV_ADVERTISING_ADDED, + MGMT_EV_ADVERTISING_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -864,7 +867,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev) return 0; } -static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) +static u8 create_default_adv_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0, flags = 0; @@ -896,7 +899,18 @@ static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) return ad_len; } -static void update_adv_data(struct hci_request *req) +static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) +{ + /* TODO: Set the appropriate entries based on advertising instance flags + * here once flags other than 0 are supported. + */ + memcpy(ptr, hdev->adv_instance.adv_data, + hdev->adv_instance.adv_data_len); + + return hdev->adv_instance.adv_data_len; +} + +static void update_adv_data_for_instance(struct hci_request *req, u8 instance) { struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_adv_data cp; @@ -907,8 +921,12 @@ static void update_adv_data(struct hci_request *req) memset(&cp, 0, sizeof(cp)); - len = create_adv_data(hdev, cp.data); + if (instance) + len = create_instance_adv_data(hdev, cp.data); + else + len = create_default_adv_data(hdev, cp.data); + /* There's nothing to do if the data hasn't changed */ if (hdev->adv_data_len == len && memcmp(cp.data, hdev->adv_data, len) == 0) return; @@ -921,6 +939,25 @@ static void update_adv_data(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } +static void update_adv_data(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 instance; + + /* The "Set Advertising" setting supersedes the "Add Advertising" + * setting. Here we set the advertising data based on which + * setting was set. When neither apply, default to the global settings, + * represented by instance "0". + */ + if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && + !hci_dev_test_flag(hdev, HCI_ADVERTISING)) + instance = 0x01; + else + instance = 0x00; + + update_adv_data_for_instance(req, instance); +} + int mgmt_update_adv_data(struct hci_dev *hdev) { struct hci_request req; @@ -4374,10 +4411,17 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static void enable_advertising_instance(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + BT_DBG("status %d", status); +} + static void set_advertising_complete(struct hci_dev *hdev, u8 status, u16 opcode) { struct cmd_lookup match = { NULL, hdev }; + struct hci_request req; hci_dev_lock(hdev); @@ -4402,6 +4446,21 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, if (match.sk) sock_put(match.sk); + /* If "Set Advertising" was just disabled and instance advertising was + * set up earlier, then enable the advertising instance. + */ + if (hci_dev_test_flag(hdev, HCI_ADVERTISING) || + !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) + goto unlock; + + hci_req_init(&req, hdev); + + update_adv_data(&req); + enable_advertising(&req); + + if (hci_req_run(&req, enable_advertising_instance) < 0) + BT_ERR("Failed to re-configure advertising"); + unlock: hci_dev_unlock(hdev); } @@ -4484,10 +4543,13 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, else hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); - if (val) + if (val) { + /* Switch to instance "0" for the Set Advertising setting. */ + update_adv_data_for_instance(&req, 0); enable_advertising(&req); - else + } else { disable_advertising(&req); + } err = hci_req_run(&req, set_advertising_complete); if (err < 0) @@ -6299,12 +6361,21 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, struct mgmt_rp_read_adv_features *rp; size_t rp_len; int err; + bool instance; BT_DBG("%s", hdev->name); hci_dev_lock(hdev); rp_len = sizeof(*rp); + + /* Currently only one instance is supported, so just add 1 to the + * response length. + */ + instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE); + if (instance) + rp_len++; + rp = kmalloc(rp_len, GFP_ATOMIC); if (!rp) { hci_dev_unlock(hdev); @@ -6314,8 +6385,17 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, rp->supported_flags = cpu_to_le32(0); rp->max_adv_data_len = HCI_MAX_AD_LENGTH; rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH; - rp->max_instances = 0; - rp->num_instances = 0; + rp->max_instances = 1; + + /* Currently only one instance is supported, so simply return the + * current instance number. + */ + if (instance) { + rp->num_instances = 1; + rp->instance[0] = 1; + } else { + rp->num_instances = 0; + } hci_dev_unlock(hdev); @@ -6327,6 +6407,179 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, return err; } +static bool adv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *adv_data, + u8 adv_data_len) +{ + u8 max_adv_len = HCI_MAX_AD_LENGTH; + int i, cur_len; + + /* TODO: Correctly reduce adv_len based on adv_flags. */ + + if (adv_data_len > max_adv_len) + return false; + + /* Make sure that adv_data is correctly formatted. */ + for (i = 0, cur_len = 0; i < adv_data_len; i += (cur_len + 1)) { + cur_len = adv_data[i]; + + /* If the current field length would exceed the total data + * length, then it's invalid. + */ + if (i + cur_len >= adv_data_len) + return false; + } + + return true; +} + +static void advertising_added(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_added ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); +} + +static void advertising_removed(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_removed ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); +} + +static void add_advertising_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_rp_add_advertising rp; + + BT_DBG("status %d", status); + + hci_dev_lock(hdev); + + cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev); + + if (status) { + hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE); + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); + advertising_removed(cmd ? cmd->sk : NULL, hdev, 1); + } + + if (!cmd) + goto unlock; + + rp.instance = 0x01; + + if (status) + mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, + mgmt_status(status)); + else + mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, + mgmt_status(status), &rp, sizeof(rp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int add_advertising(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_cp_add_advertising *cp = data; + struct mgmt_rp_add_advertising rp; + u32 flags; + u8 status; + int err; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + + BT_DBG("%s", hdev->name); + + status = mgmt_le_support(hdev); + if (status) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + status); + + flags = __le32_to_cpu(cp->flags); + + /* The current implementation only supports adding one instance and + * doesn't support flags. + */ + if (cp->instance != 0x01 || flags) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || + pending_find(MGMT_OP_SET_LE, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (!adv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + hdev->adv_instance.flags = flags; + hdev->adv_instance.adv_data_len = cp->adv_data_len; + hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len; + + if (cp->adv_data_len) + memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len); + + if (cp->scan_rsp_len) + memcpy(hdev->adv_instance.scan_rsp_data, + cp->data + cp->adv_data_len, cp->scan_rsp_len); + + if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE)) + advertising_added(sk, hdev, 1); + + /* If the HCI_ADVERTISING flag is set or the device isn't powered then + * we have no HCI communication to make. Simply return. + */ + if (!hdev_is_powered(hdev) || + hci_dev_test_flag(hdev, HCI_ADVERTISING)) { + rp.instance = 0x01; + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); + goto unlock; + } + + /* We're good to go, update advertising data, parameters, and start + * advertising. + */ + cmd = mgmt_pending_add(sk, MGMT_OP_ADD_ADVERTISING, hdev, data, + data_len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + update_adv_data(&req); + enable_advertising(&req); + + err = hci_req_run(&req, add_advertising_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + + return err; +} + static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, @@ -6411,6 +6664,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { HCI_MGMT_NO_HDEV | HCI_MGMT_UNTRUSTED }, { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, + { add_advertising, MGMT_ADD_ADVERTISING_SIZE, + HCI_MGMT_VAR_LEN }, }; void mgmt_index_added(struct hci_dev *hdev) @@ -6582,7 +6837,8 @@ static int powered_update_hci(struct hci_dev *hdev) update_scan_rsp_data(&req); } - if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + if (hci_dev_test_flag(hdev, HCI_ADVERTISING) || + hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) enable_advertising(&req); restart_le_actions(&req); @@ -6694,7 +6950,13 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) sizeof(scan), &scan); } update_class(&req); - update_adv_data(&req); + + /* Advertising instances don't use the global discoverable setting, so + * only update AD if advertising was enabled using Set Advertising. + */ + if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) + update_adv_data(&req); + hci_req_run(&req, NULL); hdev->discov_timeout = 0; @@ -7595,7 +7857,8 @@ void mgmt_reenable_advertising(struct hci_dev *hdev) { struct hci_request req; - if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) && + !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) return; hci_req_init(&req, hdev); From da929335f27d955172539bf56bed1ac9ff9b8d45 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:13 -0700 Subject: [PATCH 11/27] Bluetooth: Implement the Remove Advertising command This patch implements the "Remove Advertising" mgmt command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 103 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7b4c0b027e90..5e5a738ea95c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -101,6 +101,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_EXT_INDEX_LIST, MGMT_OP_READ_ADV_FEATURES, MGMT_OP_ADD_ADVERTISING, + MGMT_OP_REMOVE_ADVERTISING, }; static const u16 mgmt_events[] = { @@ -6518,6 +6519,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || + pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || pending_find(MGMT_OP_SET_LE, hdev)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, MGMT_STATUS_BUSY); @@ -6580,6 +6582,106 @@ unlock: return err; } +static void remove_advertising_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + struct mgmt_pending_cmd *cmd; + struct mgmt_rp_remove_advertising rp; + + BT_DBG("status %d", status); + + hci_dev_lock(hdev); + + /* A failure status here only means that we failed to disable + * advertising. Otherwise, the advertising instance has been removed, + * so report success. + */ + cmd = pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev); + if (!cmd) + goto unlock; + + rp.instance = 1; + + mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, MGMT_STATUS_SUCCESS, + &rp, sizeof(rp)); + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int remove_advertising(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_cp_remove_advertising *cp = data; + struct mgmt_rp_remove_advertising rp; + int err; + struct mgmt_pending_cmd *cmd; + struct hci_request req; + + BT_DBG("%s", hdev->name); + + /* The current implementation only allows modifying instance no 1. A + * value of 0 indicates that all instances should be cleared. + */ + if (cp->instance > 1) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || + pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || + pending_find(MGMT_OP_SET_LE, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } + + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); + + advertising_removed(sk, hdev, 1); + + hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE); + + /* If the HCI_ADVERTISING flag is set or the device isn't powered then + * we have no HCI communication to make. Simply return. + */ + if (!hdev_is_powered(hdev) || + hci_dev_test_flag(hdev, HCI_ADVERTISING)) { + rp.instance = 1; + err = mgmt_cmd_complete(sk, hdev->id, + MGMT_OP_REMOVE_ADVERTISING, + MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_ADVERTISING, hdev, data, + data_len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + disable_advertising(&req); + + err = hci_req_run(&req, remove_advertising_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + + return err; +} + static const struct hci_mgmt_handler mgmt_handlers[] = { { NULL }, /* 0x0000 (no command) */ { read_version, MGMT_READ_VERSION_SIZE, @@ -6666,6 +6768,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, { add_advertising, MGMT_ADD_ADVERTISING_SIZE, HCI_MGMT_VAR_LEN }, + { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE }, }; void mgmt_index_added(struct hci_dev *hdev) From 4117ed70a55128273f1b6d00c7725e4c8a5c0031 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:14 -0700 Subject: [PATCH 12/27] Bluetooth: Add support for instance scan response This patch implements setting the Scan Response data provided as part of an advertising instance through the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 66 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5e5a738ea95c..762ca9be9806 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -793,7 +793,7 @@ static struct mgmt_pending_cmd *pending_find_data(u16 opcode, return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data); } -static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) +static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0; size_t name_len; @@ -819,7 +819,19 @@ static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) return ad_len; } -static void update_scan_rsp_data(struct hci_request *req) +static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) +{ + /* TODO: Set the appropriate entries based on advertising instance flags + * here once flags other than 0 are supported. + */ + memcpy(ptr, hdev->adv_instance.scan_rsp_data, + hdev->adv_instance.scan_rsp_len); + + return hdev->adv_instance.scan_rsp_len; +} + +static void update_scan_rsp_data_for_instance(struct hci_request *req, + u8 instance) { struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_scan_rsp_data cp; @@ -830,10 +842,13 @@ static void update_scan_rsp_data(struct hci_request *req) memset(&cp, 0, sizeof(cp)); - len = create_scan_rsp_data(hdev, cp.data); + if (instance) + len = create_instance_scan_rsp_data(hdev, cp.data); + else + len = create_default_scan_rsp_data(hdev, cp.data); if (hdev->scan_rsp_data_len == len && - memcmp(cp.data, hdev->scan_rsp_data, len) == 0) + !memcmp(cp.data, hdev->scan_rsp_data, len)) return; memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data)); @@ -844,6 +859,25 @@ static void update_scan_rsp_data(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); } +static void update_scan_rsp_data(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 instance; + + /* The "Set Advertising" setting supersedes the "Add Advertising" + * setting. Here we set the scan response data based on which + * setting was set. When neither apply, default to the global settings, + * represented by instance "0". + */ + if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && + !hci_dev_test_flag(hdev, HCI_ADVERTISING)) + instance = 0x01; + else + instance = 0x00; + + update_scan_rsp_data_for_instance(req, instance); +} + static u8 get_adv_discov_flags(struct hci_dev *hdev) { struct mgmt_pending_cmd *cmd; @@ -4547,6 +4581,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, if (val) { /* Switch to instance "0" for the Set Advertising setting. */ update_adv_data_for_instance(&req, 0); + update_scan_rsp_data_for_instance(&req, 0); enable_advertising(&req); } else { disable_advertising(&req); @@ -6408,25 +6443,25 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, return err; } -static bool adv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *adv_data, - u8 adv_data_len) +static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, + u8 len) { - u8 max_adv_len = HCI_MAX_AD_LENGTH; + u8 max_len = HCI_MAX_AD_LENGTH; int i, cur_len; - /* TODO: Correctly reduce adv_len based on adv_flags. */ + /* TODO: Correctly reduce len based on adv_flags. */ - if (adv_data_len > max_adv_len) + if (len > max_len) return false; - /* Make sure that adv_data is correctly formatted. */ - for (i = 0, cur_len = 0; i < adv_data_len; i += (cur_len + 1)) { - cur_len = adv_data[i]; + /* Make sure that the data is correctly formatted. */ + for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) { + cur_len = data[i]; /* If the current field length would exceed the total data * length, then it's invalid. */ - if (i + cur_len >= adv_data_len) + if (i + cur_len >= len) return false; } @@ -6526,7 +6561,9 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (!adv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len)) { + if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len) || + !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len, + cp->scan_rsp_len)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); goto unlock; @@ -6570,6 +6607,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, hci_req_init(&req, hdev); update_adv_data(&req); + update_scan_rsp_data(&req); enable_advertising(&req); err = hci_req_run(&req, add_advertising_complete); From 912098a6308e37208b8dcc46c57c66d0778a854b Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Mon, 23 Mar 2015 15:57:15 -0700 Subject: [PATCH 13/27] Bluetooth: Add support for adv instance timeout This patch implements support for the timeout parameter of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/mgmt.c | 101 +++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a6d4e3d68fe..540c07feece7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -156,8 +156,10 @@ struct oob_data { }; struct adv_info { + struct delayed_work timeout_exp; __u8 instance; __u32 flags; + __u16 timeout; __u16 adv_data_len; __u8 adv_data[HCI_MAX_AD_LENGTH]; __u16 scan_rsp_len; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 762ca9be9806..eda52397a648 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1336,6 +1336,49 @@ static bool hci_stop_discovery(struct hci_request *req) return false; } +static void advertising_added(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_added ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); +} + +static void advertising_removed(struct sock *sk, struct hci_dev *hdev, + u8 instance) +{ + struct mgmt_ev_advertising_removed ev; + + ev.instance = instance; + + mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); +} + +static void clear_adv_instance(struct hci_dev *hdev) +{ + struct hci_request req; + + if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE)) + return; + + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); + advertising_removed(NULL, hdev, 1); + hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE); + + if (!hdev_is_powered(hdev) || + hci_dev_test_flag(hdev, HCI_ADVERTISING)) + return; + + hci_req_init(&req, hdev); + disable_advertising(&req); + hci_req_run(&req, NULL); +} + static int clean_up_hci_state(struct hci_dev *hdev) { struct hci_request req; @@ -1351,6 +1394,9 @@ static int clean_up_hci_state(struct hci_dev *hdev) hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } + if (hdev->adv_instance.timeout) + clear_adv_instance(hdev); + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) disable_advertising(&req); @@ -6468,26 +6514,6 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, return true; } -static void advertising_added(struct sock *sk, struct hci_dev *hdev, - u8 instance) -{ - struct mgmt_ev_advertising_added ev; - - ev.instance = instance; - - mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); -} - -static void advertising_removed(struct sock *sk, struct hci_dev *hdev, - u8 instance) -{ - struct mgmt_ev_advertising_removed ev; - - ev.instance = instance; - - mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); -} - static void add_advertising_complete(struct hci_dev *hdev, u8 status, u16 opcode) { @@ -6524,6 +6550,18 @@ unlock: hci_dev_unlock(hdev); } +static void adv_timeout_expired(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + adv_instance.timeout_exp.work); + + hdev->adv_instance.timeout = 0; + + hci_dev_lock(hdev); + clear_adv_instance(hdev); + hci_dev_unlock(hdev); +} + static int add_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6531,6 +6569,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, struct mgmt_rp_add_advertising rp; u32 flags; u8 status; + u16 timeout; int err; struct mgmt_pending_cmd *cmd; struct hci_request req; @@ -6543,6 +6582,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, status); flags = __le32_to_cpu(cp->flags); + timeout = __le16_to_cpu(cp->timeout); /* The current implementation only supports adding one instance and * doesn't support flags. @@ -6553,6 +6593,12 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); + if (timeout && !hdev_is_powered(hdev)) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, + MGMT_STATUS_REJECTED); + goto unlock; + } + if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || pending_find(MGMT_OP_SET_LE, hdev)) { @@ -6569,6 +6615,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } + INIT_DELAYED_WORK(&hdev->adv_instance.timeout_exp, adv_timeout_expired); + hdev->adv_instance.flags = flags; hdev->adv_instance.adv_data_len = cp->adv_data_len; hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len; @@ -6580,6 +6628,16 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, memcpy(hdev->adv_instance.scan_rsp_data, cp->data + cp->adv_data_len, cp->scan_rsp_len); + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + + hdev->adv_instance.timeout = timeout; + + if (timeout) + queue_delayed_work(hdev->workqueue, + &hdev->adv_instance.timeout_exp, + msecs_to_jiffies(timeout * 1000)); + if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE)) advertising_added(sk, hdev, 1); @@ -6682,6 +6740,9 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (hdev->adv_instance.timeout) + cancel_delayed_work(&hdev->adv_instance.timeout_exp); + memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance)); advertising_removed(sk, hdev, 1); From 99c679acce5d07aa1be63d8afe94df27f0aecb50 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 24 Mar 2015 17:31:03 -0700 Subject: [PATCH 14/27] Bluetooth: Filter list of supported commands/events for untrusted users When the user of the management interface is not trusted, then it only has access to a limited set of commands and events. When providing the list of supported commands and events take the trusted vs untrusted status of the user into account and return different lists. This way the untrusted user knows exactly which commands it can execute and which events it can receive. So no guesswork needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 53 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index eda52397a648..38b03bd14723 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -141,6 +141,27 @@ static const u16 mgmt_events[] = { MGMT_EV_ADVERTISING_REMOVED, }; +static const u16 mgmt_untrusted_commands[] = { + MGMT_OP_READ_INDEX_LIST, + MGMT_OP_READ_INFO, + MGMT_OP_READ_UNCONF_INDEX_LIST, + MGMT_OP_READ_CONFIG_INFO, + MGMT_OP_READ_EXT_INDEX_LIST, +}; + +static const u16 mgmt_untrusted_events[] = { + 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_UNCONF_INDEX_ADDED, + MGMT_EV_UNCONF_INDEX_REMOVED, + MGMT_EV_NEW_CONFIG_OPTIONS, + MGMT_EV_EXT_INDEX_ADDED, + MGMT_EV_EXT_INDEX_REMOVED, +}; + #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) #define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \ @@ -265,14 +286,20 @@ static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_commands *rp; - const u16 num_commands = ARRAY_SIZE(mgmt_commands); - const u16 num_events = ARRAY_SIZE(mgmt_events); - __le16 *opcode; + u16 num_commands, num_events; size_t rp_size; int i, err; BT_DBG("sock %p", sk); + if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) { + num_commands = ARRAY_SIZE(mgmt_commands); + num_events = ARRAY_SIZE(mgmt_events); + } else { + num_commands = ARRAY_SIZE(mgmt_untrusted_commands); + num_events = ARRAY_SIZE(mgmt_untrusted_events); + } + rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); rp = kmalloc(rp_size, GFP_KERNEL); @@ -282,11 +309,23 @@ static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, rp->num_commands = cpu_to_le16(num_commands); rp->num_events = cpu_to_le16(num_events); - for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++) - put_unaligned_le16(mgmt_commands[i], opcode); + if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) { + __le16 *opcode = rp->opcodes; - for (i = 0; i < num_events; i++, opcode++) - put_unaligned_le16(mgmt_events[i], opcode); + for (i = 0; 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); + } else { + __le16 *opcode = rp->opcodes; + + for (i = 0; i < num_commands; i++, opcode++) + put_unaligned_le16(mgmt_untrusted_commands[i], opcode); + + for (i = 0; i < num_events; i++, opcode++) + put_unaligned_le16(mgmt_untrusted_events[i], opcode); + } err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, rp_size); From eb17380972d563a7dd59634cd2fdb614f3b98cdd Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 25 Mar 2015 15:19:30 +0100 Subject: [PATCH 15/27] Bluetooth: hci_uart: Add setup callback Some Bluetooth controllers require initialization before being used (vendor config, firmware download). Add possibility for a HCI UART proto to implement this early init via the setup callback. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 11 +++++++++++ drivers/bluetooth/hci_uart.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index dc487b5d1156..48a0c250d5b8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -261,6 +261,16 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } +static int hci_uart_setup(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + if (hu->proto->setup) + return hu->proto->setup(hu); + + return 0; +} + /* ------ LDISC part ------ */ /* hci_uart_tty_open * @@ -426,6 +436,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; + hdev->setup = hci_uart_setup; SET_HCIDEV_DEV(hdev, hu->tty->dev); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 247488edcbf9..074ed29092b4 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -59,6 +59,7 @@ struct hci_uart_proto { int (*flush)(struct hci_uart *hu); int (*recv)(struct hci_uart *hu, void *data, int len); int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); + int (*setup)(struct hci_uart *hu); struct sk_buff *(*dequeue)(struct hci_uart *hu); }; From 08dc0e987e2855e28714670b8214dea1f76534ba Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 25 Mar 2015 18:32:13 -0700 Subject: [PATCH 16/27] Bluetooth: Fix minor typo in comment for static address setting Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 38b03bd14723..f95937c47b9e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -693,7 +693,7 @@ static u32 get_current_settings(struct hci_dev *hdev) * the second is to indicate if it is actually set. * * This means if the static address is not configured, this flag - * will never bet set. If the address is configured, then if the + * will never be set. If the address is configured, then if the * address is actually used decides if the flag is set or not. * * For single mode LE only controllers and dual-mode controllers From 495099962138732c28449d07f3b1988836659851 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:39 -0700 Subject: [PATCH 17/27] Bluetooth: Add macros for advertising instance flags This patch adds macro definitions for possible advertising instance flags that can be passed to the "Add Advertising" command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 68abd4b0c25d..b831242d48a4 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -554,6 +554,14 @@ struct mgmt_rp_add_advertising { __u8 instance; } __packed; +#define MGMT_ADV_FLAG_CONNECTABLE BIT(0) +#define MGMT_ADV_FLAG_DISCOV BIT(1) +#define MGMT_ADV_FLAG_LIMITED_DISCOV BIT(2) +#define MGMT_ADV_FLAG_MANAGED_FLAGS BIT(3) +#define MGMT_ADV_FLAG_TX_POWER BIT(4) +#define MGMT_ADV_FLAG_APPEARANCE BIT(5) +#define MGMT_ADV_FLAG_LOCAL_NAME BIT(6) + #define MGMT_OP_REMOVE_ADVERTISING 0x003F struct mgmt_cp_remove_advertising { __u8 instance; From e7a685d316e7d4b64cd3b2811687b0270d590386 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:40 -0700 Subject: [PATCH 18/27] Bluetooth: Support the "connectable mode" adv flag This patch adds support for the "connectable mode" flag of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 87 ++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f95937c47b9e..672ff5b66c21 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1013,11 +1013,8 @@ static void update_adv_data_for_instance(struct hci_request *req, u8 instance) hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } -static void update_adv_data(struct hci_request *req) +static u8 get_current_adv_instance(struct hci_dev *hdev) { - struct hci_dev *hdev = req->hdev; - u8 instance; - /* The "Set Advertising" setting supersedes the "Add Advertising" * setting. Here we set the advertising data based on which * setting was set. When neither apply, default to the global settings, @@ -1025,9 +1022,54 @@ static void update_adv_data(struct hci_request *req) */ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && !hci_dev_test_flag(hdev, HCI_ADVERTISING)) - instance = 0x01; - else - instance = 0x00; + return 0x01; + + return 0x00; +} + +static bool get_connectable(struct hci_dev *hdev) +{ + struct mgmt_pending_cmd *cmd; + + /* If there's a pending mgmt command the flag will not yet have + * it's final value, so check for this first. + */ + cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + if (cmd) { + struct mgmt_mode *cp = cmd->param; + + return cp->val; + } + + return hci_dev_test_flag(hdev, HCI_CONNECTABLE); +} + +static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance) +{ + u32 flags; + + if (instance > 0x01) + return 0; + + if (instance == 1) + return hdev->adv_instance.flags; + + flags = 0; + + /* For instance 0, assemble the flags from global settings */ + if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE) || + get_connectable(hdev)) + flags |= MGMT_ADV_FLAG_CONNECTABLE; + + /* TODO: Add the rest of the flags */ + + return flags; +} + +static void update_adv_data(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 instance = get_current_adv_instance(hdev); update_adv_data_for_instance(req, instance); } @@ -1159,22 +1201,6 @@ static void update_class(struct hci_request *req) hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); } -static bool get_connectable(struct hci_dev *hdev) -{ - struct mgmt_pending_cmd *cmd; - - /* If there's a pending mgmt command the flag will not yet have - * it's final value, so check for this first. - */ - cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); - if (cmd) { - struct mgmt_mode *cp = cmd->param; - return cp->val; - } - - return hci_dev_test_flag(hdev, HCI_CONNECTABLE); -} - static void disable_advertising(struct hci_request *req) { u8 enable = 0x00; @@ -1188,6 +1214,8 @@ static void enable_advertising(struct hci_request *req) struct hci_cp_le_set_adv_param cp; u8 own_addr_type, enable = 0x01; bool connectable; + u8 instance; + u32 flags; if (hci_conn_num(hdev, LE_LINK) > 0) return; @@ -1202,10 +1230,9 @@ static void enable_advertising(struct hci_request *req) */ hci_dev_clear_flag(hdev, HCI_LE_ADV); - if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) - connectable = true; - else - connectable = get_connectable(hdev); + instance = get_current_adv_instance(hdev); + flags = get_adv_instance_flags(hdev, instance); + connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE); /* Set require_privacy to true only when non-connectable * advertising is used. In that case it is fine to use a @@ -6623,10 +6650,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, flags = __le32_to_cpu(cp->flags); timeout = __le16_to_cpu(cp->timeout); - /* The current implementation only supports adding one instance and - * doesn't support flags. - */ - if (cp->instance != 0x01 || flags) + /* The current implementation only supports adding one instance */ + if (cp->instance != 0x01) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); From b44133ff03be30b55f23036e62f403a3f7784fce Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:41 -0700 Subject: [PATCH 19/27] Bluetooth: Support the "discoverable" adv flag This patch adds support for the "discoverable" flag of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 672ff5b66c21..c1804527fa3b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -975,13 +975,28 @@ static u8 create_default_adv_data(struct hci_dev *hdev, u8 *ptr) static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) { - /* TODO: Set the appropriate entries based on advertising instance flags - * here once flags other than 0 are supported. - */ + u8 ad_len = 0, flags = 0; + + if (hdev->adv_instance.flags & MGMT_ADV_FLAG_DISCOV) + flags |= LE_AD_GENERAL; + + if (flags) { + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + flags |= LE_AD_NO_BREDR; + + ptr[0] = 0x02; + ptr[1] = EIR_FLAGS; + ptr[2] = flags; + + ad_len += 3; + ptr += 3; + } + memcpy(ptr, hdev->adv_instance.adv_data, hdev->adv_instance.adv_data_len); + ad_len += hdev->adv_instance.adv_data_len; - return hdev->adv_instance.adv_data_len; + return ad_len; } static void update_adv_data_for_instance(struct hci_request *req, u8 instance) @@ -6556,12 +6571,16 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, } static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, - u8 len) + u8 len, bool is_adv_data) { u8 max_len = HCI_MAX_AD_LENGTH; int i, cur_len; + bool flags_managed = false; - /* TODO: Correctly reduce len based on adv_flags. */ + if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_DISCOV)) { + flags_managed = true; + max_len -= 3; + } if (len > max_len) return false; @@ -6570,6 +6589,9 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) { cur_len = data[i]; + if (flags_managed && data[i + 1] == EIR_FLAGS) + return false; + /* If the current field length would exceed the total data * length, then it's invalid. */ @@ -6671,9 +6693,9 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len) || + if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) || !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len, - cp->scan_rsp_len)) { + cp->scan_rsp_len, false)) { err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); goto unlock; From 807ec772bf484a8e93bc9a2a681e49cb8f2c4d69 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:42 -0700 Subject: [PATCH 20/27] Bluetooth: Support the "limited-discoverable" adv flag This patch adds support for the "limited-discoverable" flag of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c1804527fa3b..797cf6cf958f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -977,9 +977,15 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0, flags = 0; + /* The Add Advertising command allows userspace to set both the general + * and limited discoverable flags. + */ if (hdev->adv_instance.flags & MGMT_ADV_FLAG_DISCOV) flags |= LE_AD_GENERAL; + if (hdev->adv_instance.flags & MGMT_ADV_FLAG_LIMITED_DISCOV) + flags |= LE_AD_LIMITED; + if (flags) { if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) flags |= LE_AD_NO_BREDR; @@ -6576,8 +6582,9 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, u8 max_len = HCI_MAX_AD_LENGTH; int i, cur_len; bool flags_managed = false; + u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV; - if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_DISCOV)) { + if (is_adv_data && (adv_flags & flags_params)) { flags_managed = true; max_len -= 3; } From 67e0c0cd8f5afea17689f7688041e9d215df6875 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:43 -0700 Subject: [PATCH 21/27] Bluetooth: Support the "managed-flags" adv flag This patch adds support for the "managed-flags" flag of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 797cf6cf958f..cc4e76f8be0e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -986,7 +986,13 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) if (hdev->adv_instance.flags & MGMT_ADV_FLAG_LIMITED_DISCOV) flags |= LE_AD_LIMITED; - if (flags) { + if (flags || (hdev->adv_instance.flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) { + /* If a discovery flag wasn't provided, simply use the global + * settings. + */ + if (!flags) + flags |= get_adv_discov_flags(hdev); + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) flags |= LE_AD_NO_BREDR; @@ -6582,7 +6588,8 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, u8 max_len = HCI_MAX_AD_LENGTH; int i, cur_len; bool flags_managed = false; - u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV; + u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV | + MGMT_ADV_FLAG_MANAGED_FLAGS; if (is_adv_data && (adv_flags & flags_params)) { flags_managed = true; From 5507e358112af307c1c8595a04a0ef172d197f3c Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:44 -0700 Subject: [PATCH 22/27] Bluetooth: Support the "tx-power" adv flag This patch adds support for the "tx-power" flag of the Add Advertising command. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cc4e76f8be0e..de321b9087e7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1004,6 +1004,16 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) ptr += 3; } + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID && + (hdev->adv_instance.flags & MGMT_ADV_FLAG_TX_POWER)) { + ptr[0] = 0x02; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8)hdev->adv_tx_power; + + ad_len += 3; + ptr += 3; + } + memcpy(ptr, hdev->adv_instance.adv_data, hdev->adv_instance.adv_data_len); ad_len += hdev->adv_instance.adv_data_len; @@ -6588,6 +6598,7 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, u8 max_len = HCI_MAX_AD_LENGTH; int i, cur_len; bool flags_managed = false; + bool tx_power_managed = false; u32 flags_params = MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV | MGMT_ADV_FLAG_MANAGED_FLAGS; @@ -6596,6 +6607,11 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, max_len -= 3; } + if (is_adv_data && (adv_flags & MGMT_ADV_FLAG_TX_POWER)) { + tx_power_managed = true; + max_len -= 3; + } + if (len > max_len) return false; @@ -6606,6 +6622,9 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, if (flags_managed && data[i + 1] == EIR_FLAGS) return false; + if (tx_power_managed && data[i + 1] == EIR_TX_POWER) + return false; + /* If the current field length would exceed the total data * length, then it's invalid. */ From 089fa8c09e7fd36b9db01c23c826fb7956f25a1e Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:45 -0700 Subject: [PATCH 23/27] Bluetooth: Update supported_flags for AD features This patch updates the "supported_flags" parameter returned from the "Read Advertising Features" command. Add Advertising will now return an error if an unsupported flag is provided. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index de321b9087e7..eab09b5a71df 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6540,6 +6540,21 @@ done: return err; } +static u32 get_supported_adv_flags(struct hci_dev *hdev) +{ + u32 flags = 0; + + flags |= MGMT_ADV_FLAG_CONNECTABLE; + flags |= MGMT_ADV_FLAG_DISCOV; + flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; + flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; + + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) + flags |= MGMT_ADV_FLAG_TX_POWER; + + return flags; +} + static int read_adv_features(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -6547,9 +6562,14 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, size_t rp_len; int err; bool instance; + u32 supported_flags; BT_DBG("%s", hdev->name); + if (!lmp_le_capable(hdev)) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES, + MGMT_STATUS_REJECTED); + hci_dev_lock(hdev); rp_len = sizeof(*rp); @@ -6567,7 +6587,9 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, return -ENOMEM; } - rp->supported_flags = cpu_to_le32(0); + supported_flags = get_supported_adv_flags(hdev); + + rp->supported_flags = cpu_to_le32(supported_flags); rp->max_adv_data_len = HCI_MAX_AD_LENGTH; rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH; rp->max_instances = 1; @@ -6689,6 +6711,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, struct mgmt_cp_add_advertising *cp = data; struct mgmt_rp_add_advertising rp; u32 flags; + u32 supported_flags; u8 status; u16 timeout; int err; @@ -6705,8 +6728,11 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, flags = __le32_to_cpu(cp->flags); timeout = __le16_to_cpu(cp->timeout); - /* The current implementation only supports adding one instance */ - if (cp->instance != 0x01) + /* The current implementation only supports adding one instance and only + * a subset of the specified flags. + */ + supported_flags = get_supported_adv_flags(hdev); + if (cp->instance != 0x01 || (flags & ~supported_flags)) return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); From fdf51784cd728e55daa0ca7b0ba16966afbfeae0 Mon Sep 17 00:00:00 2001 From: Arman Uguray Date: Wed, 25 Mar 2015 18:53:46 -0700 Subject: [PATCH 24/27] Bluetooth: Unify advertising data code paths This patch simplifies the code paths for assembling the advertising data used by advertising instances 0 and 1. Signed-off-by: Arman Uguray Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 199 +++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 113 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index eab09b5a71df..fb2e764c6211 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -941,115 +941,6 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev) return 0; } -static u8 create_default_adv_data(struct hci_dev *hdev, u8 *ptr) -{ - u8 ad_len = 0, flags = 0; - - flags |= get_adv_discov_flags(hdev); - - if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) - flags |= LE_AD_NO_BREDR; - - if (flags) { - BT_DBG("adv flags 0x%02x", flags); - - ptr[0] = 2; - ptr[1] = EIR_FLAGS; - ptr[2] = flags; - - ad_len += 3; - ptr += 3; - } - - if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) { - ptr[0] = 2; - ptr[1] = EIR_TX_POWER; - ptr[2] = (u8) hdev->adv_tx_power; - - ad_len += 3; - ptr += 3; - } - - return ad_len; -} - -static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr) -{ - u8 ad_len = 0, flags = 0; - - /* The Add Advertising command allows userspace to set both the general - * and limited discoverable flags. - */ - if (hdev->adv_instance.flags & MGMT_ADV_FLAG_DISCOV) - flags |= LE_AD_GENERAL; - - if (hdev->adv_instance.flags & MGMT_ADV_FLAG_LIMITED_DISCOV) - flags |= LE_AD_LIMITED; - - if (flags || (hdev->adv_instance.flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) { - /* If a discovery flag wasn't provided, simply use the global - * settings. - */ - if (!flags) - flags |= get_adv_discov_flags(hdev); - - if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) - flags |= LE_AD_NO_BREDR; - - ptr[0] = 0x02; - ptr[1] = EIR_FLAGS; - ptr[2] = flags; - - ad_len += 3; - ptr += 3; - } - - if (hdev->adv_tx_power != HCI_TX_POWER_INVALID && - (hdev->adv_instance.flags & MGMT_ADV_FLAG_TX_POWER)) { - ptr[0] = 0x02; - ptr[1] = EIR_TX_POWER; - ptr[2] = (u8)hdev->adv_tx_power; - - ad_len += 3; - ptr += 3; - } - - memcpy(ptr, hdev->adv_instance.adv_data, - hdev->adv_instance.adv_data_len); - ad_len += hdev->adv_instance.adv_data_len; - - return ad_len; -} - -static void update_adv_data_for_instance(struct hci_request *req, u8 instance) -{ - struct hci_dev *hdev = req->hdev; - struct hci_cp_le_set_adv_data cp; - u8 len; - - if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) - return; - - memset(&cp, 0, sizeof(cp)); - - if (instance) - len = create_instance_adv_data(hdev, cp.data); - else - len = create_default_adv_data(hdev, cp.data); - - /* There's nothing to do if the data hasn't changed */ - if (hdev->adv_data_len == len && - memcmp(cp.data, hdev->adv_data, len) == 0) - return; - - memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); - hdev->adv_data_len = len; - - cp.length = len; - - hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); -} - static u8 get_current_adv_instance(struct hci_dev *hdev) { /* The "Set Advertising" setting supersedes the "Add Advertising" @@ -1088,21 +979,103 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance) if (instance > 0x01) return 0; - if (instance == 1) + if (instance == 0x01) return hdev->adv_instance.flags; - flags = 0; + /* Instance 0 always manages the "Tx Power" and "Flags" fields */ + flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS; /* For instance 0, assemble the flags from global settings */ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE) || get_connectable(hdev)) flags |= MGMT_ADV_FLAG_CONNECTABLE; - /* TODO: Add the rest of the flags */ - return flags; } +static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr) +{ + u8 ad_len = 0, flags = 0; + u32 instance_flags = get_adv_instance_flags(hdev, instance); + + /* The Add Advertising command allows userspace to set both the general + * and limited discoverable flags. + */ + if (instance_flags & MGMT_ADV_FLAG_DISCOV) + flags |= LE_AD_GENERAL; + + if (instance_flags & MGMT_ADV_FLAG_LIMITED_DISCOV) + flags |= LE_AD_LIMITED; + + if (flags || (instance_flags & MGMT_ADV_FLAG_MANAGED_FLAGS)) { + /* If a discovery flag wasn't provided, simply use the global + * settings. + */ + if (!flags) + flags |= get_adv_discov_flags(hdev); + + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + flags |= LE_AD_NO_BREDR; + + /* If flags would still be empty, then there is no need to + * include the "Flags" AD field". + */ + if (flags) { + ptr[0] = 0x02; + ptr[1] = EIR_FLAGS; + ptr[2] = flags; + + ad_len += 3; + ptr += 3; + } + } + + /* Provide Tx Power only if we can provide a valid value for it */ + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID && + (instance_flags & MGMT_ADV_FLAG_TX_POWER)) { + ptr[0] = 0x02; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8)hdev->adv_tx_power; + + ad_len += 3; + ptr += 3; + } + + if (instance) { + memcpy(ptr, hdev->adv_instance.adv_data, + hdev->adv_instance.adv_data_len); + ad_len += hdev->adv_instance.adv_data_len; + } + + return ad_len; +} + +static void update_adv_data_for_instance(struct hci_request *req, u8 instance) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_adv_data cp; + u8 len; + + if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) + return; + + memset(&cp, 0, sizeof(cp)); + + len = create_instance_adv_data(hdev, instance, cp.data); + + /* There's nothing to do if the data hasn't changed */ + if (hdev->adv_data_len == len && + memcmp(cp.data, hdev->adv_data, len) == 0) + return; + + memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); + hdev->adv_data_len = len; + + cp.length = len; + + hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); +} + static void update_adv_data(struct hci_request *req) { struct hci_dev *hdev = req->hdev; From 8bf9538a5de5f6496993fb294ef606e453232ea4 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 26 Mar 2015 12:46:28 +0100 Subject: [PATCH 25/27] mac802154: cleanup concurrent check This patch cleanups the checking of different mac phy depended values by handling depended mac settings per hw support flag in one condition. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- net/mac802154/iface.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c index 6fb6bdf9868c..38b56f9d9386 100644 --- a/net/mac802154/iface.c +++ b/net/mac802154/iface.c @@ -174,24 +174,16 @@ ieee802154_check_mac_settings(struct ieee802154_local *local, } if (local->hw.flags & IEEE802154_HW_AFILT) { - if (wpan_dev->pan_id != nwpan_dev->pan_id) - return -EBUSY; - - if (wpan_dev->short_addr != nwpan_dev->short_addr) - return -EBUSY; - - if (wpan_dev->extended_addr != nwpan_dev->extended_addr) + if (wpan_dev->pan_id != nwpan_dev->pan_id || + wpan_dev->short_addr != nwpan_dev->short_addr || + wpan_dev->extended_addr != nwpan_dev->extended_addr) return -EBUSY; } if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) { - if (wpan_dev->min_be != nwpan_dev->min_be) - return -EBUSY; - - if (wpan_dev->max_be != nwpan_dev->max_be) - return -EBUSY; - - if (wpan_dev->csma_retries != nwpan_dev->csma_retries) + if (wpan_dev->min_be != nwpan_dev->min_be || + wpan_dev->max_be != nwpan_dev->max_be || + wpan_dev->csma_retries != nwpan_dev->csma_retries) return -EBUSY; } From 3ff7092bc83b50e831c43d83141084756f4e6ae1 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 26 Mar 2015 12:46:29 +0100 Subject: [PATCH 26/27] at86rf230: remove unnecessary spinlock This spinlock isn't necessary because if we are in TX_ON/TX_ARET_ON state the transceiver can't be interrupted e.g. by receiving a frame when a SHR was detected. In this time the transceiver doesn't leave the TX_ON/TX_ARET_ON state until the tx complete irq change the state into RX_AACK_ON again. This means a receiving interrupt in state TX_ON/TX_ARET_ON can't happen and is_tx is protected by transceiver. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/at86rf230.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index cc5efa149da1..1278dd5ee187 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -96,8 +95,6 @@ struct at86rf230_local { unsigned long cal_timeout; s8 max_frame_retries; bool is_tx; - /* spinlock for is_tx protection */ - spinlock_t lock; u8 tx_retry; struct sk_buff *tx_skb; struct at86rf230_state_change tx; @@ -878,10 +875,8 @@ at86rf230_rx_trac_check(void *context) static void at86rf230_irq_trx_end(struct at86rf230_local *lp) { - spin_lock(&lp->lock); if (lp->is_tx) { lp->is_tx = 0; - spin_unlock(&lp->lock); if (lp->tx_aret) at86rf230_async_state_change(lp, &lp->irq, @@ -894,7 +889,6 @@ at86rf230_irq_trx_end(struct at86rf230_local *lp) at86rf230_tx_complete, true); } else { - spin_unlock(&lp->lock); at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq, at86rf230_rx_trac_check, true); } @@ -964,9 +958,7 @@ at86rf230_write_frame(void *context) u8 *buf = ctx->buf; int rc; - spin_lock(&lp->lock); lp->is_tx = 1; - spin_unlock(&lp->lock); buf[0] = CMD_FB | CMD_WRITE; buf[1] = skb->len + 2; @@ -1698,7 +1690,6 @@ static int at86rf230_probe(struct spi_device *spi) if (rc < 0) goto free_dev; - spin_lock_init(&lp->lock); init_completion(&lp->state_complete); spi_set_drvdata(spi, lp); From a7a484bfca218f9671a3e3bc98851eab6b628310 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Thu, 26 Mar 2015 12:46:30 +0100 Subject: [PATCH 27/27] at86rf230: fix is_tx while error handling This patch fix the error handling when is_tx is true. The error handling tries to get the transceiver into RX_AACK_ON mode then we need to be sure that is_tx is false. Signed-off-by: Alexander Aring Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/at86rf230.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 1278dd5ee187..5ad46f7f514f 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -457,6 +457,7 @@ at86rf230_async_error_recover(void *context) struct at86rf230_state_change *ctx = context; struct at86rf230_local *lp = ctx->lp; + lp->is_tx = 0; at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false); ieee802154_wake_queue(lp->hw); }