diff --git a/tools/iso-tester.c b/tools/iso-tester.c
index 0eeab4f..4bf1a28 100644
--- a/tools/iso-tester.c
+++ b/tools/iso-tester.c
const struct iovec *recv;
bool server;
bool bcast;
+ bool past;
+ bool rpa;
bool defer;
bool disconnect;
bool ts;
.base_len = sizeof(base_lc3_16_2_1),
};
+static const struct iso_client_data past_16_2_1_send = {
+ .qos = QOS_OUT_16_2_1,
+ .expect_err = 0,
+ .send = &send_16_2_1,
+ .bcast = true,
+ .past = true,
+ .rpa = true,
+ .base = base_lc3_16_2_1,
+ .base_len = sizeof(base_lc3_16_2_1),
+};
+
+static const struct iso_client_data past_16_2_1 = {
+ .qos = QOS_OUT_16_2_1,
+ .expect_err = 0,
+ .recv = &send_16_2_1,
+ .bcast = true,
+ .past = true,
+ .big = true,
+ .base = base_lc3_16_2_1,
+ .base_len = sizeof(base_lc3_16_2_1),
+};
+
static const struct iso_client_data bcast_enc_16_2_1_send = {
.qos = QOS_OUT_ENC_16_2_1,
.expect_err = 0,
.big = true,
};
+static const struct iso_client_data past_16_2_1_recv = {
+ .qos = QOS_IN_16_2_1,
+ .expect_err = 0,
+ .recv = &send_16_2_1,
+ .bcast = true,
+ .past = true,
+ .server = true,
+ .big = true,
+};
+
static const struct iso_client_data bcast_16_2_1_recv2 = {
.qos = QOS_IN_16_2_1,
.expect_err = 0,
{
struct test_data *data = user_data;
static uint8_t client_num;
+ const struct iso_client_data *isodata = data->test_data;
if (opcode != BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE)
return;
if (status)
tester_setup_failed();
else if (data->client_num == client_num) {
- tester_setup_complete();
+ if (!isodata || !isodata->past)
+ tester_setup_complete();
client_num = 0;
}
}
{
struct test_data *data = user_data;
struct bthost *host;
+ const struct iso_client_data *isodata = data->test_data;
+ static uint8_t client_num;
tester_print("New client connection with handle 0x%04x", handle);
host = hciemu_client_get_host(data->hciemu);
bthost_add_iso_hook(host, data->handle, bthost_recv_data, data,
bthost_iso_disconnected);
+
+ if (!isodata->past || data->client_num == 1)
+ return;
+
+ client_num++;
+
+ if (client_num == data->client_num)
+ tester_test_passed();
}
static uint8_t iso_accept_conn(uint16_t handle, void *user_data)
static void acl_new_conn(uint16_t handle, void *user_data)
{
struct test_data *data = user_data;
+ const struct iso_client_data *isodata = data->test_data;
tester_print("New ACL connection with handle 0x%04x", handle);
data->acl_handle = handle;
+
+ if (!isodata->past)
+ return;
+
+ tester_setup_complete();
+
+ if (!isodata->server) {
+ struct bthost *host;
+
+ host = hciemu_client_get_host(data->hciemu);
+ bthost_set_past_mode(host, data->acl_handle, 0x03);
+ }
+}
+
+static void setup_connect_recv(struct test_data *data, struct bthost *host)
+{
+ const uint8_t *bdaddr;
+
+ bdaddr = hciemu_get_central_bdaddr(data->hciemu);
+ bthost_set_connect_cb(host, acl_new_conn, data);
+ bthost_hci_connect(host, bdaddr, BDADDR_LE_PUBLIC);
+}
+
+static void setup_past_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct test_data *data = tester_get_data();
+ struct bthost *host = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Set device flags status 0x%02x", status);
+
+ setup_connect_recv(data, host);
+}
+
+static void setup_past_recv(struct test_data *data, struct bthost *host)
+{
+ struct mgmt_cp_add_device add;
+ struct mgmt_cp_set_device_flags set;
+
+ memset(&add, 0, sizeof(add));
+ bacpy(&add.addr.bdaddr,
+ (bdaddr_t *)hciemu_get_client_bdaddr(data->hciemu));
+ add.addr.type = BDADDR_LE_PUBLIC;
+ add.action = 0x01; /* Allow incoming connection */
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_DEVICE, data->mgmt_index,
+ sizeof(add), &add, NULL, NULL, NULL);
+
+ memset(&set, 0, sizeof(set));
+ bacpy(&set.addr.bdaddr,
+ (bdaddr_t *)hciemu_get_client_bdaddr(data->hciemu));
+ set.addr.type = BDADDR_LE_PUBLIC;
+ set.current_flags = BIT(3);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_DEVICE_FLAGS, data->mgmt_index,
+ sizeof(set), &set, setup_past_complete, host,
+ NULL);
}
static void setup_powered_callback(uint8_t status, uint16_t length,
bthost_set_base(host, isodata->base,
isodata->base_len);
- if (isodata->big)
+ if (isodata->big && (!isodata->past ||
+ i + 1 == data->client_num))
bthost_create_big(host, 1,
isodata->qos.bcast.encryption,
isodata->qos.bcast.bcode);
- } else if (!isodata->send && isodata->recv) {
- const uint8_t *bdaddr;
+ if (isodata->past)
+ setup_past_recv(data, host);
- bdaddr = hciemu_get_central_bdaddr(data->hciemu);
- bthost_set_connect_cb(host, acl_new_conn, data);
- bthost_hci_connect(host, bdaddr, BDADDR_LE_PUBLIC);
- }
+ } else if (!isodata->send && isodata->recv)
+ setup_connect_recv(data, host);
}
}
tester_print("Powering on controller");
- if (!isodata || !isodata->bcast)
+ if (!isodata || !isodata->bcast || isodata->past)
mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
sizeof(param), param,
NULL, NULL, NULL);
mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
sizeof(param), param, NULL, NULL, NULL);
- if (isodata && isodata->server && !isodata->bcast)
+ if (isodata && isodata->rpa) {
+ const uint8_t params[] = { 0x01,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_PRIVACY, data->mgmt_index,
+ sizeof(params), params, NULL, NULL, NULL);
+ }
+
+ if (isodata && ((isodata->server && !isodata->bcast) || isodata->past))
mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING,
data->mgmt_index, sizeof(param), param, NULL,
NULL, NULL);
addr->iso_bc->bc_sid = isodata->sid;
err = bind(sk, (struct sockaddr *) addr, sizeof(*addr) +
sizeof(*addr->iso_bc));
+ free(addr);
} else {
addr = malloc(sizeof(*addr));
memset(addr, 0, sizeof(*addr));
bacpy(&addr->iso_bdaddr, (void *) master_bdaddr);
addr->iso_bdaddr_type = BDADDR_LE_PUBLIC;
err = bind(sk, (struct sockaddr *) addr, sizeof(*addr));
+ free(addr);
}
if (err < 0) {
const struct iso_client_data *isodata = data->test_data;
unsigned int count;
+ if (isodata->past && !data->handle)
+ return TRUE;
+
data->io_id[0] = 0;
tester_print("POLLOUT event received");
iso_send_data(data, io);
if (isodata->bcast) {
+ if (isodata->past)
+ return FALSE;
tester_test_passed();
return FALSE;
}
hook_set_event_mask, data);
}
+static int iso_rebind_bcast_dst(struct test_data *data, int sk)
+{
+ struct hciemu_client *client;
+ const uint8_t *dst;
+ struct sockaddr_iso *addr = NULL;
+ int err;
+
+ client = hciemu_get_client(data->hciemu, 0);
+
+ /* Bind to local address */
+ addr = malloc(sizeof(*addr) + sizeof(*addr->iso_bc));
+ memset(addr, 0, sizeof(*addr) + sizeof(*addr->iso_bc));
+ addr->iso_family = AF_BLUETOOTH;
+
+ /* Bind to destination address in case of broadcast */
+ dst = hciemu_client_bdaddr(client);
+ if (!dst) {
+ tester_warn("No destination bdaddr");
+ free(addr);
+ return -ENODEV;
+ }
+
+ bacpy(&addr->iso_bc->bc_bdaddr, (void *) dst);
+ addr->iso_bc->bc_bdaddr_type = BDADDR_LE_PUBLIC;
+
+ err = bind(sk, (struct sockaddr *) addr, sizeof(*addr) +
+ sizeof(*addr->iso_bc));
+ if (err)
+ tester_warn("bind: %s (%d)", strerror(errno), errno);
+ else
+ tester_print("PAST in progress...");
+
+ free(addr);
+
+ return err;
+}
+
+static bool check_rpa(struct test_data *data, int sk)
+{
+ const struct iso_client_data *isodata = data->test_data;
+ struct sockaddr_iso addr;
+ socklen_t olen;
+ const uint8_t *src;
+
+ if (!isodata->rpa)
+ return true;
+
+ src = hciemu_get_central_bdaddr(data->hciemu);
+ if (!src) {
+ tester_warn("No source bdaddr");
+ return false;
+ }
+
+ olen = sizeof(addr);
+
+ memset(&addr, 0, olen);
+ if (getsockname(sk, (void *)&addr, &olen) < 0) {
+ tester_warn("getsockname: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ /* Compare socket source address (RPA) with central address, if they
+ * match it means the RPA has not been set.
+ */
+ if (!bacmp(&addr.iso_bdaddr, (bdaddr_t *)src)) {
+ char a1[18], a2[18];
+
+ ba2str(&addr.iso_bdaddr, a1);
+ ba2str((bdaddr_t *)src, a2);
+
+ tester_warn("rpa %s == %s id", a1, a2);
+ return false;
+
+ }
+
+ return true;
+}
+
static gboolean iso_connect(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
if (!isodata->bcast) {
ret = check_ucast_qos(&qos, &isodata->qos,
isodata->mconn ? &isodata->qos_2 : NULL);
- } else if (!isodata->server)
+ } else if (!isodata->server) {
ret = check_bcast_qos(&qos, &isodata->qos);
+ if (ret && isodata->past)
+ ret = iso_rebind_bcast_dst(data, sk) == 0;
+ }
if (!ret) {
tester_warn("Unexpected QoS parameter");
if (err < 0)
tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
- else
+ else {
+ if (!check_rpa(data, sk)) {
+ data->step = 0;
+ tester_test_failed();
+ return FALSE;
+ }
+
tester_print("Successfully connected");
+ }
if (err != isodata->expect_err) {
tester_warn("Expect error: %s (%d) != %s (%d)",
return err;
}
+static gboolean test_listen_past(gpointer user_data)
+{
+ struct test_data *data = tester_get_data();
+ struct bthost *host;
+
+ host = hciemu_client_get_host(data->hciemu);
+ bthost_past_set_info(host, data->acl_handle);
+
+ return FALSE;
+}
+
static void setup_listen_many(struct test_data *data, uint8_t n, uint8_t *num,
GIOFunc *func)
{
host = hciemu_client_host(client);
bthost_set_cig_params(host, 0x01, 0x01, &isodata->qos);
- bthost_create_cis(host, 257, data->acl_handle);
+ bthost_create_cis(host, 256, data->acl_handle);
+ } else if (isodata->past) {
+ if (!data->acl_handle) {
+ tester_print("ACL handle not set");
+ tester_test_failed();
+ return;
+ }
+
+ /* Wait for listen to take effect before initiating PAST
+ * procedure.
+ */
+ data->io_id[i] = g_timeout_add(250, test_listen_past, data);
}
}
setup_connect(data, 0, iso_connect_cb);
}
+static void test_past(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ uint8_t num[1] = { 1 };
+ GIOFunc funcs[1] = { iso_accept2_cb };
+
+ if (data->client_num > 1)
+ setup_listen_many(data, 1, num, funcs);
+ else
+ setup_connect(data, 0, iso_connect_cb);
+}
+
static void test_bcast_reconnect(const void *test_data)
{
struct test_data *data = tester_get_data();
setup_listen_many(data, 2, num, funcs);
}
+static void test_past_recv(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+
+ setup_listen(data, 0, iso_accept_cb);
+}
+
static void test_connect2_suspend(const void *test_data)
{
test_connect2(test_data);
test_iso("ISO Broadcaster - Success", &bcast_16_2_1_send, setup_powered,
test_bcast);
+ test_iso("ISO Broadcaster PAST Info - Success", &past_16_2_1_send,
+ setup_powered,
+ test_past);
+ test_iso("ISO Broadcaster PAST Info RPA - Success", &past_16_2_1_send,
+ setup_powered,
+ test_past);
+ test_iso2("ISO Broadcaster PAST Sender - Success", &past_16_2_1,
+ setup_powered,
+ test_past);
test_iso("ISO Broadcaster Encrypted - Success", &bcast_enc_16_2_1_send,
setup_powered,
test_bcast);
test_iso("ISO Broadcaster Receiver - Success", &bcast_16_2_1_recv,
setup_powered,
test_bcast_recv);
+ test_iso("ISO Broadcaster PAST Receiver - Success",
+ &past_16_2_1_recv,
+ setup_powered,
+ test_past_recv);
test_iso("ISO Broadcaster Receiver SID auto - Success",
&bcast_16_2_1_recv_sid,
setup_powered,