| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (C) 2021 Intel Corporation | |
| */ | |
| #include <net/bluetooth/bluetooth.h> | |
| #include <net/bluetooth/hci_core.h> | |
| #include "aosp.h" | |
| /* Command complete parameters of LE_Get_Vendor_Capabilities_Command | |
| * The parameters grow over time. The base version that declares the | |
| * version_supported field is v0.95. Refer to | |
| * https://cs.android.com/android/platform/superproject/+/master:system/ | |
| * bt/gd/hci/controller.cc;l=452?q=le_get_vendor_capabilities_handler | |
| */ | |
| struct aosp_rp_le_get_vendor_capa { | |
| /* v0.95: 15 octets */ | |
| __u8 status; | |
| __u8 max_advt_instances; | |
| __u8 offloaded_resolution_of_private_address; | |
| __le16 total_scan_results_storage; | |
| __u8 max_irk_list_sz; | |
| __u8 filtering_support; | |
| __u8 max_filter; | |
| __u8 activity_energy_info_support; | |
| __le16 version_supported; | |
| __le16 total_num_of_advt_tracked; | |
| __u8 extended_scan_support; | |
| __u8 debug_logging_supported; | |
| /* v0.96: 16 octets */ | |
| __u8 le_address_generation_offloading_support; | |
| /* v0.98: 21 octets */ | |
| __le32 a2dp_source_offload_capability_mask; | |
| __u8 bluetooth_quality_report_support; | |
| /* v1.00: 25 octets */ | |
| __le32 dynamic_audio_buffer_support; | |
| } __packed; | |
| #define VENDOR_CAPA_BASE_SIZE 15 | |
| #define VENDOR_CAPA_0_98_SIZE 21 | |
| void aosp_do_open(struct hci_dev *hdev) | |
| { | |
| struct sk_buff *skb; | |
| struct aosp_rp_le_get_vendor_capa *rp; | |
| u16 version_supported; | |
| if (!hdev->aosp_capable) | |
| return; | |
| bt_dev_dbg(hdev, "Initialize AOSP extension"); | |
| /* LE Get Vendor Capabilities Command */ | |
| skb = __hci_cmd_sync(hdev, hci_opcode_pack(0x3f, 0x153), 0, NULL, | |
| HCI_CMD_TIMEOUT); | |
| if (IS_ERR_OR_NULL(skb)) { | |
| if (!skb) | |
| skb = ERR_PTR(-EIO); | |
| bt_dev_err(hdev, "AOSP get vendor capabilities (%ld)", | |
| PTR_ERR(skb)); | |
| return; | |
| } | |
| /* A basic length check */ | |
| if (skb->len < VENDOR_CAPA_BASE_SIZE) | |
| goto length_error; | |
| rp = (struct aosp_rp_le_get_vendor_capa *)skb->data; | |
| version_supported = le16_to_cpu(rp->version_supported); | |
| /* AOSP displays the version number like v0.98, v1.00, etc. */ | |
| bt_dev_info(hdev, "AOSP extensions version v%u.%02u", | |
| version_supported >> 8, version_supported & 0xff); | |
| /* Do not support very old versions. */ | |
| if (version_supported < 95) { | |
| bt_dev_warn(hdev, "AOSP capabilities version %u too old", | |
| version_supported); | |
| goto done; | |
| } | |
| if (version_supported < 98) { | |
| bt_dev_warn(hdev, "AOSP quality report is not supported"); | |
| goto done; | |
| } | |
| if (skb->len < VENDOR_CAPA_0_98_SIZE) | |
| goto length_error; | |
| /* The bluetooth_quality_report_support is defined at version | |
| * v0.98. Refer to | |
| * https://cs.android.com/android/platform/superproject/+/ | |
| * master:system/bt/gd/hci/controller.cc;l=477 | |
| */ | |
| if (rp->bluetooth_quality_report_support) { | |
| hdev->aosp_quality_report = true; | |
| bt_dev_info(hdev, "AOSP quality report is supported"); | |
| } | |
| goto done; | |
| length_error: | |
| bt_dev_err(hdev, "AOSP capabilities length %d too short", skb->len); | |
| done: | |
| kfree_skb(skb); | |
| } | |
| void aosp_do_close(struct hci_dev *hdev) | |
| { | |
| if (!hdev->aosp_capable) | |
| return; | |
| bt_dev_dbg(hdev, "Cleanup of AOSP extension"); | |
| } | |
| /* BQR command */ | |
| #define BQR_OPCODE hci_opcode_pack(0x3f, 0x015e) | |
| /* BQR report action */ | |
| #define REPORT_ACTION_ADD 0x00 | |
| #define REPORT_ACTION_DELETE 0x01 | |
| #define REPORT_ACTION_CLEAR 0x02 | |
| /* BQR event masks */ | |
| #define QUALITY_MONITORING BIT(0) | |
| #define APPRAOCHING_LSTO BIT(1) | |
| #define A2DP_AUDIO_CHOPPY BIT(2) | |
| #define SCO_VOICE_CHOPPY BIT(3) | |
| #define DEFAULT_BQR_EVENT_MASK (QUALITY_MONITORING | APPRAOCHING_LSTO | \ | |
| A2DP_AUDIO_CHOPPY | SCO_VOICE_CHOPPY) | |
| /* Reporting at milliseconds so as not to stress the controller too much. | |
| * Range: 0 ~ 65535 ms | |
| */ | |
| #define DEFALUT_REPORT_INTERVAL_MS 5000 | |
| struct aosp_bqr_cp { | |
| __u8 report_action; | |
| __u32 event_mask; | |
| __u16 min_report_interval; | |
| } __packed; | |
| static int enable_quality_report(struct hci_dev *hdev) | |
| { | |
| struct sk_buff *skb; | |
| struct aosp_bqr_cp cp; | |
| cp.report_action = REPORT_ACTION_ADD; | |
| cp.event_mask = DEFAULT_BQR_EVENT_MASK; | |
| cp.min_report_interval = DEFALUT_REPORT_INTERVAL_MS; | |
| skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, | |
| HCI_CMD_TIMEOUT); | |
| if (IS_ERR_OR_NULL(skb)) { | |
| if (!skb) | |
| skb = ERR_PTR(-EIO); | |
| bt_dev_err(hdev, "Enabling Android BQR failed (%ld)", | |
| PTR_ERR(skb)); | |
| return PTR_ERR(skb); | |
| } | |
| kfree_skb(skb); | |
| return 0; | |
| } | |
| static int disable_quality_report(struct hci_dev *hdev) | |
| { | |
| struct sk_buff *skb; | |
| struct aosp_bqr_cp cp = { 0 }; | |
| cp.report_action = REPORT_ACTION_CLEAR; | |
| skb = __hci_cmd_sync(hdev, BQR_OPCODE, sizeof(cp), &cp, | |
| HCI_CMD_TIMEOUT); | |
| if (IS_ERR_OR_NULL(skb)) { | |
| if (!skb) | |
| skb = ERR_PTR(-EIO); | |
| bt_dev_err(hdev, "Disabling Android BQR failed (%ld)", | |
| PTR_ERR(skb)); | |
| return PTR_ERR(skb); | |
| } | |
| kfree_skb(skb); | |
| return 0; | |
| } | |
| bool aosp_has_quality_report(struct hci_dev *hdev) | |
| { | |
| return hdev->aosp_quality_report; | |
| } | |
| int aosp_set_quality_report(struct hci_dev *hdev, bool enable) | |
| { | |
| if (!aosp_has_quality_report(hdev)) | |
| return -EOPNOTSUPP; | |
| bt_dev_dbg(hdev, "quality report enable %d", enable); | |
| /* Enable or disable the quality report feature. */ | |
| if (enable) | |
| return enable_quality_report(hdev); | |
| else | |
| return disable_quality_report(hdev); | |
| } |