// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 Intel Corporation */ #include #include #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 verion 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); }