From: Zijun Hu <zijun.hu@oss.qualcomm.com>
To: Marcel Holtmann <marcel@holtmann.org>,
Luiz Augusto von Dentz <luiz.dentz@gmail.com>
Cc: Zijun Hu <zijun_hu@icloud.com>,
linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
Zijun Hu <zijun.hu@oss.qualcomm.com>
Subject: [PATCH v4 2/4] Bluetooth: Add generic support for vendor packets
Date: Thu, 10 Sep 2026 07:35:36 -0700 [thread overview]
Message-ID: <20260910-btusb_qcc2072-v4-2-e78f04b7675e@oss.qualcomm.com> (raw)
In-Reply-To: <20260910-btusb_qcc2072-v4-0-e78f04b7675e@oss.qualcomm.com>
The virtual HCI_VENDOR_PKT (0xff) has been defined but never used by
the BT core. Now, there is a requirement to implement it, as follows:
For Qualcomm multi-subsystem BT chips, the transport wire carries both
BT-HCI and PERI-HCI packets, where PERI is a subsystem in the chip, take
the upcoming QCC2072 as an example:
Packet type BT-HCI indicator PERI-HCI indicator
-----------------------------------------------------------------
CMD (Host -> Controller) 0x01 0x31
ACL Data (bidirectional) 0x02 0x32
EVENT (Controller -> Host) 0x04 0x34
There are also BT ACL packets with vendor-reserved handles found in
existing device drivers, as shown below:
+--------+--------+---------------------------+
| Vendor | Handle | Purpose |
+--------+--------+---------------------------+
| QCOM | 0xEDD | Firmware coredump |
| | 0xEDC | Firmware enhanced logging |
+--------+--------+---------------------------+
| MTK | 0xFC6F | Firmware coredump |
| | 0x05FF | Firmware debug logging 1 |
| | 0x05FE | Firmware debug logging 2 |
+--------+--------+---------------------------+
| NXP | 0xFFF | Firmware coredump |
+--------+--------+---------------------------+
Implement HCI_VENDOR_PKT to generically support such vendor packets,
which don't follow BT SIG's vendor-extension framework:
- Log them in btmon as they appear on the wire.
- Add hdev->recv_vendor_pkt() to handle them in hci_rx_work().
- Add hci_send_vendor_frame() to send them to the device driver.
- Allow them to flow over HCI_CHANNEL_USER, nested inside HCI_VENDOR_PKT.
Also solve the same issues that the commit below fixes for
HCI_EV_VENDOR, which follows BT SIG's vendor-extension framework:
commit 0bd606b31d40 ("Bluetooth: hci_event: Introduce
handle_ev_vendor() for HCI_EV_VENDOR")
Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
include/net/bluetooth/hci_core.h | 5 +++++
include/net/bluetooth/hci_mon.h | 2 ++
net/bluetooth/hci_core.c | 45 ++++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_sock.c | 8 +++++++
4 files changed, 60 insertions(+)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index c12cd6873f65..b26004a05368 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -23,16 +23,17 @@
#ifndef __HCI_CORE_H
#define __HCI_CORE_H
#include <linux/idr.h>
#include <linux/leds.h>
#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
+#include <linux/uio.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_drv.h>
#include <net/bluetooth/hci_sync.h>
#include <net/bluetooth/hci_sock.h>
#include <net/bluetooth/coredump.h>
/* HCI priority */
@@ -641,16 +642,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);
+ /* Receive HCI_VENDOR_PKT */
+ void (*recv_vendor_pkt)(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);
@@ -2395,16 +2398,18 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
}
return 0;
}
int hci_register_cb(struct hci_cb *hcb);
int hci_unregister_cb(struct hci_cb *hcb);
+int hci_send_vendor_frame(struct hci_dev *hdev, struct iov_iter *iter);
+
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param);
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h
index 4b2a0af4ed58..7710688c0d30 100644
--- a/include/net/bluetooth/hci_mon.h
+++ b/include/net/bluetooth/hci_mon.h
@@ -45,16 +45,18 @@ struct hci_mon_hdr {
#define HCI_MON_CTRL_OPEN 14
#define HCI_MON_CTRL_CLOSE 15
#define HCI_MON_CTRL_COMMAND 16
#define HCI_MON_CTRL_EVENT 17
#define HCI_MON_ISO_TX_PKT 18
#define HCI_MON_ISO_RX_PKT 19
#define HCI_MON_DRV_TX_PKT 20
#define HCI_MON_DRV_RX_PKT 21
+#define HCI_MON_VENDOR_TX_PKT 22
+#define HCI_MON_VENDOR_RX_PKT 23
struct hci_mon_new_index {
__u8 type;
__u8 bus;
bdaddr_t bdaddr;
char name[8] __nonstring;
} __packed;
#define HCI_MON_NEW_INDEX_SIZE 16
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 46453bc4ca25..c71c1e7e1f37 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2912,16 +2912,18 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
type == PA_LINK)
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
}
break;
case HCI_SCODATA_PKT:
break;
case HCI_ISODATA_PKT:
break;
+ case HCI_VENDOR_PKT:
+ break;
case HCI_DRV_PKT:
break;
default:
kfree_skb(skb);
return -EINVAL;
}
/* Incoming skb */
@@ -3048,16 +3050,51 @@ static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
static int hci_send_conn_frame(struct hci_dev *hdev, struct hci_conn *conn,
struct sk_buff *skb)
{
hci_conn_tx_queue(conn, skb);
return hci_send_frame(hdev, skb);
}
+/**
+ * hci_send_vendor_frame - Send an HCI_VENDOR_PKT frame to the HCI driver
+ * @hdev: The HCI device
+ * @iter: iov_iter carrying the frame
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int hci_send_vendor_frame(struct hci_dev *hdev, struct iov_iter *iter)
+{
+ struct sk_buff *skb;
+ unsigned int len;
+
+ if (WARN_ON(!iov_iter_is_kvec(iter)))
+ return -EINVAL;
+
+ /* Vendor frames are opaque, the caller guarantees the size. */
+ len = (unsigned int)iov_iter_count(iter);
+ if (!len)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ if (!copy_from_iter_full(skb_put(skb, len), len, iter)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
+
+ return hci_send_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_send_vendor_frame);
+
/* Send HCI command */
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param)
{
struct sk_buff *skb;
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
@@ -4063,16 +4100,24 @@ static void hci_rx_work(struct work_struct *work)
hci_scodata_packet(hdev, skb);
break;
case HCI_ISODATA_PKT:
BT_DBG("%s ISO data packet", hdev->name);
hci_isodata_packet(hdev, skb);
break;
+ case HCI_VENDOR_PKT:
+ BT_DBG("%s Vendor packet", hdev->name);
+ if (hdev->recv_vendor_pkt)
+ hdev->recv_vendor_pkt(hdev, skb);
+ else
+ kfree_skb(skb);
+ break;
+
default:
kfree_skb(skb);
break;
}
}
}
static int hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 070ca388f9ac..406b70ecaf33 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -228,16 +228,17 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
continue;
} else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
if (!bt_cb(skb)->incoming)
continue;
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_VENDOR_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT)
continue;
} else {
/* Don't send frame to other channel types */
continue;
}
if (!skb_copy) {
@@ -385,16 +386,22 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
break;
case HCI_ISODATA_PKT:
if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_ISO_RX_PKT);
else
opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT);
break;
+ case HCI_VENDOR_PKT:
+ if (bt_cb(skb)->incoming)
+ opcode = cpu_to_le16(HCI_MON_VENDOR_RX_PKT);
+ else
+ opcode = cpu_to_le16(HCI_MON_VENDOR_TX_PKT);
+ break;
case HCI_DRV_PKT:
if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT);
else
opcode = cpu_to_le16(HCI_MON_DRV_TX_PKT);
break;
case HCI_DIAG_PKT:
opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
@@ -1863,16 +1870,17 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
* since that gets enforced when binding the socket.
*
* However check that the packet type is valid.
*/
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
+ hci_skb_pkt_type(skb) != HCI_VENDOR_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
err = -EINVAL;
goto drop;
}
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
} else if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
--
2.34.1
next prev parent reply other threads:[~2026-09-10 14:35 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 14:35 [PATCH v4 0/4] Bluetooth: btusb: Support Qualcomm multi-subsystem QCC2072 Zijun Hu
2026-09-10 14:35 ` [PATCH v4 1/4] Bluetooth: btusb: Add recv_intr() hook to btusb_data Zijun Hu
2026-09-10 19:01 ` Bluetooth: btusb: Support Qualcomm multi-subsystem QCC2072 bluez.test.bot
2026-09-10 14:35 ` Zijun Hu [this message]
2026-09-10 14:35 ` [PATCH v4 3/4] Bluetooth: btusb: Build the driver from multiple source files Zijun Hu
2026-09-10 14:35 ` [PATCH v4 4/4] Bluetooth: btusb: Add support for Qualcomm multi-subsystem QCC2072 Zijun Hu
2026-09-10 15:38 ` [PATCH v4 0/4] Bluetooth: btusb: Support " Luiz Augusto von Dentz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260910-btusb_qcc2072-v4-2-e78f04b7675e@oss.qualcomm.com \
--to=zijun.hu@oss.qualcomm.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=marcel@holtmann.org \
--cc=zijun_hu@icloud.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.