aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2013-10-02 06:43:14 -0400
committerMarcel Holtmann <marcel@holtmann.org>2013-10-02 06:48:28 -0400
commit0663ca2a032eea12480a8f86fe08bef9d72f8faf (patch)
treeb9d2becadbf2cb19b9ff8b07be31d0e707476e6f
parent56f8790102f48a4959a729ecdccff332591014e1 (diff)
Bluetooth: Add a new mgmt_set_bredr command
This patch introduces a new mgmt command for enabling/disabling BR/EDR functionality. This can be convenient when one wants to make a dual-mode controller behave like a single-mode one. The command is only available for dual-mode controllers and requires that LE is enabled before using it. The BR/EDR setting can be enabled at any point, however disabling it requires the controller to be powered off (otherwise a "rejected" response will be sent). Disabling the BR/EDR setting will automatically disable all other BR/EDR related settings. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r--include/net/bluetooth/mgmt.h2
-rw-r--r--net/bluetooth/hci_event.c5
-rw-r--r--net/bluetooth/mgmt.c120
3 files changed, 127 insertions, 0 deletions
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 421d7633a91f..7347df800a2e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -354,6 +354,8 @@ struct mgmt_cp_set_device_id {
354 354
355#define MGMT_OP_SET_ADVERTISING 0x0029 355#define MGMT_OP_SET_ADVERTISING 0x0029
356 356
357#define MGMT_OP_SET_BREDR 0x002A
358
357#define MGMT_EV_CMD_COMPLETE 0x0001 359#define MGMT_EV_CMD_COMPLETE 0x0001
358struct mgmt_ev_cmd_complete { 360struct mgmt_ev_cmd_complete {
359 __le16 opcode; 361 __le16 opcode;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index d171c04bddbd..4785ab0795f5 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -297,6 +297,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
297 goto done; 297 goto done;
298 } 298 }
299 299
300 /* We need to ensure that we set this back on if someone changed
301 * the scan mode through a raw HCI socket.
302 */
303 set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
304
300 old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); 305 old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
301 old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); 306 old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
302 307
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index e1c41b0b7a75..dcce0cf1d7cc 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -75,6 +75,7 @@ static const u16 mgmt_commands[] = {
75 MGMT_OP_UNBLOCK_DEVICE, 75 MGMT_OP_UNBLOCK_DEVICE,
76 MGMT_OP_SET_DEVICE_ID, 76 MGMT_OP_SET_DEVICE_ID,
77 MGMT_OP_SET_ADVERTISING, 77 MGMT_OP_SET_ADVERTISING,
78 MGMT_OP_SET_BREDR,
78}; 79};
79 80
80static const u16 mgmt_events[] = { 81static const u16 mgmt_events[] = {
@@ -3337,6 +3338,121 @@ unlock:
3337 return err; 3338 return err;
3338} 3339}
3339 3340
3341static void set_bredr_complete(struct hci_dev *hdev, u8 status)
3342{
3343 struct pending_cmd *cmd;
3344
3345 BT_DBG("status 0x%02x", status);
3346
3347 hci_dev_lock(hdev);
3348
3349 cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
3350 if (!cmd)
3351 goto unlock;
3352
3353 if (status) {
3354 u8 mgmt_err = mgmt_status(status);
3355
3356 /* We need to restore the flag if related HCI commands
3357 * failed.
3358 */
3359 clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
3360
3361 cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
3362 } else {
3363 send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev);
3364 new_settings(hdev, cmd->sk);
3365 }
3366
3367 mgmt_pending_remove(cmd);
3368
3369unlock:
3370 hci_dev_unlock(hdev);
3371}
3372
3373static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
3374{
3375 struct mgmt_mode *cp = data;
3376 struct pending_cmd *cmd;
3377 struct hci_request req;
3378 int err;
3379
3380 BT_DBG("request for %s", hdev->name);
3381
3382 if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev))
3383 return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
3384 MGMT_STATUS_NOT_SUPPORTED);
3385
3386 if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
3387 return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
3388 MGMT_STATUS_REJECTED);
3389
3390 if (cp->val != 0x00 && cp->val != 0x01)
3391 return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
3392 MGMT_STATUS_INVALID_PARAMS);
3393
3394 hci_dev_lock(hdev);
3395
3396 if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
3397 err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
3398 goto unlock;
3399 }
3400
3401 if (!hdev_is_powered(hdev)) {
3402 if (!cp->val) {
3403 clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
3404 clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
3405 clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
3406 clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
3407 clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
3408 clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
3409 }
3410
3411 change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
3412
3413 err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
3414 if (err < 0)
3415 goto unlock;
3416
3417 err = new_settings(hdev, sk);
3418 goto unlock;
3419 }
3420
3421 /* Reject disabling when powered on */
3422 if (!cp->val) {
3423 err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
3424 MGMT_STATUS_REJECTED);
3425 goto unlock;
3426 }
3427
3428 if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
3429 err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
3430 MGMT_STATUS_BUSY);
3431 goto unlock;
3432 }
3433
3434 cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len);
3435 if (!cmd) {
3436 err = -ENOMEM;
3437 goto unlock;
3438 }
3439
3440 /* We need to flip the bit already here so that hci_update_ad
3441 * generates the correct flags.
3442 */
3443 set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
3444
3445 hci_req_init(&req, hdev);
3446 hci_update_ad(&req);
3447 err = hci_req_run(&req, set_bredr_complete);
3448 if (err < 0)
3449 mgmt_pending_remove(cmd);
3450
3451unlock:
3452 hci_dev_unlock(hdev);
3453 return err;
3454}
3455
3340static bool ltk_is_valid(struct mgmt_ltk_info *key) 3456static bool ltk_is_valid(struct mgmt_ltk_info *key)
3341{ 3457{
3342 if (key->authenticated != 0x00 && key->authenticated != 0x01) 3458 if (key->authenticated != 0x00 && key->authenticated != 0x01)
@@ -3452,6 +3568,7 @@ static const struct mgmt_handler {
3452 { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, 3568 { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
3453 { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, 3569 { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
3454 { set_advertising, false, MGMT_SETTING_SIZE }, 3570 { set_advertising, false, MGMT_SETTING_SIZE },
3571 { set_bredr, false, MGMT_SETTING_SIZE },
3455}; 3572};
3456 3573
3457 3574
@@ -3633,6 +3750,9 @@ static int powered_update_hci(struct hci_dev *hdev)
3633 cp.simul != lmp_host_le_br_capable(hdev)) 3750 cp.simul != lmp_host_le_br_capable(hdev))
3634 hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, 3751 hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
3635 sizeof(cp), &cp); 3752 sizeof(cp), &cp);
3753
3754 /* In case BR/EDR was toggled during the AUTO_OFF phase */
3755 hci_update_ad(&req);
3636 } 3756 }
3637 3757
3638 if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) { 3758 if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {