[PATCH BlueZ v2 3/3] monitor: Support vendor HCI packets

From: Zijun Hu

Date: Mon Aug 31 2026 - 01:40:16 EST


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);
+}
+
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