Skip to contentSkip to conversation

Bluetooth: ISO: support setting send Time_Stamp and LE Read ISO TX Sync

RFC PATCH In review Pauli Virtanen 3 participants 8 messages Mar 11 – Apr 9

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover Bluetooth: ISO: support setting send Time_Stamp and LE Read ISO TX Sync

Add support for userspace setting BT_SCM_PKT_SEQNUM and BT_SCM_PKT_ISO_TS as CMSG to provide timestamps and sequence numbers to controller.

Also support obtaining sent packet timestamps via HCI LE Read ISO TX Sync and transmitting those to user as HW timestamps.

User-provided BT_SCM_PKT_SEQNUM may make sense if userspace is not sending SDUs every interval, and it is in the best position to determine when it wanted things to be sent. Behavior of course varies per controller, so not sure how useful this is in practice, and if it's premature to expose this in case there will be future clarifications in the Core Specification (no idea about whether that's likely).

RFC since this is not satisfactorily tested on real hardware, and there may be some design questions and ugly bits here. Sending anyway since there seems to be interest in this.

I also send the series adding iso-tester + emulator tests.

Pauli Virtanen (2):
Bluetooth: ISO: allow userspace to set seqnum and timestamp for
sending
Bluetooth: produce HW TX timestamps for ISO from LE Read ISO TX Sync

 include/net/bluetooth/bluetooth.h |  2 +
 include/net/bluetooth/hci.h       |  2 +-
 include/net/bluetooth/hci_core.h  |  5 +-
 net/bluetooth/hci_conn.c          | 53 +++++++++++++++++++--
 net/bluetooth/hci_core.c          |  8 ++--
 net/bluetooth/hci_event.c         | 79 ++++++++++++++++++++++++++++++-
 net/bluetooth/iso.c               | 70 +++++++++++++++++++++++----
 7 files changed, 199 insertions(+), 20 deletions(-)
Signature · 1 line
2.53.0
#1 lore raw

RFC PATCH 1/2 Bluetooth: ISO: allow userspace to set seqnum and timestamp for sending

Kernel currently leaves ISO packet scheduling to userspace. To do this properly, e.g. to ensure different CIS are synchronized, userspace needs to be able to set packet timestamps. Core v6.2 Vol 6 Part G Sec 3.3 specifies that seqnum and timestamp shall correspond to each other, so userspace must also be able to override the sequence number.

Add new CMSG BT_SCM_PKT_ISO_TS for the ISO timestamp.

Support BT_SCM_PKT_SEQNUM and BT_SCM_PKT_ISO_TS CMSG for sent packets.

---

Notes:
This is more or less the same patch as
https://lore.kernel.org/linux-bluetooth/[email protected]/
but in slightly different words.

Sending anyway now, since I had the test series written against this one.

 include/net/bluetooth/bluetooth.h |  2 +
 include/net/bluetooth/hci_core.h  |  2 +-
 net/bluetooth/hci_core.c          |  8 ++--
 net/bluetooth/iso.c               | 70 +++++++++++++++++++++++++++----
 4 files changed, 68 insertions(+), 14 deletions(-)
include/net/bluetooth/bluetooth.h +2 -0
index 69eed69f7f26..7f7e13a51de7 100644
@@ -259,6 +259,8 @@ struct bt_codecs {
 
 #define BT_SCM_PKT_SEQNUM	0x05
 
+#define BT_SCM_PKT_ISO_TS	0x06
+
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
include/net/bluetooth/hci_core.h +1 -1
index a7bffb908c1e..c82cec45a9a1 100644
@@ -2336,7 +2336,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
 		 const void *param);
 void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
 void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
-void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
+void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb, bool ts);
 
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
 void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
net/bluetooth/hci_core.c +4 -4
index eb32802f6296..da2ac924aaf6 100644
@@ -3322,7 +3322,7 @@ static void hci_add_iso_hdr(struct sk_buff *skb, __u16 handle, __u8 flags)
 }
 
 static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
-			  struct sk_buff *skb)
+			  struct sk_buff *skb, u8 ts)
 {
 	struct hci_dev *hdev = conn->hdev;
 	struct sk_buff *list;
@@ -3335,7 +3335,7 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
 
 	list = skb_shinfo(skb)->frag_list;
 
-	flags = hci_iso_flags_pack(list ? ISO_START : ISO_SINGLE, 0x00);
+	flags = hci_iso_flags_pack(list ? ISO_START : ISO_SINGLE, ts);
 	hci_add_iso_hdr(skb, conn->handle, flags);
 
 	if (!list) {
@@ -3368,13 +3368,13 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
 	bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
 }
 
-void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
+void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb, bool ts)
 {
 	struct hci_dev *hdev = conn->hdev;
 
 	BT_DBG("%s len %d", hdev->name, skb->len);
 
-	hci_queue_iso(conn, &conn->data_q, skb);
+	hci_queue_iso(conn, &conn->data_q, skb, ts ? 0x01 : 0x00);
 
 	queue_work(hdev->workqueue, &hdev->tx_work);
 }
net/bluetooth/iso.c +61 -9

118 lines not on this page · raw message

Signature · 1 line
2.53.0
#2 lore raw

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list. This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1065140

---Test result---

Test Summary:

CheckPatch                    PENDING   0.33 seconds
GitLint                       PENDING   0.26 seconds
SubjectPrefix                 PASS      0.25 seconds
BuildKernel                   PASS      26.20 seconds
CheckAllWarning               PASS      28.57 seconds
CheckSparse                   WARNING   32.83 seconds
BuildKernel32                 PASS      26.03 seconds
TestRunnerSetup               PASS      574.85 seconds
TestRunner_l2cap-tester       PASS      29.62 seconds
TestRunner_iso-tester         FAIL      59.66 seconds
TestRunner_bnep-tester        PASS      6.62 seconds
TestRunner_mgmt-tester        FAIL      127.39 seconds
TestRunner_rfcomm-tester      PASS      9.87 seconds
TestRunner_sco-tester         FAIL      14.85 seconds
TestRunner_ioctl-tester       PASS      10.71 seconds
TestRunner_mesh-tester        FAIL      12.96 seconds
TestRunner_smp-tester         PASS      8.92 seconds
TestRunner_userchan-tester    PASS      6.89 seconds
IncrementalBuild              PENDING   0.63 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: CheckSparse - WARNING
Desc: Run sparse tool with linux kernel
Output:
net/bluetooth/hci_core.c:85:9: warning: context imbalance in '__hci_dev_get' - different lock contexts for basic blocknet/bluetooth/hci_core.c: note: in included file (through include/linux/notifier.h, include/linux/memory_hotplug.h, include/linux/mmzone.h, include/linux/gfp.h, include/linux/xarray.h, include/linux/radix-tree.h, ...):net/bluetooth/hci_event.c: note: in included file (through include/net/bluetooth/hci_core.h):
##############################
Test: TestRunner_iso-tester - FAIL
Desc: Run iso-tester with test-runner
Output:

BUG: KASAN: slab-use-after-free in le_read_features_complete+0x7e/0x2b0

Total: 141, Passed: 140 (99.3%), Failed: 1, Not Run: 0

Failed Test Cases
ISO Ethtool Get Ts Info - Success Failed 0.071 seconds
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4

Failed Test Cases
Read Exp Feature - Success Failed 0.114 seconds
##############################
Test: TestRunner_sco-tester - FAIL
Desc: Run sco-tester with test-runner
Output:
WARNING: possible circular locking dependency detected

BUG: sleeping function called from invalid context at net/core/sock.c:3782

Total: 30, Passed: 30 (100.0%), Failed: 0, Not Run: 0
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0

Failed Test Cases

Mesh - Send cancel - 1                               Timed out    2.478 seconds
Mesh - Send cancel - 2                               Timed out    1.995 seconds

##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series Output:

---
Regards,
Linux Bluetooth

#3 lore raw
In reply to Pauli Virtanen

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list. This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1065140

---Test result---

Test Summary:

CheckPatch                    PENDING   0.40 seconds
GitLint                       PENDING   0.23 seconds
SubjectPrefix                 PASS      0.24 seconds
BuildKernel                   PASS      25.19 seconds
CheckAllWarning               PASS      27.58 seconds
CheckSparse                   PASS      26.99 seconds
BuildKernel32                 PASS      24.46 seconds
TestRunnerSetup               PASS      528.24 seconds
TestRunner_l2cap-tester       PASS      27.63 seconds
TestRunner_iso-tester         FAIL      58.02 seconds
TestRunner_bnep-tester        PASS      6.78 seconds
TestRunner_mgmt-tester        FAIL      112.09 seconds
TestRunner_rfcomm-tester      PASS      9.49 seconds
TestRunner_sco-tester         FAIL      14.33 seconds
TestRunner_ioctl-tester       PASS      10.35 seconds
TestRunner_mesh-tester        FAIL      12.70 seconds
TestRunner_smp-tester         PASS      8.66 seconds
TestRunner_userchan-tester    PASS      6.80 seconds
TestRunner_6lowpan-tester     FAIL      8.88 seconds
IncrementalBuild              PENDING   0.30 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: TestRunner_iso-tester - FAIL
Desc: Run iso-tester with test-runner
Output:
Total: 141, Passed: 140 (99.3%), Failed: 1, Not Run: 0

Failed Test Cases
ISO Ethtool Get Ts Info - Success Failed 0.116 seconds
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4

Failed Test Cases
Read Exp Feature - Success Failed 0.130 seconds
##############################
Test: TestRunner_sco-tester - FAIL
Desc: Run sco-tester with test-runner
Output:
WARNING: possible circular locking dependency detected
7.0.0-rc2-gd45675a8dbf8 #1 Not tainted
------------------------------------------------------
kworker/u5:2/117 is trying to acquire lock:
ffff888001946240 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}, at: sco_connect_cfm+0x358/0x8d0

but task is already holding lock:
ffff8880025f7220 (&conn->lock){+.+.}-{3:3}, at: sco_connect_cfm+0x22d/0x8d0

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #1 (&conn->lock){+.+.}-{3:3}:

       lock_acquire+0xf7/0x2c0
       _raw_spin_lock+0x2a/0x40
       sco_sock_connect+0x4d7/0x1280
       __sys_connect+0x1a3/0x260
       __x64_sys_connect+0x6e/0xb0
       do_syscall_64+0xa0/0x570
       entry_SYSCALL_64_after_hwframe+0x74/0x7c

-> #0 (sk_lock-AF_BLUETOOTH-BTPROTO_SCO){+.+.}-{0:0}:

       check_prev_add+0xe9/0xc70
       __lock_acquire+0x1457/0x1df0
       lock_acquire+0xf7/0x2c0
       lock_sock_nested+0x36/0xd0
       sco_connect_cfm+0x358/0x8d0
       hci_sync_conn_complete_evt+0x3d3/0x8e0
       hci_event_packet+0x74f/0xb10
       hci_rx_work+0x398/0xd00
       process_scheduled_works+0xb16/0x1ac0
       worker_thread+0x4ff/0xba0
       kthread+0x368/0x490
       ret_from_fork+0x498/0x7e0
       ret_from_fork_asm+0x19/0x30

other info that might help us debug this:

...

BUG: sleeping function called from invalid context at net/core/sock.c:3782

in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 117, name: kworker/u5:2
preempt_count: 1, expected: 0
RCU nest depth: 0, expected: 0
INFO: lockdep is turned off.

CPU: 0 UID: 0 PID: 117 Comm: kworker/u5:2 Not tainted 7.0.0-rc2-gd45675a8dbf8 #1 PREEMPT(lazy) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-1ubuntu1.1 04/01/2014
Workqueue: hci0 hci_rx_work
Call Trace:
 <TASK>
 dump_stack_lvl+0x49/0x60
 __might_resched+0x2ea/0x500
 lock_sock_nested+0x47/0xd0
 ? sco_connect_cfm+0x358/0x8d0
 sco_connect_cfm+0x358/0x8d0
 ? hci_debugfs_create_conn+0x190/0x210
 ? __pfx_sco_connect_cfm+0x10/0x10
 hci_sync_conn_complete_evt+0x3d3/0x8e0
 hci_event_packet+0x74f/0xb10
 ? __pfx_hci_sync_conn_complete_evt+0x10/0x10
 ? __pfx_hci_event_packet+0x10/0x10
 ? mark_held_locks+0x49/0x80
 ? lockdep_hardirqs_on_prepare+0xd4/0x180
 ? _raw_spin_unlock_irqrestore+0x2c/0x50
 hci_rx_work+0x398/0xd00
 process_scheduled_works+0xb16/0x1ac0
 ? __pfx_process_scheduled_works+0x10/0x10
 ? lock_acquire+0xf7/0x2c0
 ? lock_is_held_type+0x9b/0x110
 ? __pfx_hci_rx_work+0x10/0x10
 worker_thread+0x4ff/0xba0
 ? _raw_spin_unlock_irqrestore+0x2c/0x50
 ? __pfx_worker_thread+0x10/0x10
 kthread+0x368/0x490
 ? _raw_spin_unlock_irq+0x23/0x40
 ? __pfx_kthread+0x10/0x10
 ret_from_fork+0x498/0x7e0
 ? __pfx_ret_from_fork+0x10/0x10
 ? __switch_to+0x9e4/0xe50
 ? __switch_to_asm+0x32/0x60

...
Total: 30, Passed: 30 (100.0%), Failed: 0, Not Run: 0
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0

Failed Test Cases

Mesh - Send cancel - 1                               Timed out    2.753 seconds
Mesh - Send cancel - 2                               Timed out    1.995 seconds

##############################
Test: TestRunner_6lowpan-tester - FAIL
Desc: Run 6lowpan-tester with test-runner
Output:
WARNING: possible circular locking dependency detected 7.0.0-rc2-gd45675a8dbf8 #1 Not tainted
------------------------------------------------------
kworker/0:1/11 is trying to acquire lock:
ffff8880026e4940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x75/0x180

but task is already holding lock:
ffffffff8ae4d720 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30

which lock already depends on the new lock.

the existing dependency chain (in reverse order) is:

-> #4 (rtnl_mutex){+.+.}-{4:4}:

       lock_acquire+0xf7/0x2c0
       __mutex_lock+0x16b/0x1fc0
       lowpan_register_netdev+0x11/0x30
       chan_ready_cb+0x836/0xd00
       l2cap_recv_frame+0x6a06/0x8920
       l2cap_recv_acldata+0x790/0xdf0
       hci_rx_work+0x500/0xd00
       process_scheduled_works+0xb16/0x1ac0
       worker_thread+0x4ff/0xba0
       kthread+0x368/0x490
       ret_from_fork+0x498/0x7e0
       ret_from_fork_asm+0x19/0x30

-> #3 (&chan->lock#3/1){+.+.}-{4:4}:

       lock_acquire+0xf7/0x2c0
       __mutex_lock+0x16b/0x1fc0
       l2cap_chan_connect+0x74e/0x1980
       lowpan_control_write+0x523/0x660
       full_proxy_write+0x10b/0x190
       vfs_write+0x1c0/0xf60
       ksys_write+0xf1/0x1d0
       do_syscall_64+0xa0/0x570
       entry_SYSCALL_64_after_hwframe+0x74/0x7c

-> #2 (&conn->lock){+.+.}-{4:4}:
...
Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series Output:

https://github.com/bluez/bluetooth-next/pull/20/checks

---
Regards,
Linux Bluetooth

#4 lore raw

RFC PATCH 2/2 Bluetooth: produce HW TX timestamps for ISO from LE Read ISO TX Sync

In reply to Pauli Virtanen

For ISO packets with HW TX timestamping enabled, retrieve ISO send timestamps via LE Read ISO TX Sync. Specification allows retrieving the timestamp only for the "latest" sent packet.

Report SDU Reference time as the timestamp, as this is the value relevant for userspace send synchronization.

Due to overall poor description of the timing features in ISO-over-HCI transport in Core Specification v6.2, it is ambiguous what packet the command actually reports, so timestamps may not be assigned to the exactly correct packets, user applications have to deal with it.

---
 include/net/bluetooth/hci.h      |  2 +-
 include/net/bluetooth/hci_core.h |  3 +-
 net/bluetooth/hci_conn.c         | 53 +++++++++++++++++++--
 net/bluetooth/hci_event.c        | 79 +++++++++++++++++++++++++++++++-
 4 files changed, 131 insertions(+), 6 deletions(-)
include/net/bluetooth/hci.h +1 -1
index 89ad9470fa71..b536587a909f 100644
@@ -2138,7 +2138,7 @@ struct hci_rp_le_read_iso_tx_sync {
 	__u8    status;
 	__le16  handle;
 	__le16  seq;
-	__le32  imestamp;
+	__le32  timestamp;
 	__u8    offset[3];
 } __packed;
 
include/net/bluetooth/hci_core.h +2 -1
index c82cec45a9a1..5d530349c2a4 100644
@@ -272,6 +272,7 @@ struct adv_info {
 
 struct tx_queue {
 	struct sk_buff_head queue;
+	struct sk_buff *iso_last_tx;
 	unsigned int extra;
 	unsigned int tracked;
 };
@@ -1630,7 +1631,7 @@ void hci_conn_failed(struct hci_conn *conn, u8 status);
 u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
 
 void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb);
-void hci_conn_tx_dequeue(struct hci_conn *conn);
+void hci_conn_tx_dequeue(struct hci_conn *conn, bool last);
 void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
 			    const struct sockcm_cookie *sockc);
 
net/bluetooth/hci_conn.c +50 -3

89 lines not on this page · raw message

net/bluetooth/hci_event.c +78 -1

97 lines not on this page · raw message

Signature · 1 line
2.53.0
#5 lore raw

Re: [RFC PATCH 2/2] Bluetooth: produce HW TX timestamps for ISO from LE Read ISO TX Sync

Hi Pauli,

Quoted 198 lines · Pauli Virtanen

For ISO packets with HW TX timestamping enabled, retrieve ISO send timestamps via LE Read ISO TX Sync. Specification allows retrieving the timestamp only for the "latest" sent packet.

Report SDU Reference time as the timestamp, as this is the value relevant for userspace send synchronization.

Due to overall poor description of the timing features in ISO-over-HCI transport in Core Specification v6.2, it is ambiguous what packet the command actually reports, so timestamps may not be assigned to the exactly correct packets, user applications have to deal with it.

---
 include/net/bluetooth/hci.h      |  2 +-
 include/net/bluetooth/hci_core.h |  3 +-
 net/bluetooth/hci_conn.c         | 53 +++++++++++++++++++--
 net/bluetooth/hci_event.c        | 79 +++++++++++++++++++++++++++++++-
 4 files changed, 131 insertions(+), 6 deletions(-)
include/net/bluetooth/hci.h +1 -1
index 89ad9470fa71..b536587a909f 100644
@@ -2138,7 +2138,7 @@ struct hci_rp_le_read_iso_tx_sync {
        __u8    status;
        __le16  handle;
        __le16  seq;
-       __le32  imestamp;
+       __le32  timestamp;
        __u8    offset[3];
 } __packed;
 
include/net/bluetooth/hci_core.h +2 -1
index c82cec45a9a1..5d530349c2a4 100644
@@ -272,6 +272,7 @@ struct adv_info {
 
 struct tx_queue {
        struct sk_buff_head queue;
+       struct sk_buff *iso_last_tx;
        unsigned int extra;
        unsigned int tracked;
 };
@@ -1630,7 +1631,7 @@ void hci_conn_failed(struct hci_conn *conn, u8 status);
 u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
 
 void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb);
-void hci_conn_tx_dequeue(struct hci_conn *conn);
+void hci_conn_tx_dequeue(struct hci_conn *conn, bool last);
 void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
                            const struct sockcm_cookie *sockc);
 
net/bluetooth/hci_conn.c +50 -3
index a47f5daffdbf..342747a7c225 100644
@@ -1232,6 +1232,9 @@ void hci_conn_del(struct hci_conn *conn)
        skb_queue_purge(&conn->data_q);
        skb_queue_purge(&conn->tx_q.queue);
 
+       kfree_skb(conn->tx_q.iso_last_tx);
+       conn->tx_q.iso_last_tx = NULL;
+
        /* Remove the connection from the list and cleanup its remaining
         * state. This is a separate function since for some cases like
         * BT_CONNECT_SCAN we *only* want the cleanup part without the
@@ -2388,6 +2391,20 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
        return cis;
 }
 
+static int hci_conn_le_read_iso_tx_sync(struct hci_conn *conn)
+{
+       struct hci_cp_le_read_iso_tx_sync cp;
+
+       if (!(conn->hdev->commands[41] & 0x40))
+               return -EOPNOTSUPP;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = cpu_to_le16(conn->handle);
+
+       return hci_send_cmd(conn->hdev, HCI_OP_LE_READ_ISO_TX_SYNC,
+                           sizeof(cp), &cp);
+}
+
 /* Check link security requirement */
 int hci_conn_check_link_mode(struct hci_conn *conn)
 {
@@ -3156,6 +3173,18 @@ void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
        struct tx_queue *comp = &conn->tx_q;
        bool track = false;
 
+       /* Check for HW timestamping */
+       switch (conn->type) {
+       case CIS_LINK:
+       case BIS_LINK:
+               if (skb->sk &&
+                   (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NOBPF)) {
+                       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+                       track = true;
+               }
+               break;
+       }
+
        /* Emit SND now, ie. just before sending to driver */
        if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP)
                __skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SND);
@@ -3214,7 +3243,7 @@ void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb)
        skb_queue_purge(&comp->queue);
 }
 
-void hci_conn_tx_dequeue(struct hci_conn *conn)
+void hci_conn_tx_dequeue(struct hci_conn *conn, bool last)
 {
        struct tx_queue *comp = &conn->tx_q;
        struct sk_buff *skb;
@@ -3234,8 +3263,24 @@ void hci_conn_tx_dequeue(struct hci_conn *conn)
 
        if (skb->sk) {
                comp->tracked--;
-               __skb_tstamp_tx(skb, NULL, NULL, skb->sk,
-                               SCM_TSTAMP_COMPLETION);
+
+               if (skb_shinfo(skb)->tx_flags & SKBTX_COMPLETION_TSTAMP)
+                       __skb_tstamp_tx(skb, NULL, NULL, skb->sk,
+                                       SCM_TSTAMP_COMPLETION);
+
+               switch (conn->type) {
+               case CIS_LINK:
+               case BIS_LINK:
+                       if (!last)
+                               break;
+                       if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NOBPF) {
+                               kfree_skb(conn->tx_q.iso_last_tx);
+                               conn->tx_q.iso_last_tx = skb;
+                               hci_conn_le_read_iso_tx_sync(conn);
+                               return;
+                       }
+                       break;
+               }
        }
 
        kfree_skb(skb);
@@ -3283,6 +3328,8 @@ int hci_ethtool_ts_info(unsigned int index, int sk_proto,
 
        switch (sk_proto) {
        case BTPROTO_ISO:
+               info->so_timestamping |= SOF_TIMESTAMPING_TX_HARDWARE;
+               fallthrough;
        case BTPROTO_L2CAP:
                info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE;
                info->so_timestamping |= SOF_TIMESTAMPING_TX_COMPLETION;
net/bluetooth/hci_event.c +36 -0
index 286529d2e554..f2aade36a39e 100644
@@ -3777,6 +3777,81 @@ static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
        rcu_read_unlock();
 }
 
+static u8 hci_cc_le_read_iso_tx_sync(struct hci_dev *hdev, void *data,
+                                    struct sk_buff *skb)
+{
+       struct hci_rp_le_read_iso_tx_sync *rp = data;
+       struct skb_shared_hwtstamps ssh;
+       struct hci_conn *conn;
+       u32 offset;
+
+       bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+       if (rp->status)
+               return rp->status;
+
+       offset = rp->offset[0] | (rp->offset[1] << 8) | (rp->offset[2] << 16);
+       bt_dev_dbg(hdev, "ts %u to %u", __le32_to_cpu(rp->timestamp), offset);
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (!conn)
+               goto unlock;
+
+       if (!conn->tx_q.iso_last_tx)
+               goto unlock;
+
+       /* Report SDU synchronization reference as the timestamp. This value is
+        * most useful for userspace, as it is the Time_Stamp field in sent
+        * packets. See Fig. 3.1 and 3.2 in Core v6.2 Vol 6 Part G Sec. 3.
+        *
+        * Unfortunately, the ISO-over-HCI transport is poorly described in the
+        * specification.
+        *
+        * The HCI specification contradicts itself which packet timestamp is
+        * reported by Le Read ISO TX Sync: "scheduled for transmission" in Core
+        * v6.2 Vol 4 Part E Sec 4.7 vs. "transmitted" in Sec. 7.8.96.
+        * The specification is also unclear on what these words mean.

net/bluetooth/hci_event.c static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd) · line 3825

Quoted 10 lines
net/bluetooth/hci_event.c +10 -0
+        *
+        * We assume here it means "transmitted", and additionally that
+        * "transmitted" means the last packet reported in Number of Completed
+        * Packets.
+        *
+        * The specification is also unclear what sequence number is returned by
+        * this command. We assume it is a sequence number assigned by the
+        * controller. These are equivalent to the controller clock, so they are
+        * useless for generic Host (which we are) with no direct access to
+        * controller clock.

Interesting analysis. I thought the sequence number for NOCP must match the sequence number set by the host when sending the packet, and its sole purpose was to be able to fetch the hardware time. Did you observe something different? If the sequence number had to synchronize with a clock, the host would need to generate empty packets even when there is nothing to transmit. This would consume power and memory solely to keep the sequence number in sync with the clock. I don't actually understand why NOCP was even used with ISO if the intention was to use sequence numbers like above, at very least NOCP should have been updated to carry the hardware clock and other timing information directly rather than using another command that consumes even more system power and memory.

Quoted 54 lines
net/bluetooth/hci_event.c +32 -2
+        *
+        * Userspace application that handle these timestamps must then be
+        * prepared that the ISO timestamp is reported on the wrong packet
+        * depending on controller. Actual packet TX must rely on backpressure
+        * (monitoring packet completion via COMPLETION tstamp), and the
+        * timestamps are useful only for aligning the playback of multiple ISO
+        * streams.
+        */
+
+       memset(&ssh, 0, sizeof(ssh));
+
+       /* SDU Reference = BIG/CIG Reference - Time_Offset */
+       ssh.hwtstamp = us_to_ktime(__le32_to_cpu(rp->timestamp) - offset);
+
+       /* The value may be 0, but skb_tstamp_tx() does not allow reporting zero
+        * HW timestamp. Offset by 1 ns as a workaround.
+        */
+       ssh.hwtstamp += 1;
+
+       skb_tstamp_tx(conn->tx_q.iso_last_tx, &ssh);
+
+       kfree_skb(conn->tx_q.iso_last_tx);
+       conn->tx_q.iso_last_tx = NULL;
+
+unlock:
+       hci_dev_unlock(hdev);
+       return rp->status;
+}
+
 static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
                                        struct sk_buff *skb)
 {
@@ -4224,6 +4299,8 @@ static const struct hci_cc {
        HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power,
               sizeof(struct hci_rp_le_read_transmit_power)),
        HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode),
+       HCI_CC(HCI_OP_LE_READ_ISO_TX_SYNC, hci_cc_le_read_iso_tx_sync,
+              sizeof(struct hci_rp_le_read_iso_tx_sync)),
        HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
               sizeof(struct hci_rp_le_read_buffer_size_v2)),
        HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,
@@ -4509,7 +4586,7 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
                }
 
                for (i = 0; i < count; ++i)
-                       hci_conn_tx_dequeue(conn);
+                       hci_conn_tx_dequeue(conn, (i == count - 1));
 
                switch (conn->type) {
                case ACL_LINK:
--

2.53.0

Signature · 1 line
Luiz Augusto von Dentz
#6 lore raw

Re: [RFC PATCH 2/2] Bluetooth: produce HW TX timestamps for ISO from LE Read ISO TX Sync

Hi Luiz,

ke, 2026-03-11 kello 16:30 -0400, Luiz Augusto von Dentz kirjoitti:

Quoted 263 lines

Hi Pauli,

Quoted 244 lines · Pauli Virtanen

For ISO packets with HW TX timestamping enabled, retrieve ISO send timestamps via LE Read ISO TX Sync. Specification allows retrieving the
timestamp only for the "latest" sent packet.

Report SDU Reference time as the timestamp, as this is the value relevant for userspace send synchronization.

Due to overall poor description of the timing features in ISO-over- HCI
transport in Core Specification v6.2, it is ambiguous what packet the
command actually reports, so timestamps may not be assigned to the exactly correct packets, user applications have to deal with it.

---
 include/net/bluetooth/hci.h      |  2 +- include/net/bluetooth/hci_core.h |  3 +- net/bluetooth/hci_conn.c         | 53 +++++++++++++++++++-- net/bluetooth/hci_event.c        | 79
+++++++++++++++++++++++++++++++-

 4 files changed, 131 insertions(+), 6 deletions(-)
include/net/bluetooth/hci.h +1 -1
index 89ad9470fa71..b536587a909f 100644
@@ -2138,7 +2138,7 @@ struct hci_rp_le_read_iso_tx_sync {
         __u8    status;
         __le16  handle;
         __le16  seq;
-       __le32  imestamp;
+       __le32  timestamp;
         __u8    offset[3];
  } __packed;
 
include/net/bluetooth/hci_core.h +1 -0
index c82cec45a9a1..5d530349c2a4 100644
@@ -272,6 +272,7 @@ struct adv_info {
 
  struct tx_queue {
         struct sk_buff_head queue;
+       struct sk_buff *iso_last_tx;
         unsigned int extra;
         unsigned int tracked;
  };
@@ -1630,7 +1631,7 @@ void hci_conn_failed(struct hci_conn *conn,
 u8 status);
  u8 hci_conn_set_handle(struct hci_conn *conn, u16 handle);
 

 void hci_conn_tx_queue(struct hci_conn *conn, struct sk_buff *skb);

include/net/bluetooth/hci_core.h +1 -1
-void hci_conn_tx_dequeue(struct hci_conn *conn);
+void hci_conn_tx_dequeue(struct hci_conn *conn, bool last);

 void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
                            const struct sockcm_cookie *sockc);

net/bluetooth/hci_conn.c +3 -0
index a47f5daffdbf..342747a7c225 100644
@@ -1232,6 +1232,9 @@ void hci_conn_del(struct hci_conn *conn)
         skb_queue_purge(&conn->data_q);
         skb_queue_purge(&conn->tx_q.queue);
 
+       kfree_skb(conn->tx_q.iso_last_tx);
+       conn->tx_q.iso_last_tx = NULL;
+
         /* Remove the connection from the list and cleanup its

remaining
         * state. This is a separate function since for some cases like
         * BT_CONNECT_SCAN we *only* want the cleanup part without the

net/bluetooth/hci_conn.c +14 -0
@@ -2388,6 +2391,20 @@ struct hci_conn *hci_connect_cis(struct
 hci_dev *hdev, bdaddr_t *dst,
         return cis;
  }
 
+static int hci_conn_le_read_iso_tx_sync(struct hci_conn *conn)
+{
+       struct hci_cp_le_read_iso_tx_sync cp;
+
+       if (!(conn->hdev->commands[41] & 0x40))
+               return -EOPNOTSUPP;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = cpu_to_le16(conn->handle);
+
+       return hci_send_cmd(conn->hdev, HCI_OP_LE_READ_ISO_TX_SYNC,
+                           sizeof(cp), &cp);
+}
+
  /* Check link security requirement */
  int hci_conn_check_link_mode(struct hci_conn *conn)

 {

net/bluetooth/hci_conn.c +12 -0
@@ -3156,6 +3173,18 @@ void hci_conn_tx_queue(struct hci_conn
 *conn, struct sk_buff *skb)
         struct tx_queue *comp = &conn->tx_q;
         bool track = false;
 
+       /* Check for HW timestamping */
+       switch (conn->type) {
+       case CIS_LINK:
+       case BIS_LINK:
+               if (skb->sk &&
+                   (skb_shinfo(skb)->tx_flags &
 SKBTX_HW_TSTAMP_NOBPF)) {
+                       skb_shinfo(skb)->tx_flags |=
 SKBTX_IN_PROGRESS;
+                       track = true;
+               }
+               break;
+       }
+

        /* Emit SND now, ie. just before sending to driver */ if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP) __skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SND);

net/bluetooth/hci_conn.c +1 -1
@@ -3214,7 +3243,7 @@ void hci_conn_tx_queue(struct hci_conn *conn,
 struct sk_buff *skb)
         skb_queue_purge(&comp->queue);
  }
 
-void hci_conn_tx_dequeue(struct hci_conn *conn)
+void hci_conn_tx_dequeue(struct hci_conn *conn, bool last)
  {
         struct tx_queue *comp = &conn->tx_q;

        struct sk_buff *skb;

net/bluetooth/hci_conn.c +18 -2
@@ -3234,8 +3263,24 @@ void hci_conn_tx_dequeue(struct hci_conn
 *conn)
 
         if (skb->sk) {
                 comp->tracked--;
-               __skb_tstamp_tx(skb, NULL, NULL, skb->sk,
-                               SCM_TSTAMP_COMPLETION);
+
+               if (skb_shinfo(skb)->tx_flags &
 SKBTX_COMPLETION_TSTAMP)
+                       __skb_tstamp_tx(skb, NULL, NULL, skb->sk,
+                                       SCM_TSTAMP_COMPLETION);
+
+               switch (conn->type) {
+               case CIS_LINK:
+               case BIS_LINK:
+                       if (!last)
+                               break;
+                       if (skb_shinfo(skb)->tx_flags &
 SKBTX_HW_TSTAMP_NOBPF) {
+                               kfree_skb(conn->tx_q.iso_last_tx);
+                               conn->tx_q.iso_last_tx = skb;
+                               hci_conn_le_read_iso_tx_sync(conn);
+                               return;
+                       }
+                       break;
+               }

        }

        kfree_skb(skb);

net/bluetooth/hci_conn.c +0 -0
@@ -3283,6 +3328,8 @@ int hci_ethtool_ts_info(unsigned int index,
 int sk_proto,
 
         switch (sk_proto) {

        case BTPROTO_ISO:
+               info->so_timestamping |=
SOF_TIMESTAMPING_TX_HARDWARE;
+               fallthrough;
        case BTPROTO_L2CAP:
                info->so_timestamping |=
SOF_TIMESTAMPING_TX_SOFTWARE;
                info->so_timestamping |=
SOF_TIMESTAMPING_TX_COMPLETION;

net/bluetooth/hci_event.c +15 -0
index 286529d2e554..f2aade36a39e 100644
@@ -3777,6 +3777,81 @@ static inline void
 handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
         rcu_read_unlock();
  }
 
+static u8 hci_cc_le_read_iso_tx_sync(struct hci_dev *hdev, void
 *data,
+                                    struct sk_buff *skb)
+{
+       struct hci_rp_le_read_iso_tx_sync *rp = data;
+       struct skb_shared_hwtstamps ssh;
+       struct hci_conn *conn;
+       u32 offset;
+
+       bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
+
+       if (rp->status)
+               return rp->status;
+
+       offset = rp->offset[0] | (rp->offset[1] << 8) | (rp-
 >offset[2] << 16);
+       bt_dev_dbg(hdev, "ts %u to %u", __le32_to_cpu(rp-
Quoted 1 line

timestamp), offset);

net/bluetooth/hci_event.c +4 -0
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp-
Quoted 1 line

handle));

+       if (!conn)
+               goto unlock;

net/bluetooth/hci_event.c +5 -0
+
+       if (!conn->tx_q.iso_last_tx)
+               goto unlock;
+
+       /* Report SDU synchronization reference as the timestamp.

This value is
+        * most useful for userspace, as it is the Time_Stamp field in sent
+        * packets. See Fig. 3.1 and 3.2 in Core v6.2 Vol 6 Part G Sec. 3.
+        *
+        * Unfortunately, the ISO-over-HCI transport is poorly described in the
+        * specification.
+        *
+        * The HCI specification contradicts itself which packet timestamp is
+        * reported by Le Read ISO TX Sync: "scheduled for transmission" in Core
+        * v6.2 Vol 4 Part E Sec 4.7 vs. "transmitted" in Sec. 7.8.96.
+        * The specification is also unclear on what these words mean.
+        *
+        * We assume here it means "transmitted", and additionally that
+        * "transmitted" means the last packet reported in Number of Completed
+        * Packets.
+        *
+        * The specification is also unclear what sequence number

Quoted 8 lines

is returned by
+        * this command. We assume it is a sequence number assigned by the
+        * controller. These are equivalent to the controller clock, so they are
+        * useless for generic Host (which we are) with no direct access to
+        * controller clock.

Interesting analysis. I thought the sequence number for NOCP must match the sequence number set by the host when sending the packet, and
its sole purpose was to be able to fetch the hardware time. Did you observe something different? If the sequence number had to synchronize
with a clock, the host would need to generate empty packets even when

Quoted 8 lines

there is nothing to transmit. This would consume power and memory solely to keep the sequence number in sync with the clock. I don't actually understand why NOCP was even used with ISO if the intention was to use sequence numbers like above, at very least NOCP should have
been updated to carry the hardware clock and other timing information directly rather than using another command that consumes even more system power and memory.

There's no relationship to NOCP specified in Core Spec, it's pretty vague. However, I don't see any other way for host to get clock sync for TX than via NOCP (at least for <1 packet accuracy).

It would indeed make most sense that sequence number is what the host sent in the ISO packet, but it is not specified. Core Specification v6.2 Vol 6 Part G Sec 2 also says:

"""
SDUs sent to the upper layer by the ISOAL shall be given a sequence number which is initialized to 0 when the CIS or BIS is created. SDUs received by the ISOAL from the upper layer shall be given a sequence number which is initialized to 0 when the CIS or BIS is created. The upper layer may synchronize its sequence number with the sequence number in the ISOAL once the Datapath is configured and the link is established.
"""

so one gets the impression controller shall assign new sequence numbers and use those, and host shall "synchronize" to it in some unspecified way.

I saw some related discussions by the Zephyr people, I think the outcome was along the lines that the spec is so vague that these features are unusable.

Some clarifications would be useful here, as I don't get what was the way the spec intends that these are used.

***

IIRC, in the controllers I tested (Intel AX210, Realtek 8761CU) the returned timestamp and sequence number are always zero, so they haven't actually implemented the command. I'd have to check what Mediatek MT7925 does, it was at least setting RX timestamps properly.

In Zephyr IIRC controller-assigned sequence number is used, but I'd have to double check this.

Quoted 70 lines
Quoted 69 lines

+        *
+        * Userspace application that handle these timestamps must then be
+        * prepared that the ISO timestamp is reported on the wrong packet
+        * depending on controller. Actual packet TX must rely on backpressure
+        * (monitoring packet completion via COMPLETION tstamp), and the
+        * timestamps are useful only for aligning the playback of multiple ISO
+        * streams.
+        */

net/bluetooth/hci_event.c +5 -0
+
+       memset(&ssh, 0, sizeof(ssh));
+
+       /* SDU Reference = BIG/CIG Reference - Time_Offset */
+       ssh.hwtstamp = us_to_ktime(__le32_to_cpu(rp->timestamp) -

offset);
+
+       /* The value may be 0, but skb_tstamp_tx() does not allow reporting zero
+        * HW timestamp. Offset by 1 ns as a workaround. +        */
+       ssh.hwtstamp += 1;

net/bluetooth/hci_event.c +11 -0
+
+       skb_tstamp_tx(conn->tx_q.iso_last_tx, &ssh);
+
+       kfree_skb(conn->tx_q.iso_last_tx);
+       conn->tx_q.iso_last_tx = NULL;
+
+unlock:
+       hci_dev_unlock(hdev);
+       return rp->status;
+}
+

 static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
                                        struct sk_buff *skb) {

net/bluetooth/hci_event.c +2 -0
@@ -4224,6 +4299,8 @@ static const struct hci_cc {
         HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER,
 hci_cc_le_read_transmit_power,
                sizeof(struct hci_rp_le_read_transmit_power)),
         HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE,
 hci_cc_le_set_privacy_mode),
+       HCI_CC(HCI_OP_LE_READ_ISO_TX_SYNC,
 hci_cc_le_read_iso_tx_sync,
+              sizeof(struct hci_rp_le_read_iso_tx_sync)),

        HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
               sizeof(struct hci_rp_le_read_buffer_size_v2)), HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,

net/bluetooth/hci_event.c +1 -1
@@ -4509,7 +4586,7 @@ static void hci_num_comp_pkts_evt(struct
 hci_dev *hdev, void *data,
                 }
 
                 for (i = 0; i < count; ++i)
-                       hci_conn_tx_dequeue(conn);
+                       hci_conn_tx_dequeue(conn, (i == count -
 1));
 

                switch (conn->type) {
                case ACL_LINK:
--
2.53.0

#7 lore raw

Re: [RFC PATCH 2/2] Bluetooth: produce HW TX timestamps for ISO from LE Read ISO TX Sync

ke, 2026-03-11 kello 23:19 +0200, Pauli Virtanen kirjoitti:

Quoted 1 line

Hi Luiz,

[clip]

Quoted 47 lines
Quoted 7 lines

Interesting analysis. I thought the sequence number for NOCP must match the sequence number set by the host when sending the packet, and
its sole purpose was to be able to fetch the hardware time. Did you observe something different? If the sequence number had to synchronize
with a clock, the host would need to generate empty packets even when

Quoted 8 lines

there is nothing to transmit. This would consume power and memory solely to keep the sequence number in sync with the clock. I don't actually understand why NOCP was even used with ISO if the intention was to use sequence numbers like above, at very least NOCP should have
been updated to carry the hardware clock and other timing information directly rather than using another command that consumes even more system power and memory.

There's no relationship to NOCP specified in Core Spec, it's pretty vague. However, I don't see any other way for host to get clock sync for TX than via NOCP (at least for <1 packet accuracy).

It would indeed make most sense that sequence number is what the host sent in the ISO packet, but it is not specified. Core Specification v6.2 Vol 6 Part G Sec 2 also says:

"""
SDUs sent to the upper layer by the ISOAL shall be given a sequence number which is initialized to 0 when the CIS or BIS is created. SDUs received by the ISOAL from the upper layer shall be given a sequence number which is initialized to 0 when the CIS or BIS is created. The upper layer may synchronize its sequence number with the sequence number in the ISOAL once the Datapath is configured and the link is established.
"""

so one gets the impression controller shall assign new sequence numbers and use those, and host shall "synchronize" to it in some unspecified way.

I saw some related discussions by the Zephyr people, I think the outcome was along the lines that the spec is so vague that these features are unusable.

Some clarifications would be useful here, as I don't get what was the way the spec intends that these are used.

***

Quoted 7 lines

IIRC, in the controllers I tested (Intel AX210, Realtek 8761CU) the returned timestamp and sequence number are always zero, so they haven't actually implemented the command. I'd have to check what Mediatek MT7925 does, it was at least setting RX timestamps properly.

In Zephyr IIRC controller-assigned sequence number is used, but I'd have to double check this.

Retest:

MT7925 firmware produces LE Read ISO TX Sync responses with sequence numbers from host ISO packets, but timestamp is always 0.

RTL8176CU firmware also produces responses with sequence numbers from host ISO packets, but timestamp is always 0.

Intel AX210 produces responses with both sequence number and timestamp 0.

Zephyr produces responses with controller's own sequence numbering, host ISO packet seqnum is only used for determining skipped packets. Provides timestamps.

MT/RTL/Intel seem to have same interpretation although none of them actually implements the command.

Quoted 70 lines
Quoted 70 lines
Quoted 69 lines

+        *
+        * Userspace application that handle these timestamps must then be
+        * prepared that the ISO timestamp is reported on the wrong packet
+        * depending on controller. Actual packet TX must rely on backpressure
+        * (monitoring packet completion via COMPLETION tstamp), and the
+        * timestamps are useful only for aligning the playback of multiple ISO
+        * streams.
+        */

net/bluetooth/hci_event.c +5 -0
+
+       memset(&ssh, 0, sizeof(ssh));
+
+       /* SDU Reference = BIG/CIG Reference - Time_Offset */
+       ssh.hwtstamp = us_to_ktime(__le32_to_cpu(rp->timestamp) -

offset);
+
+       /* The value may be 0, but skb_tstamp_tx() does not allow reporting zero
+        * HW timestamp. Offset by 1 ns as a workaround. +        */
+       ssh.hwtstamp += 1;

net/bluetooth/hci_event.c +11 -0
+
+       skb_tstamp_tx(conn->tx_q.iso_last_tx, &ssh);
+
+       kfree_skb(conn->tx_q.iso_last_tx);
+       conn->tx_q.iso_last_tx = NULL;
+
+unlock:
+       hci_dev_unlock(hdev);
+       return rp->status;
+}
+

 static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
                                        struct sk_buff *skb) {

(quoted diff) +2 -0
@@ -4224,6 +4299,8 @@ static const struct hci_cc {
         HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER,
 hci_cc_le_read_transmit_power,
                sizeof(struct hci_rp_le_read_transmit_power)),
         HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE,
 hci_cc_le_set_privacy_mode),
+       HCI_CC(HCI_OP_LE_READ_ISO_TX_SYNC,
 hci_cc_le_read_iso_tx_sync,
+              sizeof(struct hci_rp_le_read_iso_tx_sync)),

        HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
               sizeof(struct hci_rp_le_read_buffer_size_v2)), HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,

net/bluetooth/hci_event.c +1 -1
@@ -4509,7 +4586,7 @@ static void hci_num_comp_pkts_evt(struct
 hci_dev *hdev, void *data,
                 }
 
                 for (i = 0; i < count; ++i)
-                       hci_conn_tx_dequeue(conn);
+                       hci_conn_tx_dequeue(conn, (i == count -
 1));
 

                switch (conn->type) {
                case ACL_LINK:
--
2.53.0

Signature · 1 line
Pauli Virtanen