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