diff options
-rw-r--r-- | net/bluetooth/mgmt.c | 80 |
1 files changed, 69 insertions, 11 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 796db5849795..bd91ee5f130c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -599,12 +599,35 @@ static void update_scan_rsp_data(struct hci_request *req) | |||
599 | hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); | 599 | hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); |
600 | } | 600 | } |
601 | 601 | ||
602 | static u8 get_adv_discov_flags(struct hci_dev *hdev) | ||
603 | { | ||
604 | struct pending_cmd *cmd; | ||
605 | |||
606 | /* If there's a pending mgmt command the flags will not yet have | ||
607 | * their final values, so check for this first. | ||
608 | */ | ||
609 | cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); | ||
610 | if (cmd) { | ||
611 | struct mgmt_mode *cp = cmd->param; | ||
612 | if (cp->val == 0x01) | ||
613 | return LE_AD_GENERAL; | ||
614 | else if (cp->val == 0x02) | ||
615 | return LE_AD_LIMITED; | ||
616 | } else { | ||
617 | if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags)) | ||
618 | return LE_AD_LIMITED; | ||
619 | else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) | ||
620 | return LE_AD_GENERAL; | ||
621 | } | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
602 | static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) | 626 | static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) |
603 | { | 627 | { |
604 | u8 ad_len = 0, flags = 0; | 628 | u8 ad_len = 0, flags = 0; |
605 | 629 | ||
606 | if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) | 630 | flags |= get_adv_discov_flags(hdev); |
607 | flags |= LE_AD_GENERAL; | ||
608 | 631 | ||
609 | if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { | 632 | if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { |
610 | if (lmp_le_br_capable(hdev)) | 633 | if (lmp_le_br_capable(hdev)) |
@@ -1120,15 +1143,15 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, | |||
1120 | struct pending_cmd *cmd; | 1143 | struct pending_cmd *cmd; |
1121 | struct hci_request req; | 1144 | struct hci_request req; |
1122 | u16 timeout; | 1145 | u16 timeout; |
1123 | u8 scan, status; | 1146 | u8 scan; |
1124 | int err; | 1147 | int err; |
1125 | 1148 | ||
1126 | BT_DBG("request for %s", hdev->name); | 1149 | BT_DBG("request for %s", hdev->name); |
1127 | 1150 | ||
1128 | status = mgmt_bredr_support(hdev); | 1151 | if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && |
1129 | if (status) | 1152 | !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) |
1130 | return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, | 1153 | return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, |
1131 | status); | 1154 | MGMT_STATUS_REJECTED); |
1132 | 1155 | ||
1133 | if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) | 1156 | if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) |
1134 | return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, | 1157 | return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, |
@@ -1228,6 +1251,12 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, | |||
1228 | 1251 | ||
1229 | hci_req_init(&req, hdev); | 1252 | hci_req_init(&req, hdev); |
1230 | 1253 | ||
1254 | /* The procedure for LE-only controllers is much simpler - just | ||
1255 | * update the advertising data. | ||
1256 | */ | ||
1257 | if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) | ||
1258 | goto update_ad; | ||
1259 | |||
1231 | scan = SCAN_PAGE; | 1260 | scan = SCAN_PAGE; |
1232 | 1261 | ||
1233 | if (cp->val) { | 1262 | if (cp->val) { |
@@ -1260,6 +1289,9 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, | |||
1260 | 1289 | ||
1261 | hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); | 1290 | hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); |
1262 | 1291 | ||
1292 | update_ad: | ||
1293 | update_adv_data(&req); | ||
1294 | |||
1263 | err = hci_req_run(&req, set_discoverable_complete); | 1295 | err = hci_req_run(&req, set_discoverable_complete); |
1264 | if (err < 0) | 1296 | if (err < 0) |
1265 | mgmt_pending_remove(cmd); | 1297 | mgmt_pending_remove(cmd); |
@@ -1451,8 +1483,17 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, | |||
1451 | 1483 | ||
1452 | hci_req_init(&req, hdev); | 1484 | hci_req_init(&req, hdev); |
1453 | 1485 | ||
1454 | if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) && | 1486 | /* If BR/EDR is not enabled and we disable advertising as a |
1455 | cp->val != test_bit(HCI_PSCAN, &hdev->flags)) { | 1487 | * by-product of disabling connectable, we need to update the |
1488 | * advertising flags. | ||
1489 | */ | ||
1490 | if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { | ||
1491 | if (!cp->val) { | ||
1492 | clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); | ||
1493 | clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | ||
1494 | } | ||
1495 | update_adv_data(&req); | ||
1496 | } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) { | ||
1456 | if (cp->val) { | 1497 | if (cp->val) { |
1457 | scan = SCAN_PAGE; | 1498 | scan = SCAN_PAGE; |
1458 | } else { | 1499 | } else { |
@@ -4348,6 +4389,7 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) | |||
4348 | * safe to unconditionally clear the flag. | 4389 | * safe to unconditionally clear the flag. |
4349 | */ | 4390 | */ |
4350 | clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); | 4391 | clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); |
4392 | clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | ||
4351 | 4393 | ||
4352 | hci_req_init(&req, hdev); | 4394 | hci_req_init(&req, hdev); |
4353 | if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { | 4395 | if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { |
@@ -4356,10 +4398,13 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) | |||
4356 | sizeof(scan), &scan); | 4398 | sizeof(scan), &scan); |
4357 | } | 4399 | } |
4358 | update_class(&req); | 4400 | update_class(&req); |
4401 | update_adv_data(&req); | ||
4359 | hci_req_run(&req, NULL); | 4402 | hci_req_run(&req, NULL); |
4360 | 4403 | ||
4361 | hdev->discov_timeout = 0; | 4404 | hdev->discov_timeout = 0; |
4362 | 4405 | ||
4406 | new_settings(hdev, NULL); | ||
4407 | |||
4363 | hci_dev_unlock(hdev); | 4408 | hci_dev_unlock(hdev); |
4364 | } | 4409 | } |
4365 | 4410 | ||
@@ -4374,13 +4419,26 @@ void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) | |||
4374 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) | 4419 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) |
4375 | return; | 4420 | return; |
4376 | 4421 | ||
4377 | if (discoverable) | 4422 | if (discoverable) { |
4378 | changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | 4423 | changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); |
4379 | else | 4424 | } else { |
4425 | clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); | ||
4380 | changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); | 4426 | changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); |
4427 | } | ||
4428 | |||
4429 | if (changed) { | ||
4430 | struct hci_request req; | ||
4431 | |||
4432 | /* In case this change in discoverable was triggered by | ||
4433 | * a disabling of connectable there could be a need to | ||
4434 | * update the advertising flags. | ||
4435 | */ | ||
4436 | hci_req_init(&req, hdev); | ||
4437 | update_adv_data(&req); | ||
4438 | hci_req_run(&req, NULL); | ||
4381 | 4439 | ||
4382 | if (changed) | ||
4383 | new_settings(hdev, NULL); | 4440 | new_settings(hdev, NULL); |
4441 | } | ||
4384 | } | 4442 | } |
4385 | 4443 | ||
4386 | void mgmt_connectable(struct hci_dev *hdev, u8 connectable) | 4444 | void mgmt_connectable(struct hci_dev *hdev, u8 connectable) |