@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
+#include <poll.h>
#include <stdbool.h>
#include <glib.h>
@@ -81,6 +82,7 @@ struct l2cap_data {
bool server_not_advertising;
bool direct_advertising;
bool close_1;
+ bool defer;
bool shut_sock_wr;
};
@@ -540,6 +542,64 @@ static const struct l2cap_data le_server_nval_scid_test = {
.expect_cmd_len = sizeof(nval_le_connect_rsp),
};
+static const uint8_t ecred_connect_req[] = { 0x80, 0x00, /* PSM */
+ 0x40, 0x00, /* MTU */
+ 0x40, 0x00, /* MPS */
+ 0x05, 0x00, /* Credits */
+ 0x41, 0x00, /* SCID #1 */
+ 0x42, 0x00, /* SCID #2 */
+ 0x43, 0x00, /* SCID #3 */
+ 0x44, 0x00, /* SCID #4 */
+ 0x45, 0x00, /* SCID #5 */
+};
+
+static const uint8_t ecred_connect_rsp[] = { 0xa0, 0x02, /* MTU */
+ 0xbc, 0x00, /* MPS */
+ 0x04, 0x00, /* Credits */
+ 0x00, 0x00, /* Result */
+ 0x40, 0x00, /* DCID #1 */
+ 0x41, 0x00, /* DCID #2 */
+ 0x42, 0x00, /* DCID #3 */
+ 0x43, 0x00, /* DCID #4 */
+ 0x44, 0x00, /* DCID #5 */
+};
+
+static const struct l2cap_data ext_flowctl_server_success_test = {
+ .server_psm = 0x0080,
+ .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ,
+ .send_cmd = ecred_connect_req,
+ .send_cmd_len = sizeof(ecred_connect_req),
+ .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP,
+ .expect_cmd = ecred_connect_rsp,
+ .expect_cmd_len = sizeof(ecred_connect_rsp),
+};
+
+static const uint8_t nval_ecred_connect_req[] = {
+ 0x80, 0x00, /* PSM */
+ 0x40, 0x00, /* MTU */
+ 0x40, 0x00, /* MPS */
+ 0x05, 0x00, /* Credits */
+ 0x01, 0x00, /* SCID #1 */
+};
+
+static const uint8_t nval_ecred_connect_rsp[] = {
+ 0x00, 0x00, /* MTU */
+ 0x00, 0x00, /* MPS */
+ 0x00, 0x00, /* Credits */
+ 0x09, 0x00, /* Result */
+ 0x00, 0x00, /* DCID #1 */
+};
+
+static const struct l2cap_data ext_flowctl_server_nval_scid_test = {
+ .server_psm = 0x0080,
+ .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ,
+ .send_cmd = nval_ecred_connect_req,
+ .send_cmd_len = sizeof(nval_ecred_connect_req),
+ .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP,
+ .expect_cmd = nval_ecred_connect_rsp,
+ .expect_cmd_len = sizeof(nval_ecred_connect_rsp),
+};
+
static const struct l2cap_data le_att_client_connect_success_test_1 = {
.cid = 0x0004,
.sec_level = BT_SECURITY_LOW,
@@ -549,6 +609,69 @@ static const struct l2cap_data le_att_server_success_test_1 = {
.cid = 0x0004,
};
+static const struct l2cap_data le_eatt_client_connect_success_test_1 = {
+ .client_psm = 0x0027,
+ .server_psm = 0x0027,
+ .mode = BT_MODE_EXT_FLOWCTL,
+ .sec_level = BT_SECURITY_LOW,
+};
+
+static const uint8_t eatt_connect_req[] = { 0x27, 0x00, /* PSM */
+ 0x40, 0x00, /* MTU */
+ 0x40, 0x00, /* MPS */
+ 0x05, 0x00, /* Credits */
+ 0x41, 0x00, /* SCID #1 */
+};
+
+static const uint8_t eatt_connect_rsp[] = { 0xa0, 0x02, /* MTU */
+ 0xbc, 0x00, /* MPS */
+ 0x04, 0x00, /* Credits */
+ 0x00, 0x00, /* Result */
+ 0x40, 0x00, /* DCID #1 */
+};
+
+static const struct l2cap_data le_eatt_server_success_test_1 = {
+ .server_psm = 0x0027,
+ .mode = BT_MODE_EXT_FLOWCTL,
+ .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ,
+ .send_cmd = eatt_connect_req,
+ .send_cmd_len = sizeof(eatt_connect_req),
+ .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP,
+ .expect_cmd = eatt_connect_rsp,
+ .expect_cmd_len = sizeof(eatt_connect_rsp),
+ .defer = true,
+};
+
+static const uint8_t eatt_reject_req[] = { 0x27, 0x00, /* PSM */
+ 0x40, 0x00, /* MTU */
+ 0x40, 0x00, /* MPS */
+ 0x05, 0x00, /* Credits */
+ 0x41, 0x00, /* SCID #1 */
+ 0x42, 0x00, /* SCID #2 */
+ 0x43, 0x00, /* SCID #3 */
+ 0x44, 0x00, /* SCID #4 */
+ 0x45, 0x00, /* SCID #5 */
+};
+
+static const uint8_t eatt_reject_rsp[] = { 0xa0, 0x02, /* MTU */
+ 0xbc, 0x00, /* MPS */
+ 0x04, 0x00, /* Credits */
+ 0x06, 0x00, /* Result */
+};
+
+static const struct l2cap_data le_eatt_server_reject_test_1 = {
+ .server_psm = 0x0027,
+ .mode = BT_MODE_EXT_FLOWCTL,
+ .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ,
+ .send_cmd = eatt_reject_req,
+ .send_cmd_len = sizeof(eatt_reject_req),
+ .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP,
+ .expect_cmd = eatt_reject_rsp,
+ .expect_cmd_len = sizeof(eatt_reject_rsp),
+ .defer = true,
+ .expect_err = -1,
+};
+
static const struct l2cap_data ext_flowctl_client_connect_success_test_1 = {
.client_psm = 0x0080,
.server_psm = 0x0080,
@@ -1689,6 +1812,89 @@ static void test_connect_2(const void *test_data)
defer);
}
+static gboolean l2cap_accept_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct l2cap_data *l2data = data->test_data;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ if (!check_mtu(data, sk)) {
+ tester_test_failed();
+ return FALSE;
+ }
+
+ if (l2data->read_data) {
+ struct bthost *bthost;
+
+ bthost = hciemu_client_get_host(data->hciemu);
+ g_io_add_watch(io, G_IO_IN, server_received_data, NULL);
+ bthost_send_cid(bthost, data->handle, data->dcid,
+ l2data->read_data, l2data->data_len);
+
+ g_io_channel_unref(io);
+
+ return FALSE;
+ } else if (l2data->write_data) {
+ struct bthost *bthost;
+ ssize_t ret;
+
+ bthost = hciemu_client_get_host(data->hciemu);
+ bthost_add_cid_hook(bthost, data->handle, data->scid,
+ server_bthost_received_data, NULL);
+
+ ret = write(sk, l2data->write_data, l2data->data_len);
+
+ if (ret != l2data->data_len) {
+ tester_warn("Unable to write all data");
+ tester_test_failed();
+ }
+
+ return FALSE;
+ }
+
+ tester_print("Successfully connected");
+
+ tester_test_passed();
+
+ return FALSE;
+}
+
+static bool defer_accept(struct test_data *data, GIOChannel *io)
+{
+ int sk;
+ char c;
+ struct pollfd pfd;
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sk;
+ pfd.events = POLLOUT;
+
+ if (poll(&pfd, 1, 0) < 0) {
+ tester_warn("poll: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+
+ if (!(pfd.revents & POLLOUT)) {
+ if (read(sk, &c, 1) < 0) {
+ tester_warn("read: %s (%d)", strerror(errno), errno);
+ return false;
+ }
+ }
+
+ data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_accept_cb, NULL);
+
+ g_io_channel_unref(io);
+
+ tester_print("Accept deferred setup");
+
+ return true;
+}
+
static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
@@ -1707,53 +1913,23 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
return FALSE;
}
- if (!check_mtu(data, new_sk)) {
- tester_test_failed();
- close(new_sk);
- return FALSE;
- }
+ io = g_io_channel_unix_new(new_sk);
+ g_io_channel_set_close_on_unref(io, TRUE);
- if (l2data->read_data) {
- struct bthost *bthost;
- GIOChannel *new_io;
-
- new_io = g_io_channel_unix_new(new_sk);
- g_io_channel_set_close_on_unref(new_io, TRUE);
-
- bthost = hciemu_client_get_host(data->hciemu);
- g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL);
- bthost_send_cid(bthost, data->handle, data->dcid,
- l2data->read_data, l2data->data_len);
-
- g_io_channel_unref(new_io);
-
- return FALSE;
- } else if (l2data->write_data) {
- struct bthost *bthost;
- ssize_t ret;
-
- bthost = hciemu_client_get_host(data->hciemu);
- bthost_add_cid_hook(bthost, data->handle, data->scid,
- server_bthost_received_data, NULL);
-
- ret = write(new_sk, l2data->write_data, l2data->data_len);
- close(new_sk);
-
- if (ret != l2data->data_len) {
- tester_warn("Unable to write all data");
- tester_test_failed();
+ if (l2data->defer) {
+ if (l2data->expect_err < 0) {
+ g_io_channel_unref(io);
+ return FALSE;
}
+ if (!defer_accept(data, io)) {
+ tester_warn("Unable to accept deferred setup");
+ tester_test_failed();
+ }
return FALSE;
}
- tester_print("Successfully connected");
-
- close(new_sk);
-
- tester_test_passed();
-
- return FALSE;
+ return l2cap_accept_cb(io, cond, user_data);
}
static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
@@ -1767,7 +1943,7 @@ static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
if (code != l2data->expect_cmd_code) {
tester_warn("Unexpected L2CAP response code (expected 0x%02x)",
l2data->expect_cmd_code);
- return;
+ goto failed;
}
if (code == BT_L2CAP_PDU_CONN_RSP) {
@@ -1844,6 +2020,8 @@ static void test_server(const void *test_data)
int sk;
if (l2data->server_psm || l2data->cid) {
+ int opt = 1;
+
sk = create_l2cap_sock(data, l2data->server_psm,
l2data->cid, l2data->sec_level,
l2data->mode);
@@ -1852,6 +2030,15 @@ static void test_server(const void *test_data)
return;
}
+ if (l2data->defer && setsockopt(sk, SOL_BLUETOOTH,
+ BT_DEFER_SETUP, &opt, sizeof(opt)) < 0) {
+ tester_warn("Can't enable deferred setup: %s (%d)",
+ strerror(errno), errno);
+ tester_test_failed();
+ close(sk);
+ return;
+ }
+
if (listen(sk, 5) < 0) {
tester_warn("listening on socket failed: %s (%u)",
strerror(errno), errno);
@@ -2066,6 +2253,13 @@ int main(int argc, char *argv[])
setup_powered_client,
test_connect_2);
+ test_l2cap_le("L2CAP Ext-Flowctl Server - Success",
+ &ext_flowctl_server_success_test,
+ setup_powered_server, test_server);
+ test_l2cap_le("L2CAP Ext-Flowctl Server - Nval SCID",
+ &ext_flowctl_server_nval_scid_test,
+ setup_powered_server, test_server);
+
test_l2cap_le("L2CAP LE ATT Client - Success",
&le_att_client_connect_success_test_1,
setup_powered_client, test_connect);
@@ -2073,5 +2267,15 @@ int main(int argc, char *argv[])
&le_att_server_success_test_1,
setup_powered_server, test_server);
+ test_l2cap_le("L2CAP LE EATT Client - Success",
+ &le_eatt_client_connect_success_test_1,
+ setup_powered_client, test_connect);
+ test_l2cap_le("L2CAP LE EATT Server - Success",
+ &le_eatt_server_success_test_1,
+ setup_powered_server, test_server);
+ test_l2cap_le("L2CAP LE EATT Server - Reject",
+ &le_eatt_server_reject_test_1,
+ setup_powered_server, test_server);
+
return tester_run();
}
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> This adds the following tests: L2CAP Ext-Flowctl Server - Success L2CAP Ext-Flowctl Server - Nval SCID L2CAP LE EATT Client - Success L2CAP LE EATT Server - Success L2CAP LE EATT Server - Reject --- tools/l2cap-tester.c | 288 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 246 insertions(+), 42 deletions(-)