@@ -239,6 +239,43 @@ struct bt_codecs {
#define BT_ISO_BASE 20
+/* BTGETTXINFO: Transmission latency information.
+ *
+ * Produces information of a previous event when a packet was sent, and the
+ * length of packet queue at that time.
+ *
+ * Applicable to: Bluetooth ISO sockets.
+ *
+ * Input: Zero-initialize reserved flag bits and other fields.
+ *
+ * Output:
+ *
+ * Fails with ENOENT if no packet has been sent.
+ *
+ * - flags: currently always 0, all bits reserved for extensions.
+ *
+ * - queue: total number of packets in queue (controller and socket buffers)
+ * at the time of the event.
+ *
+ * - time: reference event timestamp (nsec, in system monotonic clock).
+ *
+ * - offset: offset (nsec) of actual packet timing from the reference timestamp.
+ * Currently always 0.
+ *
+ * For packet latencies in nanoseconds, the application can track timestamps
+ * t[j] when it sent packet j. Then, given t[k] < tx_info.time < t[k + 1],
+ * event packet j = k - tx.queue and ref_latency_nsec = tx_info.time - t[j].
+ */
+
+#define BTGETTXINFO _IOWR('B', 100, struct bt_tx_info)
+
+struct bt_tx_info {
+ __u32 flags;
+ __u32 queue;
+ __s64 time;
+ __s64 offset;
+} __packed;
+
__printf(1, 2)
void bt_info(const char *fmt, ...);
__printf(1, 2)
@@ -500,6 +500,8 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb)
if (skb->len > qos->ucast.out.sdu)
return -EMSGSIZE;
+ if (sk->sk_state != BT_CONNECTED)
+ return -ENOTCONN;
len = skb->len;
@@ -509,10 +511,9 @@ static int iso_send_frame(struct sock *sk, struct sk_buff *skb)
hdr->slen = cpu_to_le16(hci_iso_data_len_pack(len,
HCI_ISO_STATUS_VALID));
- if (sk->sk_state == BT_CONNECTED)
- hci_send_iso(conn->hcon, skb);
- else
- len = -ENOTCONN;
+ hci_mark_tx_latency(&conn->hcon->tx_latency, skb);
+
+ hci_send_iso(conn->hcon, skb);
return len;
}
@@ -2232,6 +2233,53 @@ void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
kfree_skb(skb);
}
+static int iso_sock_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ struct tx_latency latency;
+ struct bt_tx_info info;
+
+ BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg);
+
+ switch (cmd) {
+ case BTGETTXINFO:
+ /* Require zero-initialized, to allow later extensions */
+ if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
+ return -EFAULT;
+ if (info.flags || info.queue || info.time || info.offset)
+ return -EINVAL;
+
+ memset(&info, 0, sizeof(info));
+
+ lock_sock(sk);
+
+ if (sk->sk_state != BT_CONNECTED) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ hci_copy_tx_latency(&latency,
+ &iso_pi(sk)->conn->hcon->tx_latency);
+
+ release_sock(sk);
+
+ if (!latency.now.time)
+ return -ENOENT;
+
+ info.queue = latency.now.queue;
+ info.time = ktime_to_ns(latency.now.time);
+ info.offset = latency.now.offset;
+
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ return bt_sock_ioctl(sock, cmd, arg);
+}
+
static struct hci_cb iso_cb = {
.name = "ISO",
.connect_cfm = iso_connect_cfm,
@@ -2270,7 +2318,7 @@ static const struct proto_ops iso_sock_ops = {
.sendmsg = iso_sock_sendmsg,
.recvmsg = iso_sock_recvmsg,
.poll = bt_sock_poll,
- .ioctl = bt_sock_ioctl,
+ .ioctl = iso_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = iso_sock_shutdown,
Add ioctl BTGETTXINFO to read information of a previous packet completion event concerning a given ISO socket. It contains sufficient information so the user can know which previous sendmsg() the packet corresponds to and what was its completion time in monotonic clock, so latency can be calculated. The user can then also know the packet queue length (in units of their sendmsg packets). Mark submitted skb so that hci_core updates the latency information, and read it in the ioctl. Signed-off-by: Pauli Virtanen <pav@iki.fi> --- include/net/bluetooth/bluetooth.h | 37 ++++++++++++++++++++ net/bluetooth/iso.c | 58 ++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 5 deletions(-)