| // 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(skb)) { |
| 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(skb)) { |
| 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(skb)) { |
| 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); |
| } |
| |
| #define BLUETOOTH_QUALITY_REPORT_EV 0x58 |
| struct bqr_data { |
| __u8 quality_report_id; |
| __u8 packet_type; |
| __le16 conn_handle; |
| __u8 conn_role; |
| __s8 tx_power_level; |
| __s8 rssi; |
| __u8 snr; |
| __u8 unused_afh_channel_count; |
| __u8 afh_select_unideal_channel_count; |
| __le16 lsto; |
| __le32 conn_piconet_clock; |
| __le32 retransmission_count; |
| __le32 no_rx_count; |
| __le32 nak_count; |
| __le32 last_tx_ack_timestamp; |
| __le32 flow_off_count; |
| __le32 last_flow_on_timestamp; |
| __le32 buffer_overflow_bytes; |
| __le32 buffer_underflow_bytes; |
| |
| /* Vendor Specific Parameter */ |
| __u8 vsp[0]; |
| } __packed; |
| |
| struct aosp_hci_vs_data { |
| __u8 code; |
| __u8 data[0]; |
| } __packed; |
| |
| bool aosp_is_quality_report_evt(struct sk_buff *skb) |
| { |
| struct aosp_hci_vs_data *ev; |
| |
| if (skb->len < sizeof(struct aosp_hci_vs_data)) |
| return false; |
| |
| ev = (struct aosp_hci_vs_data *)skb->data; |
| |
| return ev->code == BLUETOOTH_QUALITY_REPORT_EV; |
| } |
| |
| bool aosp_pull_quality_report_data(struct sk_buff *skb) |
| { |
| size_t bqr_data_len = sizeof(struct bqr_data); |
| |
| skb_pull(skb, sizeof(struct aosp_hci_vs_data)); |
| |
| /* skb->len is allowed to be larger than bqr_data_len to have |
| * the Vendor Specific Parameter (vsp) field. |
| */ |
| if (skb->len < bqr_data_len) { |
| BT_ERR("AOSP evt data len %u too short (%zu expected)", |
| skb->len, bqr_data_len); |
| return false; |
| } |
| |
| return true; |
| } |