Re: [PATCH] Bluetooth: ISO: add timestamp for outgoing HCI ISO

3 messages, 2 authors, 2026-03-19 · open the first message on its own page

Re: [PATCH] Bluetooth: ISO: add timestamp for outgoing HCI ISO

From: Tailu Shi <hidden>
Date: 2026-03-18 11:09:45

Hi Pauli,

Thanks for your valuable feedback in your previous email.

On Fri, 13 Mar 2026 22:50:33 +0200, Pauli worte:
TMAP likely assumes the controller implements the specification so that
providing correct time stamps would be possible, but when they in
practice don't, spec compliance seems dubious and not setting known-
wrong Time_Stamp on packets sounds safer.
Okay! I see. Thank you.
I think we will want LE Read ISO TX Sync support at least, as this will
solve the CIS/BIS synchronization problem, as user application can then
know what ISO packets controller considers are playing at the same time
(if controller actually implements this command properly).

It would be good to have information how different manufacturers have
interpreted the specification vs. LE Read ISO TX Sync. Right now, I can
only read what Zephyr is doing, and tested some other controllers
return zeros. If you have access to controllers that implement this
command, it would be very useful to know their precise interpretation.
Yes, I can confirm that we have a working controller that implements this
command. However, I am unable to disclose any specific hardware details
due to our company's confidentiality policy. I appreciate your understanding.

To facilitate upstream integration, I can give a concise overview of the
implementation of the LE Read ISO TX Sync command on the controller side.

The controller returns information about the currently ongoing CIG/BIG.
- The sequence number is tied to the currently active CIG or BIG event.
  It originates from the HOST and is extracted from ISO packet.
- The timestamp is the reference point of the ongoing CIG (or the anchor
  point of the BIG). It is taken from the controller's free running clock.

 Partial btsnoop output:

 *No.* -- *Time* ----------------------*Info* -------------------------*Source* ------------- *Destination* --------- *Protocol*
 46971 | 72.066457 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46972 | 72.067106 | Handle: 0xa01, SeqNo: 4408, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46973 | 72.067118 | Handle: 0xa02, SeqNo: 4408, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data

 46981 | 72.072452 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT
 46982 | 72.072462 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46984 | 72.074644 | Handle: 0xa01, SeqNo: 4409, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46985 | 72.074683 | Handle: 0xa01, SeqNo: 4410, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46986 | 72.074695 | Handle: 0xa02, SeqNo: 4409, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data
 46987 | 72.074701 | Handle: 0xa02, SeqNo: 4410, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data
 
 46992 | 72.077452 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT
 46993 | 72.077461 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46994 | 72.079450 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT

// sdu_interval: 7.5ms, Packing: Sequential, Unframed.
// HOST transmits SDU, SeqNum = 4408, handle = 0x0a01
 Frame 46972: 72 bytes on wire (576 bits), 72 bytes captured (576 bits)
 Bluetooth HCI ISO Packet
     .... 1010 0000 0001 = Connection Handle: 0xa01
     ..10 .... .... .... = PB Flag: Complete SDU (0x2)
     .1.. .... .... .... = Timestamp present: True
     0... .... .... .... = Reserved: 0x0
     ..00 0000 0100 0100 = Data Length: 68
     Data
     [Connect in frame: 6122]
     Bluetooth ISO Data
     Timestamp: 193973666µs
     Sequence Number: 4408
     .... 0000 0011 1100 = SDU Length: 60
     SDU

// CMPLT reported in frame frame #46981 for the command issued in frame #46971 
 Frame 46981: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a01
     SDU Packet Sequence Number: 4407
     SDU Packet Timestamp: 212355139µs
     SDU Packet Offset: 0µs
     [Command in frame: 46971]
     [Command-Response Delta: 5.995ms]

// CMPLT reported in frame frame #46992 for the command issued in frame #46982 
 Frame 46992: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a01
     SDU Packet Sequence Number: 4408
     SDU Packet Timestamp: 212362639µs
     SDU Packet Offset: 0µs
     [Command in frame: 46982]
     [Command-Response Delta: 4.99ms]

// CMPLT reported in frame frame #46994 for the command issued in frame #46993 
 Frame 46994: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a02
     SDU Packet Sequence Number: 4408
     SDU Packet Timestamp: 212356039µs
     SDU Packet Offset: 0µs
     [Command in frame: 46993]
     [Command-Response Delta: 1.989ms]

I hope this information will be of some help.
@Luiz: thoughts on the below?

For setting Time_Stamp, I think there should be plan how userspace
application uses the new APIs to produce working sound on different
controllers, without having to maintain a quirk list for each
controller. I think quirk lists should be in the kernel driver.

Depending on what hardware is doing, we could add driver quirks along
the lines of

HCI_QUIRK_BROKEN_LE_READ_ISO_TX_SYNC, /* always zero / dummy value */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_HOST_PREV, /* most recent from host */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_NOCP, /* most recent completed */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_HOST_SEQ, /* uses host seqnum */

and should affect what skb is reported in hci_cc_le_read_iso_tx_sync(),
and if SOF_TIMESTAMPING_TX_HARDWARE capability is reported in ethtool.

(Eg. IIUC Zephyr HCI appears to interpret LE Read ISO TX Sync meaning
as follows:

- The sequence number is controller-assigned.
- The response corresponds to the ISO data packet that
the controller *most recently received from host*.

This interpretation makes sense, but is different from my patch and
iiuc also different from what Luiz had in mind.)

If no quirk is set but the command is supported, probably should emit
visible error in kernel log, or something along such lines, so the
driver authors can see something is not right.

But knowing what is the precise hardware behavior for each
chip/firmware seems a bit involved, probably should have some testing
tool for this.

***

What it seems userspace can do:

Userspace application would check via ethtool
SOF_TIMESTAMPING_TX_HARDWARE capability to know whether controller
handles Time_Stamp in a non-broken way. (For testing, there could be a
switch to force it to use timestamps regardless.)

It could then rely that the HW timestamp corresponds to specific packet
it sent earlier. It could assume that in its clock domain, the HW
timestamp is after SND timestamp, before COMPLETION timestamp, and
likely close to COMPLETION.

Since it doesn't know the exact correspondence, probably it would
always have to ensure a few packets are always queued in controller.

There also needs to be a way to recover from a situation where the
application is setting timestamps that are in the past in controller
clock domain.

The controller does not necessarily send them I think, but will report
them completed immediately (after older ones get flushed from its
buffers).

In this case LE Read ISO TX Sync timestamp would not advance, as no
packets get sent. Kernel would also not send HW timestamps to user
application in this case.

If the application when it doesn't receive HW timestamps from kernel,
just increments its send timestamp by SDU interval for each packet, it
eventually catches up the controller clock, and receives HW timestamp
and can resync.

Alternatively the user application may stop setting timestamps, in
which case controller shall send it in next SDU interval, and in this
case HW timestamp is received and user application can resync.

Or, alternatively, controller will decide the clock is out of sync and
send the SDU regardless, in which case user application will receive a
new HW timestamp and can again resync. 
 

Thanks!

Best regards!

Tailu

Re: [PATCH] Bluetooth: ISO: add timestamp for outgoing HCI ISO

From: Pauli Virtanen <hidden>
Date: 2026-03-18 21:35:57

Hi,

ke, 2026-03-18 kello 19:09 +0800, Tailu Shi kirjoitti:
Hi Pauli,

Thanks for your valuable feedback in your previous email.

On Fri, 13 Mar 2026 22:50:33 +0200, Pauli worte:
quoted
TMAP likely assumes the controller implements the specification so that
providing correct time stamps would be possible, but when they in
practice don't, spec compliance seems dubious and not setting known-
wrong Time_Stamp on packets sounds safer.
Okay! I see. Thank you.
quoted
I think we will want LE Read ISO TX Sync support at least, as this will
solve the CIS/BIS synchronization problem, as user application can then
know what ISO packets controller considers are playing at the same time
(if controller actually implements this command properly).

It would be good to have information how different manufacturers have
interpreted the specification vs. LE Read ISO TX Sync. Right now, I can
only read what Zephyr is doing, and tested some other controllers
return zeros. If you have access to controllers that implement this
command, it would be very useful to know their precise interpretation.
Yes, I can confirm that we have a working controller that implements this
command. However, I am unable to disclose any specific hardware details
due to our company's confidentiality policy. I appreciate your understanding.

To facilitate upstream integration, I can give a concise overview of the
implementation of the LE Read ISO TX Sync command on the controller side.

The controller returns information about the currently ongoing CIG/BIG.
- The sequence number is tied to the currently active CIG or BIG event.
  It originates from the HOST and is extracted from ISO packet.
- The timestamp is the reference point of the ongoing CIG (or the anchor
  point of the BIG). It is taken from the controller's free running clock.
Thank you, this is very useful, and probably sufficient information for
implementation. I'll still need to check MT7925 behavior on this, but
it is probably enough to start with.
 Partial btsnoop output:

 *No.* -- *Time* ----------------------*Info* -------------------------*Source* ------------- *Destination* --------- *Protocol*
 46971 | 72.066457 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46972 | 72.067106 | Handle: 0xa01, SeqNo: 4408, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46973 | 72.067118 | Handle: 0xa02, SeqNo: 4408, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data

 46981 | 72.072452 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT
 46982 | 72.072462 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46984 | 72.074644 | Handle: 0xa01, SeqNo: 4409, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46985 | 72.074683 | Handle: 0xa01, SeqNo: 4410, SDU length: 60 | 44:67:93:7e:00:9d | e4:e3:a6:e7:45:45 (INZONE Buds) | ISO Data
 46986 | 72.074695 | Handle: 0xa02, SeqNo: 4409, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data
 46987 | 72.074701 | Handle: 0xa02, SeqNo: 4410, SDU length: 60 | 44:67:93:7e:00:9d | e4:ec:1a:c4:cf:01 (INZONE Buds) | ISO Data
 
 46992 | 72.077452 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT
 46993 | 72.077461 | Sent LE Read ISO Tx Sync                   | host              | controller                      | HCI_CMD
 46994 | 72.079450 | Rcvd Command Complete (LE Read ISO Tx Sync)| controller        | host                            | HCI_EVT

// sdu_interval: 7.5ms, Packing: Sequential, Unframed.
// HOST transmits SDU, SeqNum = 4408, handle = 0x0a01
 Frame 46972: 72 bytes on wire (576 bits), 72 bytes captured (576 bits)
 Bluetooth HCI ISO Packet
     .... 1010 0000 0001 = Connection Handle: 0xa01
     ..10 .... .... .... = PB Flag: Complete SDU (0x2)
     .1.. .... .... .... = Timestamp present: True
     0... .... .... .... = Reserved: 0x0
     ..00 0000 0100 0100 = Data Length: 68
     Data
     [Connect in frame: 6122]
     Bluetooth ISO Data
     Timestamp: 193973666µs
     Sequence Number: 4408
     .... 0000 0011 1100 = SDU Length: 60
     SDU

// CMPLT reported in frame frame #46981 for the command issued in frame #46971 
 Frame 46981: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a01
     SDU Packet Sequence Number: 4407
     SDU Packet Timestamp: 212355139µs
     SDU Packet Offset: 0µs
     [Command in frame: 46971]
     [Command-Response Delta: 5.995ms]

// CMPLT reported in frame frame #46992 for the command issued in frame #46982 
 Frame 46992: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a01
     SDU Packet Sequence Number: 4408
     SDU Packet Timestamp: 212362639µs
     SDU Packet Offset: 0µs
     [Command in frame: 46982]
     [Command-Response Delta: 4.99ms]

// CMPLT reported in frame frame #46994 for the command issued in frame #46993 
 Frame 46994: 17 bytes on wire (136 bits), 17 bytes captured (136 bits)
 Bluetooth HCI Event - Command Complete
     Event Code: Command Complete (0x0e)
     Parameter Total Length: 15
     Number of Allowed Command Packets: 1
     Command Opcode: LE Read ISO Tx Sync (0x2061)
     Status: Success (0x00)
     CIS/BIS Handle: 0x0a02
     SDU Packet Sequence Number: 4408
     SDU Packet Timestamp: 212356039µs
     SDU Packet Offset: 0µs
     [Command in frame: 46993]
     [Command-Response Delta: 1.989ms]

I hope this information will be of some help.
quoted
@Luiz: thoughts on the below?

For setting Time_Stamp, I think there should be plan how userspace
application uses the new APIs to produce working sound on different
controllers, without having to maintain a quirk list for each
controller. I think quirk lists should be in the kernel driver.

Depending on what hardware is doing, we could add driver quirks along
the lines of

HCI_QUIRK_BROKEN_LE_READ_ISO_TX_SYNC, /* always zero / dummy value */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_HOST_PREV, /* most recent from host */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_NOCP, /* most recent completed */
HCI_QUIRK_LE_READ_ISO_TX_SYNC_HOST_SEQ, /* uses host seqnum */

and should affect what skb is reported in hci_cc_le_read_iso_tx_sync(),
and if SOF_TIMESTAMPING_TX_HARDWARE capability is reported in ethtool.

(Eg. IIUC Zephyr HCI appears to interpret LE Read ISO TX Sync meaning
as follows:

- The sequence number is controller-assigned.
- The response corresponds to the ISO data packet that
the controller *most recently received from host*.

This interpretation makes sense, but is different from my patch and
iiuc also different from what Luiz had in mind.)

If no quirk is set but the command is supported, probably should emit
visible error in kernel log, or something along such lines, so the
driver authors can see something is not right.

But knowing what is the precise hardware behavior for each
chip/firmware seems a bit involved, probably should have some testing
tool for this.

***

What it seems userspace can do:

Userspace application would check via ethtool
SOF_TIMESTAMPING_TX_HARDWARE capability to know whether controller
handles Time_Stamp in a non-broken way. (For testing, there could be a
switch to force it to use timestamps regardless.)

It could then rely that the HW timestamp corresponds to specific packet
it sent earlier. It could assume that in its clock domain, the HW
timestamp is after SND timestamp, before COMPLETION timestamp, and
likely close to COMPLETION.

Since it doesn't know the exact correspondence, probably it would
always have to ensure a few packets are always queued in controller.

There also needs to be a way to recover from a situation where the
application is setting timestamps that are in the past in controller
clock domain.

The controller does not necessarily send them I think, but will report
them completed immediately (after older ones get flushed from its
buffers).

In this case LE Read ISO TX Sync timestamp would not advance, as no
packets get sent. Kernel would also not send HW timestamps to user
application in this case.

If the application when it doesn't receive HW timestamps from kernel,
just increments its send timestamp by SDU interval for each packet, it
eventually catches up the controller clock, and receives HW timestamp
and can resync.

Alternatively the user application may stop setting timestamps, in
which case controller shall send it in next SDU interval, and in this
case HW timestamp is received and user application can resync.

Or, alternatively, controller will decide the clock is out of sync and
send the SDU regardless, in which case user application will receive a
new HW timestamp and can again resync. 
 

Thanks!

Best regards!

Tailu
-- 
Pauli Virtanen

Re: [PATCH] Bluetooth: ISO: add timestamp for outgoing HCI ISO

From: Tailu Shi <hidden>
Date: 2026-03-19 05:30:19

On Wed, 18 Mar 2026 23:35:37 +0200 Pauli Virtanen wrote: 
Thank you, this is very useful, and probably sufficient information for
implementation. I'll still need to check MT7925 behavior on this, but
it is probably enough to start with.
Dear Pauli,

Thank you for the update. I'm glad that the information helps advance
the patch integration.

Thanks again,

Best regards,

Tailu
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help