diff options
author | Arman Uguray <armansito@chromium.org> | 2015-03-23 18:57:14 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2015-03-23 20:53:47 -0400 |
commit | 4117ed70a55128273f1b6d00c7725e4c8a5c0031 (patch) | |
tree | 93cd4ed4655711e1a626d85b65a5949ae8638deb | |
parent | da929335f27d955172539bf56bed1ac9ff9b8d45 (diff) |
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 <armansito@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r-- | net/bluetooth/mgmt.c | 66 |
1 files 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, | |||
793 | return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data); | 793 | return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data); |
794 | } | 794 | } |
795 | 795 | ||
796 | static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) | 796 | static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) |
797 | { | 797 | { |
798 | u8 ad_len = 0; | 798 | u8 ad_len = 0; |
799 | size_t name_len; | 799 | size_t name_len; |
@@ -819,7 +819,19 @@ static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) | |||
819 | return ad_len; | 819 | return ad_len; |
820 | } | 820 | } |
821 | 821 | ||
822 | static void update_scan_rsp_data(struct hci_request *req) | 822 | static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) |
823 | { | ||
824 | /* TODO: Set the appropriate entries based on advertising instance flags | ||
825 | * here once flags other than 0 are supported. | ||
826 | */ | ||
827 | memcpy(ptr, hdev->adv_instance.scan_rsp_data, | ||
828 | hdev->adv_instance.scan_rsp_len); | ||
829 | |||
830 | return hdev->adv_instance.scan_rsp_len; | ||
831 | } | ||
832 | |||
833 | static void update_scan_rsp_data_for_instance(struct hci_request *req, | ||
834 | u8 instance) | ||
823 | { | 835 | { |
824 | struct hci_dev *hdev = req->hdev; | 836 | struct hci_dev *hdev = req->hdev; |
825 | struct hci_cp_le_set_scan_rsp_data cp; | 837 | struct hci_cp_le_set_scan_rsp_data cp; |
@@ -830,10 +842,13 @@ static void update_scan_rsp_data(struct hci_request *req) | |||
830 | 842 | ||
831 | memset(&cp, 0, sizeof(cp)); | 843 | memset(&cp, 0, sizeof(cp)); |
832 | 844 | ||
833 | len = create_scan_rsp_data(hdev, cp.data); | 845 | if (instance) |
846 | len = create_instance_scan_rsp_data(hdev, cp.data); | ||
847 | else | ||
848 | len = create_default_scan_rsp_data(hdev, cp.data); | ||
834 | 849 | ||
835 | if (hdev->scan_rsp_data_len == len && | 850 | if (hdev->scan_rsp_data_len == len && |
836 | memcmp(cp.data, hdev->scan_rsp_data, len) == 0) | 851 | !memcmp(cp.data, hdev->scan_rsp_data, len)) |
837 | return; | 852 | return; |
838 | 853 | ||
839 | memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data)); | 854 | memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data)); |
@@ -844,6 +859,25 @@ static void update_scan_rsp_data(struct hci_request *req) | |||
844 | hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); | 859 | hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); |
845 | } | 860 | } |
846 | 861 | ||
862 | static void update_scan_rsp_data(struct hci_request *req) | ||
863 | { | ||
864 | struct hci_dev *hdev = req->hdev; | ||
865 | u8 instance; | ||
866 | |||
867 | /* The "Set Advertising" setting supersedes the "Add Advertising" | ||
868 | * setting. Here we set the scan response data based on which | ||
869 | * setting was set. When neither apply, default to the global settings, | ||
870 | * represented by instance "0". | ||
871 | */ | ||
872 | if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) && | ||
873 | !hci_dev_test_flag(hdev, HCI_ADVERTISING)) | ||
874 | instance = 0x01; | ||
875 | else | ||
876 | instance = 0x00; | ||
877 | |||
878 | update_scan_rsp_data_for_instance(req, instance); | ||
879 | } | ||
880 | |||
847 | static u8 get_adv_discov_flags(struct hci_dev *hdev) | 881 | static u8 get_adv_discov_flags(struct hci_dev *hdev) |
848 | { | 882 | { |
849 | struct mgmt_pending_cmd *cmd; | 883 | struct mgmt_pending_cmd *cmd; |
@@ -4547,6 +4581,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, | |||
4547 | if (val) { | 4581 | if (val) { |
4548 | /* Switch to instance "0" for the Set Advertising setting. */ | 4582 | /* Switch to instance "0" for the Set Advertising setting. */ |
4549 | update_adv_data_for_instance(&req, 0); | 4583 | update_adv_data_for_instance(&req, 0); |
4584 | update_scan_rsp_data_for_instance(&req, 0); | ||
4550 | enable_advertising(&req); | 4585 | enable_advertising(&req); |
4551 | } else { | 4586 | } else { |
4552 | disable_advertising(&req); | 4587 | disable_advertising(&req); |
@@ -6408,25 +6443,25 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev, | |||
6408 | return err; | 6443 | return err; |
6409 | } | 6444 | } |
6410 | 6445 | ||
6411 | static bool adv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *adv_data, | 6446 | static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, |
6412 | u8 adv_data_len) | 6447 | u8 len) |
6413 | { | 6448 | { |
6414 | u8 max_adv_len = HCI_MAX_AD_LENGTH; | 6449 | u8 max_len = HCI_MAX_AD_LENGTH; |
6415 | int i, cur_len; | 6450 | int i, cur_len; |
6416 | 6451 | ||
6417 | /* TODO: Correctly reduce adv_len based on adv_flags. */ | 6452 | /* TODO: Correctly reduce len based on adv_flags. */ |
6418 | 6453 | ||
6419 | if (adv_data_len > max_adv_len) | 6454 | if (len > max_len) |
6420 | return false; | 6455 | return false; |
6421 | 6456 | ||
6422 | /* Make sure that adv_data is correctly formatted. */ | 6457 | /* Make sure that the data is correctly formatted. */ |
6423 | for (i = 0, cur_len = 0; i < adv_data_len; i += (cur_len + 1)) { | 6458 | for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) { |
6424 | cur_len = adv_data[i]; | 6459 | cur_len = data[i]; |
6425 | 6460 | ||
6426 | /* If the current field length would exceed the total data | 6461 | /* If the current field length would exceed the total data |
6427 | * length, then it's invalid. | 6462 | * length, then it's invalid. |
6428 | */ | 6463 | */ |
6429 | if (i + cur_len >= adv_data_len) | 6464 | if (i + cur_len >= len) |
6430 | return false; | 6465 | return false; |
6431 | } | 6466 | } |
6432 | 6467 | ||
@@ -6526,7 +6561,9 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, | |||
6526 | goto unlock; | 6561 | goto unlock; |
6527 | } | 6562 | } |
6528 | 6563 | ||
6529 | if (!adv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len)) { | 6564 | if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len) || |
6565 | !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len, | ||
6566 | cp->scan_rsp_len)) { | ||
6530 | err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, | 6567 | err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, |
6531 | MGMT_STATUS_INVALID_PARAMS); | 6568 | MGMT_STATUS_INVALID_PARAMS); |
6532 | goto unlock; | 6569 | goto unlock; |
@@ -6570,6 +6607,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev, | |||
6570 | hci_req_init(&req, hdev); | 6607 | hci_req_init(&req, hdev); |
6571 | 6608 | ||
6572 | update_adv_data(&req); | 6609 | update_adv_data(&req); |
6610 | update_scan_rsp_data(&req); | ||
6573 | enable_advertising(&req); | 6611 | enable_advertising(&req); |
6574 | 6612 | ||
6575 | err = hci_req_run(&req, add_advertising_complete); | 6613 | err = hci_req_run(&req, add_advertising_complete); |