Re: [PATCH BlueZ v2 3/3] monitor: Support vendor HCI packets
From: Luiz Augusto von Dentz
Date: Mon Aug 31 2026 - 14:06:55 EST
Hi Zijun,
On Mon, Aug 31, 2026 at 1:39 AM Zijun Hu <zijun.hu@xxxxxxxxxxxxxxxx> wrote:
>
> Vendor HCI packets have 0xff as packet type.
>
> Support them in tool btmon.
>
> ---
> Changes since previous version:
> - Rename COLOR_VENDOR_HCI to COLOR_HCI_VENDOR
> - Rename packet_vendor_hci() to packet_hci_vendor()
> - Improve the commit title and message
> ---
> doc/btsnoop-protocol.rst | 8 ++++++++
> monitor/analyze.c | 23 +++++++++++++++++++++++
> monitor/packet.c | 26 ++++++++++++++++++++++++++
> monitor/packet.h | 2 ++
> src/shared/btsnoop.h | 2 ++
> 5 files changed, 61 insertions(+)
>
> diff --git a/doc/btsnoop-protocol.rst b/doc/btsnoop-protocol.rst
> index a875db63a9dd..81cd8fdc71ae 100644
> --- a/doc/btsnoop-protocol.rst
> +++ b/doc/btsnoop-protocol.rst
> @@ -111,16 +111,24 @@ values match the definitions in ``src/shared/btsnoop.h``.
> * - BTSNOOP_OPCODE_ISO_TX_PKT
> - 18
> - 0x0012
> - Outgoing ISO packet
> * - BTSNOOP_OPCODE_ISO_RX_PKT
> - 19
> - 0x0013
> - Incoming ISO packet
> + * - BTSNOOP_OPCODE_VENDOR_TX_PKT
> + - 22
> + - 0x0016
> + - Outgoing vendor HCI packet
> + * - BTSNOOP_OPCODE_VENDOR_RX_PKT
> + - 23
> + - 0x0017
> + - Incoming vendor HCI packet
>
> New Index
> ---------
>
> Code: 0x0000
>
> Parameters:
>
> diff --git a/monitor/analyze.c b/monitor/analyze.c
> index de9c23603a21..59e8ac819272 100644
> --- a/monitor/analyze.c
> +++ b/monitor/analyze.c
> @@ -39,16 +39,17 @@ struct hci_dev {
> struct timeval time_added;
> struct timeval time_removed;
> unsigned long num_hci;
> unsigned long num_cmd;
> unsigned long num_evt;
> unsigned long num_acl;
> unsigned long num_sco;
> unsigned long num_iso;
> + unsigned long vendor_hci;
> unsigned long vendor_diag;
> unsigned long system_note;
> unsigned long user_log;
> unsigned long ctrl_msg;
> unsigned long unknown;
> uint16_t manufacturer;
> struct queue *conn_list;
> };
> @@ -460,16 +461,17 @@ static void dev_destroy(void *data)
> printf("\n");
>
>
> printf(" %lu commands\n", dev->num_cmd);
> printf(" %lu events\n", dev->num_evt);
> printf(" %lu ACL packets\n", dev->num_acl);
> printf(" %lu SCO packets\n", dev->num_sco);
> printf(" %lu ISO packets\n", dev->num_iso);
> + printf(" %lu vendor HCI packets\n", dev->vendor_hci);
> printf(" %lu vendor diagnostics\n", dev->vendor_diag);
> printf(" %lu system notes\n", dev->system_note);
> printf(" %lu user logs\n", dev->user_log);
> printf(" %lu control messages \n", dev->ctrl_msg);
> printf(" %lu unknown opcodes\n", dev->unknown);
> queue_destroy(dev->conn_list, conn_destroy);
> printf("\n");
>
> @@ -1357,16 +1359,29 @@ static void iso_pkt(struct timeval *tv, uint16_t index, bool out,
>
> if (out) {
> conn_pkt_tx(conn, tv, size - sizeof(*hdr), NULL);
> } else {
> conn_pkt_rx(conn, tv, size - sizeof(*hdr), NULL);
> }
> }
>
> +static void vendor_hci_pkt(struct timeval *tv, uint16_t index, bool out,
> + const void *data, uint16_t size)
> +{
> + struct hci_dev *dev;
> +
> + dev = dev_lookup(index);
> + if (!dev)
> + return;
> +
> + dev->num_hci++;
> + dev->vendor_hci++;
> +}
> +
> static void unknown_opcode(struct timeval *tv, uint16_t index,
> const void *data, uint16_t size)
> {
> struct hci_dev *dev;
>
> dev = dev_lookup(index);
> if (!dev)
> return;
> @@ -1463,16 +1478,24 @@ void analyze_trace(const char *path)
> case BTSNOOP_OPCODE_ISO_TX_PKT:
> num_frames++;
> iso_pkt(&tv, index, true, buf, pktlen);
> break;
> case BTSNOOP_OPCODE_ISO_RX_PKT:
> num_frames++;
> iso_pkt(&tv, index, false, buf, pktlen);
> break;
> + case BTSNOOP_OPCODE_VENDOR_TX_PKT:
> + num_frames++;
> + vendor_hci_pkt(&tv, index, true, buf, pktlen);
> + break;
> + case BTSNOOP_OPCODE_VENDOR_RX_PKT:
> + num_frames++;
> + vendor_hci_pkt(&tv, index, false, buf, pktlen);
> + break;
> default:
> unknown_opcode(&tv, index, buf, pktlen);
> break;
> }
>
> num_packets++;
> }
>
> diff --git a/monitor/packet.c b/monitor/packet.c
> index 0d3b23cc3fb7..7adcf64a3631 100644
> --- a/monitor/packet.c
> +++ b/monitor/packet.c
> @@ -67,16 +67,17 @@
>
> #define COLOR_HCI_COMMAND COLOR_BLUE
> #define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG
> #define COLOR_HCI_EVENT COLOR_MAGENTA
> #define COLOR_HCI_EVENT_UNKNOWN COLOR_WHITE_BG
> #define COLOR_HCI_ACLDATA COLOR_CYAN
> #define COLOR_HCI_SCODATA COLOR_YELLOW
> #define COLOR_HCI_ISODATA COLOR_YELLOW
> +#define COLOR_HCI_VENDOR COLOR_GREEN
>
> #define COLOR_UNKNOWN_ERROR COLOR_WHITE_BG
> #define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG
> #define COLOR_UNKNOWN_COMMAND_BIT COLOR_WHITE_BG
> #define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG
> #define COLOR_UNKNOWN_LE_STATES COLOR_WHITE_BG
> #define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG
> #define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG
> @@ -4523,16 +4524,22 @@ void packet_monitor(struct timeval *tv, struct ucred *cred,
> packet_hci_scodata(tv, cred, index, true, data, size);
> break;
> case BTSNOOP_OPCODE_ISO_TX_PKT:
> packet_hci_isodata(tv, cred, index, false, data, size);
> break;
> case BTSNOOP_OPCODE_ISO_RX_PKT:
> packet_hci_isodata(tv, cred, index, true, data, size);
> break;
> + case BTSNOOP_OPCODE_VENDOR_TX_PKT:
> + packet_hci_vendor(tv, cred, index, false, data, size);
> + break;
> + case BTSNOOP_OPCODE_VENDOR_RX_PKT:
> + packet_hci_vendor(tv, cred, index, true, data, size);
> + break;
> case BTSNOOP_OPCODE_OPEN_INDEX:
> if (index < MAX_INDEX)
> addr2str(index_list[index].bdaddr, str);
> else
> sprintf(str, "00:00:00:00:00:00");
>
> packet_open_index(tv, index, str);
> break;
> @@ -14650,16 +14657,35 @@ malformed:
> print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
> "Malformed ISO Data RX packet", NULL, NULL);
> else
> print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
> "Malformed ISO Data TX packet", NULL, NULL);
> packet_hexdump(data, size);
> }
>
> +void packet_hci_vendor(struct timeval *tv, struct ucred *cred, uint16_t index,
> + bool in, const void *data, uint16_t size)
> +{
> + char extra_str[16];
> +
> + if (index >= MAX_INDEX) {
> + print_field("Invalid index (%d).", index);
> + return;
> + }
> +
> + index_list[index].frame++;
> +
> + sprintf(extra_str, "(len %d)", size);
> + print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_VENDOR,
> + "Vendor HCI Packet", NULL, extra_str);
> +
> + packet_hexdump(data, size);
> +}
There is already support for vendor traffic:
static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
{
uint16_t manufacturer, msft_opcode;
if (index_current < MAX_INDEX) {
manufacturer = index_list[index_current].manufacturer;
msft_opcode = index_list[index_current].msft_opcode;
} else {
manufacturer = fallback_manufacturer;
msft_opcode = BT_HCI_CMD_NOP;
}
if (msft_opcode != BT_HCI_CMD_NOP &&
cmd_opcode_ocf(msft_opcode) == ocf)
return msft_vendor_ocf();
switch (manufacturer) {
case COMPANY_ID_INTEL:
return intel_vendor_ocf(ocf);
case COMPANY_ID_BROADCOM:
return broadcom_vendor_ocf(ocf);
}
return NULL;
}
static const struct vendor_evt *current_vendor_evt(const void *data,
uint8_t size, int *consumed_size)
{
uint16_t manufacturer;
uint8_t evt = *((const uint8_t *) data);
if (msft_event_prefix_match(data, size)) {
*consumed_size = index_list[index_current].msft_evt_len;
return msft_vendor_evt();
}
/* A regular vendor event consumes 1 byte. */
*consumed_size = 1;
if (index_current < MAX_INDEX)
manufacturer = index_list[index_current].manufacturer;
else
manufacturer = fallback_manufacturer;
switch (manufacturer) {
case COMPANY_ID_INTEL:
return intel_vendor_evt(data, consumed_size);
case COMPANY_ID_BROADCOM:
return broadcom_vendor_evt(evt);
}
return NULL;
}
So this not only fails to use the manufacturer info to hook into its
own dedicated file but it makes the vendor packets completely opaque
by just hexdumping their content.
> void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
> const void *data, uint16_t size)
> {
> uint32_t cookie;
> uint16_t format;
> char channel[11];
>
> if (size < 6) {
> diff --git a/monitor/packet.h b/monitor/packet.h
> index 73a86f64b242..8c519eca61c1 100644
> --- a/monitor/packet.h
> +++ b/monitor/packet.h
> @@ -130,16 +130,18 @@ void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
> void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index,
> const void *data, uint16_t size);
> void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index,
> bool in, const void *data, uint16_t size);
> void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
> bool in, const void *data, uint16_t size);
> void packet_hci_isodata(struct timeval *tv, struct ucred *cred, uint16_t index,
> bool in, const void *data, uint16_t size);
> +void packet_hci_vendor(struct timeval *tv, struct ucred *cred, uint16_t index,
> + bool in, const void *data, uint16_t size);
>
> void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
> const void *data, uint16_t size);
> void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
> const void *data, uint16_t size);
> void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
> const void *data, uint16_t size);
> void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
> diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
> index c24755d56729..efa87a8f2971 100644
> --- a/src/shared/btsnoop.h
> +++ b/src/shared/btsnoop.h
> @@ -37,16 +37,18 @@
> #define BTSNOOP_OPCODE_SYSTEM_NOTE 12
> #define BTSNOOP_OPCODE_USER_LOGGING 13
> #define BTSNOOP_OPCODE_CTRL_OPEN 14
> #define BTSNOOP_OPCODE_CTRL_CLOSE 15
> #define BTSNOOP_OPCODE_CTRL_COMMAND 16
> #define BTSNOOP_OPCODE_CTRL_EVENT 17
> #define BTSNOOP_OPCODE_ISO_TX_PKT 18
> #define BTSNOOP_OPCODE_ISO_RX_PKT 19
> +#define BTSNOOP_OPCODE_VENDOR_TX_PKT 22
> +#define BTSNOOP_OPCODE_VENDOR_RX_PKT 23
>
> #define BTSNOOP_MAX_PACKET_SIZE (1486 + 4)
>
> #define BTSNOOP_TYPE_PRIMARY 0
> #define BTSNOOP_TYPE_AMP 1
>
> #define BTSNOOP_BUS_VIRTUAL 0
> #define BTSNOOP_BUS_USB 1
>
> --
> 2.34.1
>
--
Luiz Augusto von Dentz