Message ID | f40c875abeb7a777812666741a3b4a18ddb498fe.1712076170.git.pav@iki.fi (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | tests: add TX timestamping tests | expand |
Context | Check | Description |
---|---|---|
tedd_an/pre-ci_am | success | Success |
tedd_an/CheckPatch | success | CheckPatch PASS |
tedd_an/GitLint | success | Gitlint PASS |
tedd_an/IncrementalBuild | fail | [BlueZ,v3,2/8] iso-tester: Add tests for TX timestamping tools/iso-tester.c: In function ‘iso_tx_timestamping’: tools/iso-tester.c:2130:9: error: variable ‘so’ has initializer but incomplete type 2130 | struct so_timestamping so = { | ^~~~~~~~~~~~~~~ tools/iso-tester.c:2131:4: error: ‘struct so_timestamping’ has no member named ‘flags’ 2131 | .flags = isodata->so_timestamping, | ^~~~~ tools/iso-tester.c:2131:12: error: excess elements in struct initializer [-Werror] 2131 | .flags = isodata->so_timestamping, | ^~~~~~~ tools/iso-tester.c:2131:12: note: (near initialization for ‘so’) tools/iso-tester.c:2130:25: error: storage size of ‘so’ isn’t known 2130 | struct so_timestamping so = { | ^~ tools/iso-tester.c:2130:25: error: unused variable ‘so’ [-Werror=unused-variable] cc1: all warnings being treated as errors make[1]: *** [Makefile:7807: tools/iso-tester.o] Error 1 make[1]: *** Waiting for unfinished jobs.... tools/mgmt-tester.c: In function ‘main’: tools/mgmt-tester.c:12721:5: note: variable tracking size limit exceeded with ‘-fvar-tracking-assignments’, retrying without 12721 | int main(int argc, char *argv[]) | ^~~~ make: *** [Makefile:4649: all] Error 2 |
Hi Pauli, On Tue, Apr 2, 2024 at 12:43 PM Pauli Virtanen <pav@iki.fi> wrote: > > Add TX timestamping test utilities in new tester-utils.h, so that they > can be shared between testers. > > Add tests: > > ISO Send - TX Timestamping > ISO Send - TX Sched Timestamping > ISO Send - TX Msg Timestamping > --- > tools/iso-tester.c | 169 ++++++++++++++++++++++++++++++++++++++++--- > tools/tester-utils.h | 163 +++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 322 insertions(+), 10 deletions(-) > create mode 100644 tools/tester-utils.h > > diff --git a/tools/iso-tester.c b/tools/iso-tester.c > index 60afef301..c12675a18 100644 > --- a/tools/iso-tester.c > +++ b/tools/iso-tester.c > @@ -18,6 +18,9 @@ > #include <poll.h> > #include <stdbool.h> > > +#include <linux/errqueue.h> > +#include <linux/net_tstamp.h> > + > #include <glib.h> > > #include "lib/bluetooth.h" > @@ -34,6 +37,8 @@ > #include "src/shared/util.h" > #include "src/shared/queue.h" > > +#include "tester-utils.h" > + > #define QOS_IO(_interval, _latency, _sdu, _phy, _rtn) \ > { \ > .interval = _interval, \ > @@ -462,11 +467,12 @@ struct test_data { > uint16_t handle; > uint16_t acl_handle; > struct queue *io_queue; > - unsigned int io_id[2]; > + unsigned int io_id[3]; > uint8_t client_num; > int step; > bool reconnect; > bool suspending; > + struct tx_tstamp_data tx_ts; > }; > > struct iso_client_data { > @@ -487,6 +493,10 @@ struct iso_client_data { > size_t base_len; > bool listen_bind; > bool pa_bind; > + uint32_t so_timestamping; > + bool msg_timestamping; > + unsigned int send_extra; > + unsigned int send_extra_pre_ts; It is probably a good idea to start documenting what each of these fields are meant to do, I have no idea what are the last 2 for btw. > }; > > typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io); > @@ -677,15 +687,14 @@ static void io_free(void *data) > static void test_data_free(void *test_data) > { > struct test_data *data = test_data; > + unsigned int i; > > if (data->io_queue) > queue_destroy(data->io_queue, io_free); > > - if (data->io_id[0] > 0) > - g_source_remove(data->io_id[0]); > - > - if (data->io_id[1] > 0) > - g_source_remove(data->io_id[1]); > + for (i = 0; i < ARRAY_SIZE(data->io_id); ++i) > + if (data->io_id[i] > 0) > + g_source_remove(data->io_id[i]); > > free(data); > } > @@ -987,6 +996,38 @@ static const struct iso_client_data connect_16_2_1_send = { > .send = &send_16_2_1, > }; > > +static const struct iso_client_data connect_send_tx_timestamping = { > + .qos = QOS_16_2_1, > + .expect_err = 0, > + .send = &send_16_2_1, > + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | > + SOF_TIMESTAMPING_OPT_ID | > + SOF_TIMESTAMPING_TX_SOFTWARE), > + .send_extra = 1, > + .send_extra_pre_ts = 2, > +}; > + > +static const struct iso_client_data connect_send_tx_sched_timestamping = { > + .qos = QOS_16_2_1, > + .expect_err = 0, > + .send = &send_16_2_1, > + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | > + SOF_TIMESTAMPING_TX_SOFTWARE | > + SOF_TIMESTAMPING_OPT_TSONLY | > + SOF_TIMESTAMPING_TX_SCHED), > + .send_extra = 1, > +}; > + > +static const struct iso_client_data connect_send_tx_msg_timestamping = { > + .qos = QOS_16_2_1, > + .expect_err = 0, > + .send = &send_16_2_1, > + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | > + SOF_TIMESTAMPING_TX_SOFTWARE), > + .send_extra = 1, > + .msg_timestamping = true, > +}; > + > static const struct iso_client_data listen_16_2_1_recv = { > .qos = QOS_16_2_1, > .expect_err = 0, > @@ -1410,14 +1451,17 @@ static void bthost_recv_data(const void *buf, uint16_t len, void *user_data) > struct test_data *data = user_data; > const struct iso_client_data *isodata = data->test_data; > > + --data->step; > + > tester_print("Client received %u bytes of data", len); > > if (isodata->send && (isodata->send->iov_len != len || > memcmp(isodata->send->iov_base, buf, len))) { > if (!isodata->recv->iov_base) > tester_test_failed(); > - } else > + } else if (!data->step) { > tester_test_passed(); > + } > } > > static void bthost_iso_disconnected(void *user_data) > @@ -2058,17 +2102,95 @@ static void iso_recv(struct test_data *data, GIOChannel *io) > data->io_id[0] = g_io_add_watch(io, G_IO_IN, iso_recv_data, data); > } > > -static void iso_send(struct test_data *data, GIOChannel *io) > +static gboolean iso_recv_errqueue(GIOChannel *io, GIOCondition cond, > + gpointer user_data) > { > + struct test_data *data = user_data; > const struct iso_client_data *isodata = data->test_data; > - ssize_t ret; > + int sk = g_io_channel_unix_get_fd(io); > + int err; > + > + data->step--; > + > + err = tx_tstamp_recv(&data->tx_ts, sk, isodata->send->iov_len); > + if (err > 0) > + return TRUE; > + else if (!err && !data->step) > + tester_test_passed(); > + else > + tester_test_failed(); > + > + data->io_id[2] = 0; > + return FALSE; > +} > + > +static void iso_tx_timestamping(struct test_data *data, GIOChannel *io) > +{ > + const struct iso_client_data *isodata = data->test_data; > + struct so_timestamping so = { > + .flags = isodata->so_timestamping, > + }; > int sk; > + int err; > + unsigned int count; > + > + if (!(isodata->so_timestamping & SOF_TIMESTAMPING_TX_RECORD_MASK)) > + return; > + > + tester_print("Enabling TX timestamping"); > + > + tx_tstamp_init(&data->tx_ts, isodata->so_timestamping); > + > + for (count = 0; count < isodata->send_extra + 1; ++count) > + data->step += tx_tstamp_expect(&data->tx_ts); > > sk = g_io_channel_unix_get_fd(io); > > + data->io_id[2] = g_io_add_watch(io, G_IO_ERR, iso_recv_errqueue, data); > + > + if (isodata->msg_timestamping) > + so.flags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK; > + > + err = setsockopt(sk, SOL_SOCKET, SO_TIMESTAMPING, &so, sizeof(so)); > + if (err < 0) { > + tester_warn("setsockopt SO_TIMESTAMPING: %s (%d)", > + strerror(errno), errno); > + tester_test_failed(); > + return; > + } > +} > + > +static void iso_send_data(struct test_data *data, GIOChannel *io) > +{ > + const struct iso_client_data *isodata = data->test_data; > + char control[CMSG_SPACE(sizeof(uint32_t))]; > + struct msghdr msg = { > + .msg_iov = (struct iovec *)isodata->send, > + .msg_iovlen = 1, > + }; > + struct cmsghdr *cmsg; > + ssize_t ret; > + int sk; > + > tester_print("Writing %zu bytes of data", isodata->send->iov_len); > > - ret = writev(sk, isodata->send, 1); > + sk = g_io_channel_unix_get_fd(io); > + > + if (isodata->msg_timestamping) { > + memset(control, 0, sizeof(control)); > + msg.msg_control = control; > + msg.msg_controllen = sizeof(control); > + > + cmsg = CMSG_FIRSTHDR(&msg); > + cmsg->cmsg_level = SOL_SOCKET; > + cmsg->cmsg_type = SO_TIMESTAMPING; > + cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); > + > + *((uint32_t *)CMSG_DATA(cmsg)) = (isodata->so_timestamping & > + SOF_TIMESTAMPING_TX_RECORD_MASK); > + } > + > + ret = sendmsg(sk, &msg, 0); > if (ret < 0 || isodata->send->iov_len != (size_t) ret) { > tester_warn("Failed to write %zu bytes: %s (%d)", > isodata->send->iov_len, strerror(errno), errno); > @@ -2076,6 +2198,22 @@ static void iso_send(struct test_data *data, GIOChannel *io) > return; > } > > + data->step++; > +} > + > +static void iso_send(struct test_data *data, GIOChannel *io) > +{ > + const struct iso_client_data *isodata = data->test_data; > + unsigned int count; > + > + for (count = 0; count < isodata->send_extra_pre_ts; ++count) > + iso_send_data(data, io); > + > + iso_tx_timestamping(data, io); > + > + for (count = 0; count < isodata->send_extra + 1; ++count) > + iso_send_data(data, io); > + > if (isodata->bcast) { > tester_test_passed(); > return; > @@ -3197,6 +3335,17 @@ int main(int argc, char *argv[]) > test_iso("ISO Send - Success", &connect_16_2_1_send, setup_powered, > test_connect); > > + test_iso("ISO Send - TX Timestamping", &connect_send_tx_timestamping, > + setup_powered, test_connect); > + > + test_iso("ISO Send - TX Sched Timestamping", > + &connect_send_tx_sched_timestamping, setup_powered, > + test_connect); > + > + test_iso("ISO Send - TX Msg Timestamping", > + &connect_send_tx_msg_timestamping, setup_powered, > + test_connect); Add a comment for each test to describe what they are doing, are these supposed to test different flags that can be passed on to SO_TXTIMESTAMP? > test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered, > test_listen); > > diff --git a/tools/tester-utils.h b/tools/tester-utils.h Perhaps just have as tools/tester.h > new file mode 100644 > index 000000000..617de842e > --- /dev/null > +++ b/tools/tester-utils.h > @@ -0,0 +1,163 @@ > +// SPDX-License-Identifier: LGPL-2.1-or-later > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2022 Intel Corporation. > + * > + */ > + > +#include <stdbool.h> > +#include <stdlib.h> > +#include <stdint.h> > +#include <time.h> > +#include <sys/socket.h> > +#include <linux/errqueue.h> > +#include <linux/net_tstamp.h> > + > +#include <glib.h> > + > +#define SEC_NSEC(_t) ((_t) * 1000000000LL) > +#define TS_NSEC(_ts) (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec) > + > +struct tx_tstamp_data { > + struct { > + uint32_t id; > + uint32_t type; > + } expect[16]; > + unsigned int pos; > + unsigned int count; > + unsigned int sent; > + uint32_t so_timestamping; > +}; > + > +static inline void tx_tstamp_init(struct tx_tstamp_data *data, > + uint32_t so_timestamping) > +{ > + memset(data, 0, sizeof(*data)); > + memset(data->expect, 0xff, sizeof(data->expect)); > + > + data->so_timestamping = so_timestamping; > +} > + > +static inline int tx_tstamp_expect(struct tx_tstamp_data *data) > +{ > + unsigned int pos = data->count; > + int steps; > + > + if (data->so_timestamping & SOF_TIMESTAMPING_TX_SCHED) { > + g_assert(pos < ARRAY_SIZE(data->expect)); > + data->expect[pos].type = SCM_TSTAMP_SCHED; > + data->expect[pos].id = data->sent; > + pos++; > + } > + > + if (data->so_timestamping & SOF_TIMESTAMPING_TX_SOFTWARE) { > + g_assert(pos < ARRAY_SIZE(data->expect)); > + data->expect[pos].type = SCM_TSTAMP_SND; > + data->expect[pos].id = data->sent; > + pos++; > + } > + > + data->sent++; > + > + steps = pos - data->count; > + data->count = pos; > + return steps; > +} > + > +static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) > +{ > + unsigned char control[512]; > + ssize_t ret; > + char buf[1024]; > + struct msghdr msg; > + struct iovec iov; > + struct cmsghdr *cmsg; > + struct scm_timestamping *tss = NULL; > + struct sock_extended_err *serr = NULL; > + struct timespec now; > + > + iov.iov_base = buf; > + iov.iov_len = sizeof(buf); > + > + memset(&msg, 0, sizeof(msg)); > + msg.msg_iov = &iov; > + msg.msg_iovlen = 1; > + msg.msg_control = control; > + msg.msg_controllen = sizeof(control); > + > + ret = recvmsg(sk, &msg, MSG_ERRQUEUE); > + if (ret < 0) { > + tester_warn("Failed to read from errqueue: %s (%d)", > + strerror(errno), errno); > + return -EINVAL; > + } > + > + if (data->so_timestamping & SOF_TIMESTAMPING_OPT_TSONLY) { > + if (ret != 0) { > + tester_warn("Packet copied back to errqueue"); > + return -EINVAL; > + } > + } else if (len > ret) { > + tester_warn("Packet not copied back to errqueue: %zd", ret); > + return -EINVAL; > + } > + > + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; > + cmsg = CMSG_NXTHDR(&msg, cmsg)) { > + if (cmsg->cmsg_level == SOL_SOCKET && > + cmsg->cmsg_type == SCM_TIMESTAMPING) { > + tss = (void *)CMSG_DATA(cmsg); > + } else if (cmsg->cmsg_level == SOL_BLUETOOTH && > + cmsg->cmsg_type == BT_SCM_ERROR) { > + serr = (void *)CMSG_DATA(cmsg); > + } > + } > + > + if (!tss) { > + tester_warn("SCM_TIMESTAMPING not found"); > + return -EINVAL; > + } > + > + if (!serr) { > + tester_warn("BT_SCM_ERROR not found"); > + return -EINVAL; > + } > + > + if (serr->ee_errno != ENOMSG || > + serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { > + tester_warn("BT_SCM_ERROR wrong for timestamping"); > + return -EINVAL; > + } > + > + clock_gettime(CLOCK_REALTIME, &now); > + > + if (TS_NSEC(&now) < TS_NSEC(tss->ts) || > + TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) { > + tester_warn("nonsense in timestamp"); > + return -EINVAL; > + } > + > + if (data->pos >= data->count) { > + tester_warn("Too many timestamps"); > + return -EINVAL; > + } > + > + if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && > + serr->ee_data != data->expect[data->pos].id) { > + tester_warn("Bad timestamp id %u", serr->ee_data); > + return -EINVAL; > + } > + > + if (serr->ee_info != data->expect[data->pos].type) { > + tester_warn("Bad timestamp type %u", serr->ee_info); > + return -EINVAL; > + } > + > + tester_print("Got valid TX timestamp %u", data->pos); > + > + ++data->pos; > + > + return data->count - data->pos; > +} > -- > 2.44.0 > >
diff --git a/tools/iso-tester.c b/tools/iso-tester.c index 60afef301..c12675a18 100644 --- a/tools/iso-tester.c +++ b/tools/iso-tester.c @@ -18,6 +18,9 @@ #include <poll.h> #include <stdbool.h> +#include <linux/errqueue.h> +#include <linux/net_tstamp.h> + #include <glib.h> #include "lib/bluetooth.h" @@ -34,6 +37,8 @@ #include "src/shared/util.h" #include "src/shared/queue.h" +#include "tester-utils.h" + #define QOS_IO(_interval, _latency, _sdu, _phy, _rtn) \ { \ .interval = _interval, \ @@ -462,11 +467,12 @@ struct test_data { uint16_t handle; uint16_t acl_handle; struct queue *io_queue; - unsigned int io_id[2]; + unsigned int io_id[3]; uint8_t client_num; int step; bool reconnect; bool suspending; + struct tx_tstamp_data tx_ts; }; struct iso_client_data { @@ -487,6 +493,10 @@ struct iso_client_data { size_t base_len; bool listen_bind; bool pa_bind; + uint32_t so_timestamping; + bool msg_timestamping; + unsigned int send_extra; + unsigned int send_extra_pre_ts; }; typedef bool (*iso_defer_accept_t)(struct test_data *data, GIOChannel *io); @@ -677,15 +687,14 @@ static void io_free(void *data) static void test_data_free(void *test_data) { struct test_data *data = test_data; + unsigned int i; if (data->io_queue) queue_destroy(data->io_queue, io_free); - if (data->io_id[0] > 0) - g_source_remove(data->io_id[0]); - - if (data->io_id[1] > 0) - g_source_remove(data->io_id[1]); + for (i = 0; i < ARRAY_SIZE(data->io_id); ++i) + if (data->io_id[i] > 0) + g_source_remove(data->io_id[i]); free(data); } @@ -987,6 +996,38 @@ static const struct iso_client_data connect_16_2_1_send = { .send = &send_16_2_1, }; +static const struct iso_client_data connect_send_tx_timestamping = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_ID | + SOF_TIMESTAMPING_TX_SOFTWARE), + .send_extra = 1, + .send_extra_pre_ts = 2, +}; + +static const struct iso_client_data connect_send_tx_sched_timestamping = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_OPT_TSONLY | + SOF_TIMESTAMPING_TX_SCHED), + .send_extra = 1, +}; + +static const struct iso_client_data connect_send_tx_msg_timestamping = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .so_timestamping = (SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_SOFTWARE), + .send_extra = 1, + .msg_timestamping = true, +}; + static const struct iso_client_data listen_16_2_1_recv = { .qos = QOS_16_2_1, .expect_err = 0, @@ -1410,14 +1451,17 @@ static void bthost_recv_data(const void *buf, uint16_t len, void *user_data) struct test_data *data = user_data; const struct iso_client_data *isodata = data->test_data; + --data->step; + tester_print("Client received %u bytes of data", len); if (isodata->send && (isodata->send->iov_len != len || memcmp(isodata->send->iov_base, buf, len))) { if (!isodata->recv->iov_base) tester_test_failed(); - } else + } else if (!data->step) { tester_test_passed(); + } } static void bthost_iso_disconnected(void *user_data) @@ -2058,17 +2102,95 @@ static void iso_recv(struct test_data *data, GIOChannel *io) data->io_id[0] = g_io_add_watch(io, G_IO_IN, iso_recv_data, data); } -static void iso_send(struct test_data *data, GIOChannel *io) +static gboolean iso_recv_errqueue(GIOChannel *io, GIOCondition cond, + gpointer user_data) { + struct test_data *data = user_data; const struct iso_client_data *isodata = data->test_data; - ssize_t ret; + int sk = g_io_channel_unix_get_fd(io); + int err; + + data->step--; + + err = tx_tstamp_recv(&data->tx_ts, sk, isodata->send->iov_len); + if (err > 0) + return TRUE; + else if (!err && !data->step) + tester_test_passed(); + else + tester_test_failed(); + + data->io_id[2] = 0; + return FALSE; +} + +static void iso_tx_timestamping(struct test_data *data, GIOChannel *io) +{ + const struct iso_client_data *isodata = data->test_data; + struct so_timestamping so = { + .flags = isodata->so_timestamping, + }; int sk; + int err; + unsigned int count; + + if (!(isodata->so_timestamping & SOF_TIMESTAMPING_TX_RECORD_MASK)) + return; + + tester_print("Enabling TX timestamping"); + + tx_tstamp_init(&data->tx_ts, isodata->so_timestamping); + + for (count = 0; count < isodata->send_extra + 1; ++count) + data->step += tx_tstamp_expect(&data->tx_ts); sk = g_io_channel_unix_get_fd(io); + data->io_id[2] = g_io_add_watch(io, G_IO_ERR, iso_recv_errqueue, data); + + if (isodata->msg_timestamping) + so.flags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK; + + err = setsockopt(sk, SOL_SOCKET, SO_TIMESTAMPING, &so, sizeof(so)); + if (err < 0) { + tester_warn("setsockopt SO_TIMESTAMPING: %s (%d)", + strerror(errno), errno); + tester_test_failed(); + return; + } +} + +static void iso_send_data(struct test_data *data, GIOChannel *io) +{ + const struct iso_client_data *isodata = data->test_data; + char control[CMSG_SPACE(sizeof(uint32_t))]; + struct msghdr msg = { + .msg_iov = (struct iovec *)isodata->send, + .msg_iovlen = 1, + }; + struct cmsghdr *cmsg; + ssize_t ret; + int sk; + tester_print("Writing %zu bytes of data", isodata->send->iov_len); - ret = writev(sk, isodata->send, 1); + sk = g_io_channel_unix_get_fd(io); + + if (isodata->msg_timestamping) { + memset(control, 0, sizeof(control)); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_TIMESTAMPING; + cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); + + *((uint32_t *)CMSG_DATA(cmsg)) = (isodata->so_timestamping & + SOF_TIMESTAMPING_TX_RECORD_MASK); + } + + ret = sendmsg(sk, &msg, 0); if (ret < 0 || isodata->send->iov_len != (size_t) ret) { tester_warn("Failed to write %zu bytes: %s (%d)", isodata->send->iov_len, strerror(errno), errno); @@ -2076,6 +2198,22 @@ static void iso_send(struct test_data *data, GIOChannel *io) return; } + data->step++; +} + +static void iso_send(struct test_data *data, GIOChannel *io) +{ + const struct iso_client_data *isodata = data->test_data; + unsigned int count; + + for (count = 0; count < isodata->send_extra_pre_ts; ++count) + iso_send_data(data, io); + + iso_tx_timestamping(data, io); + + for (count = 0; count < isodata->send_extra + 1; ++count) + iso_send_data(data, io); + if (isodata->bcast) { tester_test_passed(); return; @@ -3197,6 +3335,17 @@ int main(int argc, char *argv[]) test_iso("ISO Send - Success", &connect_16_2_1_send, setup_powered, test_connect); + test_iso("ISO Send - TX Timestamping", &connect_send_tx_timestamping, + setup_powered, test_connect); + + test_iso("ISO Send - TX Sched Timestamping", + &connect_send_tx_sched_timestamping, setup_powered, + test_connect); + + test_iso("ISO Send - TX Msg Timestamping", + &connect_send_tx_msg_timestamping, setup_powered, + test_connect); + test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered, test_listen); diff --git a/tools/tester-utils.h b/tools/tester-utils.h new file mode 100644 index 000000000..617de842e --- /dev/null +++ b/tools/tester-utils.h @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2022 Intel Corporation. + * + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include <time.h> +#include <sys/socket.h> +#include <linux/errqueue.h> +#include <linux/net_tstamp.h> + +#include <glib.h> + +#define SEC_NSEC(_t) ((_t) * 1000000000LL) +#define TS_NSEC(_ts) (SEC_NSEC((_ts)->tv_sec) + (_ts)->tv_nsec) + +struct tx_tstamp_data { + struct { + uint32_t id; + uint32_t type; + } expect[16]; + unsigned int pos; + unsigned int count; + unsigned int sent; + uint32_t so_timestamping; +}; + +static inline void tx_tstamp_init(struct tx_tstamp_data *data, + uint32_t so_timestamping) +{ + memset(data, 0, sizeof(*data)); + memset(data->expect, 0xff, sizeof(data->expect)); + + data->so_timestamping = so_timestamping; +} + +static inline int tx_tstamp_expect(struct tx_tstamp_data *data) +{ + unsigned int pos = data->count; + int steps; + + if (data->so_timestamping & SOF_TIMESTAMPING_TX_SCHED) { + g_assert(pos < ARRAY_SIZE(data->expect)); + data->expect[pos].type = SCM_TSTAMP_SCHED; + data->expect[pos].id = data->sent; + pos++; + } + + if (data->so_timestamping & SOF_TIMESTAMPING_TX_SOFTWARE) { + g_assert(pos < ARRAY_SIZE(data->expect)); + data->expect[pos].type = SCM_TSTAMP_SND; + data->expect[pos].id = data->sent; + pos++; + } + + data->sent++; + + steps = pos - data->count; + data->count = pos; + return steps; +} + +static inline int tx_tstamp_recv(struct tx_tstamp_data *data, int sk, int len) +{ + unsigned char control[512]; + ssize_t ret; + char buf[1024]; + struct msghdr msg; + struct iovec iov; + struct cmsghdr *cmsg; + struct scm_timestamping *tss = NULL; + struct sock_extended_err *serr = NULL; + struct timespec now; + + iov.iov_base = buf; + iov.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + ret = recvmsg(sk, &msg, MSG_ERRQUEUE); + if (ret < 0) { + tester_warn("Failed to read from errqueue: %s (%d)", + strerror(errno), errno); + return -EINVAL; + } + + if (data->so_timestamping & SOF_TIMESTAMPING_OPT_TSONLY) { + if (ret != 0) { + tester_warn("Packet copied back to errqueue"); + return -EINVAL; + } + } else if (len > ret) { + tester_warn("Packet not copied back to errqueue: %zd", ret); + return -EINVAL; + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_TIMESTAMPING) { + tss = (void *)CMSG_DATA(cmsg); + } else if (cmsg->cmsg_level == SOL_BLUETOOTH && + cmsg->cmsg_type == BT_SCM_ERROR) { + serr = (void *)CMSG_DATA(cmsg); + } + } + + if (!tss) { + tester_warn("SCM_TIMESTAMPING not found"); + return -EINVAL; + } + + if (!serr) { + tester_warn("BT_SCM_ERROR not found"); + return -EINVAL; + } + + if (serr->ee_errno != ENOMSG || + serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { + tester_warn("BT_SCM_ERROR wrong for timestamping"); + return -EINVAL; + } + + clock_gettime(CLOCK_REALTIME, &now); + + if (TS_NSEC(&now) < TS_NSEC(tss->ts) || + TS_NSEC(&now) > TS_NSEC(tss->ts) + SEC_NSEC(10)) { + tester_warn("nonsense in timestamp"); + return -EINVAL; + } + + if (data->pos >= data->count) { + tester_warn("Too many timestamps"); + return -EINVAL; + } + + if ((data->so_timestamping & SOF_TIMESTAMPING_OPT_ID) && + serr->ee_data != data->expect[data->pos].id) { + tester_warn("Bad timestamp id %u", serr->ee_data); + return -EINVAL; + } + + if (serr->ee_info != data->expect[data->pos].type) { + tester_warn("Bad timestamp type %u", serr->ee_info); + return -EINVAL; + } + + tester_print("Got valid TX timestamp %u", data->pos); + + ++data->pos; + + return data->count - data->pos; +}