Bluetooth: Add framework for Microsoft vendor extension
authorMiao-chen Chou <mcchou@chromium.org>
Fri, 3 Apr 2020 19:44:01 +0000 (21:44 +0200)
committerJohan Hedberg <johan.hedberg@intel.com>
Sun, 5 Apr 2020 11:53:05 +0000 (14:53 +0300)
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>
include/net/bluetooth/hci_core.h
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/msft.c [new file with mode: 0644]
net/bluetooth/msft.h [new file with mode: 0644]

index d4e28773d378192717e4fe0d34c5d36192a65260..3cb0f82d0c83346f258e8eef632de489fd6287d9 100644 (file)
@@ -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);
@@ -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);
index 77703216a2e35e5dc4fe2d981283c98f87e425b7..9e25c6570170c2a1b84a2a8694d4b86c52821796 100644 (file)
@@ -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
index fda41c0b478189767ba52e52164e8c61354d24f3..41dd541a44a5249b5e18b3f9afd7afd1d89c244a 100644 (file)
@@ -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
index 5fb9db0b2b7bcf41125363f998a09981803a7b5e..ef0ee3a3d9eddbff82f183b47b804d029cc083d0 100644 (file)
@@ -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);
@@ -1563,6 +1564,8 @@ setup_failed:
            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) {
@@ -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);
 
index af396cb69602c2845649cfaa66e245d4e2191d58..2803beaa1c44ea7ee2aca7c64c1334d2130f97ef 100644 (file)
@@ -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"
@@ -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;
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
new file mode 100644 (file)
index 0000000..d6c4e6b
--- /dev/null
@@ -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);
+}
diff --git a/net/bluetooth/msft.h b/net/bluetooth/msft.h
new file mode 100644 (file)
index 0000000..5aa9130
--- /dev/null
@@ -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