diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2014-07-04 12:11:55 -0400 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@intel.com> | 2014-07-04 14:10:30 -0400 |
commit | dbece37a3233933ec89f77f04049e13ad9b29634 (patch) | |
tree | 911e51a4adacd04e71ad34d881efed01f3e3c9aa /net/bluetooth/mgmt.c | |
parent | 0ad184ef5828542e3e73cce8a875fb4e029725b7 (diff) |
Bluetooth: Add support for Set External Configuration management command
The Set External Configuration management command allows for switching
between configured and unconfigured start if HCI_QURIK_EXTERNAL_CONFIG
is set by the transport driver.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 77 |
1 files changed, 75 insertions, 2 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d2d0e051e4f2..91bccef63f30 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -91,6 +91,7 @@ static const u16 mgmt_commands[] = { | |||
91 | MGMT_OP_LOAD_CONN_PARAM, | 91 | MGMT_OP_LOAD_CONN_PARAM, |
92 | MGMT_OP_READ_UNCONF_INDEX_LIST, | 92 | MGMT_OP_READ_UNCONF_INDEX_LIST, |
93 | MGMT_OP_READ_CONFIG_INFO, | 93 | MGMT_OP_READ_CONFIG_INFO, |
94 | MGMT_OP_SET_EXTERNAL_CONFIG, | ||
94 | }; | 95 | }; |
95 | 96 | ||
96 | static const u16 mgmt_events[] = { | 97 | static const u16 mgmt_events[] = { |
@@ -441,11 +442,25 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, | |||
441 | return err; | 442 | return err; |
442 | } | 443 | } |
443 | 444 | ||
445 | static bool is_configured(struct hci_dev *hdev) | ||
446 | { | ||
447 | if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && | ||
448 | !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) | ||
449 | return false; | ||
450 | |||
451 | if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && | ||
452 | !bacmp(&hdev->public_addr, BDADDR_ANY)) | ||
453 | return false; | ||
454 | |||
455 | return true; | ||
456 | } | ||
457 | |||
444 | static __le32 get_missing_options(struct hci_dev *hdev) | 458 | static __le32 get_missing_options(struct hci_dev *hdev) |
445 | { | 459 | { |
446 | u32 options = 0; | 460 | u32 options = 0; |
447 | 461 | ||
448 | if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) | 462 | if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && |
463 | !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) | ||
449 | options |= MGMT_OPTION_EXTERNAL_CONFIG; | 464 | options |= MGMT_OPTION_EXTERNAL_CONFIG; |
450 | 465 | ||
451 | if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && | 466 | if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && |
@@ -455,6 +470,14 @@ static __le32 get_missing_options(struct hci_dev *hdev) | |||
455 | return cpu_to_le32(options); | 470 | return cpu_to_le32(options); |
456 | } | 471 | } |
457 | 472 | ||
473 | static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) | ||
474 | { | ||
475 | __le32 options = get_missing_options(hdev); | ||
476 | |||
477 | return cmd_complete(sk, hdev->id, opcode, 0, &options, | ||
478 | sizeof(options)); | ||
479 | } | ||
480 | |||
458 | static int read_config_info(struct sock *sk, struct hci_dev *hdev, | 481 | static int read_config_info(struct sock *sk, struct hci_dev *hdev, |
459 | void *data, u16 data_len) | 482 | void *data, u16 data_len) |
460 | { | 483 | { |
@@ -5355,6 +5378,54 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, | |||
5355 | return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); | 5378 | return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); |
5356 | } | 5379 | } |
5357 | 5380 | ||
5381 | static int set_external_config(struct sock *sk, struct hci_dev *hdev, | ||
5382 | void *data, u16 len) | ||
5383 | { | ||
5384 | struct mgmt_cp_set_external_config *cp = data; | ||
5385 | bool changed; | ||
5386 | int err; | ||
5387 | |||
5388 | BT_DBG("%s", hdev->name); | ||
5389 | |||
5390 | if (hdev_is_powered(hdev)) | ||
5391 | return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, | ||
5392 | MGMT_STATUS_REJECTED); | ||
5393 | |||
5394 | if (cp->config != 0x00 && cp->config != 0x01) | ||
5395 | return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, | ||
5396 | MGMT_STATUS_INVALID_PARAMS); | ||
5397 | |||
5398 | if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) | ||
5399 | return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, | ||
5400 | MGMT_STATUS_NOT_SUPPORTED); | ||
5401 | |||
5402 | hci_dev_lock(hdev); | ||
5403 | |||
5404 | if (cp->config) | ||
5405 | changed = !test_and_set_bit(HCI_EXT_CONFIGURED, | ||
5406 | &hdev->dev_flags); | ||
5407 | else | ||
5408 | changed = test_and_clear_bit(HCI_EXT_CONFIGURED, | ||
5409 | &hdev->dev_flags); | ||
5410 | |||
5411 | err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev); | ||
5412 | if (err < 0) | ||
5413 | goto unlock; | ||
5414 | |||
5415 | if (!changed) | ||
5416 | goto unlock; | ||
5417 | |||
5418 | if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { | ||
5419 | mgmt_index_removed(hdev); | ||
5420 | change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); | ||
5421 | mgmt_index_added(hdev); | ||
5422 | } | ||
5423 | |||
5424 | unlock: | ||
5425 | hci_dev_unlock(hdev); | ||
5426 | return err; | ||
5427 | } | ||
5428 | |||
5358 | static const struct mgmt_handler { | 5429 | static const struct mgmt_handler { |
5359 | int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, | 5430 | int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, |
5360 | u16 data_len); | 5431 | u16 data_len); |
@@ -5417,6 +5488,7 @@ static const struct mgmt_handler { | |||
5417 | { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, | 5488 | { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, |
5418 | { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, | 5489 | { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, |
5419 | { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, | 5490 | { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, |
5491 | { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, | ||
5420 | }; | 5492 | }; |
5421 | 5493 | ||
5422 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 5494 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
@@ -5469,7 +5541,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
5469 | } | 5541 | } |
5470 | 5542 | ||
5471 | if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && | 5543 | if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && |
5472 | opcode != MGMT_OP_READ_CONFIG_INFO) { | 5544 | opcode != MGMT_OP_READ_CONFIG_INFO && |
5545 | opcode != MGMT_OP_SET_EXTERNAL_CONFIG) { | ||
5473 | err = cmd_status(sk, index, opcode, | 5546 | err = cmd_status(sk, index, opcode, |
5474 | MGMT_STATUS_INVALID_INDEX); | 5547 | MGMT_STATUS_INVALID_INDEX); |
5475 | goto done; | 5548 | goto done; |