BTSNOOP(7)

πŸ’– btsnoop(7) β€” BTSnoop/Monitor protocol documentation πŸ’–

okay so this is btsnoop(7), straight out of the Linux System Administration section of the manual β€” and bestie, it's literally the blueprint for how HCI/PHY traffic gets recorded and transported. no cap. ✨


✨ SYNOPSIS ✨

This document describes the BTSnoop/Monitor formats used to record and transport HCI/PHY traffic. The formats are used by tools such as btmon and the BlueZ btsnoop implementation.

like, if you've ever wondered how btmon and the BlueZ btsnoop girlies do their thing? it's this. 🫢


🌸 DESCRIPTION 🌸

πŸŽ€ Opcode definitions

The following opcodes are used by the BTSnoop/Monitor formats. The numeric values match the definitions in src/shared/btsnoop.h.

πŸ‘‘ BTSnoop opcodes

  • BTSNOOP_OPCODE_NEW_INDEX β€” code 0 (dec) / 0x0000 (hex) β€” New controller index (parameters: type, bus, bdaddr, name)
  • BTSNOOP_OPCODE_DEL_INDEX β€” code 1 (dec) / 0x0001 (hex) β€” Deleted controller index
  • BTSNOOP_OPCODE_COMMAND_PKT β€” code 2 (dec) / 0x0002 (hex) β€” HCI Command packet
  • BTSNOOP_OPCODE_EVENT_PKT β€” code 3 (dec) / 0x0003 (hex) β€” HCI Event packet
  • BTSNOOP_OPCODE_ACL_TX_PKT β€” code 4 (dec) / 0x0004 (hex) β€” Outgoing ACL packet
  • BTSNOOP_OPCODE_ACL_RX_PKT β€” code 5 (dec) / 0x0005 (hex) β€” Incoming ACL packet
  • BTSNOOP_OPCODE_SCO_TX_PKT β€” code 6 (dec) / 0x0006 (hex) β€” Outgoing SCO packet
  • BTSNOOP_OPCODE_SCO_RX_PKT β€” code 7 (dec) / 0x0007 (hex) β€” Incoming SCO packet
  • BTSNOOP_OPCODE_OPEN_INDEX β€” code 8 (dec) / 0x0008 (hex) β€” HCI transport for the specified controller opened
  • BTSNOOP_OPCODE_CLOSE_INDEX β€” code 9 (dec) / 0x0009 (hex) β€” HCI transport for the specified controller closed
  • BTSNOOP_OPCODE_INDEX_INFO β€” code 10 (dec) / 0x000a (hex) β€” Index information (parameters: bdaddr, manufacturer)
  • BTSNOOP_OPCODE_VENDOR_DIAG β€” code 11 (dec) / 0x000b (hex) β€” Vendor diagnostic information
  • BTSNOOP_OPCODE_SYSTEM_NOTE β€” code 12 (dec) / 0x000c (hex) β€” System note
  • BTSNOOP_OPCODE_USER_LOGGING β€” code 13 (dec) / 0x000d (hex) β€” User logging (parameters: priority, ident_len, ident)
  • BTSNOOP_OPCODE_CTRL_OPEN β€” code 14 (dec) / 0x000e (hex) β€” Control channel opened
  • BTSNOOP_OPCODE_CTRL_CLOSE β€” code 15 (dec) / 0x000f (hex) β€” Control channel closed
  • BTSNOOP_OPCODE_CTRL_COMMAND β€” code 16 (dec) / 0x0010 (hex) β€” Control command packet
  • BTSNOOP_OPCODE_CTRL_EVENT β€” code 17 (dec) / 0x0011 (hex) β€” Control event packet
  • BTSNOOP_OPCODE_ISO_TX_PKT β€” code 18 (dec) / 0x0012 (hex) β€” Outgoing ISO packet
  • BTSNOOP_OPCODE_ISO_RX_PKT β€” code 19 (dec) / 0x0013 (hex) β€” Incoming ISO packet

πŸ¦‹ New Index

Code: 0x0000

Parameters:

  • Type (1 octet)
  • Bus (1 octet)
  • BD_Addr (6 octets)
  • Name (8 octets)

This opcode indicates that a new controller instance with a given index was added. With some transports (for example a single TTY device) the index is implicitly 0.

lowkey important: that implicit 0 thing is transport-dependent, so don't go assuming it everywhere πŸ‘€

πŸ“ Deleted Index

Code: 0x0001

Indicates that the controller with a specific index was removed.

bye bestie πŸ‘‹ that index is gone.


🧚 TTY-based protocol

The TTY protocol used by btmon with the --tty option is a little-endian packet format. Each packet uses this header:

struct tty_hdr {
    uint16_t data_len;
    uint16_t opcode;
    uint8_t  flags;
    uint8_t  hdr_len;
    uint8_t  ext_hdr[0];
} __attribute__ ((packed));

The payload starts at ext_hdr + hdr_len and has length data_len - 4 - hdr_len.

like, that math is literally doing the most but it's giving exact byte boundaries, periodt. πŸ’…


🌈 Extended header format

Each extension field is encoded as:

struct {
    uint8_t type;
    uint8_t value[length];
};

Defined types:

…and the actual list of defined types is literally in part 2, bestie β€” stay tuned 🫢✨

✨ Extended header types ✨

Okay so these are the little extension fields you can slap onto the end of a packet record, and each one has a type number and a length. Here's the whole lineup, bestie:

  • Type 1, length 1 byte β€” Command drops (like, dropped HCI command packets 🫳)
  • Type 2, length 1 byte β€” Event drops (dropped HCI event packets, rip)
  • Type 3, length 1 byte β€” ACL TX drops
  • Type 4, length 1 byte β€” ACL RX drops
  • Type 5, length 1 byte β€” SCO TX drops
  • Type 6, length 1 byte β€” SCO RX drops
  • Type 7, length 1 byte β€” Other drops
  • Type 8, length 4 bytes β€” a 32-bit timestamp, measured in 1/10th ms ⏱️

The drops fields are literally just a count β€” the number of packets the implementation had to drop since the last reported drop count. Not cumulative, bestie, it resets each time. πŸ’…

And here's the rule you cannot skip: extension fields must be sorted by increasing type so that unknown types can be skipped and the payload location discovered. Yes, this is load-bearing. Do not come crying to me when you write them out of order and everything falls apart 😭


🌈 RESOURCES 🌈

http://www.bluez.org

That's the spot. Go forth. ✨


πŸ™ˆ REPORTING BUGS πŸ™ˆ

linux-bluetooth@vger.kernel.org

Found something cursed? Email the list, don't suffer in silence, fr fr.


πŸ¦‹ SEE ALSO πŸ¦‹

btmon(1)

The other main character, obvi. Go read that one too πŸ‘‘


Free use of this software is granted under the terms of the GNU Lesser General Public Licenses (LGPL).

Like, actually free. Slay. πŸŽ€


And that's the whole page, bestie β€” brought to you by BlueZ, dated February 2026, filed under BTSNOOP(7) in the manual pages. Go capture some packets, king. πŸ’…βœ¨

πŸ“¦ where this page comes from