* [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
@ 2026-07-20 5:13 Zijun Hu
2026-07-20 7:28 ` [RFC] " bluez.test.bot
2026-07-20 14:30 ` [PATCH RFC] " Luiz Augusto von Dentz
0 siblings, 2 replies; 9+ messages in thread
From: Zijun Hu @ 2026-07-20 5:13 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz
Cc: Zijun Hu, linux-bluetooth, linux-kernel, Zijun Hu
For Qualcomm multi-subsystem BT chips, the transport wire carries both
BT-HCI and PERI-HCI frames. PERI is a subsystem on the chip. Take the
upcoming QCC2072 support 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
To generically support vendor HCI frames:
- Show them in btmon logs as they appear on the wire.
- Allow userspace to send/receive them via HCI_CHANNEL_USER, with a
socket option to control RX, defaulting off to eliminate regression
risk for existing applications.
- Add hdev->recv_vendor() to handle vendor frames in hci_rx_work() like
BT frame handlers.
- Add hci_send_vendor_frame() API similar to existing __hci_cmd_send().
Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
---
Hi Luiz
To address the below concerns you raised on previous
discussion, and inspired from your suggestion to add hdev callback:
- Not use safer skb helper like skb_pull_data()
- It will skip sending to the monitor, making debugging much harder
Looking forward to your further comments.
Why hdev->is_vendor() instead of multiplexing vendor frames under a
virtual HCI_VENDOR_PKT + vendor indicator byte ?
1) The virtual HCI_VENDOR_PKT isn't used by the core itself
currently, and may be removed later.
2) It makes full use of the indicator (hci_skb_pkt_type(skb)) space,
which is large enough to accommodate vendor frames without an
extra layer of nesting inside HCI_VENDOR_PKT.
3) It lets userspace and btmon logs see the exact same frame as it
appears on the wire.
Previous discussion link:
https://lore.kernel.org/all/CABBYNZJpdeoZ16bObLRPhng2dfyNeK9ix9_-z_hMZhJ0QxTxGg@mail.gmail.com
https://lore.kernel.org/all/CABBYNZJ0zoVVVxuM48L=Km==gnrQuskrbjB6q_aNV0KEEY3+5w@mail.gmail.com
---
include/net/bluetooth/bluetooth.h | 2 +
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 13 +++++++
include/net/bluetooth/hci_mon.h | 2 +
net/bluetooth/hci_core.c | 22 +++++++++++
net/bluetooth/hci_sock.c | 78 ++++++++++++++++++++++++++++++++++-----
6 files changed, 108 insertions(+), 10 deletions(-)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index b624da5026f5..d9870a43a2c4 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -256,6 +256,8 @@ struct bt_codecs {
#define BT_SCM_PKT_SEQNUM 0x05
+#define BT_RCV_VENDOR_PKT 23
+
__printf(1, 2)
void bt_info(const char *fmt, ...);
__printf(1, 2)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 50f0eef71fb1..ac97550cbdba 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -398,6 +398,7 @@ enum {
/* HCI socket flags */
enum {
HCI_SOCK_TRUSTED,
+ HCI_SOCK_RCV_VENDOR_PKT,
HCI_MGMT_INDEX_EVENTS,
HCI_MGMT_UNCONF_INDEX_EVENTS,
HCI_MGMT_EXT_INDEX_EVENTS,
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index e7133ff87fbf..20a455d90ef3 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -645,6 +645,8 @@ struct hci_dev {
int (*setup)(struct hci_dev *hdev);
int (*shutdown)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
+ bool (*is_vendor)(struct hci_dev *hdev, const struct sk_buff *skb);
+ void (*recv_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);
@@ -660,6 +662,15 @@ struct hci_dev {
u8 (*classify_pkt_type)(struct hci_dev *hdev, struct sk_buff *skb);
};
+static inline bool hci_is_vendor_frame(struct hci_dev *hdev,
+ const struct sk_buff *skb)
+{
+ if (!hdev->is_vendor)
+ return false;
+
+ return hdev->is_vendor(hdev, skb);
+}
+
#define hci_set_quirk(hdev, nr) set_bit((nr), (hdev)->quirk_flags)
#define hci_clear_quirk(hdev, nr) clear_bit((nr), (hdev)->quirk_flags)
#define hci_test_quirk(hdev, nr) test_bit((nr), (hdev)->quirk_flags)
@@ -2327,6 +2338,8 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
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 sk_buff *skb);
+
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
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
@@ -50,6 +50,8 @@ struct hci_mon_hdr {
#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;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 1e324c05af24..c12c180e8293 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2920,6 +2920,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_DRV_PKT:
break;
default:
+ if (hci_is_vendor_frame(hdev, skb))
+ break;
kfree_skb(skb);
return -EINVAL;
}
@@ -3053,6 +3055,18 @@ static int hci_send_conn_frame(struct hci_dev *hdev, struct hci_conn *conn,
return hci_send_frame(hdev, skb);
}
+int hci_send_vendor_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ if (hci_is_vendor_frame(hdev, skb))
+ return hci_send_frame(hdev, skb);
+
+ bt_dev_err(hdev, "invalid vendor frame with pkt_type 0x%2.2x",
+ hci_skb_pkt_type(skb));
+ kfree_skb(skb);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(hci_send_vendor_frame);
+
/* Send HCI command */
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param)
@@ -4066,6 +4080,14 @@ static void hci_rx_work(struct work_struct *work)
break;
default:
+ if (hci_is_vendor_frame(hdev, skb)) {
+ BT_DBG("%s Vendor packet with type 0x%2.2x",
+ hdev->name, hci_skb_pkt_type(skb));
+ if (hdev->recv_vendor) {
+ hdev->recv_vendor(hdev, skb);
+ break;
+ }
+ }
kfree_skb(skb);
break;
}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 070ca388f9ac..903498e6db3d 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -229,12 +229,16 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
} 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_DRV_PKT)
+ if (hci_is_vendor_frame(hdev, skb)) {
+ if (!hci_sock_test_flag(sk, HCI_SOCK_RCV_VENDOR_PKT))
+ continue;
+ } else 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_DRV_PKT) {
continue;
+ }
} else {
/* Don't send frame to other channel types */
continue;
@@ -359,6 +363,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
struct sk_buff *skb_copy = NULL;
struct hci_mon_hdr *hdr;
__le16 opcode;
+ bool is_vendor_frame = false;
if (!atomic_read(&monitor_promisc))
return;
@@ -400,7 +405,26 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
break;
default:
- return;
+ is_vendor_frame = hci_is_vendor_frame(hdev, skb);
+ if (!is_vendor_frame)
+ return;
+ 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;
+ }
+
+ if (is_vendor_frame) {
+ skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE + 1,
+ GFP_ATOMIC, true);
+ if (!skb_copy)
+ return;
+
+ *(u8 *)skb_push(skb_copy, 1) = hci_skb_pkt_type(skb);
+ hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
+ hdr->len = cpu_to_le16(skb->len + 1);
+ goto out_comm;
}
/* Create a private copy with headroom */
@@ -408,14 +432,15 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
if (!skb_copy)
return;
- hci_sock_copy_creds(skb->sk, skb_copy);
-
/* Put header before the data */
hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
+ hdr->len = cpu_to_le16(skb->len);
+
+out_comm:
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
- hdr->len = cpu_to_le16(skb->len);
+ hci_sock_copy_creds(skb->sk, skb_copy);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb_copy);
@@ -1868,7 +1893,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
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_DRV_PKT) {
+ hci_skb_pkt_type(skb) != HCI_DRV_PKT &&
+ !hci_is_vendor_frame(hdev, skb)) {
err = -EINVAL;
goto drop;
}
@@ -2017,6 +2043,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
int err = 0;
+ int opt_int;
u16 opt;
BT_DBG("sk %p, opt %d", sk, optname);
@@ -2050,6 +2077,23 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
hci_pi(sk)->mtu = opt;
break;
+ case BT_RCV_VENDOR_PKT:
+ if (hci_pi(sk)->channel != HCI_CHANNEL_USER) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ err = copy_safe_from_sockptr(&opt_int, sizeof(opt_int),
+ optval, optlen);
+ if (err)
+ break;
+
+ if (opt_int)
+ hci_sock_set_flag(sk, HCI_SOCK_RCV_VENDOR_PKT);
+ else
+ hci_sock_clear_flag(sk, HCI_SOCK_RCV_VENDOR_PKT);
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -2132,6 +2176,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
{
struct sock *sk = sock->sk;
int err = 0;
+ int opt_int;
u16 mtu;
BT_DBG("sk %p, opt %d", sk, optname);
@@ -2153,6 +2198,19 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;
+ case BT_RCV_VENDOR_PKT:
+ if (hci_pi(sk)->channel != HCI_CHANNEL_USER) {
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ opt_int = hci_sock_test_flag(sk, HCI_SOCK_RCV_VENDOR_PKT) ?
+ 1 : 0;
+ if (copy_to_iter(&opt_int, sizeof(opt_int),
+ &sopt->iter_out) != sizeof(opt_int))
+ err = -EFAULT;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
---
base-commit: 7f02cdbb4cf17df8aa1977eea0cf490c472a706d
change-id: 20260719-support_vendor_hci-1fbb4449ab8e
Best regards,
--
Zijun Hu <zijun.hu@oss.qualcomm.com>
^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: [RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-20 5:13 [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames Zijun Hu
@ 2026-07-20 7:28 ` bluez.test.bot
2026-07-20 14:30 ` [PATCH RFC] " Luiz Augusto von Dentz
1 sibling, 0 replies; 9+ messages in thread
From: bluez.test.bot @ 2026-07-20 7:28 UTC (permalink / raw)
To: linux-bluetooth, zijun.hu
[-- Attachment #1: Type: text/plain, Size: 2571 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1130540
---Test result---
Test Summary:
CheckPatch PASS 1.79 seconds
VerifyFixes PASS 0.10 seconds
VerifySignedoff PASS 0.10 seconds
GitLint FAIL 0.26 seconds
SubjectPrefix PASS 0.09 seconds
BuildKernel PASS 26.85 seconds
CheckAllWarning PASS 30.04 seconds
CheckSparse PASS 28.79 seconds
BuildKernel32 PASS 26.49 seconds
CheckKernelLLVM SKIP 0.00 seconds
TestRunnerSetup PASS 505.38 seconds
TestRunner_l2cap-tester PASS 61.40 seconds
TestRunner_iso-tester PASS 79.89 seconds
TestRunner_bnep-tester PASS 19.48 seconds
TestRunner_mgmt-tester FAIL 219.66 seconds
TestRunner_rfcomm-tester PASS 25.80 seconds
TestRunner_sco-tester PASS 32.02 seconds
TestRunner_ioctl-tester PASS 26.18 seconds
TestRunner_mesh-tester FAIL 25.89 seconds
TestRunner_smp-tester PASS 23.59 seconds
TestRunner_userchan-tester PASS 20.33 seconds
TestRunner_6lowpan-tester PASS 23.03 seconds
IncrementalBuild PASS 25.40 seconds
Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[RFC] Bluetooth: Add generic support for vendor HCI frames
61: B2 Line has trailing whitespace: "-- "
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.235 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.254 seconds
Mesh - Send cancel - 2 Timed out 1.991 seconds
https://github.com/bluez/bluetooth-next/pull/460
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-20 5:13 [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames Zijun Hu
2026-07-20 7:28 ` [RFC] " bluez.test.bot
@ 2026-07-20 14:30 ` Luiz Augusto von Dentz
2026-07-21 8:52 ` Zijun Hu
1 sibling, 1 reply; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-20 14:30 UTC (permalink / raw)
To: Zijun Hu; +Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 15937 bytes --]
Hi Zijun,
On Mon, Jul 20, 2026 at 1:14 AM Zijun Hu <zijun.hu@oss.qualcomm.com> wrote:
>
> For Qualcomm multi-subsystem BT chips, the transport wire carries both
> BT-HCI and PERI-HCI frames. PERI is a subsystem on the chip. Take the
> upcoming QCC2072 support 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
I would say this was fine, but you actuall need to reclassify these to
0xff and then use an extra byte for the vendor opcode. Btw, you said
you could define H4 headers like this? The spec defines either:
https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/three-wire-uart-transport-layer.html#UUID-1cf959bb-57a0-e782-4324-a9bc4ee3f134_table-idm13359018144714
Or
https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/uart-transport-layer.html#UUID-83881875-5fbb-c366-400f-055ba726f70e_table-idm13359015052542
We end up defining vendor 0xff because it is at the end of the range
which is probably safer.
> To generically support vendor HCI frames:
>
> - Show them in btmon logs as they appear on the wire.
> - Allow userspace to send/receive them via HCI_CHANNEL_USER, with a
> socket option to control RX, defaulting off to eliminate regression
> risk for existing applications.
We shouldn't need to do this, we should just accept 0xff as the vendor
packet type and then use the following byte as the real opcode (0x31,
0x32, 0x34). Otherwise, if we start accepting vendor packet types
outside 0xff this will get crowded quickly and could potentially clash
with future specs.
> - Add hdev->recv_vendor() to handle vendor frames in hci_rx_work() like
> BT frame handlers.
> - Add hci_send_vendor_frame() API similar to existing __hci_cmd_send().
Encoding/decoding of the frames should be transparent. I'm fine adding
code to the likes of btmon to decode vendor packets, we already have
something similar for Intel although that uses a vendor event not a
vendor packet (both use 0xff, causing the confusion). The user of the
user channel shall be able to read/write starting with 0xff then
decode/encode the next byte as the actual vendor opcode.
> Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
> ---
> Hi Luiz
> To address the below concerns you raised on previous
> discussion, and inspired from your suggestion to add hdev callback:
>
> - Not use safer skb helper like skb_pull_data()
> - It will skip sending to the monitor, making debugging much harder
>
> Looking forward to your further comments.
>
> Why hdev->is_vendor() instead of multiplexing vendor frames under a
> virtual HCI_VENDOR_PKT + vendor indicator byte ?
>
> 1) The virtual HCI_VENDOR_PKT isn't used by the core itself
> currently, and may be removed later.
> 2) It makes full use of the indicator (hci_skb_pkt_type(skb)) space,
> which is large enough to accommodate vendor frames without an
> extra layer of nesting inside HCI_VENDOR_PKT.
> 3) It lets userspace and btmon logs see the exact same frame as it
> appears on the wire.
>
> Previous discussion link:
> https://lore.kernel.org/all/CABBYNZJpdeoZ16bObLRPhng2dfyNeK9ix9_-z_hMZhJ0QxTxGg@mail.gmail.com
> https://lore.kernel.org/all/CABBYNZJ0zoVVVxuM48L=Km==gnrQuskrbjB6q_aNV0KEEY3+5w@mail.gmail.com
> ---
> include/net/bluetooth/bluetooth.h | 2 +
> include/net/bluetooth/hci.h | 1 +
> include/net/bluetooth/hci_core.h | 13 +++++++
> include/net/bluetooth/hci_mon.h | 2 +
> net/bluetooth/hci_core.c | 22 +++++++++++
> net/bluetooth/hci_sock.c | 78 ++++++++++++++++++++++++++++++++++-----
> 6 files changed, 108 insertions(+), 10 deletions(-)
>
> diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
> index b624da5026f5..d9870a43a2c4 100644
> --- a/include/net/bluetooth/bluetooth.h
> +++ b/include/net/bluetooth/bluetooth.h
> @@ -256,6 +256,8 @@ struct bt_codecs {
>
> #define BT_SCM_PKT_SEQNUM 0x05
>
> +#define BT_RCV_VENDOR_PKT 23
> +
> __printf(1, 2)
> void bt_info(const char *fmt, ...);
> __printf(1, 2)
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index 50f0eef71fb1..ac97550cbdba 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -398,6 +398,7 @@ enum {
> /* HCI socket flags */
> enum {
> HCI_SOCK_TRUSTED,
> + HCI_SOCK_RCV_VENDOR_PKT,
> HCI_MGMT_INDEX_EVENTS,
> HCI_MGMT_UNCONF_INDEX_EVENTS,
> HCI_MGMT_EXT_INDEX_EVENTS,
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e7133ff87fbf..20a455d90ef3 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -645,6 +645,8 @@ struct hci_dev {
> int (*setup)(struct hci_dev *hdev);
> int (*shutdown)(struct hci_dev *hdev);
> int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
> + bool (*is_vendor)(struct hci_dev *hdev, const struct sk_buff *skb);
> + void (*recv_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);
> @@ -660,6 +662,15 @@ struct hci_dev {
> u8 (*classify_pkt_type)(struct hci_dev *hdev, struct sk_buff *skb);
> };
>
> +static inline bool hci_is_vendor_frame(struct hci_dev *hdev,
> + const struct sk_buff *skb)
> +{
> + if (!hdev->is_vendor)
> + return false;
> +
> + return hdev->is_vendor(hdev, skb);
> +}
> +
> #define hci_set_quirk(hdev, nr) set_bit((nr), (hdev)->quirk_flags)
> #define hci_clear_quirk(hdev, nr) clear_bit((nr), (hdev)->quirk_flags)
> #define hci_test_quirk(hdev, nr) test_bit((nr), (hdev)->quirk_flags)
> @@ -2327,6 +2338,8 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
> 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 sk_buff *skb);
> +
> int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
> const void *param);
>
> 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
> @@ -50,6 +50,8 @@ struct hci_mon_hdr {
> #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;
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 1e324c05af24..c12c180e8293 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2920,6 +2920,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
> case HCI_DRV_PKT:
> break;
> default:
> + if (hci_is_vendor_frame(hdev, skb))
> + break;
> kfree_skb(skb);
> return -EINVAL;
> }
> @@ -3053,6 +3055,18 @@ static int hci_send_conn_frame(struct hci_dev *hdev, struct hci_conn *conn,
> return hci_send_frame(hdev, skb);
> }
>
> +int hci_send_vendor_frame(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> + if (hci_is_vendor_frame(hdev, skb))
> + return hci_send_frame(hdev, skb);
> +
> + bt_dev_err(hdev, "invalid vendor frame with pkt_type 0x%2.2x",
> + hci_skb_pkt_type(skb));
> + kfree_skb(skb);
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL(hci_send_vendor_frame);
> +
> /* Send HCI command */
> int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
> const void *param)
> @@ -4066,6 +4080,14 @@ static void hci_rx_work(struct work_struct *work)
> break;
>
> default:
> + if (hci_is_vendor_frame(hdev, skb)) {
> + BT_DBG("%s Vendor packet with type 0x%2.2x",
> + hdev->name, hci_skb_pkt_type(skb));
> + if (hdev->recv_vendor) {
> + hdev->recv_vendor(hdev, skb);
> + break;
> + }
> + }
> kfree_skb(skb);
> break;
> }
> diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
> index 070ca388f9ac..903498e6db3d 100644
> --- a/net/bluetooth/hci_sock.c
> +++ b/net/bluetooth/hci_sock.c
> @@ -229,12 +229,16 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
> } 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_DRV_PKT)
> + if (hci_is_vendor_frame(hdev, skb)) {
> + if (!hci_sock_test_flag(sk, HCI_SOCK_RCV_VENDOR_PKT))
> + continue;
> + } else 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_DRV_PKT) {
> continue;
> + }
> } else {
> /* Don't send frame to other channel types */
> continue;
> @@ -359,6 +363,7 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
> struct sk_buff *skb_copy = NULL;
> struct hci_mon_hdr *hdr;
> __le16 opcode;
> + bool is_vendor_frame = false;
>
> if (!atomic_read(&monitor_promisc))
> return;
> @@ -400,7 +405,26 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
> opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
> break;
> default:
> - return;
> + is_vendor_frame = hci_is_vendor_frame(hdev, skb);
> + if (!is_vendor_frame)
> + return;
> + 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;
> + }
> +
> + if (is_vendor_frame) {
> + skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE + 1,
> + GFP_ATOMIC, true);
> + if (!skb_copy)
> + return;
> +
> + *(u8 *)skb_push(skb_copy, 1) = hci_skb_pkt_type(skb);
> + hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
> + hdr->len = cpu_to_le16(skb->len + 1);
> + goto out_comm;
> }
>
> /* Create a private copy with headroom */
> @@ -408,14 +432,15 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
> if (!skb_copy)
> return;
>
> - hci_sock_copy_creds(skb->sk, skb_copy);
> -
> /* Put header before the data */
> hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
> + hdr->len = cpu_to_le16(skb->len);
> +
> +out_comm:
> hdr->opcode = opcode;
> hdr->index = cpu_to_le16(hdev->id);
> - hdr->len = cpu_to_le16(skb->len);
>
> + hci_sock_copy_creds(skb->sk, skb_copy);
> hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy,
> HCI_SOCK_TRUSTED, NULL);
> kfree_skb(skb_copy);
> @@ -1868,7 +1893,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
> 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_DRV_PKT) {
> + hci_skb_pkt_type(skb) != HCI_DRV_PKT &&
> + !hci_is_vendor_frame(hdev, skb)) {
> err = -EINVAL;
> goto drop;
> }
> @@ -2017,6 +2043,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + int opt_int;
> u16 opt;
>
> BT_DBG("sk %p, opt %d", sk, optname);
> @@ -2050,6 +2077,23 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
> hci_pi(sk)->mtu = opt;
> break;
>
> + case BT_RCV_VENDOR_PKT:
> + if (hci_pi(sk)->channel != HCI_CHANNEL_USER) {
> + err = -ENOPROTOOPT;
> + break;
> + }
> +
> + err = copy_safe_from_sockptr(&opt_int, sizeof(opt_int),
> + optval, optlen);
> + if (err)
> + break;
> +
> + if (opt_int)
> + hci_sock_set_flag(sk, HCI_SOCK_RCV_VENDOR_PKT);
> + else
> + hci_sock_clear_flag(sk, HCI_SOCK_RCV_VENDOR_PKT);
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
> @@ -2132,6 +2176,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
> {
> struct sock *sk = sock->sk;
> int err = 0;
> + int opt_int;
> u16 mtu;
>
> BT_DBG("sk %p, opt %d", sk, optname);
> @@ -2153,6 +2198,19 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
> err = -EFAULT;
> break;
>
> + case BT_RCV_VENDOR_PKT:
> + if (hci_pi(sk)->channel != HCI_CHANNEL_USER) {
> + err = -ENOPROTOOPT;
> + break;
> + }
> +
> + opt_int = hci_sock_test_flag(sk, HCI_SOCK_RCV_VENDOR_PKT) ?
> + 1 : 0;
> + if (copy_to_iter(&opt_int, sizeof(opt_int),
> + &sopt->iter_out) != sizeof(opt_int))
> + err = -EFAULT;
> + break;
> +
> default:
> err = -ENOPROTOOPT;
> break;
>
> ---
> base-commit: 7f02cdbb4cf17df8aa1977eea0cf490c472a706d
> change-id: 20260719-support_vendor_hci-1fbb4449ab8e
>
> Best regards,
> --
> Zijun Hu <zijun.hu@oss.qualcomm.com>
>
--
Luiz Augusto von Dentz
[-- Attachment #2: image.png --]
[-- Type: image/png, Size: 47751 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-20 14:30 ` [PATCH RFC] " Luiz Augusto von Dentz
@ 2026-07-21 8:52 ` Zijun Hu
2026-07-21 11:36 ` Zijun Hu
2026-07-21 14:22 ` Luiz Augusto von Dentz
0 siblings, 2 replies; 9+ messages in thread
From: Zijun Hu @ 2026-07-21 8:52 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
On 7/20/2026 10:30 PM, Luiz Augusto von Dentz wrote:
>> For Qualcomm multi-subsystem BT chips, the transport wire carries both
>> BT-HCI and PERI-HCI frames. PERI is a subsystem on the chip. Take the
>> upcoming QCC2072 support 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
> I would say this was fine, but you actuall need to reclassify these to
> 0xff and then use an extra byte for the vendor opcode. Btw, you said
> you could define H4 headers like this? The spec defines either:
>
> https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/three-wire-uart-transport-layer.html#UUID-1cf959bb-57a0-e782-4324-a9bc4ee3f134_table-idm13359018144714
>
> Or
>
> https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/uart-transport-layer.html#UUID-83881875-5fbb-c366-400f-055ba726f70e_table-idm13359015052542
>
Hi Luiz,
Let us label this approach — which multiplexes the vendor indicator under 0xff — Solution A:
virtual HCI_VENDOR_PKT (0xff) + vendor indicator (0x31/0x32/0x34) + body per indicator
I did consider this solution — let us discuss it in the section below.
The PERI-HCI indicators (0x31/0x32/0x34) are defined by the Qualcomm Bluetooth Extensions
specification for the USB (Bulk Serialization mode), H4 UART, and SPI transports. They have
already been deployed in several generations of commercial multi-subsystem BT chips.
> We end up defining vendor 0xff because it is at the end of the range
> which is probably safer.
>
I am not sure that fully holds here:
First, vendors likely reason the same way — "end of range is safer", Take Qualcomm as an example:
it already used 0xfb/0xfc/0xfd/0xfe as internal indicators.
Second, current transport drivers already use 0xff in ways that break that expectation,
as shown below:
1) bpa10x uses 0xff as the wire indicator for HCI_DIAG_PKT (0xf0):
https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/bpa10x.c#L72
2) btmrvl remaps its wire vendor type 0xFE onto 0xff to carry Marvell vendor events:
https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/btmrvl_sdio.c#L797
3) hci_vhci uses it for the handshake that creates the virtual hdev:
https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/hci_vhci.c#L462
https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/hci_vhci.c#L523
>> To generically support vendor HCI frames:
>>
>> - Show them in btmon logs as they appear on the wire.
>> - Allow userspace to send/receive them via HCI_CHANNEL_USER, with a
>> socket option to control RX, defaulting off to eliminate regression
>> risk for existing applications.
> We shouldn't need to do this, we should just accept 0xff as the vendor
> packet type and then use the following byte as the real opcode (0x31,
> 0x32, 0x34). Otherwise, if we start accepting vendor packet types
> outside 0xff this will get crowded quickly and could potentially clash
> with future specs.
>
The indicator is a full u8, the entire [0x10, 0xef] indicator range is currently
unused, and vendor indicators should be deliberately chosen to avoid conflicting with
the BT SIG assignments.
>> - Add hdev->recv_vendor() to handle vendor frames in hci_rx_work() like
>> BT frame handlers.
>> - Add hci_send_vendor_frame() API similar to existing __hci_cmd_send().
> Encoding/decoding of the frames should be transparent. I'm fine adding
> code to the likes of btmon to decode vendor packets, we already have
> something similar for Intel although that uses a vendor event not a
> vendor packet (both use 0xff, causing the confusion). The user of the
> user channel shall be able to read/write starting with 0xff then
> decode/encode the next byte as the actual vendor opcode.
>
>> Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
>> ---
>> Hi Luiz
>> To address the below concerns you raised on previous
>> discussion, and inspired from your suggestion to add hdev callback:
>>
>> - Not use safer skb helper like skb_pull_data()
>> - It will skip sending to the monitor, making debugging much harder
>>
>> Looking forward to your further comments.
>>
>> Why hdev->is_vendor() instead of multiplexing vendor frames under a
>> virtual HCI_VENDOR_PKT + vendor indicator byte ?
>>
>> 1) The virtual HCI_VENDOR_PKT isn't used by the core itself
>> currently, and may be removed later.
>> 2) It makes full use of the indicator (hci_skb_pkt_type(skb)) space,
>> which is large enough to accommodate vendor frames without an
>> extra layer of nesting inside HCI_VENDOR_PKT.
>> 3) It lets userspace and btmon logs see the exact same frame as it
>> appears on the wire.
As noted above, I chose is_vendor() over Solution A. My reasoning follows,
for your reference.
1) Userspace sees exactly what is on the wire, in the same form as BT-HCI frames —
clear and consistent, as shown below:
┌───────┬──────────┬────────────┬────────────┐
│ Frame │ BT-HCI │ PERI-HCI │ PERI-HCI │
│ │ existing │ is_vendor()│ Solution A │
├───────┼──────────┼────────────┼────────────┤
│ CMD │ 0x01 … │ 0x31 … │ 0xff 0x31… │
├───────┼──────────┼────────────┼────────────┤
│ ACL │ 0x02 … │ 0x32 … │ 0xff 0x32… │
├───────┼──────────┼────────────┼────────────┤
│ EVENT │ 0x04 … │ 0x34 … │ 0xff 0x34… │
└───────┴──────────┴────────────┴────────────┘
2) is_vendor() puts PERI-HCI on an equal footing with BT-HCI, so userspace and the transport
driver can reuse the existing BT-HCI path rather than add a separate encode/decode layer —
less effort on both sides.
Could you please give me a clear direction
Solution A or is_vendor()?
I need it before I can move forward with the remaining QCC2072 btusb development work.
thank you.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-21 8:52 ` Zijun Hu
@ 2026-07-21 11:36 ` Zijun Hu
2026-07-21 14:22 ` Luiz Augusto von Dentz
1 sibling, 0 replies; 9+ messages in thread
From: Zijun Hu @ 2026-07-21 11:36 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
On 7/21/2026 4:52 PM, Zijun Hu wrote:
>>> Hi Luiz
>>> To address the below concerns you raised on previous
>>> discussion, and inspired from your suggestion to add hdev callback:
>>>
>>> - Not use safer skb helper like skb_pull_data()
>>> - It will skip sending to the monitor, making debugging much harder
>>>
>>> Looking forward to your further comments.
>>>
>>> Why hdev->is_vendor() instead of multiplexing vendor frames under a
>>> virtual HCI_VENDOR_PKT + vendor indicator byte ?
>>>
>>> 1) The virtual HCI_VENDOR_PKT isn't used by the core itself
>>> currently, and may be removed later.
>>> 2) It makes full use of the indicator (hci_skb_pkt_type(skb)) space,
>>> which is large enough to accommodate vendor frames without an
>>> extra layer of nesting inside HCI_VENDOR_PKT.
>>> 3) It lets userspace and btmon logs see the exact same frame as it
>>> appears on the wire.
> As noted above, I chose is_vendor() over Solution A. My reasoning follows,
> for your reference.
>
> 1) Userspace sees exactly what is on the wire, in the same form as BT-HCI frames —
> clear and consistent, as shown below:
> ┌───────┬──────────┬────────────┬────────────┐
> │ Frame │ BT-HCI │ PERI-HCI │ PERI-HCI │
> │ │ existing │ is_vendor()│ Solution A │
> ├───────┼──────────┼────────────┼────────────┤
> │ CMD │ 0x01 … │ 0x31 … │ 0xff 0x31… │
> ├───────┼──────────┼────────────┼────────────┤
> │ ACL │ 0x02 … │ 0x32 … │ 0xff 0x32… │
> ├───────┼──────────┼────────────┼────────────┤
> │ EVENT │ 0x04 … │ 0x34 … │ 0xff 0x34… │
> └───────┴──────────┴────────────┴────────────┘
> 2) is_vendor() puts PERI-HCI on an equal footing with BT-HCI, so userspace and the transport
> driver can reuse the existing BT-HCI path rather than add a separate encode/decode layer —
> less effort on both sides.
>
add one more entry:
3) It can let transport drivers avoid pre-processing unsolicited BT
vendor-specific events (VSE) before hci_recv_frame(), which many
transport drivers currently do (e.g. the QCA memdump event), and
handle them in recv_vendor() within the stack instead, if put in
hci_rx_work() as below:
if (hci_is_vendor_frame(hdev, skb)) {
BT_DBG("%s Vendor packet with type 0x%2.2x",
hdev->name, hci_skb_pkt_type(skb));
if (hdev->recv_vendor)
hdev->recv_vendor(hdev, skb);
else
kfree_skb(skb);
continue;
}
/* Process frame */
switch (hci_skb_pkt_type(skb)) {
case HCI_EVENT_PKT:
> Could you please give me a clear direction
> Solution A or is_vendor()?
>
> I need it before I can move forward with the remaining QCC2072 btusb development work.
>
> thank you.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-21 8:52 ` Zijun Hu
2026-07-21 11:36 ` Zijun Hu
@ 2026-07-21 14:22 ` Luiz Augusto von Dentz
2026-07-21 19:40 ` Zijun Hu
1 sibling, 1 reply; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-21 14:22 UTC (permalink / raw)
To: Zijun Hu; +Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
Hi Zijun,
On Tue, Jul 21, 2026 at 4:52 AM Zijun Hu <zijun.hu@oss.qualcomm.com> wrote:
>
> On 7/20/2026 10:30 PM, Luiz Augusto von Dentz wrote:
> >> For Qualcomm multi-subsystem BT chips, the transport wire carries both
> >> BT-HCI and PERI-HCI frames. PERI is a subsystem on the chip. Take the
> >> upcoming QCC2072 support 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
> > I would say this was fine, but you actuall need to reclassify these to
> > 0xff and then use an extra byte for the vendor opcode. Btw, you said
> > you could define H4 headers like this? The spec defines either:
> >
> > https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/three-wire-uart-transport-layer.html#UUID-1cf959bb-57a0-e782-4324-a9bc4ee3f134_table-idm13359018144714
> >
> > Or
> >
> > https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core_v6.3/out/en/host-controller-interface/uart-transport-layer.html#UUID-83881875-5fbb-c366-400f-055ba726f70e_table-idm13359015052542
> >
> Hi Luiz,
>
> Let us label this approach — which multiplexes the vendor indicator under 0xff — Solution A:
>
> virtual HCI_VENDOR_PKT (0xff) + vendor indicator (0x31/0x32/0x34) + body per indicator
> I did consider this solution — let us discuss it in the section below.
>
> The PERI-HCI indicators (0x31/0x32/0x34) are defined by the Qualcomm Bluetooth Extensions
> specification for the USB (Bulk Serialization mode), H4 UART, and SPI transports. They have
> already been deployed in several generations of commercial multi-subsystem BT chips.
I know you probably don't want to change the format between the driver
and controller—that's fair if it's already in use—but that doesn't
mean it's valid to use them as is outside the driver's scope.
> > We end up defining vendor 0xff because it is at the end of the range
> > which is probably safer.
> >
>
> I am not sure that fully holds here:
>
> First, vendors likely reason the same way — "end of range is safer", Take Qualcomm as an example:
> it already used 0xfb/0xfc/0xfd/0xfe as internal indicators.
You can use that within the driver to controller transport but not above that.
> Second, current transport drivers already use 0xff in ways that break that expectation,
> as shown below:
>
> 1) bpa10x uses 0xff as the wire indicator for HCI_DIAG_PKT (0xf0):
> https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/bpa10x.c#L72
>
> 2) btmrvl remaps its wire vendor type 0xFE onto 0xff to carry Marvell vendor events:
> https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/btmrvl_sdio.c#L797
>
> 3) hci_vhci uses it for the handshake that creates the virtual hdev:
> https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/hci_vhci.c#L462
> https://elixir.bootlin.com/linux/v7.1/source/drivers/bluetooth/hci_vhci.c#L523
Actually this reinforces my point that vendors should just multiplex
using 0xff, otherwise this will multiply. Still, I don't think this
proliferates outside the driver, so my comments hold: we should just
use 0xff to communicate vendor packets. That way, tools like btmon
understand it is a vendor packet and we don't need to build multiple
tables to express the domain of types each vendor uses.
> >> To generically support vendor HCI frames:
> >>
> >> - Show them in btmon logs as they appear on the wire.
> >> - Allow userspace to send/receive them via HCI_CHANNEL_USER, with a
> >> socket option to control RX, defaulting off to eliminate regression
> >> risk for existing applications.
> > We shouldn't need to do this, we should just accept 0xff as the vendor
> > packet type and then use the following byte as the real opcode (0x31,
> > 0x32, 0x34). Otherwise, if we start accepting vendor packet types
> > outside 0xff this will get crowded quickly and could potentially clash
> > with future specs.
> >
>
> The indicator is a full u8, the entire [0x10, 0xef] indicator range is currently
> unused, and vendor indicators should be deliberately chosen to avoid conflicting with
> the BT SIG assignments.
Well you already pointed out vendors are all over the place with their
own packet types.
> >> - Add hdev->recv_vendor() to handle vendor frames in hci_rx_work() like
> >> BT frame handlers.
> >> - Add hci_send_vendor_frame() API similar to existing __hci_cmd_send().
> > Encoding/decoding of the frames should be transparent. I'm fine adding
> > code to the likes of btmon to decode vendor packets, we already have
> > something similar for Intel although that uses a vendor event not a
> > vendor packet (both use 0xff, causing the confusion). The user of the
> > user channel shall be able to read/write starting with 0xff then
> > decode/encode the next byte as the actual vendor opcode.
> >
> >> Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
> >> ---
> >> Hi Luiz
> >> To address the below concerns you raised on previous
> >> discussion, and inspired from your suggestion to add hdev callback:
> >>
> >> - Not use safer skb helper like skb_pull_data()
> >> - It will skip sending to the monitor, making debugging much harder
> >>
> >> Looking forward to your further comments.
> >>
> >> Why hdev->is_vendor() instead of multiplexing vendor frames under a
> >> virtual HCI_VENDOR_PKT + vendor indicator byte ?
> >>
> >> 1) The virtual HCI_VENDOR_PKT isn't used by the core itself
> >> currently, and may be removed later.
> >> 2) It makes full use of the indicator (hci_skb_pkt_type(skb)) space,
> >> which is large enough to accommodate vendor frames without an
> >> extra layer of nesting inside HCI_VENDOR_PKT.
> >> 3) It lets userspace and btmon logs see the exact same frame as it
> >> appears on the wire.
>
> As noted above, I chose is_vendor() over Solution A. My reasoning follows,
> for your reference.
>
> 1) Userspace sees exactly what is on the wire, in the same form as BT-HCI frames —
> clear and consistent, as shown below:
> ┌───────┬──────────┬────────────┬────────────┐
> │ Frame │ BT-HCI │ PERI-HCI │ PERI-HCI │
> │ │ existing │ is_vendor()│ Solution A │
> ├───────┼──────────┼────────────┼────────────┤
> │ CMD │ 0x01 … │ 0x31 … │ 0xff 0x31… │
> ├───────┼──────────┼────────────┼────────────┤
> │ ACL │ 0x02 … │ 0x32 … │ 0xff 0x32… │
> ├───────┼──────────┼────────────┼────────────┤
> │ EVENT │ 0x04 … │ 0x34 … │ 0xff 0x34… │
> └───────┴──────────┴────────────┴────────────┘
> 2) is_vendor() puts PERI-HCI on an equal footing with BT-HCI, so userspace and the transport
> driver can reuse the existing BT-HCI path rather than add a separate encode/decode layer —
> less effort on both sides.
>
> Could you please give me a clear direction
> Solution A or is_vendor()?
There might be a simpler approach, just consider anything other then
the known packets a vendor packet, so it is automatically stamped with
HCI_VENDOR_PKT then push it back to driver from processing.
> I need it before I can move forward with the remaining QCC2072 btusb development work.
>
> thank you.
>
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-21 14:22 ` Luiz Augusto von Dentz
@ 2026-07-21 19:40 ` Zijun Hu
2026-07-21 19:49 ` Luiz Augusto von Dentz
0 siblings, 1 reply; 9+ messages in thread
From: Zijun Hu @ 2026-07-21 19:40 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
On 7/21/2026 10:22 PM, Luiz Augusto von Dentz wrote:
>> Could you please give me a clear direction
>> Solution A or is_vendor()?
> There might be a simpler approach, just consider anything other then
> the known packets a vendor packet, so it is automatically stamped with
> HCI_VENDOR_PKT then push it back to driver from processing.
For this simpler approach:
in TX, userspace send data to kernel by 0x31[inidicator] ....
in RX, how does kernel send data to userspace
by 0xff, 0x34(indicator)....
OR 0x34(indicator) without 0xff ?
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-21 19:40 ` Zijun Hu
@ 2026-07-21 19:49 ` Luiz Augusto von Dentz
2026-07-21 20:11 ` Zijun Hu
0 siblings, 1 reply; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-21 19:49 UTC (permalink / raw)
To: Zijun Hu; +Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
Hi Zijun,
On Tue, Jul 21, 2026 at 3:40 PM Zijun Hu <zijun.hu@oss.qualcomm.com> wrote:
>
> On 7/21/2026 10:22 PM, Luiz Augusto von Dentz wrote:
> >> Could you please give me a clear direction
> >> Solution A or is_vendor()?
> > There might be a simpler approach, just consider anything other then
> > the known packets a vendor packet, so it is automatically stamped with
> > HCI_VENDOR_PKT then push it back to driver from processing.
>
> For this simpler approach:
> in TX, userspace send data to kernel by 0x31[inidicator] ....
>
> in RX, how does kernel send data to userspace
> by 0xff, 0x34(indicator)....
> OR 0x34(indicator) without 0xff ?
For the user channel I would say it would be better to always include
0xff. That way, it remains agnostic and it won't suddenly match a
different packet type if you change the vendor or something like that.
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames
2026-07-21 19:49 ` Luiz Augusto von Dentz
@ 2026-07-21 20:11 ` Zijun Hu
0 siblings, 0 replies; 9+ messages in thread
From: Zijun Hu @ 2026-07-21 20:11 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Marcel Holtmann, Zijun Hu, linux-bluetooth, linux-kernel
On 7/22/2026 3:49 AM, Luiz Augusto von Dentz wrote:
> For the user channel I would say it would be better to always include
> 0xff. That way, it remains agnostic and it won't suddenly match a
> different packet type if you change the vendor or something like that.
so, perhaps, you original proposal(Option A) is better and easier to implement than this
simpler one since it uses consistent way to decide vendor HCI frame.
1)
userspace <-> kernel : 0xff + vendor hci indicator.
2)
for RX, transport driver stamp with HCI_VENDOR_PKT before hci_recv_frame()
For TX, transport driver pull one more byte(indicator) for HCI_VENDOR_PKT frame
tell type of vendor hci frames.
let me also give a implementation for this 'Option A' as well.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-07-21 20:11 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20 5:13 [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames Zijun Hu
2026-07-20 7:28 ` [RFC] " bluez.test.bot
2026-07-20 14:30 ` [PATCH RFC] " Luiz Augusto von Dentz
2026-07-21 8:52 ` Zijun Hu
2026-07-21 11:36 ` Zijun Hu
2026-07-21 14:22 ` Luiz Augusto von Dentz
2026-07-21 19:40 ` Zijun Hu
2026-07-21 19:49 ` Luiz Augusto von Dentz
2026-07-21 20:11 ` Zijun Hu
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.