[PATCH v3 6/7] Bluetooth: hci_event: Introduce handle_ev_vendor() for HCI_EV_VENDOR

Zijun Hu <[email protected]> Sat, 01 Aug 2026 23:31:41 -0700
Newsgroups org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Introduce the hook to solve issues below:

msft_vendor_evt(), the current handler for all VSEs, is unsuitable
since:
- many VSEs are not MSFT ones;
- it always corrupts the non-MSFT VSEs by calling skb_pull_data()
  once the MSFT extension is enabled.

Several issues are caused by many transport drivers pre-processing
VSEs in their RX path, often an IRQ-disabled atomic context. Take
the two typical cases below as examples:

Case 1:
  // no btmon log, no way to reach userspace
  Step 1: handle and free @original_skb directly

Case 2:
  // hurts performance and consumes GFP_ATOMIC memory
  Step 1: cloned_skb = skb_clone(original_skb, GFP_ATOMIC);
  // the VSE is handled here
  Step 2: handle and free @cloned_skb
  Step 3: hci_recv_frame(hdev, original_skb);
  // already handled, but re-enters the stack's event-handling path
  Step 4: hci_event_packet(hdev, original_skb);

Fix by introducing the hook with usage:
1) the transport driver registers the hook for VSEs of interest;
2) the stack calls it in process context, handling the VSE like any
   other event:
   - if interested, handle the VSE - no need to free it - and
     return true;
   - otherwise return false.

Signed-off-by: Zijun Hu <[email protected]>
---
Previous version:
https://lore.kernel.org/all/[email protected]

Changes since previous version:
1) Don't handle ACL frames with a vendor-reserved handle for special
   usage.
2) Rename recv_bt_vendor() to handle_ev_vendor(), since the event is
   only in the RX direction.
---
 include/net/bluetooth/hci_core.h |  2 ++
 net/bluetooth/hci_event.c        | 10 +++++++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index b7e71733f360..87db877f8c5c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -641,16 +641,18 @@ struct hci_dev {
 #endif
 
 	int (*open)(struct hci_dev *hdev);
 	int (*close)(struct hci_dev *hdev);
 	int (*flush)(struct hci_dev *hdev);
 	int (*setup)(struct hci_dev *hdev);
 	int (*shutdown)(struct hci_dev *hdev);
 	int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
+	/* Handle HCI_EV_VENDOR; return true if handled, false otherwise */
+	bool (*handle_ev_vendor)(struct hci_dev *hdev, struct sk_buff *skb);
 	void (*notify)(struct hci_dev *hdev, unsigned int evt);
 	void (*hw_error)(struct hci_dev *hdev, u8 code);
 	int (*post_init)(struct hci_dev *hdev);
 	int (*set_diag)(struct hci_dev *hdev, bool enable);
 	int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 	void (*reset)(struct hci_dev *hdev);
 	bool (*wakeup)(struct hci_dev *hdev);
 	int (*set_quality_report)(struct hci_dev *hdev, bool enable);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9c8bf6708356..8452d372524f 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -7597,16 +7597,24 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
 			    ev->subevent, skb->len, subev->max_len);
 	data = hci_le_ev_skb_pull(hdev, skb, ev->subevent, subev->min_len);
 	if (!data)
 		return;
 
 	subev->func(hdev, data, skb);
 }
 
+static void hci_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
+{
+	if (hdev->handle_ev_vendor && hdev->handle_ev_vendor(hdev, skb))
+		return;
+
+	msft_vendor_evt(hdev, data, skb);
+}
+
 static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
 				 u8 event, struct sk_buff *skb)
 {
 	struct hci_ev_cmd_complete *ev;
 	struct hci_event_hdr *hdr;
 
 	if (!skb)
 		return false;
@@ -7824,17 +7832,17 @@ static const struct hci_ev {
 	       sizeof(struct hci_ev_keypress_notify)),
 	/* [0x3d = HCI_EV_REMOTE_HOST_FEATURES] */
 	HCI_EV(HCI_EV_REMOTE_HOST_FEATURES, hci_remote_host_features_evt,
 	       sizeof(struct hci_ev_remote_host_features)),
 	/* [0x3e = HCI_EV_LE_META] */
 	HCI_EV_REQ_VL(HCI_EV_LE_META, hci_le_meta_evt,
 		      sizeof(struct hci_ev_le_meta), HCI_MAX_EVENT_SIZE),
 	/* [0xff = HCI_EV_VENDOR] */
-	HCI_EV_VL(HCI_EV_VENDOR, msft_vendor_evt, 0, HCI_MAX_EVENT_SIZE),
+	HCI_EV_VL(HCI_EV_VENDOR, hci_vendor_evt, 0, HCI_MAX_EVENT_SIZE),
 };
 
 static void hci_event_func(struct hci_dev *hdev, u8 event, struct sk_buff *skb,
 			   u16 *opcode, u8 *status,
 			   hci_req_complete_t *req_complete,
 			   hci_req_complete_skb_t *req_complete_skb)
 {
 	const struct hci_ev *ev = &hci_ev_table[event];

-- 
2.34.1