aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorArman Uguray <armansito@chromium.org>2015-03-23 18:57:12 -0400
committerMarcel Holtmann <marcel@holtmann.org>2015-03-23 20:53:46 -0400
commit24b4f38fc9ebf93af223c67169a946d6baf9db61 (patch)
treea8ca6f13c81224b2a9059e53eb386958770bb185 /net
parent203fea0178d7e165dbe834d1bdd9d243018fd5bf (diff)
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 <armansito@chromium.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/mgmt.c283
1 files 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[] = {
100 MGMT_OP_READ_LOCAL_OOB_EXT_DATA, 100 MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
101 MGMT_OP_READ_EXT_INDEX_LIST, 101 MGMT_OP_READ_EXT_INDEX_LIST,
102 MGMT_OP_READ_ADV_FEATURES, 102 MGMT_OP_READ_ADV_FEATURES,
103 MGMT_OP_ADD_ADVERTISING,
103}; 104};
104 105
105static const u16 mgmt_events[] = { 106static const u16 mgmt_events[] = {
@@ -135,6 +136,8 @@ static const u16 mgmt_events[] = {
135 MGMT_EV_EXT_INDEX_ADDED, 136 MGMT_EV_EXT_INDEX_ADDED,
136 MGMT_EV_EXT_INDEX_REMOVED, 137 MGMT_EV_EXT_INDEX_REMOVED,
137 MGMT_EV_LOCAL_OOB_DATA_UPDATED, 138 MGMT_EV_LOCAL_OOB_DATA_UPDATED,
139 MGMT_EV_ADVERTISING_ADDED,
140 MGMT_EV_ADVERTISING_REMOVED,
138}; 141};
139 142
140#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) 143#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
@@ -864,7 +867,7 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
864 return 0; 867 return 0;
865} 868}
866 869
867static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) 870static u8 create_default_adv_data(struct hci_dev *hdev, u8 *ptr)
868{ 871{
869 u8 ad_len = 0, flags = 0; 872 u8 ad_len = 0, flags = 0;
870 873
@@ -896,7 +899,18 @@ static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
896 return ad_len; 899 return ad_len;
897} 900}
898 901
899static void update_adv_data(struct hci_request *req) 902static u8 create_instance_adv_data(struct hci_dev *hdev, u8 *ptr)
903{
904 /* TODO: Set the appropriate entries based on advertising instance flags
905 * here once flags other than 0 are supported.
906 */
907 memcpy(ptr, hdev->adv_instance.adv_data,
908 hdev->adv_instance.adv_data_len);
909
910 return hdev->adv_instance.adv_data_len;
911}
912
913static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
900{ 914{
901 struct hci_dev *hdev = req->hdev; 915 struct hci_dev *hdev = req->hdev;
902 struct hci_cp_le_set_adv_data cp; 916 struct hci_cp_le_set_adv_data cp;
@@ -907,8 +921,12 @@ static void update_adv_data(struct hci_request *req)
907 921
908 memset(&cp, 0, sizeof(cp)); 922 memset(&cp, 0, sizeof(cp));
909 923
910 len = create_adv_data(hdev, cp.data); 924 if (instance)
925 len = create_instance_adv_data(hdev, cp.data);
926 else
927 len = create_default_adv_data(hdev, cp.data);
911 928
929 /* There's nothing to do if the data hasn't changed */
912 if (hdev->adv_data_len == len && 930 if (hdev->adv_data_len == len &&
913 memcmp(cp.data, hdev->adv_data, len) == 0) 931 memcmp(cp.data, hdev->adv_data, len) == 0)
914 return; 932 return;
@@ -921,6 +939,25 @@ static void update_adv_data(struct hci_request *req)
921 hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); 939 hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
922} 940}
923 941
942static void update_adv_data(struct hci_request *req)
943{
944 struct hci_dev *hdev = req->hdev;
945 u8 instance;
946
947 /* The "Set Advertising" setting supersedes the "Add Advertising"
948 * setting. Here we set the advertising data based on which
949 * setting was set. When neither apply, default to the global settings,
950 * represented by instance "0".
951 */
952 if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
953 !hci_dev_test_flag(hdev, HCI_ADVERTISING))
954 instance = 0x01;
955 else
956 instance = 0x00;
957
958 update_adv_data_for_instance(req, instance);
959}
960
924int mgmt_update_adv_data(struct hci_dev *hdev) 961int mgmt_update_adv_data(struct hci_dev *hdev)
925{ 962{
926 struct hci_request req; 963 struct hci_request req;
@@ -4374,10 +4411,17 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
4374 return err; 4411 return err;
4375} 4412}
4376 4413
4414static void enable_advertising_instance(struct hci_dev *hdev, u8 status,
4415 u16 opcode)
4416{
4417 BT_DBG("status %d", status);
4418}
4419
4377static void set_advertising_complete(struct hci_dev *hdev, u8 status, 4420static void set_advertising_complete(struct hci_dev *hdev, u8 status,
4378 u16 opcode) 4421 u16 opcode)
4379{ 4422{
4380 struct cmd_lookup match = { NULL, hdev }; 4423 struct cmd_lookup match = { NULL, hdev };
4424 struct hci_request req;
4381 4425
4382 hci_dev_lock(hdev); 4426 hci_dev_lock(hdev);
4383 4427
@@ -4402,6 +4446,21 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
4402 if (match.sk) 4446 if (match.sk)
4403 sock_put(match.sk); 4447 sock_put(match.sk);
4404 4448
4449 /* If "Set Advertising" was just disabled and instance advertising was
4450 * set up earlier, then enable the advertising instance.
4451 */
4452 if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
4453 !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
4454 goto unlock;
4455
4456 hci_req_init(&req, hdev);
4457
4458 update_adv_data(&req);
4459 enable_advertising(&req);
4460
4461 if (hci_req_run(&req, enable_advertising_instance) < 0)
4462 BT_ERR("Failed to re-configure advertising");
4463
4405unlock: 4464unlock:
4406 hci_dev_unlock(hdev); 4465 hci_dev_unlock(hdev);
4407} 4466}
@@ -4484,10 +4543,13 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
4484 else 4543 else
4485 hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 4544 hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
4486 4545
4487 if (val) 4546 if (val) {
4547 /* Switch to instance "0" for the Set Advertising setting. */
4548 update_adv_data_for_instance(&req, 0);
4488 enable_advertising(&req); 4549 enable_advertising(&req);
4489 else 4550 } else {
4490 disable_advertising(&req); 4551 disable_advertising(&req);
4552 }
4491 4553
4492 err = hci_req_run(&req, set_advertising_complete); 4554 err = hci_req_run(&req, set_advertising_complete);
4493 if (err < 0) 4555 if (err < 0)
@@ -6299,12 +6361,21 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
6299 struct mgmt_rp_read_adv_features *rp; 6361 struct mgmt_rp_read_adv_features *rp;
6300 size_t rp_len; 6362 size_t rp_len;
6301 int err; 6363 int err;
6364 bool instance;
6302 6365
6303 BT_DBG("%s", hdev->name); 6366 BT_DBG("%s", hdev->name);
6304 6367
6305 hci_dev_lock(hdev); 6368 hci_dev_lock(hdev);
6306 6369
6307 rp_len = sizeof(*rp); 6370 rp_len = sizeof(*rp);
6371
6372 /* Currently only one instance is supported, so just add 1 to the
6373 * response length.
6374 */
6375 instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
6376 if (instance)
6377 rp_len++;
6378
6308 rp = kmalloc(rp_len, GFP_ATOMIC); 6379 rp = kmalloc(rp_len, GFP_ATOMIC);
6309 if (!rp) { 6380 if (!rp) {
6310 hci_dev_unlock(hdev); 6381 hci_dev_unlock(hdev);
@@ -6314,8 +6385,17 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
6314 rp->supported_flags = cpu_to_le32(0); 6385 rp->supported_flags = cpu_to_le32(0);
6315 rp->max_adv_data_len = HCI_MAX_AD_LENGTH; 6386 rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
6316 rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH; 6387 rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
6317 rp->max_instances = 0; 6388 rp->max_instances = 1;
6318 rp->num_instances = 0; 6389
6390 /* Currently only one instance is supported, so simply return the
6391 * current instance number.
6392 */
6393 if (instance) {
6394 rp->num_instances = 1;
6395 rp->instance[0] = 1;
6396 } else {
6397 rp->num_instances = 0;
6398 }
6319 6399
6320 hci_dev_unlock(hdev); 6400 hci_dev_unlock(hdev);
6321 6401
@@ -6327,6 +6407,179 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
6327 return err; 6407 return err;
6328} 6408}
6329 6409
6410static bool adv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *adv_data,
6411 u8 adv_data_len)
6412{
6413 u8 max_adv_len = HCI_MAX_AD_LENGTH;
6414 int i, cur_len;
6415
6416 /* TODO: Correctly reduce adv_len based on adv_flags. */
6417
6418 if (adv_data_len > max_adv_len)
6419 return false;
6420
6421 /* Make sure that adv_data is correctly formatted. */
6422 for (i = 0, cur_len = 0; i < adv_data_len; i += (cur_len + 1)) {
6423 cur_len = adv_data[i];
6424
6425 /* If the current field length would exceed the total data
6426 * length, then it's invalid.
6427 */
6428 if (i + cur_len >= adv_data_len)
6429 return false;
6430 }
6431
6432 return true;
6433}
6434
6435static void advertising_added(struct sock *sk, struct hci_dev *hdev,
6436 u8 instance)
6437{
6438 struct mgmt_ev_advertising_added ev;
6439
6440 ev.instance = instance;
6441
6442 mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk);
6443}
6444
6445static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
6446 u8 instance)
6447{
6448 struct mgmt_ev_advertising_removed ev;
6449
6450 ev.instance = instance;
6451
6452 mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
6453}
6454
6455static void add_advertising_complete(struct hci_dev *hdev, u8 status,
6456 u16 opcode)
6457{
6458 struct mgmt_pending_cmd *cmd;
6459 struct mgmt_rp_add_advertising rp;
6460
6461 BT_DBG("status %d", status);
6462
6463 hci_dev_lock(hdev);
6464
6465 cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
6466
6467 if (status) {
6468 hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
6469 memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
6470 advertising_removed(cmd ? cmd->sk : NULL, hdev, 1);
6471 }
6472
6473 if (!cmd)
6474 goto unlock;
6475
6476 rp.instance = 0x01;
6477
6478 if (status)
6479 mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
6480 mgmt_status(status));
6481 else
6482 mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
6483 mgmt_status(status), &rp, sizeof(rp));
6484
6485 mgmt_pending_remove(cmd);
6486
6487unlock:
6488 hci_dev_unlock(hdev);
6489}
6490
6491static int add_advertising(struct sock *sk, struct hci_dev *hdev,
6492 void *data, u16 data_len)
6493{
6494 struct mgmt_cp_add_advertising *cp = data;
6495 struct mgmt_rp_add_advertising rp;
6496 u32 flags;
6497 u8 status;
6498 int err;
6499 struct mgmt_pending_cmd *cmd;
6500 struct hci_request req;
6501
6502 BT_DBG("%s", hdev->name);
6503
6504 status = mgmt_le_support(hdev);
6505 if (status)
6506 return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
6507 status);
6508
6509 flags = __le32_to_cpu(cp->flags);
6510
6511 /* The current implementation only supports adding one instance and
6512 * doesn't support flags.
6513 */
6514 if (cp->instance != 0x01 || flags)
6515 return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
6516 MGMT_STATUS_INVALID_PARAMS);
6517
6518 hci_dev_lock(hdev);
6519
6520 if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
6521 pending_find(MGMT_OP_SET_LE, hdev)) {
6522 err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
6523 MGMT_STATUS_BUSY);
6524 goto unlock;
6525 }
6526
6527 if (!adv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len)) {
6528 err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
6529 MGMT_STATUS_INVALID_PARAMS);
6530 goto unlock;
6531 }
6532
6533 hdev->adv_instance.flags = flags;
6534 hdev->adv_instance.adv_data_len = cp->adv_data_len;
6535 hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len;
6536
6537 if (cp->adv_data_len)
6538 memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len);
6539
6540 if (cp->scan_rsp_len)
6541 memcpy(hdev->adv_instance.scan_rsp_data,
6542 cp->data + cp->adv_data_len, cp->scan_rsp_len);
6543
6544 if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE))
6545 advertising_added(sk, hdev, 1);
6546
6547 /* If the HCI_ADVERTISING flag is set or the device isn't powered then
6548 * we have no HCI communication to make. Simply return.
6549 */
6550 if (!hdev_is_powered(hdev) ||
6551 hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
6552 rp.instance = 0x01;
6553 err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
6554 MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
6555 goto unlock;
6556 }
6557
6558 /* We're good to go, update advertising data, parameters, and start
6559 * advertising.
6560 */
6561 cmd = mgmt_pending_add(sk, MGMT_OP_ADD_ADVERTISING, hdev, data,
6562 data_len);
6563 if (!cmd) {
6564 err = -ENOMEM;
6565 goto unlock;
6566 }
6567
6568 hci_req_init(&req, hdev);
6569
6570 update_adv_data(&req);
6571 enable_advertising(&req);
6572
6573 err = hci_req_run(&req, add_advertising_complete);
6574 if (err < 0)
6575 mgmt_pending_remove(cmd);
6576
6577unlock:
6578 hci_dev_unlock(hdev);
6579
6580 return err;
6581}
6582
6330static const struct hci_mgmt_handler mgmt_handlers[] = { 6583static const struct hci_mgmt_handler mgmt_handlers[] = {
6331 { NULL }, /* 0x0000 (no command) */ 6584 { NULL }, /* 0x0000 (no command) */
6332 { read_version, MGMT_READ_VERSION_SIZE, 6585 { read_version, MGMT_READ_VERSION_SIZE,
@@ -6411,6 +6664,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
6411 HCI_MGMT_NO_HDEV | 6664 HCI_MGMT_NO_HDEV |
6412 HCI_MGMT_UNTRUSTED }, 6665 HCI_MGMT_UNTRUSTED },
6413 { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, 6666 { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE },
6667 { add_advertising, MGMT_ADD_ADVERTISING_SIZE,
6668 HCI_MGMT_VAR_LEN },
6414}; 6669};
6415 6670
6416void mgmt_index_added(struct hci_dev *hdev) 6671void mgmt_index_added(struct hci_dev *hdev)
@@ -6582,7 +6837,8 @@ static int powered_update_hci(struct hci_dev *hdev)
6582 update_scan_rsp_data(&req); 6837 update_scan_rsp_data(&req);
6583 } 6838 }
6584 6839
6585 if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) 6840 if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
6841 hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
6586 enable_advertising(&req); 6842 enable_advertising(&req);
6587 6843
6588 restart_le_actions(&req); 6844 restart_le_actions(&req);
@@ -6694,7 +6950,13 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev)
6694 sizeof(scan), &scan); 6950 sizeof(scan), &scan);
6695 } 6951 }
6696 update_class(&req); 6952 update_class(&req);
6697 update_adv_data(&req); 6953
6954 /* Advertising instances don't use the global discoverable setting, so
6955 * only update AD if advertising was enabled using Set Advertising.
6956 */
6957 if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
6958 update_adv_data(&req);
6959
6698 hci_req_run(&req, NULL); 6960 hci_req_run(&req, NULL);
6699 6961
6700 hdev->discov_timeout = 0; 6962 hdev->discov_timeout = 0;
@@ -7595,7 +7857,8 @@ void mgmt_reenable_advertising(struct hci_dev *hdev)
7595{ 7857{
7596 struct hci_request req; 7858 struct hci_request req;
7597 7859
7598 if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) 7860 if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
7861 !hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
7599 return; 7862 return;
7600 7863
7601 hci_req_init(&req, hdev); 7864 hci_req_init(&req, hdev);