Skip to content

Commit

Permalink
Bluetooth: Add framework for Microsoft vendor extension
Browse files Browse the repository at this point in the history
Micrsoft defined a set for HCI vendor extensions. Check the following
link for details:

https://docs.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events

This provides the basic framework to enable the extension and read its
supported features. Drivers still have to declare support for this
extension before it can be utilized by the host stack.

Signed-off-by: Miao-chen Chou <mcchou@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
  • Loading branch information
Miao-chen Chou authored and Johan Hedberg committed Apr 5, 2020
1 parent 3d23360 commit 145373c
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 0 deletions.
13 changes: 13 additions & 0 deletions include/net/bluetooth/hci_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,11 @@ struct hci_dev {
struct led_trigger *power_led;
#endif

#if IS_ENABLED(CONFIG_BT_MSFTEXT)
__u16 msft_opcode;
void *msft_data;
#endif

int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
Expand Down Expand Up @@ -1116,6 +1121,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
__printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
__printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);

static inline void hci_set_msft_opcode(struct hci_dev *hdev, __u16 opcode)
{
#if IS_ENABLED(CONFIG_BT_MSFTEXT)
hdev->msft_opcode = opcode;
#endif
}

int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev);
int hci_dev_do_close(struct hci_dev *hdev);
Expand Down
7 changes: 7 additions & 0 deletions net/bluetooth/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ config BT_LEDS
This option selects a few LED triggers for different
Bluetooth events.

config BT_MSFTEXT
bool "Enable Microsoft extensions"
depends on BT
help
This options enables support for the Microsoft defined HCI
vendor extensions.

config BT_DEBUGFS
bool "Export Bluetooth internals in debugfs"
depends on BT && DEBUG_FS
Expand Down
1 change: 1 addition & 0 deletions net/bluetooth/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
bluetooth-$(CONFIG_BT_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
bluetooth-$(CONFIG_BT_LEDS) += leds.o
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
5 changes: 5 additions & 0 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "hci_debugfs.h"
#include "smp.h"
#include "leds.h"
#include "msft.h"

static void hci_rx_work(struct work_struct *work);
static void hci_cmd_work(struct work_struct *work);
Expand Down Expand Up @@ -1563,6 +1564,8 @@ static int hci_dev_do_open(struct hci_dev *hdev)
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
ret = hdev->set_diag(hdev, true);

msft_do_open(hdev);

clear_bit(HCI_INIT, &hdev->flags);

if (!ret) {
Expand Down Expand Up @@ -1758,6 +1761,8 @@ int hci_dev_do_close(struct hci_dev *hdev)

hci_sock_dev_event(hdev, HCI_DEV_DOWN);

msft_do_close(hdev);

if (hdev->flush)
hdev->flush(hdev);

Expand Down
5 changes: 5 additions & 0 deletions net/bluetooth/hci_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "a2mp.h"
#include "amp.h"
#include "smp.h"
#include "msft.h"

#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
"\x00\x00\x00\x00\x00\x00\x00\x00"
Expand Down Expand Up @@ -6166,6 +6167,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_num_comp_blocks_evt(hdev, skb);
break;

case HCI_EV_VENDOR:
msft_vendor_evt(hdev, skb);
break;

default:
BT_DBG("%s event 0x%2.2x", hdev->name, event);
break;
Expand Down
141 changes: 141 additions & 0 deletions net/bluetooth/msft.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 Google Corporation
*/

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

#include "msft.h"

#define MSFT_OP_READ_SUPPORTED_FEATURES 0x00
struct msft_cp_read_supported_features {
__u8 sub_opcode;
} __packed;
struct msft_rp_read_supported_features {
__u8 status;
__u8 sub_opcode;
__le64 features;
__u8 evt_prefix_len;
__u8 evt_prefix[0];
} __packed;

struct msft_data {
__u64 features;
__u8 evt_prefix_len;
__u8 *evt_prefix;
};

static bool read_supported_features(struct hci_dev *hdev,
struct msft_data *msft)
{
struct msft_cp_read_supported_features cp;
struct msft_rp_read_supported_features *rp;
struct sk_buff *skb;

cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES;

skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)",
PTR_ERR(skb));
return false;
}

if (skb->len < sizeof(*rp)) {
bt_dev_err(hdev, "MSFT supported features length mismatch");
goto failed;
}

rp = (struct msft_rp_read_supported_features *)skb->data;

if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
goto failed;

if (rp->evt_prefix_len > 0) {
msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
GFP_KERNEL);
if (!msft->evt_prefix)
goto failed;
}

msft->evt_prefix_len = rp->evt_prefix_len;
msft->features = __le64_to_cpu(rp->features);

kfree_skb(skb);
return true;

failed:
kfree_skb(skb);
return false;
}

void msft_do_open(struct hci_dev *hdev)
{
struct msft_data *msft;

if (hdev->msft_opcode == HCI_OP_NOP)
return;

bt_dev_dbg(hdev, "Initialize MSFT extension");

msft = kzalloc(sizeof(*msft), GFP_KERNEL);
if (!msft)
return;

if (!read_supported_features(hdev, msft)) {
kfree(msft);
return;
}

hdev->msft_data = msft;
}

void msft_do_close(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;

if (!msft)
return;

bt_dev_dbg(hdev, "Cleanup of MSFT extension");

hdev->msft_data = NULL;

kfree(msft->evt_prefix);
kfree(msft);
}

void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct msft_data *msft = hdev->msft_data;
u8 event;

if (!msft)
return;

/* When the extension has defined an event prefix, check that it
* matches, and otherwise just return.
*/
if (msft->evt_prefix_len > 0) {
if (skb->len < msft->evt_prefix_len)
return;

if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
return;

skb_pull(skb, msft->evt_prefix_len);
}

/* Every event starts at least with an event code and the rest of
* the data is variable and depends on the event code.
*/
if (skb->len < 1)
return;

event = *skb->data;
skb_pull(skb, 1);

bt_dev_dbg(hdev, "MSFT vendor event %u", event);
}
18 changes: 18 additions & 0 deletions net/bluetooth/msft.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 Google Corporation
*/

#if IS_ENABLED(CONFIG_BT_MSFTEXT)

void msft_do_open(struct hci_dev *hdev);
void msft_do_close(struct hci_dev *hdev);
void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb);

#else

static inline void msft_do_open(struct hci_dev *hdev) {}
static inline void msft_do_close(struct hci_dev *hdev) {}
static inline void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) {}

#endif

0 comments on commit 145373c

Please sign in to comment.