]> git.dujemihanovic.xyz Git - linux.git/commitdiff
Bluetooth: aosp: Support AOSP Bluetooth Quality Report
authorJoseph Hwang <josephsih@chromium.org>
Tue, 2 Nov 2021 07:19:29 +0000 (15:19 +0800)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 2 Nov 2021 18:37:52 +0000 (19:37 +0100)
This patch adds the support of the AOSP Bluetooth Quality Report
(BQR) events.

Multiple vendors have supported the AOSP Bluetooth Quality Report.
When a Bluetooth controller supports the capability, it can enable
the aosp capability through hci_set_aosp_capable. Then hci_core will
set up the hdev->aosp_set_quality_report callback through aosp_do_open
if the controller responds to support the quality report capability.

Note that Intel also supports a distinct telemetry quality report
specification. Intel sets up the hdev->set_quality_report callback
in the btusb driver module.

Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Signed-off-by: Joseph Hwang <josephsih@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
net/bluetooth/aosp.c
net/bluetooth/aosp.h
net/bluetooth/mgmt.c

index 871e889b7e17953ca13497d8fd67e0cda7fb4966..432ae3aac9e31d60375f60f81a604f8fed3c944c 100644 (file)
@@ -112,3 +112,90 @@ void aosp_do_close(struct hci_dev *hdev)
 
        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);
+}
index 328fc6d39f70300dcc04d4b8bc44c7945ec9cbcf..2fd8886d51b26a9fbffa09a626c5cad501e8b781 100644 (file)
@@ -8,9 +8,22 @@
 void aosp_do_open(struct hci_dev *hdev);
 void aosp_do_close(struct hci_dev *hdev);
 
+bool aosp_has_quality_report(struct hci_dev *hdev);
+int aosp_set_quality_report(struct hci_dev *hdev, bool enable);
+
 #else
 
 static inline void aosp_do_open(struct hci_dev *hdev) {}
 static inline void aosp_do_close(struct hci_dev *hdev) {}
 
+static inline bool aosp_has_quality_report(struct hci_dev *hdev)
+{
+       return false;
+}
+
+static inline int aosp_set_quality_report(struct hci_dev *hdev, bool enable)
+{
+       return -EOPNOTSUPP;
+}
+
 #endif
index a7d35c138713222064a9b588e42e3941eec3deca..06384d7619282ef75aca0bb1151c6831fc7e3ba0 100644 (file)
@@ -39,6 +39,7 @@
 #include "mgmt_config.h"
 #include "msft.h"
 #include "eir.h"
+#include "aosp.h"
 
 #define MGMT_VERSION   1
 #define MGMT_REVISION  21
@@ -3934,7 +3935,8 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
                idx++;
        }
 
-       if (hdev && hdev->set_quality_report) {
+       if (hdev && (aosp_has_quality_report(hdev) ||
+                    hdev->set_quality_report)) {
                if (hci_dev_test_flag(hdev, HCI_QUALITY_REPORT))
                        flags = BIT(0);
                else
@@ -4198,7 +4200,7 @@ static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
        val = !!cp->param[0];
        changed = (val != hci_dev_test_flag(hdev, HCI_QUALITY_REPORT));
 
-       if (!hdev->set_quality_report) {
+       if (!aosp_has_quality_report(hdev) && !hdev->set_quality_report) {
                err = mgmt_cmd_status(sk, hdev->id,
                                      MGMT_OP_SET_EXP_FEATURE,
                                      MGMT_STATUS_NOT_SUPPORTED);
@@ -4206,13 +4208,18 @@ static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
        }
 
        if (changed) {
-               err = hdev->set_quality_report(hdev, val);
+               if (hdev->set_quality_report)
+                       err = hdev->set_quality_report(hdev, val);
+               else
+                       err = aosp_set_quality_report(hdev, val);
+
                if (err) {
                        err = mgmt_cmd_status(sk, hdev->id,
                                              MGMT_OP_SET_EXP_FEATURE,
                                              MGMT_STATUS_FAILED);
                        goto unlock_quality_report;
                }
+
                if (val)
                        hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
                else
@@ -4224,8 +4231,8 @@ static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
        memcpy(rp.uuid, quality_report_uuid, 16);
        rp.flags = cpu_to_le32(val ? BIT(0) : 0);
        hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
-       err = mgmt_cmd_complete(sk, hdev->id,
-                               MGMT_OP_SET_EXP_FEATURE, 0,
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_EXP_FEATURE, 0,
                                &rp, sizeof(rp));
 
        if (changed)