diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2013-10-02 06:43:14 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2013-10-02 06:48:28 -0400 |
commit | 0663ca2a032eea12480a8f86fe08bef9d72f8faf (patch) | |
tree | b9d2becadbf2cb19b9ff8b07be31d0e707476e6f | |
parent | 56f8790102f48a4959a729ecdccff332591014e1 (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.h | 2 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 5 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 120 |
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 |
358 | struct mgmt_ev_cmd_complete { | 360 | struct 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 | ||
80 | static const u16 mgmt_events[] = { | 81 | static const u16 mgmt_events[] = { |
@@ -3337,6 +3338,121 @@ unlock: | |||
3337 | return err; | 3338 | return err; |
3338 | } | 3339 | } |
3339 | 3340 | ||
3341 | static 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 | |||
3369 | unlock: | ||
3370 | hci_dev_unlock(hdev); | ||
3371 | } | ||
3372 | |||
3373 | static 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 | |||
3451 | unlock: | ||
3452 | hci_dev_unlock(hdev); | ||
3453 | return err; | ||
3454 | } | ||
3455 | |||
3340 | static bool ltk_is_valid(struct mgmt_ltk_info *key) | 3456 | static 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)) { |