|  | // 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 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); | 
|  | } |