Re: [PATCH RFC] Bluetooth: Add generic support for vendor HCI frames

From: Luiz Augusto von Dentz

Date: Tue Jul 21 2026 - 10:33:59 EST


Hi Zijun,

On Tue, Jul 21, 2026 at 4:52 AM Zijun Hu <zijun.hu@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx>
> >> ---
> >> 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