mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-13 14:24:11 +08:00
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: Miao-chen Chou <mcchou@chromium.org> Signed-off-by: Joseph Hwang <josephsih@chromium.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
749a6c5942
commit
258f56d11b
@ -112,3 +112,90 @@ void aosp_do_close(struct hci_dev *hdev)
|
|||||||
|
|
||||||
bt_dev_dbg(hdev, "Cleanup of AOSP extension");
|
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);
|
||||||
|
}
|
||||||
|
@ -8,9 +8,22 @@
|
|||||||
void aosp_do_open(struct hci_dev *hdev);
|
void aosp_do_open(struct hci_dev *hdev);
|
||||||
void aosp_do_close(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
|
#else
|
||||||
|
|
||||||
static inline void aosp_do_open(struct hci_dev *hdev) {}
|
static inline void aosp_do_open(struct hci_dev *hdev) {}
|
||||||
static inline void aosp_do_close(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
|
#endif
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "mgmt_config.h"
|
#include "mgmt_config.h"
|
||||||
#include "msft.h"
|
#include "msft.h"
|
||||||
#include "eir.h"
|
#include "eir.h"
|
||||||
|
#include "aosp.h"
|
||||||
|
|
||||||
#define MGMT_VERSION 1
|
#define MGMT_VERSION 1
|
||||||
#define MGMT_REVISION 21
|
#define MGMT_REVISION 21
|
||||||
@ -3934,7 +3935,8 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
|||||||
idx++;
|
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))
|
if (hci_dev_test_flag(hdev, HCI_QUALITY_REPORT))
|
||||||
flags = BIT(0);
|
flags = BIT(0);
|
||||||
else
|
else
|
||||||
@ -4198,7 +4200,7 @@ static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
|
|||||||
val = !!cp->param[0];
|
val = !!cp->param[0];
|
||||||
changed = (val != hci_dev_test_flag(hdev, HCI_QUALITY_REPORT));
|
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,
|
err = mgmt_cmd_status(sk, hdev->id,
|
||||||
MGMT_OP_SET_EXP_FEATURE,
|
MGMT_OP_SET_EXP_FEATURE,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
@ -4206,13 +4208,18 @@ static int set_quality_report_func(struct sock *sk, struct hci_dev *hdev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (changed) {
|
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) {
|
if (err) {
|
||||||
err = mgmt_cmd_status(sk, hdev->id,
|
err = mgmt_cmd_status(sk, hdev->id,
|
||||||
MGMT_OP_SET_EXP_FEATURE,
|
MGMT_OP_SET_EXP_FEATURE,
|
||||||
MGMT_STATUS_FAILED);
|
MGMT_STATUS_FAILED);
|
||||||
goto unlock_quality_report;
|
goto unlock_quality_report;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val)
|
if (val)
|
||||||
hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
|
hci_dev_set_flag(hdev, HCI_QUALITY_REPORT);
|
||||||
else
|
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);
|
memcpy(rp.uuid, quality_report_uuid, 16);
|
||||||
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
|
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
|
||||||
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
|
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));
|
&rp, sizeof(rp));
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
|
Loading…
Reference in New Issue
Block a user