diff options
-rw-r--r-- | include/net/bluetooth/mgmt.h | 19 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 26 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 182 |
3 files changed, 227 insertions, 0 deletions
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1c93d6e83a6c..0916e203e5d9 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h | |||
@@ -629,6 +629,25 @@ struct mgmt_rp_get_phy_confguration { | |||
629 | #define MGMT_PHY_LE_CODED_TX 0x00002000 | 629 | #define MGMT_PHY_LE_CODED_TX 0x00002000 |
630 | #define MGMT_PHY_LE_CODED_RX 0x00004000 | 630 | #define MGMT_PHY_LE_CODED_RX 0x00004000 |
631 | 631 | ||
632 | #define MGMT_PHY_BREDR_MASK (MGMT_PHY_BR_1M_1SLOT | MGMT_PHY_BR_1M_3SLOT | \ | ||
633 | MGMT_PHY_BR_1M_5SLOT | MGMT_PHY_EDR_2M_1SLOT | \ | ||
634 | MGMT_PHY_EDR_2M_3SLOT | MGMT_PHY_EDR_2M_5SLOT | \ | ||
635 | MGMT_PHY_EDR_3M_1SLOT | MGMT_PHY_EDR_3M_3SLOT | \ | ||
636 | MGMT_PHY_EDR_3M_5SLOT) | ||
637 | #define MGMT_PHY_LE_MASK (MGMT_PHY_LE_1M_TX | MGMT_PHY_LE_1M_RX | \ | ||
638 | MGMT_PHY_LE_2M_TX | MGMT_PHY_LE_2M_RX | \ | ||
639 | MGMT_PHY_LE_CODED_TX | MGMT_PHY_LE_CODED_RX) | ||
640 | #define MGMT_PHY_LE_TX_MASK (MGMT_PHY_LE_1M_TX | MGMT_PHY_LE_2M_TX | \ | ||
641 | MGMT_PHY_LE_CODED_TX) | ||
642 | #define MGMT_PHY_LE_RX_MASK (MGMT_PHY_LE_1M_RX | MGMT_PHY_LE_2M_RX | \ | ||
643 | MGMT_PHY_LE_CODED_RX) | ||
644 | |||
645 | #define MGMT_OP_SET_PHY_CONFIGURATION 0x0045 | ||
646 | struct mgmt_cp_set_phy_confguration { | ||
647 | __le32 selected_phys; | ||
648 | } __packed; | ||
649 | #define MGMT_SET_PHY_CONFIGURATION_SIZE 4 | ||
650 | |||
632 | #define MGMT_EV_CMD_COMPLETE 0x0001 | 651 | #define MGMT_EV_CMD_COMPLETE 0x0001 |
633 | struct mgmt_ev_cmd_complete { | 652 | struct mgmt_ev_cmd_complete { |
634 | __le16 opcode; | 653 | __le16 opcode; |
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 68192152c23b..694231541a4c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -1042,6 +1042,28 @@ static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb) | |||
1042 | hci_dev_unlock(hdev); | 1042 | hci_dev_unlock(hdev); |
1043 | } | 1043 | } |
1044 | 1044 | ||
1045 | static void hci_cc_le_set_default_phy(struct hci_dev *hdev, struct sk_buff *skb) | ||
1046 | { | ||
1047 | __u8 status = *((__u8 *) skb->data); | ||
1048 | struct hci_cp_le_set_default_phy *cp; | ||
1049 | |||
1050 | BT_DBG("%s status 0x%2.2x", hdev->name, status); | ||
1051 | |||
1052 | if (status) | ||
1053 | return; | ||
1054 | |||
1055 | cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_DEFAULT_PHY); | ||
1056 | if (!cp) | ||
1057 | return; | ||
1058 | |||
1059 | hci_dev_lock(hdev); | ||
1060 | |||
1061 | hdev->le_tx_def_phys = cp->tx_phys; | ||
1062 | hdev->le_rx_def_phys = cp->rx_phys; | ||
1063 | |||
1064 | hci_dev_unlock(hdev); | ||
1065 | } | ||
1066 | |||
1045 | static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) | 1067 | static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) |
1046 | { | 1068 | { |
1047 | __u8 *sent, status = *((__u8 *) skb->data); | 1069 | __u8 *sent, status = *((__u8 *) skb->data); |
@@ -3163,6 +3185,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, | |||
3163 | hci_cc_le_set_ext_scan_enable(hdev, skb); | 3185 | hci_cc_le_set_ext_scan_enable(hdev, skb); |
3164 | break; | 3186 | break; |
3165 | 3187 | ||
3188 | case HCI_OP_LE_SET_DEFAULT_PHY: | ||
3189 | hci_cc_le_set_default_phy(hdev, skb); | ||
3190 | break; | ||
3191 | |||
3166 | default: | 3192 | default: |
3167 | BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); | 3193 | BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode); |
3168 | break; | 3194 | break; |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c8c3b39fa9f2..7cd6a37a63ee 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -3328,6 +3328,187 @@ static int get_phy_configuration(struct sock *sk, struct hci_dev *hdev, | |||
3328 | &rp, sizeof(rp)); | 3328 | &rp, sizeof(rp)); |
3329 | } | 3329 | } |
3330 | 3330 | ||
3331 | static void set_default_phy_complete(struct hci_dev *hdev, u8 status, | ||
3332 | u16 opcode, struct sk_buff *skb) | ||
3333 | { | ||
3334 | struct mgmt_cp_set_phy_confguration *cp; | ||
3335 | struct mgmt_pending_cmd *cmd; | ||
3336 | |||
3337 | BT_DBG("status 0x%02x", status); | ||
3338 | |||
3339 | hci_dev_lock(hdev); | ||
3340 | |||
3341 | cmd = pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev); | ||
3342 | if (!cmd) | ||
3343 | goto unlock; | ||
3344 | |||
3345 | cp = cmd->param; | ||
3346 | |||
3347 | if (status) { | ||
3348 | mgmt_cmd_status(cmd->sk, hdev->id, | ||
3349 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3350 | mgmt_status(status)); | ||
3351 | } else { | ||
3352 | mgmt_cmd_complete(cmd->sk, hdev->id, | ||
3353 | MGMT_OP_SET_PHY_CONFIGURATION, 0, | ||
3354 | NULL, 0); | ||
3355 | } | ||
3356 | |||
3357 | mgmt_pending_remove(cmd); | ||
3358 | |||
3359 | unlock: | ||
3360 | hci_dev_unlock(hdev); | ||
3361 | } | ||
3362 | |||
3363 | static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev, | ||
3364 | void *data, u16 len) | ||
3365 | { | ||
3366 | struct mgmt_cp_set_phy_confguration *cp = data; | ||
3367 | struct hci_cp_le_set_default_phy cp_phy; | ||
3368 | struct mgmt_pending_cmd *cmd; | ||
3369 | struct hci_request req; | ||
3370 | u32 selected_phys, configurable_phys, supported_phys, unconfigure_phys; | ||
3371 | u16 pkt_type = (HCI_DH1 | HCI_DM1); | ||
3372 | int err; | ||
3373 | |||
3374 | BT_DBG("sock %p %s", sk, hdev->name); | ||
3375 | |||
3376 | configurable_phys = get_configurable_phys(hdev); | ||
3377 | supported_phys = get_supported_phys(hdev); | ||
3378 | selected_phys = __le32_to_cpu(cp->selected_phys); | ||
3379 | |||
3380 | if (selected_phys & ~supported_phys) | ||
3381 | return mgmt_cmd_status(sk, hdev->id, | ||
3382 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3383 | MGMT_STATUS_INVALID_PARAMS); | ||
3384 | |||
3385 | unconfigure_phys = supported_phys & ~configurable_phys; | ||
3386 | |||
3387 | if ((selected_phys & unconfigure_phys) != unconfigure_phys) | ||
3388 | return mgmt_cmd_status(sk, hdev->id, | ||
3389 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3390 | MGMT_STATUS_INVALID_PARAMS); | ||
3391 | |||
3392 | if (selected_phys == get_selected_phys(hdev)) | ||
3393 | return mgmt_cmd_complete(sk, hdev->id, | ||
3394 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3395 | 0, NULL, 0); | ||
3396 | |||
3397 | hci_dev_lock(hdev); | ||
3398 | |||
3399 | if (!hdev_is_powered(hdev)) { | ||
3400 | err = mgmt_cmd_status(sk, hdev->id, | ||
3401 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3402 | MGMT_STATUS_REJECTED); | ||
3403 | goto unlock; | ||
3404 | } | ||
3405 | |||
3406 | if (pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev)) { | ||
3407 | err = mgmt_cmd_status(sk, hdev->id, | ||
3408 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3409 | MGMT_STATUS_BUSY); | ||
3410 | goto unlock; | ||
3411 | } | ||
3412 | |||
3413 | if (selected_phys & MGMT_PHY_BR_1M_3SLOT) | ||
3414 | pkt_type |= (HCI_DH3 | HCI_DM3); | ||
3415 | else | ||
3416 | pkt_type &= ~(HCI_DH3 | HCI_DM3); | ||
3417 | |||
3418 | if (selected_phys & MGMT_PHY_BR_1M_5SLOT) | ||
3419 | pkt_type |= (HCI_DH5 | HCI_DM5); | ||
3420 | else | ||
3421 | pkt_type &= ~(HCI_DH5 | HCI_DM5); | ||
3422 | |||
3423 | if (selected_phys & MGMT_PHY_EDR_2M_1SLOT) | ||
3424 | pkt_type &= ~HCI_2DH1; | ||
3425 | else | ||
3426 | pkt_type |= HCI_2DH1; | ||
3427 | |||
3428 | if (selected_phys & MGMT_PHY_EDR_2M_3SLOT) | ||
3429 | pkt_type &= ~HCI_2DH3; | ||
3430 | else | ||
3431 | pkt_type |= HCI_2DH3; | ||
3432 | |||
3433 | if (selected_phys & MGMT_PHY_EDR_2M_5SLOT) | ||
3434 | pkt_type &= ~HCI_2DH5; | ||
3435 | else | ||
3436 | pkt_type |= HCI_2DH5; | ||
3437 | |||
3438 | if (selected_phys & MGMT_PHY_EDR_3M_1SLOT) | ||
3439 | pkt_type &= ~HCI_3DH1; | ||
3440 | else | ||
3441 | pkt_type |= HCI_3DH1; | ||
3442 | |||
3443 | if (selected_phys & MGMT_PHY_EDR_3M_3SLOT) | ||
3444 | pkt_type &= ~HCI_3DH3; | ||
3445 | else | ||
3446 | pkt_type |= HCI_3DH3; | ||
3447 | |||
3448 | if (selected_phys & MGMT_PHY_EDR_3M_5SLOT) | ||
3449 | pkt_type &= ~HCI_3DH5; | ||
3450 | else | ||
3451 | pkt_type |= HCI_3DH5; | ||
3452 | |||
3453 | if (pkt_type != hdev->pkt_type) | ||
3454 | hdev->pkt_type = pkt_type; | ||
3455 | |||
3456 | if ((selected_phys & MGMT_PHY_LE_MASK) == | ||
3457 | (get_selected_phys(hdev) & MGMT_PHY_LE_MASK)) { | ||
3458 | err = mgmt_cmd_complete(sk, hdev->id, | ||
3459 | MGMT_OP_SET_PHY_CONFIGURATION, | ||
3460 | 0, NULL, 0); | ||
3461 | |||
3462 | goto unlock; | ||
3463 | } | ||
3464 | |||
3465 | cmd = mgmt_pending_add(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data, | ||
3466 | len); | ||
3467 | if (!cmd) { | ||
3468 | err = -ENOMEM; | ||
3469 | goto unlock; | ||
3470 | } | ||
3471 | |||
3472 | hci_req_init(&req, hdev); | ||
3473 | |||
3474 | memset(&cp_phy, 0, sizeof(cp_phy)); | ||
3475 | |||
3476 | if (!(selected_phys & MGMT_PHY_LE_TX_MASK)) | ||
3477 | cp_phy.all_phys |= 0x01; | ||
3478 | |||
3479 | if (!(selected_phys & MGMT_PHY_LE_RX_MASK)) | ||
3480 | cp_phy.all_phys |= 0x02; | ||
3481 | |||
3482 | if (selected_phys & MGMT_PHY_LE_1M_TX) | ||
3483 | cp_phy.tx_phys |= HCI_LE_SET_PHY_1M; | ||
3484 | |||
3485 | if (selected_phys & MGMT_PHY_LE_2M_TX) | ||
3486 | cp_phy.tx_phys |= HCI_LE_SET_PHY_2M; | ||
3487 | |||
3488 | if (selected_phys & MGMT_PHY_LE_CODED_TX) | ||
3489 | cp_phy.tx_phys |= HCI_LE_SET_PHY_CODED; | ||
3490 | |||
3491 | if (selected_phys & MGMT_PHY_LE_1M_RX) | ||
3492 | cp_phy.rx_phys |= HCI_LE_SET_PHY_1M; | ||
3493 | |||
3494 | if (selected_phys & MGMT_PHY_LE_2M_RX) | ||
3495 | cp_phy.rx_phys |= HCI_LE_SET_PHY_2M; | ||
3496 | |||
3497 | if (selected_phys & MGMT_PHY_LE_CODED_RX) | ||
3498 | cp_phy.rx_phys |= HCI_LE_SET_PHY_CODED; | ||
3499 | |||
3500 | hci_req_add(&req, HCI_OP_LE_SET_DEFAULT_PHY, sizeof(cp_phy), &cp_phy); | ||
3501 | |||
3502 | err = hci_req_run_skb(&req, set_default_phy_complete); | ||
3503 | if (err < 0) | ||
3504 | mgmt_pending_remove(cmd); | ||
3505 | |||
3506 | unlock: | ||
3507 | hci_dev_unlock(hdev); | ||
3508 | |||
3509 | return err; | ||
3510 | } | ||
3511 | |||
3331 | static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status, | 3512 | static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status, |
3332 | u16 opcode, struct sk_buff *skb) | 3513 | u16 opcode, struct sk_buff *skb) |
3333 | { | 3514 | { |
@@ -6689,6 +6870,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { | |||
6689 | HCI_MGMT_UNTRUSTED }, | 6870 | HCI_MGMT_UNTRUSTED }, |
6690 | { set_appearance, MGMT_SET_APPEARANCE_SIZE }, | 6871 | { set_appearance, MGMT_SET_APPEARANCE_SIZE }, |
6691 | { get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE }, | 6872 | { get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE }, |
6873 | { set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE }, | ||
6692 | }; | 6874 | }; |
6693 | 6875 | ||
6694 | void mgmt_index_added(struct hci_dev *hdev) | 6876 | void mgmt_index_added(struct hci_dev *hdev) |