Commit 258f56d1 authored by Joseph Hwang's avatar Joseph Hwang Committed by Marcel Holtmann
Browse files

Bluetooth: aosp: Support AOSP Bluetooth Quality Report



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: default avatarMiao-chen Chou <mcchou@chromium.org>
Signed-off-by: default avatarJoseph Hwang <josephsih@chromium.org>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 749a6c59
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -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);
}
+13 −0
Original line number Diff line number Diff line
@@ -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
+12 −5
Original line number Diff line number Diff line
@@ -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) {
		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)