From patchwork Wed Jun 22 22:27:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891540 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 669FFC433EF for ; Wed, 22 Jun 2022 22:28:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1357800AbiFVW2M (ORCPT ); Wed, 22 Jun 2022 18:28:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59814 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358195AbiFVW2L (ORCPT ); Wed, 22 Jun 2022 18:28:11 -0400 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DF9AC61 for ; Wed, 22 Jun 2022 15:28:04 -0700 (PDT) Received: by mail-pj1-x1034.google.com with SMTP id b12-20020a17090a6acc00b001ec2b181c98so734828pjm.4 for ; Wed, 22 Jun 2022 15:28:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=Vv42n/CXFtmXKiytAwGXUBHYtpFJN5DHMBKlRP0RBuE=; b=lZtccvAHliHXPSFmRgCFwohi3YjYwJYZx99uDa/ToXu0QEqcoM9w2qCKziECjFPYbq IAD4TgU0floGBywGy2UHI6hBFjOo8itO6sVqNmrF+AK3jwvY8pbPsdSKRKGkLyyVPd1b yPmOag2SuyBMZJ/6qJYDs7e4XUlYEqrIS4xd//b4fqT9Y9Cmor3ezVtqCYUwB7jLddGV qJLDh9YkCpIeUQX3qqkLtAO6txO5I0xsIi++jRe0NXzWyZNhHxxC036MDkj2zxQiWaAZ os4c3upPet4TV/ofKZkgOppRU23rwEtBpDRKg9bWHQ+GaPJNBiYos1VB7nshw8eSFOw8 g7fQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=Vv42n/CXFtmXKiytAwGXUBHYtpFJN5DHMBKlRP0RBuE=; b=7HGtUr3lGoc3M+Whchm6FX9QHIUmjjwLUfNxwo5TP4lYBgoDpEhTWTI9Og2Scp37Zp Yaa2gHngtTG0zfuGbXDm2MRDbduNUY2PpbhtkJ5YS8iNZfNI3G2GrmxT0OPvNrRafgYL QPefVEbstvQaMYB9yWNJwTz6qWsxr5G3WBazZ8fJPu3OCzwuSrqqKh8ACJToJAFQPSS4 6+f9ezrpNQS0cXkPOWDNb6ftCeLseF8hJ0gcF6jvfQa6ihm+fGX+h7YtJmvBYrBB81ke TMWdir8f41F7wvWHOyYjaxA0d3dvUqC4ZPGdVUw1+tCR2gIYlVIkQd6eb7TfJXHvcalI TQsQ== X-Gm-Message-State: AJIora8GUJgyVpYDOoqSETtxCG1iPDna8phviIITZH6DC9OE0gsCp29S cNIHud3B5MBBltOg+PGV7saaShRph6lIow== X-Google-Smtp-Source: AGRyM1udCMNrQs61utT/OGOqdZQdLL+NTviCgVVE4ULt3kFQgc56R889XrCRODReOXVInumZYiHiPw== X-Received: by 2002:a17:90b:3b52:b0:1ec:db2a:b946 with SMTP id ot18-20020a17090b3b5200b001ecdb2ab946mr596007pjb.229.1655936883170; Wed, 22 Jun 2022 15:28:03 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.02 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:02 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 1/8] lib: Add definitions for ISO socket Date: Wed, 22 Jun 2022 15:27:54 -0700 Message-Id: <20220622222801.2676431-1-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz --- v2: Fix CI findings. v3: Add patch fixing mgmt-tester Read EXP Features tests. v4: Rebase and add flag EXP_FEAT_ISO_SOCKET v5: Add BT_DEFER_SETUP tests to iso-tester v6: Make iso-tester disable ISO Socket experimental UUID after each test v7: Fix scan-build findings Makefile.am | 2 +- lib/bluetooth.h | 38 +++++++++++++++++++++++++++++++++++++- lib/iso.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 lib/iso.h diff --git a/Makefile.am b/Makefile.am index 0074ea3ac..cead4b8c6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,7 +69,7 @@ lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h -extra_headers = lib/mgmt.h lib/uuid.h lib/a2mp.h lib/amp.h +extra_headers = lib/mgmt.h lib/uuid.h lib/a2mp.h lib/amp.h lib/iso.h extra_sources = lib/uuid.c local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) diff --git a/lib/bluetooth.h b/lib/bluetooth.h index e6171cef0..af5fbcfbc 100644 --- a/lib/bluetooth.h +++ b/lib/bluetooth.h @@ -37,6 +37,7 @@ extern "C" { #define BTPROTO_CMTP 5 #define BTPROTO_HIDP 6 #define BTPROTO_AVDTP 7 +#define BTPROTO_ISO 8 #define SOL_HCI 0 #define SOL_L2CAP 6 @@ -140,7 +141,39 @@ struct bt_voice { #define BT_SCM_PKT_STATUS 0x03 -#define BT_CODEC 19 +#define BT_ISO_QOS 17 + +#define BT_ISO_QOS_CIG_UNSET 0xff +#define BT_ISO_QOS_CIS_UNSET 0xff + +struct bt_iso_io_qos { + uint32_t interval; + uint16_t latency; + uint16_t sdu; + uint8_t phy; + uint8_t rtn; +}; + +struct bt_iso_qos { + union { + uint8_t cig; + uint8_t big; + }; + union { + uint8_t cis; + uint8_t bis; + }; + union { + uint8_t sca; + uint8_t sync_interval; + }; + uint8_t packing; + uint8_t framing; + struct bt_iso_io_qos in; + struct bt_iso_io_qos out; +}; + +#define BT_CODEC 19 struct bt_codec { uint8_t id; uint16_t cid; @@ -158,6 +191,7 @@ struct bt_codecs { struct bt_codec codecs[]; } __attribute__((packed)); + /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ @@ -171,6 +205,8 @@ enum { BT_CLOSED }; +#define BT_ISO_BASE 20 + /* Byte order conversions */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define htobs(d) (d) diff --git a/lib/iso.h b/lib/iso.h new file mode 100644 index 000000000..1e9f79ce5 --- /dev/null +++ b/lib/iso.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2022 Intel Corporation. + * + */ + +#ifndef __ISO_H +#define __ISO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ISO defaults */ +#define ISO_DEFAULT_MTU 251 +#define ISO_MAX_NUM_BIS 0x1f + +/* ISO socket broadcast address */ +struct sockaddr_iso_bc { + bdaddr_t bc_bdaddr; + uint8_t bc_bdaddr_type; + uint8_t bc_sid; + uint8_t bc_num_bis; + uint8_t bc_bis[ISO_MAX_NUM_BIS]; +}; + +/* ISO socket address */ +struct sockaddr_iso { + sa_family_t iso_family; + bdaddr_t iso_bdaddr; + uint8_t iso_bdaddr_type; + struct sockaddr_iso_bc iso_bc[]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __ISO_H */ From patchwork Wed Jun 22 22:27:55 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891541 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2309BC43334 for ; Wed, 22 Jun 2022 22:28:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358340AbiFVW2N (ORCPT ); Wed, 22 Jun 2022 18:28:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59838 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358301AbiFVW2L (ORCPT ); Wed, 22 Jun 2022 18:28:11 -0400 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7E47FC69 for ; Wed, 22 Jun 2022 15:28:05 -0700 (PDT) Received: by mail-pg1-x535.google.com with SMTP id 23so11205802pgc.8 for ; Wed, 22 Jun 2022 15:28:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=BY74aJKawL8ZJHJdZMpTNWRQDCAFbo7G5NU4C+yjJ6Y=; b=jfstvvb869LNps0K8YXch6lAZuaU3gYFNvdBUD1afQTPhpG9MitdoTb3CYxO8IhILY yaJD4uaypCSyyq4Iw7EIUonEfLEAW09u67vDcNP1gCFFjWCjCNRXy/SouFJrITsp3cYi C3ncB7Ie1h3yWJavt4UsZvRBz73c2aJMh78iJS8nyHyXcjMbw7YC5urvDNbiYXCz4V3W 8JkClWaJTwuV8qMy+uL3T1eWmu5kC33IhcMRRlsqAO+s7oBTv5PLXIh/U2AF5Qlt27mJ fu2BHDqwOcdfPYOSOoY6qi43VBz6H5rAEI2DziYMIYbkc6FGDJlsrCr2BCNB7gi6PsK1 gUtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=BY74aJKawL8ZJHJdZMpTNWRQDCAFbo7G5NU4C+yjJ6Y=; b=6D+xOOzc4Ae6OjBj++Pk1KS0IU7o4nBj2N6ZE7jqv+e46Rd7NnTTXe3PHmy7JIafHQ azKwnCcx0epe4NlsblVSwVbDUNYluO0JGYCBhCas2/dgLjj04Tk/XWbXYC1R8JWSDfu5 3TX6StVwvAEN0kmFSo6AHDD2oCDIRZOOCN3Kiwj7JrddhEx/ur8FgMqBrKrfTOJngcGK 49yNIL6CLQBDcJ1R2Ou6BOpMNoMDCFue2jMgyegHeGl9c04SWmBGp7gdNXE9E0sM7aFQ GBBrwcsoy++Gx/fDaCryH8gZUl6wZ55uZDllxQpd6VXZyJeKRHs4FjFBsfeic7V8FlgH ujGw== X-Gm-Message-State: AJIora8gJngRJCXkPIIWST/pxaQfN8hhBJET0RMoMbjwGdpgJS3ewpKo kXv7DXgrieaZb5m0HALwzTLxsG+nMbwM0w== X-Google-Smtp-Source: AGRyM1sWTjM0qyRwcuSqGXEdKlJcPXaP6WNL5CdyL6kI/Df/9BAtQfHNj9vEhWWwZ/ePnDA7Cph39g== X-Received: by 2002:a62:3302:0:b0:524:e839:c3b8 with SMTP id z2-20020a623302000000b00524e839c3b8mr28127444pfz.76.1655936884446; Wed, 22 Jun 2022 15:28:04 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.03 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:03 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 2/8] shared/util: Decode BlueZ Experimental ISO Socket UUID Date: Wed, 22 Jun 2022 15:27:55 -0700 Message-Id: <20220622222801.2676431-2-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds BlueZ experimental ISO Socket UUID to uuid128_table so it is decoded by the likes of btmon. --- src/shared/util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shared/util.c b/src/shared/util.c index 33196bf8b..b74a005ce 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -1149,6 +1149,8 @@ static const struct { { "330859bc-7506-492d-9370-9a6f0614037f", "BlueZ Experimental Bluetooth Quality Report" }, { "a6695ace-ee7f-4fb9-881a-5fac66c629af", "BlueZ Offload Codecs"}, + { "6fbaf188-05e0-496a-9885-d6ddfdb4e03e", + "BlueZ Experimental ISO Socket"}, { } }; From patchwork Wed Jun 22 22:27:56 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891543 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 048CCC433EF for ; Wed, 22 Jun 2022 22:28:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358393AbiFVW2Q (ORCPT ); Wed, 22 Jun 2022 18:28:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59836 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358232AbiFVW2L (ORCPT ); Wed, 22 Jun 2022 18:28:11 -0400 Received: from mail-pf1-x42d.google.com (mail-pf1-x42d.google.com [IPv6:2607:f8b0:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7E549C70 for ; Wed, 22 Jun 2022 15:28:06 -0700 (PDT) Received: by mail-pf1-x42d.google.com with SMTP id bo5so17331603pfb.4 for ; Wed, 22 Jun 2022 15:28:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=EWqhkDEeWuIdX2lQ40ACatIUFzeKwauHCTCOZyB1Qqk=; b=QSjaYxYdDb2/+XsSzVLed5bSSUTIhNM09UUKWT7Y5yyslIButkM46YlI/pJXTiMlPW 3T/j0pYcbFp/Uqw27CgsnRTNzQJbGy+QvpnkyKOPtbN4aQl7Kby/o0P5u7ZKdtwAtaSu TwGSX3h9Xr9s37MyjiIIQ5WbcIPZE+c/vwrEq+67ZXGE6AwYHKPviqxU4mWTZew+dsmb w5wS9KVOiJmYl7thOJzv0Fdsi5pvXs+Rz+Ff4mRApdsvwXpgf3je0sGfHefycUpfk2F1 9gb536xEd5p/kwV/rlEQYtXsdyh8WWfXaOm7e+K0PJu/orhn3BUUIu92LbUb/4vT+BZp KNqw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=EWqhkDEeWuIdX2lQ40ACatIUFzeKwauHCTCOZyB1Qqk=; b=sPap7gx5TF1O/zSgkF2WWFJy1LVWgzPLvkyx1E45MuB9YCde79N2SdrGCbdbw1kbPi BycQp2bzgaXcoxaXOE4bAjFPUOP2Oa+7zeqXNCG5cgwHUCl5QjZdkCY+rKM8VnuxY26H TWqpp/QAFvg7eK8vzXPfBj18TG5hqBPSMps6UtBRKJyuByVM6AI0TxCVJzsQDjGBxb3d g7eYgGuiTN3YSOB3IIh/NnHqlfLRfXEa6Ja8JSALh3vu/cXf3dXxPVeZkfNVX1jxblKP G1VcSKJEltLNbfXCeIvL3Vh32WT87XV4ZtF8RxVZJSwyphrQXExkg34F6YnL5WyrGSWl ZAOA== X-Gm-Message-State: AJIora/uyPqTRNRfzJY1XXp3Vj1DFBXmFFDZ7N1/pUs5N0rs2fEJU6op rFEwHlyGk2GNoyRmNUavr4Sz6bhW0HACqw== X-Google-Smtp-Source: AGRyM1uwylkK1G9+0Q5gjOok9s0DpFIP9exx39HhzNBBxHXgGmE6beDBKLtlrx37u9n/5k7VcoXVZg== X-Received: by 2002:a05:6a02:117:b0:3fa:de2:357a with SMTP id bg23-20020a056a02011700b003fa0de2357amr4762846pgb.169.1655936885694; Wed, 22 Jun 2022 15:28:05 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:05 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 3/8] mgmt-tester: Fix Read Exp Feature tests Date: Wed, 22 Jun 2022 15:27:56 -0700 Message-Id: <20220622222801.2676431-3-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds ISO Socket UUID as response to Read Exp Feature. --- tools/mgmt-tester.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c index f45a6c015..4191840ea 100644 --- a/tools/mgmt-tester.c +++ b/tools/mgmt-tester.c @@ -9858,7 +9858,7 @@ static const struct generic_data set_dev_flags_fail_3 = { }; static const uint8_t read_exp_feat_param_success[] = { - 0x03, 0x00, /* Feature Count */ + 0x04, 0x00, /* Feature Count */ 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, /* UUID - Simultaneous */ 0x27, 0x92, 0x96, 0x46, 0xc0, 0x42, /* Central Peripheral */ 0xb5, 0x10, 0x1b, 0x67, @@ -9870,7 +9870,11 @@ static const uint8_t read_exp_feat_param_success[] = { 0xaf, 0x29, 0xc6, 0x66, 0xac, 0x5f, /* UUID - Codec Offload */ 0x1a, 0x88, 0xb9, 0x4f, 0x7f, 0xee, 0xce, 0x5a, 0x69, 0xa6, - 0x00, 0x00, 0x00, 0x00 /* Flags */ + 0x00, 0x00, 0x00, 0x00, /* Flags */ + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, /* UUID - ISO Socket */ + 0x85, 0x98, 0x6a, 0x49, 0xe0, 0x05, + 0x88, 0xf1, 0xba, 0x6f, + 0x00, 0x00, 0x00, 0x00, /* Flags */ }; static const struct generic_data read_exp_feat_success = { @@ -9882,11 +9886,15 @@ static const struct generic_data read_exp_feat_success = { static const uint8_t read_exp_feat_param_success_index_none[] = { - 0x01, 0x00, /* Feature Count */ + 0x02, 0x00, /* Feature Count */ 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, /* UUID - Debug */ 0x01, 0xab, 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4, 0x00, 0x00, 0x00, 0x00, /* Flags */ + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, /* UUID - ISO Socket */ + 0x85, 0x98, 0x6a, 0x49, 0xe0, 0x05, + 0x88, 0xf1, 0xba, 0x6f, + 0x00, 0x00, 0x00, 0x00, /* Flags */ }; static const struct generic_data read_exp_feat_success_index_none = { From patchwork Wed Jun 22 22:27:57 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891544 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DA6EFC43334 for ; Wed, 22 Jun 2022 22:28:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358395AbiFVW2R (ORCPT ); Wed, 22 Jun 2022 18:28:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59840 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358214AbiFVW2L (ORCPT ); Wed, 22 Jun 2022 18:28:11 -0400 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 26B12CCC for ; Wed, 22 Jun 2022 15:28:08 -0700 (PDT) Received: by mail-pg1-x535.google.com with SMTP id g186so17384593pgc.1 for ; Wed, 22 Jun 2022 15:28:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=whZTdz3OP8Yu5clRqSco0PrrQlonxEk5mOHyA5xX/sM=; b=TZLsBc8EjVzF9+tCCw3c8eBNumM60lPC1y5Y6Jv7iB+kWdNzsNjAR7dPmC5uADlQP6 aq+sLC5ZNepYW0RWAD9lrWEbf3bmS1rCW4areScw4+MdpTNtC0PIxQOMlb4Rz0eJ4KGE EV8lUkwbAX0YIcB3fd+MvJgC2Kx3STjvDmg3mVC7lZ9HxQIQdoYBPWPAbO5ZroIEviyO wAe+a0/+pPYa9Uu3WsJ0rO8zbzwvyDtZT3BWPiz4nKazPwrU9URSUJz9BBCw/ejZOgPe 44BF4So7RCJHEY9WqkMkpGMNpci68C3DOT34VDk7RAH73HhSqeZw49jo5fxEv5QoAF4M ksdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=whZTdz3OP8Yu5clRqSco0PrrQlonxEk5mOHyA5xX/sM=; b=NDmRPCGSxeLuAH5rKhF//Gh/PVBo3uFMk5E+J8/gGKa6WEKj2eTNFiCebvRX/qxqfE f8lSGUnaSSRFNh24I1SgfLh/g+YxamC5mRjdRmeDItJI4Xj39ZF02DStRyPOoIlrWW3P dS1gv8plp3vlfBoQvj69cGv+9cdlfWfcXeHCmqBQEjml1ba09xguI5mgc5F+AoIN0wzi TKaZWzoWvTXAsA1dUOX3wHdU+Q4KRK8M8Z/yZehcYuuIk1LcmEjj46y1RQ1R44Zcv+8q MTX6iuZN49ew2+qQ+MMp9hsdIwpvyU9LXdbJ2Iegj2E8BlIYTtEo0mDM1w+oGfiXB5og 7iXw== X-Gm-Message-State: AJIora86HOsqTrqqofIUVKJibKHUGeAvemFS4u90vd1/ZuTBjfNjmuKI DdLHZImYGwFxEB+2jDRL+hXboEz+PjZThA== X-Google-Smtp-Source: AGRyM1tGWtn9rdaCHKaElbj67Apd6k+/PoQ0JhYlBV3NOTjJQNfOhbN6eRNxTpmTOSbhpGixH5a63w== X-Received: by 2002:a63:7257:0:b0:40c:ab23:31d9 with SMTP id c23-20020a637257000000b0040cab2331d9mr4722168pgn.210.1655936886827; Wed, 22 Jun 2022 15:28:06 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.05 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:06 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 4/8] adapter: Add support for setting ISO Socket experimental feature Date: Wed, 22 Jun 2022 15:27:57 -0700 Message-Id: <20220622222801.2676431-4-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds support for setting ISO Socket experimental UUID which enables the use of BTPROTO_ISO on the system. --- src/adapter.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/adapter.h | 1 + src/main.c | 1 + src/main.conf | 1 + 4 files changed, 47 insertions(+) diff --git a/src/adapter.c b/src/adapter.c index 43884cf15..f876cb992 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -141,6 +141,13 @@ static const struct mgmt_exp_uuid codec_offload_uuid = { .str = "a6695ace-ee7f-4fb9-881a-5fac66c629af" }; +/* 6fbaf188-05e0-496a-9885-d6ddfdb4e03e */ +static const struct mgmt_exp_uuid iso_socket_uuid = { + .val = { 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f }, + .str = "6fbaf188-05e0-496a-9885-d6ddfdb4e03e" +}; + static DBusConnection *dbus_conn = NULL; static uint32_t kernel_features = 0; @@ -9708,6 +9715,42 @@ static void codec_offload_func(struct btd_adapter *adapter, uint8_t action) btd_error(adapter->dev_id, "Failed to set Codec Offload"); } +static void iso_socket_complete(uint8_t status, uint16_t len, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t action; + + if (status != 0) { + error("Set ISO Socket failed with status 0x%02x (%s)", + status, mgmt_errstr(status)); + return; + } + + action = btd_kernel_experimental_enabled(iso_socket_uuid.str); + + DBG("ISO Socket successfully %s", action ? "set" : "reset"); + + if (action) + queue_push_tail(adapter->exps, (void *)iso_socket_uuid.val); +} + +static void iso_socket_func(struct btd_adapter *adapter, uint8_t action) +{ + struct mgmt_cp_set_exp_feature cp; + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.uuid, iso_socket_uuid.val, 16); + cp.action = action; + + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_EXP_FEATURE, + MGMT_INDEX_NONE, sizeof(cp), &cp, + iso_socket_complete, adapter, NULL) > 0) + return; + + btd_error(adapter->dev_id, "Failed to set ISO Socket"); +} + static const struct exp_feat { uint32_t flag; const struct mgmt_exp_uuid *uuid; @@ -9721,6 +9764,7 @@ static const struct exp_feat { rpa_resolution_func), EXP_FEAT(EXP_FEAT_CODEC_OFFLOAD, &codec_offload_uuid, codec_offload_func), + EXP_FEAT(EXP_FEAT_ISO_SOCKET, &iso_socket_uuid, iso_socket_func), }; static void read_exp_features_complete(uint8_t status, uint16_t length, diff --git a/src/adapter.h b/src/adapter.h index 688ed51c6..b09044edd 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -260,6 +260,7 @@ enum experimental_features { EXP_FEAT_BQR = 1 << 2, EXP_FEAT_RPA_RESOLUTION = 1 << 3, EXP_FEAT_CODEC_OFFLOAD = 1 << 4, + EXP_FEAT_ISO_SOCKET = 1 << 5, }; bool btd_adapter_has_exp_feature(struct btd_adapter *adapter, uint32_t feature); diff --git a/src/main.c b/src/main.c index 34a54d43f..4dd24df1c 100644 --- a/src/main.c +++ b/src/main.c @@ -607,6 +607,7 @@ static const char *valid_uuids[] = { "15c0a148-c273-11ea-b3de-0242ac130004", "330859bc-7506-492d-9370-9a6f0614037f", "a6695ace-ee7f-4fb9-881a-5fac66c629af", + "6fbaf188-05e0-496a-9885-d6ddfdb4e03e", "*" }; diff --git a/src/main.conf b/src/main.conf index 3816cf362..2796f155e 100644 --- a/src/main.conf +++ b/src/main.conf @@ -124,6 +124,7 @@ # 15c0a148-c273-11ea-b3de-0242ac130004 (BlueZ Experimental LL privacy) # 330859bc-7506-492d-9370-9a6f0614037f (BlueZ Experimental Bluetooth Quality Report) # a6695ace-ee7f-4fb9-881a-5fac66c629af (BlueZ Experimental Offload Codecs) +# 6fbaf188-05e0-496a-9885-d6ddfdb4e03e (BlueZ Experimental ISO socket) # Defaults to false. #KernelExperimental = false From patchwork Wed Jun 22 22:27:58 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891542 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 84E6ECCA479 for ; Wed, 22 Jun 2022 22:28:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358301AbiFVW2P (ORCPT ); Wed, 22 Jun 2022 18:28:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59842 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358275AbiFVW2L (ORCPT ); Wed, 22 Jun 2022 18:28:11 -0400 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4D54DCD0 for ; Wed, 22 Jun 2022 15:28:09 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id r1so16555413plo.10 for ; Wed, 22 Jun 2022 15:28:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=G0I6PE9lOfRSgqTFSncFhbyq+MxOnFHNlaHjTCTYb+s=; b=LU2+UFQgfqnJSksKTLAvaiAR6EDCdYajnum+f/zZAQGBiddpx7NEKIZ9Fn5ViMVKc2 7dFZBZECFx351Vre1yVk0Z/G5nfd+VfmUAMbGxDxwXckMvPn8R9ZfUY3Qtcso3lb0jqd 6LKRq4BaP072f7LeL8Q8dXCcirCC5FJQf6Ft4ID+fP0nHpgmRDQ9ZFVUJbzopBFNgDGs uCawUs677kF8vmZbqxua1j0CNH/xTutshhUFIfAi7qQTZo39i0wkKLfn1HBulgrZKOwp JBpPObsonJOPA39dyXuwsAYKgno5hfwPvWI5yQS8YCtDWr5f3AuFBUsAUPo/TOLR4Vyd VRUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=G0I6PE9lOfRSgqTFSncFhbyq+MxOnFHNlaHjTCTYb+s=; b=dlF+Hr374P7guPniRw/OtQv1J8ruWZnusbfiXOWKH07x5kBFF5tsDJGZu8dH6+955F s2tYCByv8d6/J5mfCUQlS64PUCPXlEf9Wfllq3kUzgZ3V1S6QuRbWRdT6UWPa36q3e19 YuqiLDxLXvpOVfJG8eEROIyw2ap0obubgUDquIZoT5f4ZZFFsh6OxeuvUA3ycOdGauyJ i+AwInmhHSx6DeDy/X2/eD8pMHm/pqv4vNV2GeldF62/F+kFrbRFM2tDmZEa6pkLEGzE dw3Cy1cMSJFlVLFvOCg62OiEYwudmwdJyDp8swbV1cpunsgSXofUatxkbN6LHgODDqik U1MA== X-Gm-Message-State: AJIora8aYiWuvX7wtfc06dCEacJ88+AJuBWbiJMyOmLev5zkhB2iHlqi qqIxPW4RsOboe2D+0Y/W+NRCIcT6KAgKbA== X-Google-Smtp-Source: AGRyM1tKLow/UXMWP0AwiauNGTPeCLI4MbpUK5ObAZkZ5y7Re3GGFHa4x9nofVZAOyo9/jeE+qksUw== X-Received: by 2002:a17:903:1211:b0:15e:8208:8cc0 with SMTP id l17-20020a170903121100b0015e82088cc0mr36530344plh.52.1655936888191; Wed, 22 Jun 2022 15:28:08 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.07 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:07 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 5/8] btio: Add support for ISO sockets Date: Wed, 22 Jun 2022 15:27:58 -0700 Message-Id: <20220622222801.2676431-5-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds support to create objects that map to ISO sockets. --- btio/btio.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++ btio/btio.h | 4 +- tools/btiotest.c | 110 ++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 1 deletion(-) diff --git a/btio/btio.c b/btio/btio.c index f4f53574c..75d17e7aa 100644 --- a/btio/btio.c +++ b/btio/btio.c @@ -27,6 +27,7 @@ #include "lib/l2cap.h" #include "lib/rfcomm.h" #include "lib/sco.h" +#include "lib/iso.h" #include "btio.h" @@ -44,6 +45,7 @@ typedef enum { BT_IO_L2CAP, BT_IO_RFCOMM, BT_IO_SCO, + BT_IO_ISO, BT_IO_INVALID, } BtIOType; @@ -66,6 +68,7 @@ struct set_opts { int flushable; uint32_t priority; uint16_t voice; + struct bt_iso_qos qos; }; struct connect { @@ -123,6 +126,8 @@ static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr) return BT_IO_SCO; case BTPROTO_L2CAP: return BT_IO_L2CAP; + case BTPROTO_ISO: + return BT_IO_ISO; default: g_set_error(gerr, BT_IO_ERROR, EINVAL, "Unknown BtIO socket type"); @@ -763,6 +768,24 @@ static int sco_bind(int sock, const bdaddr_t *src, GError **err) return 0; } +static int iso_bind(int sock, const bdaddr_t *src, uint8_t src_type, + GError **err) +{ + struct sockaddr_iso addr; + + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, src); + addr.iso_bdaddr_type = src_type; + + if (!bind(sock, (struct sockaddr *) &addr, sizeof(addr))) + return 0; + + ERROR_FAILED(err, "iso_bind", errno); + + return -errno; +} + static int sco_connect(int sock, const bdaddr_t *dst) { struct sockaddr_sco addr; @@ -779,6 +802,23 @@ static int sco_connect(int sock, const bdaddr_t *dst) return 0; } +static int iso_connect(int sock, const bdaddr_t *dst, uint8_t dst_type) +{ + struct sockaddr_iso addr; + int err; + + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, dst); + addr.iso_bdaddr_type = dst_type; + + err = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) + return -errno; + + return 0; +} + static gboolean sco_set(int sock, uint16_t mtu, uint16_t voice, GError **err) { struct sco_options sco_opt; @@ -817,6 +857,17 @@ voice: return TRUE; } +static gboolean iso_set(int sock, struct bt_iso_qos *qos, GError **err) +{ + if (setsockopt(sock, SOL_BLUETOOTH, BT_ISO_QOS, qos, + sizeof(*qos)) < 0) { + ERROR_FAILED(err, "setsockopt(BT_ISO_QOS)", errno); + return FALSE; + } + + return TRUE; +} + static gboolean parse_set_opts(struct set_opts *opts, GError **err, BtIOOption opt1, va_list args) { @@ -894,6 +945,13 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, break; case BT_IO_OPT_MODE: opts->mode = va_arg(args, int); + if (opts->mode == BT_IO_MODE_ISO) { + opts->type = BT_IO_ISO; + if (opts->src_type == BDADDR_BREDR) + opts->src_type = BDADDR_LE_PUBLIC; + if (opts->dst_type == BDADDR_BREDR) + opts->dst_type = BDADDR_LE_PUBLIC; + } break; case BT_IO_OPT_FLUSHABLE: opts->flushable = va_arg(args, gboolean); @@ -904,6 +962,9 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err, case BT_IO_OPT_VOICE: opts->voice = va_arg(args, int); break; + case BT_IO_OPT_QOS: + opts->qos = *va_arg(args, struct bt_iso_qos *); + break; case BT_IO_OPT_INVALID: case BT_IO_OPT_KEY_SIZE: case BT_IO_OPT_SOURCE_CHANNEL: @@ -1227,6 +1288,7 @@ parse_opts: case BT_IO_OPT_DEST_CHANNEL: case BT_IO_OPT_MTU: case BT_IO_OPT_VOICE: + case BT_IO_OPT_QOS: default: g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); @@ -1380,6 +1442,7 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1, case BT_IO_OPT_FLUSHABLE: case BT_IO_OPT_PRIORITY: case BT_IO_OPT_VOICE: + case BT_IO_OPT_QOS: case BT_IO_OPT_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, @@ -1489,6 +1552,95 @@ static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args) case BT_IO_OPT_FLUSHABLE: case BT_IO_OPT_PRIORITY: case BT_IO_OPT_VOICE: + case BT_IO_OPT_QOS: + case BT_IO_OPT_INVALID: + default: + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown option %d", opt); + return FALSE; + } + + opt = va_arg(args, int); + } + + return TRUE; +} + +static gboolean iso_get(int sock, GError **err, BtIOOption opt1, va_list args) +{ + BtIOOption opt = opt1; + struct sockaddr_iso src, dst; + struct bt_iso_qos qos; + socklen_t len; + uint32_t phy; + + len = sizeof(qos); + memset(&qos, 0, len); + if (getsockopt(sock, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { + ERROR_FAILED(err, "getsockopt(BT_ISO_QOS)", errno); + return FALSE; + } + + if (!get_src(sock, &src, sizeof(src), err)) + return FALSE; + + if (!get_dst(sock, &dst, sizeof(dst), err)) + return FALSE; + + while (opt != BT_IO_OPT_INVALID) { + switch (opt) { + case BT_IO_OPT_SOURCE: + ba2str(&src.iso_bdaddr, va_arg(args, char *)); + break; + case BT_IO_OPT_SOURCE_BDADDR: + bacpy(va_arg(args, bdaddr_t *), &src.iso_bdaddr); + break; + case BT_IO_OPT_SOURCE_TYPE: + *(va_arg(args, uint8_t *)) = src.iso_bdaddr_type; + break; + case BT_IO_OPT_DEST: + ba2str(&dst.iso_bdaddr, va_arg(args, char *)); + break; + case BT_IO_OPT_DEST_BDADDR: + bacpy(va_arg(args, bdaddr_t *), &dst.iso_bdaddr); + break; + case BT_IO_OPT_DEST_TYPE: + *(va_arg(args, uint8_t *)) = dst.iso_bdaddr_type; + break; + case BT_IO_OPT_MTU: + *(va_arg(args, uint16_t *)) = qos.out.sdu; + break; + case BT_IO_OPT_IMTU: + *(va_arg(args, uint16_t *)) = qos.in.sdu; + break; + case BT_IO_OPT_OMTU: + *(va_arg(args, uint16_t *)) = qos.out.sdu; + break; + case BT_IO_OPT_PHY: + if (get_phy(sock, &phy) < 0) { + ERROR_FAILED(err, "get_phy", errno); + return FALSE; + } + *(va_arg(args, uint32_t *)) = phy; + break; + case BT_IO_OPT_QOS: + *(va_arg(args, struct bt_iso_qos *)) = qos; + break; + case BT_IO_OPT_HANDLE: + case BT_IO_OPT_CLASS: + case BT_IO_OPT_DEFER_TIMEOUT: + case BT_IO_OPT_SEC_LEVEL: + case BT_IO_OPT_KEY_SIZE: + case BT_IO_OPT_CHANNEL: + case BT_IO_OPT_SOURCE_CHANNEL: + case BT_IO_OPT_DEST_CHANNEL: + case BT_IO_OPT_PSM: + case BT_IO_OPT_CID: + case BT_IO_OPT_CENTRAL: + case BT_IO_OPT_MODE: + case BT_IO_OPT_FLUSHABLE: + case BT_IO_OPT_PRIORITY: + case BT_IO_OPT_VOICE: case BT_IO_OPT_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, @@ -1516,6 +1668,8 @@ static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err, return rfcomm_get(sock, err, opt1, args); case BT_IO_SCO: return sco_get(sock, err, opt1, args); + case BT_IO_ISO: + return iso_get(sock, err, opt1, args); case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, @@ -1584,6 +1738,8 @@ gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...) return rfcomm_set(sock, opts.sec_level, opts.central, err); case BT_IO_SCO: return sco_set(sock, opts.mtu, opts.voice, err); + case BT_IO_ISO: + return iso_set(sock, &opts.qos, err); case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, @@ -1655,6 +1811,17 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts, if (!sco_set(sock, opts->mtu, opts->voice, err)) goto failed; break; + case BT_IO_ISO: + sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sock < 0) { + ERROR_FAILED(err, "socket(SEQPACKET, ISO)", errno); + return NULL; + } + if (iso_bind(sock, &opts->src, opts->src_type, err) < 0) + goto failed; + if (!iso_set(sock, &opts->qos, err)) + goto failed; + break; case BT_IO_INVALID: default: g_set_error(err, BT_IO_ERROR, EINVAL, @@ -1719,6 +1886,9 @@ GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, case BT_IO_SCO: err = sco_connect(sock, &opts.dst); break; + case BT_IO_ISO: + err = iso_connect(sock, &opts.dst, opts.dst_type); + break; case BT_IO_INVALID: default: g_set_error(gerr, BT_IO_ERROR, EINVAL, diff --git a/btio/btio.h b/btio/btio.h index 50a2a4dc0..9636fd467 100644 --- a/btio/btio.h +++ b/btio/btio.h @@ -44,6 +44,7 @@ typedef enum { BT_IO_OPT_PRIORITY, BT_IO_OPT_VOICE, BT_IO_OPT_PHY, + BT_IO_OPT_QOS, } BtIOOption; typedef enum { @@ -58,7 +59,8 @@ typedef enum { BT_IO_MODE_ERTM, BT_IO_MODE_STREAMING, BT_IO_MODE_LE_FLOWCTL, - BT_IO_MODE_EXT_FLOWCTL + BT_IO_MODE_EXT_FLOWCTL, + BT_IO_MODE_ISO } BtIOMode; typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data); diff --git a/tools/btiotest.c b/tools/btiotest.c index 70d74ffbe..193e1395b 100644 --- a/tools/btiotest.c +++ b/tools/btiotest.c @@ -29,6 +29,25 @@ #define DEFAULT_ACCEPT_TIMEOUT 2 static int opt_update_sec = 0; +#define DEFAULT_IO_QOS \ +{ \ + .interval = 10000, \ + .latency = 10, \ + .sdu = 40, \ + .phy = 0x02, \ + .rtn = 2, \ +} + +struct bt_iso_qos qos = { + .cig = BT_ISO_QOS_CIG_UNSET, + .cis = BT_ISO_QOS_CIG_UNSET, + .sca = 0x07, + .packing = 0x00, + .framing = 0x00, + .in = DEFAULT_IO_QOS, + .out = DEFAULT_IO_QOS, +}; + struct io_data { guint ref; GIOChannel *io; @@ -36,6 +55,7 @@ struct io_data { int disconn; int accept; int voice; + struct bt_iso_qos *qos; }; static void io_data_unref(struct io_data *data) @@ -67,6 +87,7 @@ static struct io_data *io_data_new(GIOChannel *io, int reject, int disconn, data->reject = reject; data->disconn = disconn; data->accept = accept; + data->qos = &qos; return io_data_ref(data); } @@ -530,9 +551,88 @@ static void sco_listen(const char *src, gboolean defer, int reject, g_io_channel_unref(sco_srv); } +static void iso_connect(const char *src, const char *dst, int disconn) +{ + struct io_data *data; + GError *err = NULL; + + printf("Connecting ISO to %s\n", dst); + + data = io_data_new(NULL, -1, disconn, -1); + + if (src) + data->io = bt_io_connect(connect_cb, data, + (GDestroyNotify) io_data_unref, + &err, + BT_IO_OPT_SOURCE, src, + BT_IO_OPT_DEST, dst, + BT_IO_OPT_MODE, BT_IO_MODE_ISO, + BT_IO_OPT_QOS, data->qos, + BT_IO_OPT_INVALID); + else + data->io = bt_io_connect(connect_cb, data, + (GDestroyNotify) io_data_unref, + &err, + BT_IO_OPT_DEST, dst, + BT_IO_OPT_MODE, BT_IO_MODE_ISO, + BT_IO_OPT_QOS, data->qos, + BT_IO_OPT_INVALID); + + if (!data->io) { + printf("Connecting to %s failed: %s\n", dst, err->message); + g_error_free(err); + exit(EXIT_FAILURE); + } +} + +static void iso_listen(const char *src, gboolean defer, int reject, + int disconn, int accept) +{ + struct io_data *data; + BtIOConnect conn; + BtIOConfirm cfm; + GIOChannel *iso_srv; + GError *err = NULL; + + printf("Listening for ISO connections\n"); + + if (defer) { + conn = NULL; + cfm = confirm_cb; + } else { + conn = connect_cb; + cfm = NULL; + } + + data = io_data_new(NULL, reject, disconn, accept); + + if (src) + iso_srv = bt_io_listen(conn, cfm, data, + (GDestroyNotify) io_data_unref, + &err, + BT_IO_OPT_SOURCE, src, + BT_IO_OPT_MODE, BT_IO_MODE_ISO, + BT_IO_OPT_INVALID); + else + iso_srv = bt_io_listen(conn, cfm, data, + (GDestroyNotify) io_data_unref, + &err, + BT_IO_OPT_MODE, BT_IO_MODE_ISO, + BT_IO_OPT_INVALID); + + if (!iso_srv) { + printf("Listening failed: %s\n", err->message); + g_error_free(err); + exit(EXIT_FAILURE); + } + + g_io_channel_unref(iso_srv); +} + static int opt_channel = -1; static int opt_psm = 0; static gboolean opt_sco = FALSE; +static gboolean opt_iso = FALSE; static gboolean opt_defer = FALSE; static gint opt_voice = 0; static char *opt_dev = NULL; @@ -559,6 +659,8 @@ static GOptionEntry options[] = { "(0 BR/EDR 1 LE Public 2 LE Random" }, { "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco, "Use SCO" }, + { "iso", 'o', 0, G_OPTION_ARG_NONE, &opt_iso, + "Use ISO" }, { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer, "Use DEFER_SETUP for incoming connections" }, { "voice", 'V', 0, G_OPTION_ARG_INT, &opt_voice, @@ -637,6 +739,14 @@ int main(int argc, char *argv[]) opt_disconn, opt_accept, opt_voice); } + if (opt_iso) { + if (argc > 1) + iso_connect(opt_dev, argv[1], opt_disconn); + else + iso_listen(opt_dev, opt_defer, opt_reject, + opt_disconn, opt_accept); + } + signal(SIGTERM, sig_term); signal(SIGINT, sig_term); From patchwork Wed Jun 22 22:27:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891546 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 52349C433EF for ; Wed, 22 Jun 2022 22:28:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358214AbiFVW2V (ORCPT ); Wed, 22 Jun 2022 18:28:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59902 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1357976AbiFVW2O (ORCPT ); Wed, 22 Jun 2022 18:28:14 -0400 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 34DC5D56 for ; Wed, 22 Jun 2022 15:28:11 -0700 (PDT) Received: by mail-pj1-x1034.google.com with SMTP id t3-20020a17090a510300b001ea87ef9a3dso780396pjh.4 for ; Wed, 22 Jun 2022 15:28:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=nkbZP3T2ap04mntUB8QrRlNR93+LyzySAIncYA2aEfg=; b=O8pKWONNMN2+nSsPhnkq7bdQlZuUduQE4iIBpbZeNuajFF60alDheQZnHxRMhS0hyN phEtxzFdBtd0v7uTdbfKGGXwjSwcyX+fX7fVzuqPWH1uNpNCVvTOFLrAmCMB1YdcS62/ ul0Osx8mRd5pLLWCRTFhUd1solAmiCDDx2/WvDWnZNJWOJllgRxXYel3WdM7kcFsbxZu y+0GeOxsydki8kzlB626sPiC2Djzf7NSJSM1kcZ7uu4dVkmvOifq0SJBuQaGmXolSFy4 FokW5wGNHlLlYoCvGMe3cVgbYMLAw/PPXYIb4Z4yglbAV213uHNeUI4DDikWLZQXKViD w3rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=nkbZP3T2ap04mntUB8QrRlNR93+LyzySAIncYA2aEfg=; b=IdwZFG2bxYsRe1CQO/LINyi7hoXlSvSEsfW6JtyD5v+zVEmOIZ53yVMznBgiK/H+ik xS13XrofguW1pev2AgJCqBnoCD/eH92ZgdmfAR26yC47fPJtjnGK3iiGz6sxmHGcjlRK 59sODvpVyucYMIskEfepi6BeF55NmGWxnVvn5JbBxDnmxywHrWdoT7smhe2ppsdeR540 vlEv2/DQT+YXx7NyA/6o/FIRVXrsZIVtR0y43h79yoj/s22jwh7iZp5XUOKUMx9ZWM0W t/ObdE/QluUt0/PyGiHwBOZL6pHvzuc++yjwDp114nvlBErkzlXAlKDWl7KwF/fdGu5J XeRw== X-Gm-Message-State: AJIora9x3qA4T/ukPNTS3q0P2PfDfKpKVssvaDrKVoercca6MRItvSE/ SBwE71LUMBm68hl6UtU7po7r7PupODGU6A== X-Google-Smtp-Source: AGRyM1vc2rv1rGUSy/j0cto+qNXoM5wEg9WwzipeJRyYn9nw1wH4nDQbmxqvbJ/88rzpsLAVOtRDkA== X-Received: by 2002:a17:902:b597:b0:168:d8ce:4a63 with SMTP id a23-20020a170902b59700b00168d8ce4a63mr36535739pls.57.1655936889449; Wed, 22 Jun 2022 15:28:09 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.08 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:08 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 6/8] tools: Add iso-tester Date: Wed, 22 Jun 2022 15:27:59 -0700 Message-Id: <20220622222801.2676431-6-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds iso-tester which tests BTPROTO_ISO socket: Basic Framework - Success Basic ISO Socket - Success Basic ISO Get Socket Option - Success Basic ISO Set Socket Option - Success ISO QoS 8_1_1 - Success ISO QoS 8_2_1 - Success ISO QoS 16_1_1 - Success ISO QoS 16_2_1 - Success ISO QoS 16_2_1 CIG 0x01 - Success ISO QoS 16_2_1 CIG 0x01 CIS 0x01 - Success ISO QoS 24_1_1 - Success ISO QoS 24_2_1 - Success ISO QoS 32_1_1 - Success ISO QoS 32_2_1 - Success ISO QoS 44_1_1 - Success ISO QoS 44_2_1 - Success ISO QoS 48_1_1 - Success ISO QoS 48_2_1 - Success ISO QoS 48_3_1 - Success ISO QoS 48_4_1 - Success ISO QoS 48_5_1 - Success ISO QoS 48_6_1 - Success ISO QoS 8_1_2 - Success ISO QoS 8_2_2 - Success ISO QoS 16_1_2 - Success ISO QoS 16_2_2 - Success ISO QoS 24_1_2 - Success ISO QoS 24_2_2 - Success ISO QoS 32_1_2 - Success ISO QoS 32_2_2 - Success ISO QoS 44_1_2 - Success ISO QoS 44_2_2 - Success ISO QoS 48_1_2 - Success ISO QoS 48_2_2 - Success ISO QoS 48_3_2 - Success ISO QoS 48_4_2 - Success ISO QoS 48_5_2 - Success ISO QoS 48_6_2 - Success ISO QoS - Invalid ISO Connect2 CIG 0x01 - Success ISO Send - Success ISO Receive - Success ISO Defer Receive - Success ISO Defer Reject - Success ISO Send and Receive - Success ISO Broadcaster - Success ISO Broadcaster BIG 0x01 - Success ISO Broadcaster BIG 0x01 BIS 0x01 - Success ISO Broadcaster Receiver - Success Basic Framework - Success Basic ISO Socket - Success Basic ISO Get Socket Option - Success Basic ISO Set Socket Option - Success --- Makefile.tools | 11 +- tools/iso-tester.c | 1611 +++++++++++++++++++++++++++++++++++++++++++ tools/test-runner.c | 5 +- 3 files changed, 1624 insertions(+), 3 deletions(-) create mode 100644 tools/iso-tester.c diff --git a/Makefile.tools b/Makefile.tools index 4b513366f..f2f82062c 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -86,7 +86,7 @@ noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp \ tools/l2cap-tester tools/sco-tester \ tools/smp-tester tools/hci-tester \ tools/rfcomm-tester tools/bnep-tester \ - tools/userchan-tester + tools/userchan-tester tools/iso-tester emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ emulator/serial.h emulator/serial.c \ @@ -194,6 +194,15 @@ tools_userchan_tester_SOURCES = tools/userchan-tester.c monitor/bt.h \ emulator/smp.c tools_userchan_tester_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la $(GLIB_LIBS) + +tools_iso_tester_SOURCES = tools/iso-tester.c monitor/bt.h \ + emulator/hciemu.h emulator/hciemu.c \ + emulator/vhci.h emulator/vhci.c \ + emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c +tools_iso_tester_LDADD = lib/libbluetooth-internal.la \ + src/libshared-glib.la $(GLIB_LIBS) endif if TOOLS diff --git a/tools/iso-tester.c b/tools/iso-tester.c new file mode 100644 index 000000000..f45fc055b --- /dev/null +++ b/tools/iso-tester.c @@ -0,0 +1,1611 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2022 Intel Corporation. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/iso.h" +#include "lib/mgmt.h" + +#include "monitor/bt.h" +#include "emulator/bthost.h" +#include "emulator/hciemu.h" + +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/util.h" + +#define QOS_IO(_interval, _latency, _sdu, _phy, _rtn) \ +{ \ + .interval = _interval, \ + .latency = _latency, \ + .sdu = _sdu, \ + .phy = _phy, \ + .rtn = _rtn, \ +} + +#define QOS_FULL(_cig, _cis, _in, _out) \ +{ \ + .cig = _cig, \ + .cis = _cis, \ + .sca = 0x07, \ + .packing = 0x00, \ + .framing = 0x00, \ + .in = _in, \ + .out = _out, \ +} + +#define QOS(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(BT_ISO_QOS_CIG_UNSET, BT_ISO_QOS_CIS_UNSET, \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn), \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_1(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(0x01, BT_ISO_QOS_CIS_UNSET, \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn), \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_1_1(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(0x01, 0x01, \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn), \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_OUT(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(BT_ISO_QOS_CIG_UNSET, BT_ISO_QOS_CIS_UNSET, \ + {}, QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_OUT_1(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(0x01, BT_ISO_QOS_CIS_UNSET, \ + {}, QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_OUT_1_1(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(0x01, 0x01, \ + {}, QOS_IO(_interval, _latency, _sdu, _phy, _rtn)) + +#define QOS_IN(_interval, _latency, _sdu, _phy, _rtn) \ + QOS_FULL(BT_ISO_QOS_CIG_UNSET, BT_ISO_QOS_CIS_UNSET, \ + QOS_IO(_interval, _latency, _sdu, _phy, _rtn), {}) + +/* QoS Configuration settings for low latency audio data */ +#define QOS_8_1_1 QOS(7500, 8, 26, 0x02, 2) +#define QOS_8_2_1 QOS(10000, 10, 30, 0x02, 2) +#define QOS_16_1_1 QOS(7500, 8, 30, 0x02, 2) +#define QOS_16_2_1 QOS(10000, 10, 40, 0x02, 2) +#define QOS_1_16_2_1 QOS_1(10000, 10, 40, 0x02, 2) +#define QOS_1_1_16_2_1 QOS_1_1(10000, 10, 40, 0x02, 2) +#define QOS_24_1_1 QOS(7500, 8, 45, 0x02, 2) +#define QOS_24_2_1 QOS(10000, 10, 60, 0x02, 2) +#define QOS_32_1_1 QOS(7500, 8, 60, 0x02, 2) +#define QOS_32_2_1 QOS(10000, 10, 80, 0x02, 2) +#define QOS_44_1_1 QOS_OUT(8163, 24, 98, 0x02, 5) +#define QOS_44_2_1 QOS_OUT(10884, 31, 130, 0x02, 5) +#define QOS_48_1_1 QOS_OUT(7500, 15, 75, 0x02, 5) +#define QOS_48_2_1 QOS_OUT(10000, 20, 100, 0x02, 5) +#define QOS_48_3_1 QOS_OUT(7500, 15, 90, 0x02, 5) +#define QOS_48_4_1 QOS_OUT(10000, 20, 120, 0x02, 5) +#define QOS_48_5_1 QOS_OUT(7500, 15, 117, 0x02, 5) +#define QOS_48_6_1 QOS_OUT(10000, 20, 155, 0x02, 5) +/* QoS Configuration settings for high reliability audio data */ +#define QOS_8_1_2 QOS(7500, 45, 26, 0x02, 41) +#define QOS_8_2_2 QOS(10000, 60, 30, 0x02, 53) +#define QOS_16_1_2 QOS(7500, 45, 30, 0x02, 41) +#define QOS_16_2_2 QOS(10000, 60, 40, 0x02, 47) +#define QOS_24_1_2 QOS(7500, 45, 45, 0x02, 35) +#define QOS_24_2_2 QOS(10000, 60, 60, 0x02, 41) +#define QOS_32_1_2 QOS(7500, 45, 60, 0x02, 29) +#define QOS_32_2_2 QOS(10000, 60, 80, 0x02, 35) +#define QOS_44_1_2 QOS_OUT(8163, 54, 98, 0x02, 23) +#define QOS_44_2_2 QOS_OUT(10884, 71, 130, 0x02, 23) +#define QOS_48_1_2 QOS_OUT(7500, 45, 75, 0x02, 23) +#define QOS_48_2_2 QOS_OUT(10000, 60, 100, 0x02, 23) +#define QOS_48_3_2 QOS_OUT(7500, 45, 90, 0x02, 23) +#define QOS_48_4_2 QOS_OUT(10000, 60, 120, 0x02, 23) +#define QOS_48_5_2 QOS_OUT(7500, 45, 117, 0x02, 23) +#define QOS_48_6_2 QOS_OUT(10000, 60, 155, 0x02, 23) + +#define QOS_OUT_16_2_1 QOS_OUT(10000, 10, 40, 0x02, 2) +#define QOS_OUT_1_16_2_1 QOS_OUT_1(10000, 10, 40, 0x02, 2) +#define QOS_OUT_1_1_16_2_1 QOS_OUT_1_1(10000, 10, 40, 0x02, 2) +#define QOS_IN_16_2_1 QOS_IN(10000, 10, 40, 0x02, 2) + +struct test_data { + const void *test_data; + struct mgmt *mgmt; + uint16_t mgmt_index; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + uint16_t handle; + uint16_t acl_handle; + GIOChannel *io; + unsigned int io_id[2]; + uint8_t client_num; + int step; +}; + +struct iso_client_data { + struct bt_iso_qos qos; + int expect_err; + const struct iovec *send; + const struct iovec *recv; + bool server; + bool bcast; + bool defer; +}; + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void hciemu_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new_num(HCIEMU_TYPE_BREDRLE52, data->client_num); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + return; + } + + if (tester_use_debug()) + hciemu_set_debug(data->hciemu, hciemu_debug, "hciemu: ", NULL); + + tester_print("New hciemu instance created"); +} + +static const uint8_t set_iso_socket_param[] = { + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */ + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f, + 0x01, /* Action - enable */ +}; + +static const uint8_t reset_iso_socket_param[] = { + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */ + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f, + 0x00, /* Action - disable */ +}; + +static void set_iso_socket_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + tester_print("ISO socket feature could not be enabled"); + return; + } + + tester_print("ISO socket feature is enabled"); +} + +static void test_pre_setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + data->mgmt = mgmt_new_default(); + if (!data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + if (tester_use_debug()) + mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE, + sizeof(set_iso_socket_param), set_iso_socket_param, + set_iso_socket_callback, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, + read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + mgmt_send(data->mgmt, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE, + sizeof(reset_iso_socket_param), reset_iso_socket_param, + NULL, NULL, NULL); + + hciemu_unref(data->hciemu); + data->hciemu = NULL; +} + +static void test_data_free(void *test_data) +{ + struct test_data *data = test_data; + + if (data->io) + g_io_channel_unref(data->io); + + 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]); + + free(data); +} + +#define test_iso_full(name, data, setup, func, num) \ + do { \ + struct test_data *user; \ + user = new0(struct test_data, 1); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ + user->test_data = data; \ + user->client_num = num; \ + tester_add_full(name, data, \ + test_pre_setup, setup, func, NULL, \ + test_post_teardown, 2, user, test_data_free); \ + } while (0) + +#define test_iso(name, data, setup, func) \ + test_iso_full(name, data, setup, func, 1) + +#define test_iso2(name, data, setup, func) \ + test_iso_full(name, data, setup, func, 2) + +static const struct iso_client_data connect_8_1_1 = { + .qos = QOS_8_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_8_2_1 = { + .qos = QOS_8_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_16_1_1 = { + .qos = QOS_16_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_16_2_1 = { + .qos = QOS_16_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_1_16_2_1 = { + .qos = QOS_1_16_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_1_1_16_2_1 = { + .qos = QOS_1_1_16_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_24_1_1 = { + .qos = QOS_24_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_24_2_1 = { + .qos = QOS_24_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_32_1_1 = { + .qos = QOS_32_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_32_2_1 = { + .qos = QOS_32_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_44_1_1 = { + .qos = QOS_44_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_44_2_1 = { + .qos = QOS_44_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_1_1 = { + .qos = QOS_48_1_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_2_1 = { + .qos = QOS_48_2_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_3_1 = { + .qos = QOS_48_3_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_4_1 = { + .qos = QOS_48_4_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_5_1 = { + .qos = QOS_48_5_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_6_1 = { + .qos = QOS_48_6_1, + .expect_err = 0 +}; + +static const struct iso_client_data connect_8_1_2 = { + .qos = QOS_8_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_8_2_2 = { + .qos = QOS_8_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_16_1_2 = { + .qos = QOS_16_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_16_2_2 = { + .qos = QOS_16_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_24_1_2 = { + .qos = QOS_24_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_24_2_2 = { + .qos = QOS_24_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_32_1_2 = { + .qos = QOS_32_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_32_2_2 = { + .qos = QOS_32_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_44_1_2 = { + .qos = QOS_44_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_44_2_2 = { + .qos = QOS_44_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_1_2 = { + .qos = QOS_48_1_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_2_2 = { + .qos = QOS_48_2_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_3_2 = { + .qos = QOS_48_3_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_4_2 = { + .qos = QOS_48_4_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_5_2 = { + .qos = QOS_48_5_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_48_6_2 = { + .qos = QOS_48_6_2, + .expect_err = 0 +}; + +static const struct iso_client_data connect_invalid = { + .qos = QOS(0, 0, 0, 0, 0), + .expect_err = -EINVAL +}; + +static const uint8_t data_16_2_1[40] = { [0 ... 39] = 0xff }; +static const struct iovec send_16_2_1 = { + .iov_base = (void *)data_16_2_1, + .iov_len = sizeof(data_16_2_1), +}; + +static const struct iso_client_data connect_16_2_1_send = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, +}; + +static const struct iso_client_data listen_16_2_1_recv = { + .qos = QOS_16_2_1, + .expect_err = 0, + .recv = &send_16_2_1, + .server = true, +}; + +static const struct iso_client_data listen_16_2_1_defer_recv = { + .qos = QOS_16_2_1, + .expect_err = 0, + .recv = &send_16_2_1, + .server = true, + .defer = true, +}; + +static const struct iso_client_data listen_16_2_1_defer_reject = { + .qos = QOS_16_2_1, + .expect_err = -1, + .recv = &send_16_2_1, + .server = true, + .defer = true, +}; + +static const struct iso_client_data connect_16_2_1_send_recv = { + .qos = QOS_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .recv = &send_16_2_1, +}; + +static const struct iso_client_data bcast_16_2_1_send = { + .qos = QOS_OUT_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .bcast = true, +}; + +static const struct iso_client_data bcast_1_16_2_1_send = { + .qos = QOS_OUT_1_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .bcast = true, +}; + +static const struct iso_client_data bcast_1_1_16_2_1_send = { + .qos = QOS_OUT_1_1_16_2_1, + .expect_err = 0, + .send = &send_16_2_1, + .bcast = true, +}; + +static const struct iso_client_data bcast_16_2_1_recv = { + .qos = QOS_IN_16_2_1, + .expect_err = 0, + .recv = &send_16_2_1, + .bcast = true, +}; + +static void client_connectable_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + struct test_data *data = user_data; + static uint8_t client_num; + + if (opcode != BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE) + return; + + tester_print("Client %u set connectable status 0x%02x", client_num, + status); + + client_num++; + + if (status) + tester_setup_failed(); + else if (data->client_num == client_num) { + tester_setup_complete(); + client_num = 0; + } +} + +static void iso_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = user_data; + + tester_print("New client connection with handle 0x%04x", handle); + + data->handle = handle; +} + +static void acl_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = user_data; + + tester_print("New ACL connection with handle 0x%04x", handle); + + data->acl_handle = handle; +} + +static void setup_powered_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct iso_client_data *isodata = data->test_data; + uint8_t i; + + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Controller powered on"); + + for (i = 0; i < data->client_num; i++) { + struct hciemu_client *client; + struct bthost *host; + + client = hciemu_get_client(data->hciemu, i); + host = hciemu_client_host(client); + bthost_set_cmd_complete_cb(host, client_connectable_complete, + data); + bthost_set_ext_adv_params(host); + bthost_set_ext_adv_enable(host, 0x01); + + if (!isodata) + continue; + + if (isodata->send || isodata->recv) + bthost_set_iso_cb(host, iso_new_conn, data); + + if (isodata->bcast) { + bthost_set_pa_params(host); + bthost_set_pa_enable(host, 0x01); + bthost_create_big(host, 1); + } else if (!isodata->send && isodata->recv) { + 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_powered(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const struct iso_client_data *isodata = data->test_data; + unsigned char param[] = { 0x01 }; + + tester_print("Powering on controller"); + + if (!isodata || !isodata->bcast) + mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, + sizeof(param), param, + NULL, NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_SSP, 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) + mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, + data->mgmt_index, sizeof(param), param, NULL, + NULL, NULL); + + mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, + sizeof(param), param, + setup_powered_callback, NULL, NULL); +} + +static void test_framework(const void *test_data) +{ + tester_test_passed(); +} + +static void test_socket(const void *test_data) +{ + int sk; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) { + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + tester_test_abort(); + return; + } + + close(sk); + + tester_test_passed(); +} + +static void test_getsockopt(const void *test_data) +{ + int sk, err; + socklen_t len; + struct bt_iso_qos qos; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) { + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + tester_test_abort(); + return; + } + + len = sizeof(qos); + memset(&qos, 0, len); + + err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len); + if (err < 0) { + tester_warn("Can't get socket option : %s (%d)", + strerror(errno), errno); + tester_test_failed(); + goto end; + } + + tester_test_passed(); + +end: + close(sk); +} + +static void test_setsockopt(const void *test_data) +{ + int sk, err; + socklen_t len; + struct bt_iso_qos qos = QOS_16_1_2; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) { + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + tester_test_abort(); + goto end; + } + + err = setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, sizeof(qos)); + if (err < 0) { + tester_warn("Can't set socket option : %s (%d)", + strerror(errno), errno); + tester_test_failed(); + goto end; + } + + len = sizeof(qos); + memset(&qos, 0, len); + + err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len); + if (err < 0) { + tester_warn("Can't get socket option : %s (%d)", + strerror(errno), errno); + tester_test_failed(); + goto end; + } + + tester_test_passed(); + +end: + close(sk); +} + +static int create_iso_sock(struct test_data *data) +{ + const uint8_t *master_bdaddr; + struct sockaddr_iso addr; + int sk, err; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_ISO); + if (sk < 0) { + err = -errno; + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + return err; + } + + master_bdaddr = hciemu_get_central_bdaddr(data->hciemu); + if (!master_bdaddr) { + tester_warn("No master bdaddr"); + return -ENODEV; + } + + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, (void *) master_bdaddr); + addr.iso_bdaddr_type = BDADDR_LE_PUBLIC; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; + tester_warn("Can't bind socket: %s (%d)", strerror(errno), + errno); + close(sk); + return err; + } + + return sk; +} + +static const uint8_t base_lc3_16_2_1[] = { + 0x28, 0x00, 0x00, /* Presentation Delay */ + 0x01, /* Number of Subgroups */ + 0x01, /* Number of BIS */ + 0x06, 0x00, 0x00, 0x00, 0x00, /* Code ID = LC3 (0x06) */ + 0x11, /* Codec Specific Configuration */ + 0x02, 0x01, 0x03, /* 16 KHZ */ + 0x02, 0x02, 0x01, /* 10 ms */ + 0x05, 0x03, 0x01, 0x00, 0x00, 0x00, /* Front Left */ + 0x03, 0x04, 0x28, 0x00, /* Frame Length 40 bytes */ + 0x04, /* Metadata */ + 0x03, 0x02, 0x02, 0x00, /* Audio Context: Convertional */ + 0x01, /* BIS */ + 0x00, /* Codec Specific Configuration */ +}; + +static int connect_iso_sock(struct test_data *data, uint8_t num, int sk) +{ + const struct iso_client_data *isodata = data->test_data; + struct hciemu_client *client; + const uint8_t *client_bdaddr = NULL; + struct sockaddr_iso addr; + char str[18]; + int err; + + client = hciemu_get_client(data->hciemu, num); + if (!client) { + tester_warn("No client"); + return -ENODEV; + } + + if (!isodata->bcast) { + client_bdaddr = hciemu_client_bdaddr(client); + if (!client_bdaddr) { + tester_warn("No client bdaddr"); + return -ENODEV; + } + } else { + err = setsockopt(sk, SOL_BLUETOOTH, BT_ISO_BASE, + base_lc3_16_2_1, sizeof(base_lc3_16_2_1)); + if (err < 0) { + tester_warn("Can't set socket BT_ISO_BASE option: " + "%s (%d)", strerror(errno), errno); + tester_test_failed(); + return -EINVAL; + } + } + + err = setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &isodata->qos, + sizeof(isodata->qos)); + if (err < 0) { + tester_warn("Can't set socket BT_ISO_QOS option : %s (%d)", + strerror(errno), errno); + tester_test_failed(); + return -EINVAL; + } + + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, client_bdaddr ? (void *) client_bdaddr : + BDADDR_ANY); + addr.iso_bdaddr_type = BDADDR_LE_PUBLIC; + + ba2str(&addr.iso_bdaddr, str); + + tester_print("Connecting to %s...", str); + + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { + err = -errno; + tester_warn("Can't connect socket: %s (%d)", strerror(errno), + errno); + return err; + } + + return 0; +} + +static bool check_io_qos(const struct bt_iso_io_qos *io1, + const struct bt_iso_io_qos *io2) +{ + if (io1->interval && io2->interval && io1->interval != io2->interval) { + tester_warn("Unexpected IO interval: %u != %u", + io1->interval, io2->interval); + return false; + } + + if (io1->latency && io2->latency && io1->latency != io2->latency) { + tester_warn("Unexpected IO latency: %u != %u", + io1->latency, io2->latency); + return false; + } + + if (io1->sdu != io2->sdu) { + tester_warn("Unexpected IO SDU: %u != %u", io1->sdu, io2->sdu); + return false; + } + + if (io1->phy && io2->phy && io1->phy != io2->phy) { + tester_warn("Unexpected IO PHY: 0x%02x != 0x%02x", + io1->phy, io2->phy); + return false; + } + + if (io1->rtn && io2->rtn && io1->rtn != io2->rtn) { + tester_warn("Unexpected IO RTN: %u != %u", io1->rtn, io2->rtn); + return false; + } + + return true; +} + +static bool check_qos(const struct bt_iso_qos *qos1, + const struct bt_iso_qos *qos2) +{ + if (qos1->cig != BT_ISO_QOS_CIG_UNSET && + qos2->cig != BT_ISO_QOS_CIG_UNSET && + qos1->cig != qos2->cig) { + tester_warn("Unexpected CIG ID: 0x%02x != 0x%02x", + qos1->cig, qos2->cig); + return false; + } + + if (qos1->cis != BT_ISO_QOS_CIS_UNSET && + qos2->cis != BT_ISO_QOS_CIS_UNSET && + qos1->cis != qos2->cis) { + tester_warn("Unexpected CIS ID: 0x%02x != 0x%02x", + qos1->cis, qos2->cis); + return false; + } + + if (qos1->packing != qos2->packing) { + tester_warn("Unexpected QoS packing: 0x%02x != 0x%02x", + qos1->packing, qos2->packing); + return false; + } + + if (qos1->framing != qos2->framing) { + tester_warn("Unexpected QoS framing: 0x%02x != 0x%02x", + qos1->framing, qos2->framing); + return false; + } + + if (!check_io_qos(&qos1->in, &qos2->in)) { + tester_warn("Unexpected Input QoS"); + return false; + } + + if (!check_io_qos(&qos1->out, &qos2->out)) { + tester_warn("Unexpected Output QoS"); + return false; + } + + return true; +} + +static gboolean iso_recv_data(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = user_data; + const struct iso_client_data *isodata = data->test_data; + int sk = g_io_channel_unix_get_fd(io); + ssize_t ret; + char buf[1024]; + + data->io_id[0] = 0; + + ret = read(sk, buf, isodata->recv->iov_len); + if (ret < 0 || isodata->recv->iov_len != (size_t) ret) { + tester_warn("Failed to read %zu bytes: %s (%d)", + isodata->recv->iov_len, strerror(errno), errno); + tester_test_failed(); + return FALSE; + } + + if (memcmp(buf, isodata->recv->iov_base, ret)) + tester_test_failed(); + else + tester_test_passed(); + + return FALSE; +} + +static void iso_recv(struct test_data *data, GIOChannel *io) +{ + const struct iso_client_data *isodata = data->test_data; + struct bthost *host; + + tester_print("Receive %zu bytes of data", isodata->recv->iov_len); + + if (!data->handle) { + tester_warn("ISO handle not set"); + tester_test_failed(); + return; + } + + host = hciemu_client_get_host(data->hciemu); + bthost_send_iso(host, data->handle, isodata->recv, 1); + + data->io_id[0] = g_io_add_watch(io, G_IO_IN, iso_recv_data, data); +} + +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; + + 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 + tester_test_passed(); +} + +static void iso_send(struct test_data *data, GIOChannel *io) +{ + const struct iso_client_data *isodata = data->test_data; + struct bthost *host; + ssize_t ret; + int sk; + + sk = g_io_channel_unix_get_fd(io); + + tester_print("Writing %zu bytes of data", isodata->send->iov_len); + + host = hciemu_client_get_host(data->hciemu); + bthost_add_iso_hook(host, data->handle, bthost_recv_data, data); + + ret = writev(sk, isodata->send, 1); + 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); + tester_test_failed(); + return; + } + + if (isodata->bcast) { + tester_test_passed(); + return; + } + + if (isodata->recv) + iso_recv(data, io); +} + +static gboolean iso_connect(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct iso_client_data *isodata = data->test_data; + int err, sk_err, sk; + socklen_t len; + struct bt_iso_qos qos; + + sk = g_io_channel_unix_get_fd(io); + + len = sizeof(qos); + memset(&qos, 0, len); + + err = getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len); + if (err < 0) { + tester_warn("Can't get socket option : %s (%d)", + strerror(errno), errno); + tester_test_failed(); + return FALSE; + } + + if (!check_qos(&qos, &isodata->qos)) { + tester_warn("Unexpected QoS parameter"); + tester_test_failed(); + return FALSE; + } + + len = sizeof(sk_err); + + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; + + if (err < 0) + tester_warn("Connect failed: %s (%d)", strerror(-err), -err); + else + tester_print("Successfully connected"); + + if (-err != isodata->expect_err) { + tester_warn("Expect error: %s (%d) != %s (%d)", + strerror(-isodata->expect_err), + -isodata->expect_err, strerror(-err), -err); + tester_test_failed(); + } else { + data->step--; + if (data->step) + tester_print("Step %u", data->step); + else if (isodata->send) + iso_send(data, io); + else if (isodata->recv) + iso_recv(data, io); + else + tester_test_passed(); + } + + return FALSE; +} + +static gboolean iso_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + + data->io_id[0] = 0; + + return iso_connect(io, cond, user_data); +} + +static gboolean iso_connect2_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + + data->io_id[1] = 0; + + return iso_connect(io, cond, user_data); +} + +static void setup_connect(struct test_data *data, uint8_t num, GIOFunc func) +{ + GIOChannel *io; + int sk, err; + + sk = create_iso_sock(data); + if (sk < 0) { + if (sk == -EPROTONOSUPPORT) + tester_test_abort(); + else + tester_test_failed(); + return; + } + + err = connect_iso_sock(data, num, sk); + if (err < 0) { + const struct iso_client_data *isodata = data->test_data; + + close(sk); + + if (isodata->expect_err == err) + tester_test_passed(); + else + tester_test_failed(); + + return; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, TRUE); + + data->io_id[num] = g_io_add_watch(io, G_IO_OUT, func, NULL); + + g_io_channel_unref(io); + + tester_print("Connect in progress"); + + data->step++; +} + +static void test_connect(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + setup_connect(data, 0, iso_connect_cb); +} + +static int listen_iso_sock(struct test_data *data) +{ + const struct iso_client_data *isodata = data->test_data; + const uint8_t *src, *dst; + struct sockaddr_iso *addr = NULL; + int sk, err; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK, BTPROTO_ISO); + if (sk < 0) { + err = -errno; + tester_warn("Can't create socket: %s (%d)", strerror(errno), + errno); + return err; + } + + src = hciemu_get_central_bdaddr(data->hciemu); + if (!src) { + tester_warn("No source bdaddr"); + err = -ENODEV; + goto fail; + } + + /* 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; + bacpy(&addr->iso_bdaddr, (void *) src); + addr->iso_bdaddr_type = BDADDR_LE_PUBLIC; + + if (isodata->bcast) { + /* Bind to destination address in case of broadcast */ + dst = hciemu_get_client_bdaddr(data->hciemu); + if (!dst) { + tester_warn("No source bdaddr"); + err = -ENODEV; + goto fail; + } + + bacpy(&addr->iso_bc->bc_bdaddr, (void *) dst); + addr->iso_bc->bc_bdaddr_type = BDADDR_LE_PUBLIC; + addr->iso_bc->bc_num_bis = 1; + addr->iso_bc->bc_bis[0] = 1; + + err = bind(sk, (struct sockaddr *) addr, sizeof(*addr) + + sizeof(*addr->iso_bc)); + } else + err = bind(sk, (struct sockaddr *) addr, sizeof(*addr)); + + + if (err < 0) { + err = -errno; + tester_warn("Can't bind socket: %s (%d)", strerror(errno), + errno); + goto fail; + } + + if (isodata->defer) { + int opt = 1; + + if (setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP, &opt, + sizeof(opt)) < 0) { + tester_print("Can't enable deferred setup: %s (%d)", + strerror(errno), errno); + goto fail; + } + } + + if (listen(sk, 10)) { + err = -errno; + tester_warn("Can't listen socket: %s (%d)", strerror(errno), + errno); + goto fail; + } + + free(addr); + + return sk; + +fail: + free(addr); + close(sk); + return err; +} + +static void setup_listen(struct test_data *data, uint8_t num, GIOFunc func) +{ + const struct iso_client_data *isodata = data->test_data; + GIOChannel *io; + int sk; + + sk = listen_iso_sock(data); + if (sk < 0) { + if (sk == -EPROTONOSUPPORT) + tester_test_abort(); + else + tester_test_failed(); + return; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, TRUE); + + data->io_id[num] = g_io_add_watch(io, G_IO_IN, func, NULL); + + g_io_channel_unref(io); + + tester_print("Listen in progress"); + + data->step++; + + if (!isodata->bcast) { + struct hciemu_client *client; + struct bthost *host; + + if (!data->acl_handle) { + tester_print("ACL handle not set"); + tester_test_failed(); + return; + } + + client = hciemu_get_client(data->hciemu, 0); + host = hciemu_client_host(client); + + bthost_set_cig_params(host, 0x01, 0x01); + bthost_create_cis(host, 257, data->acl_handle); + } +} + +static bool iso_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; + } + } + + tester_print("Accept deferred setup"); + + data->io = io; + data->io_id[0] = g_io_add_watch(io, G_IO_OUT, iso_connect_cb, NULL); + + return true; +} + +static gboolean iso_accept_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct iso_client_data *isodata = data->test_data; + int sk, new_sk; + + data->io_id[0] = 0; + + sk = g_io_channel_unix_get_fd(io); + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + tester_test_failed(); + return false; + } + + io = g_io_channel_unix_new(new_sk); + g_io_channel_set_close_on_unref(io, TRUE); + + if (isodata->defer) { + if (isodata->expect_err < 0) { + g_io_channel_unref(io); + tester_test_passed(); + return false; + } + + if (!iso_defer_accept(data, io)) { + tester_warn("Unable to accept deferred setup"); + tester_test_failed(); + } + return false; + } + + return iso_connect(io, cond, user_data); +} + +static void test_listen(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + setup_listen(data, 0, iso_accept_cb); +} + +static void test_connect2(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + setup_connect(data, 0, iso_connect_cb); + setup_connect(data, 1, iso_connect2_cb); +} + +static void test_bcast(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + setup_connect(data, 0, iso_connect_cb); +} + +static void test_bcast_recv(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + setup_listen(data, 0, iso_accept_cb); +} + +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + test_iso("Basic Framework - Success", NULL, setup_powered, + test_framework); + + test_iso("Basic ISO Socket - Success", NULL, setup_powered, + test_socket); + + test_iso("Basic ISO Get Socket Option - Success", NULL, setup_powered, + test_getsockopt); + + test_iso("Basic ISO Set Socket Option - Success", NULL, setup_powered, + test_setsockopt); + + test_iso("ISO QoS 8_1_1 - Success", &connect_8_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 8_2_1 - Success", &connect_8_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 16_1_1 - Success", &connect_16_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 16_2_1 - Success", &connect_16_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 16_2_1 CIG 0x01 - Success", &connect_1_16_2_1, + setup_powered, + test_connect); + + test_iso("ISO QoS 16_2_1 CIG 0x01 CIS 0x01 - Success", + &connect_1_1_16_2_1, + setup_powered, + test_connect); + + test_iso("ISO QoS 24_1_1 - Success", &connect_24_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 24_2_1 - Success", &connect_24_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 32_1_1 - Success", &connect_32_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 32_2_1 - Success", &connect_32_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 44_1_1 - Success", &connect_44_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 44_2_1 - Success", &connect_44_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_1_1 - Success", &connect_48_1_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_2_1 - Success", &connect_48_2_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_3_1 - Success", &connect_48_3_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_4_1 - Success", &connect_48_4_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_5_1 - Success", &connect_48_5_1, setup_powered, + test_connect); + + test_iso("ISO QoS 48_6_1 - Success", &connect_48_6_1, setup_powered, + test_connect); + + test_iso("ISO QoS 8_1_2 - Success", &connect_8_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 8_2_2 - Success", &connect_8_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 16_1_2 - Success", &connect_16_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 16_2_2 - Success", &connect_16_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 24_1_2 - Success", &connect_24_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 24_2_2 - Success", &connect_24_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 32_1_2 - Success", &connect_32_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 32_2_2 - Success", &connect_32_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 44_1_2 - Success", &connect_44_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 44_2_2 - Success", &connect_44_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_1_2 - Success", &connect_48_1_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_2_2 - Success", &connect_48_2_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_3_2 - Success", &connect_48_3_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_4_2 - Success", &connect_48_4_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_5_2 - Success", &connect_48_5_2, setup_powered, + test_connect); + + test_iso("ISO QoS 48_6_2 - Success", &connect_48_6_2, setup_powered, + test_connect); + + test_iso("ISO QoS - Invalid", &connect_invalid, setup_powered, + test_connect); + + test_iso2("ISO Connect2 CIG 0x01 - Success", &connect_1_16_2_1, + setup_powered, + test_connect2); + + test_iso("ISO Send - Success", &connect_16_2_1_send, setup_powered, + test_connect); + + test_iso("ISO Receive - Success", &listen_16_2_1_recv, setup_powered, + test_listen); + + test_iso("ISO Defer Receive - Success", &listen_16_2_1_defer_recv, + setup_powered, test_listen); + + test_iso("ISO Defer Reject - Success", &listen_16_2_1_defer_reject, + setup_powered, test_listen); + + test_iso("ISO Send and Receive - Success", &connect_16_2_1_send_recv, + setup_powered, + test_connect); + + test_iso("ISO Broadcaster - Success", &bcast_16_2_1_send, setup_powered, + test_bcast); + test_iso("ISO Broadcaster BIG 0x01 - Success", &bcast_1_16_2_1_send, + setup_powered, + test_bcast); + test_iso("ISO Broadcaster BIG 0x01 BIS 0x01 - Success", + &bcast_1_1_16_2_1_send, + setup_powered, + test_bcast); + + test_iso("ISO Broadcaster Receiver - Success", &bcast_16_2_1_recv, + setup_powered, + test_bcast_recv); + + return tester_run(); +} diff --git a/tools/test-runner.c b/tools/test-runner.c index 6886d66c6..7e8c4dd56 100644 --- a/tools/test-runner.c +++ b/tools/test-runner.c @@ -192,7 +192,6 @@ static char *const qemu_argv[] = { "-machine", "type=q35,accel=kvm:tcg", "-m", "192M", "-nographic", - "-vga", "none", "-net", "none", "-no-acpi", "-no-hpet", @@ -249,7 +248,7 @@ static void start_qemu(void) snprintf(cmdline, sizeof(cmdline), "console=ttyS0,115200n8 earlyprintk=serial " "rootfstype=9p " - "rootflags=trans=virtio,version=9p2000.L " + "rootflags=trans=virtio,version=9p2000.u " "acpi=off pci=noacpi noapic quiet ro init=%s " "bluetooth.enable_ecred=1 " "TESTHOME=%s TESTDBUS=%u TESTDAEMON=%u " @@ -609,6 +608,7 @@ static const char *test_table[] = { "l2cap-tester", "rfcomm-tester", "sco-tester", + "iso-tester", "bnep-tester", "check-selftest", "tools/mgmt-tester", @@ -616,6 +616,7 @@ static const char *test_table[] = { "tools/l2cap-tester", "tools/rfcomm-tester", "tools/sco-tester", + "tools/iso-tester", "tools/bnep-tester", "tools/check-selftest", NULL From patchwork Wed Jun 22 22:28:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891547 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F1A21C433EF for ; Wed, 22 Jun 2022 22:28:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358549AbiFVW2Y (ORCPT ); Wed, 22 Jun 2022 18:28:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59942 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358386AbiFVW2Q (ORCPT ); Wed, 22 Jun 2022 18:28:16 -0400 Received: from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com [IPv6:2607:f8b0:4864:20::1035]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 75B8A100A for ; Wed, 22 Jun 2022 15:28:12 -0700 (PDT) Received: by mail-pj1-x1035.google.com with SMTP id w19-20020a17090a8a1300b001ec79064d8dso753057pjn.2 for ; Wed, 22 Jun 2022 15:28:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=lhTtTlE6LhMu+l8W1tfKWXWPUiO/zud4tqMIfdHDPNg=; b=d8uZQNEUpKqV3L65Bt+oXdhm0nX2UXAdvRGfpnIXvJIO3Iojg1Gi1aXdneb///GC2+ L4+fDndPOqmb4pv3WD55CRIKVIKCWPOxoZsAB7eb4HoF7YQHXTQIlxylr6rdTptHLUGh 9jgnJc+caQBwHkIR1rWsYDueixccoArfjuxhOcE9IMxHeCBM/Ec/3MYiBOl5R/Nb45N6 c7jGvjEZw8dMSSsWnjKmYvbw8ZHyEDZtl1IBh4xkb02WudZXF+kluU/O7STAcxrnzcVF JC/voZSehQ2xK17kmWYQE9WkrQM2q0/5gyDstrHZqtl5hQRPP8p807AF+tVAVOCUOoV2 NlaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lhTtTlE6LhMu+l8W1tfKWXWPUiO/zud4tqMIfdHDPNg=; b=lmc6f36/lromC36dUQ9QYVY0BEOIJqdFNb17WDYVwdz8dGLSszr7X0QPfGr8GxXl40 C9KTLRL8wCj6gbyW9EViAfG7CZZ7ljtIFXt2gBdE3FlWdwg9yDK508C9IBrgCCzc2UHN 7rXKcDQpI70f9hMZfOAla5B4NrK2JQVFQkBb0w7VSRqxTuaCoze5zAZ+QB/6n0rjTRiQ V+lNXgZQczqwOJUCG3O0euASxj5fof3WfBZ0qy2Al4XDgCJIqc1bTpfx3IY+BvZQpP8A T8LbHexR3yqHAWag5yGKwkHlZidjiQXzbRvdit06wUQZRzGuMzwv45dCnWhZ+x7T4X2O NAFw== X-Gm-Message-State: AJIora/YN3ncrl+lZLypoiL5Gy8eQuQcE4ndZDBdrpP6lMMn8rZhWDVN eoZC/DYf5mR7dZd9N2hOXjqlbOLk4UtnkA== X-Google-Smtp-Source: AGRyM1sKwbFq4yKjcNVuNoMfbpzzcc9uKxLIJ9XhWg/NEGE0bDgiAn0kyy2+zKuF3jViXyG2NjovIg== X-Received: by 2002:a17:90b:38c8:b0:1e8:5202:f6d4 with SMTP id nn8-20020a17090b38c800b001e85202f6d4mr599501pjb.149.1655936891396; Wed, 22 Jun 2022 15:28:11 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:10 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 7/8] tools: Add isotest tool Date: Wed, 22 Jun 2022 15:28:00 -0700 Message-Id: <20220622222801.2676431-7-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds isotest tool which can be used to test ISO sockets. --- Makefile.tools | 4 +- tools/isotest.c | 1217 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1220 insertions(+), 1 deletion(-) create mode 100644 tools/isotest.c diff --git a/Makefile.tools b/Makefile.tools index f2f82062c..4e5ff73b0 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -207,7 +207,7 @@ endif if TOOLS bin_PROGRAMS += tools/rctest tools/l2test tools/l2ping tools/bluemoon \ - tools/hex2hcd tools/mpris-proxy tools/btattach + tools/hex2hcd tools/mpris-proxy tools/btattach tools/isotest noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \ tools/scotest tools/amptest tools/hwdb \ @@ -319,6 +319,8 @@ tools_gatt_service_SOURCES = tools/gatt-service.c tools_gatt_service_LDADD = gdbus/libgdbus-internal.la \ src/libshared-mainloop.la $(GLIB_LIBS) $(DBUS_LIBS) +tools_isotest_LDADD = lib/libbluetooth-internal.la + profiles_iap_iapd_SOURCES = profiles/iap/main.c profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) diff --git a/tools/isotest.c b/tools/isotest.c new file mode 100644 index 000000000..8a50bfee8 --- /dev/null +++ b/tools/isotest.c @@ -0,0 +1,1217 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2022 Intel Corporation. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/mgmt.h" +#include "lib/iso.h" + +#include "src/shared/util.h" + +#define NSEC_USEC(_t) (_t / 1000L) +#define SEC_USEC(_t) (_t * 1000000L) +#define TS_USEC(_ts) (SEC_USEC((_ts)->tv_sec) + NSEC_USEC((_ts)->tv_nsec)) + +/* Test modes */ +enum { + SEND, + RECV, + RECONNECT, + MULTY, + DUMP, + CONNECT +}; + +static unsigned char *buf; + +/* Default data size */ +static long data_size = 251; + +static int mgmt_index = MGMT_INDEX_NONE; +static bdaddr_t bdaddr; +static int bdaddr_type = BDADDR_LE_PUBLIC; + +static int defer_setup; +static int sndbuf; +static struct timeval sndto; +static bool quiet; + +struct bt_iso_qos *iso_qos; +static bool inout; + +struct lookup_table { + const char *name; + int flag; +}; + +static struct lookup_table bdaddr_types[] = { + { "le_public", BDADDR_LE_PUBLIC }, + { "le_random", BDADDR_LE_RANDOM }, + { NULL, 0 }, +}; + +static int get_lookup_flag(struct lookup_table *table, char *name) +{ + int i; + + for (i = 0; table[i].name; i++) + if (!strcasecmp(table[i].name, name)) + return table[i].flag; + + return -1; +} + +static void print_lookup_values(struct lookup_table *table, char *header) +{ + int i; + + printf("%s\n", header); + + for (i = 0; table[i].name; i++) + printf("\t%s\n", table[i].name); +} + +static float tv2fl(struct timeval tv) +{ + return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0); +} + +static const uint8_t set_iso_socket_param[] = { + 0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98, /* UUID - ISO Socket */ + 0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f, + 0x01, /* Action - enable */ +}; + +static int mgmt_recv(int fd) +{ + uint8_t buf[1024]; + + return read(fd, buf, sizeof(buf)); +} + +static int mgmt_send_cmd(int fd, uint16_t op, uint16_t id, const void *data, + size_t len) +{ + struct mgmt_hdr hdr; + struct iovec iov[2]; + int ret; + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = htobs(op); + hdr.index = htobs(id); + hdr.len = htobs(len); + + iov[0].iov_base = &hdr; + iov[0].iov_len = sizeof(hdr); + + iov[1].iov_base = (void *)data; + iov[1].iov_len = len; + + ret = writev(fd, iov, 2); + if (ret < 0) + return ret; + + /* Wait for MGMT to respond */ + ret = mgmt_recv(fd); + if (ret < 0) + return ret; + + return 0; +} + +static int mgmt_open(void) +{ + union { + struct sockaddr common; + struct sockaddr_hci hci; + } addr; + int fd, err; + + fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, + BTPROTO_HCI); + if (fd < 0) { + syslog(LOG_ERR, "Can't create mgmt socket: %s (%d)", + strerror(errno), errno); + return -errno; + } + + syslog(LOG_ERR, "mgmt socket: fd %d", fd); + + memset(&addr, 0, sizeof(addr)); + addr.hci.hci_family = AF_BLUETOOTH; + addr.hci.hci_dev = HCI_DEV_NONE; + addr.hci.hci_channel = HCI_CHANNEL_CONTROL; + + if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) { + syslog(LOG_ERR, "Can't bind mgmt socket: %s (%d)", + strerror(errno), errno); + err = -errno; + close(fd); + return err; + } + + return fd; +} + + +static const uint8_t set_le_param[] = { + 0x01, /* Action - enable */ +}; + +static int mgmt_set_le(int fd) +{ + int err, index; + + index = mgmt_index; + if (index == MGMT_INDEX_NONE) + index = 0; + + err = mgmt_send_cmd(fd, MGMT_OP_SET_LE, index, + set_le_param, sizeof(set_le_param)); + if (err < 0) { + syslog(LOG_ERR, "Fail to write mgmt socket: %s (%d)", + strerror(errno), errno); + err = -errno; + } + + syslog(LOG_ERR, "%s: err %d", __func__, err); + + return err < 0 ? err : 0; +} + +static int mgmt_set_experimental(void) +{ + int fd, err; + + fd = mgmt_open(); + if (fd < 0) + return fd; + + err = mgmt_set_le(fd); + if (err < 0) + goto fail; + + err = mgmt_send_cmd(fd, MGMT_OP_SET_EXP_FEATURE, MGMT_INDEX_NONE, + set_iso_socket_param, sizeof(set_iso_socket_param)); + if (err < 0) { + syslog(LOG_ERR, "Fail to write mgmt socket: %s (%d)", + strerror(errno), errno); + err = -errno; + } + + syslog(LOG_ERR, "%s: err %d", __func__, err); + +fail: + close(fd); + + return err < 0 ? err : 0; +} + +static void print_qos(int sk, struct sockaddr_iso *addr) +{ + struct bt_iso_qos qos; + socklen_t len; + + /* Read Out QOS */ + memset(&qos, 0, sizeof(qos)); + len = sizeof(qos); + + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { + syslog(LOG_ERR, "Can't get QoS socket option: %s (%d)", + strerror(errno), errno); + return; + } + + if (!bacmp(&addr->iso_bdaddr, BDADDR_ANY)) { + syslog(LOG_INFO, "QoS BIG 0x%02x BIS 0x%02x Packing 0x%02x " + "Framing 0x%02x]", qos.big, qos.bis, qos.packing, + qos.framing); + } else { + syslog(LOG_INFO, "QoS CIG 0x%02x CIS 0x%02x Packing 0x%02x " + "Framing 0x%02x]", qos.cig, qos.cis, qos.packing, + qos.framing); + syslog(LOG_INFO, "Input QoS [Interval %u us Latency %u " + "ms SDU %u PHY 0x%02x RTN %u]", qos.in.interval, + qos.in.latency, qos.in.sdu, qos.in.phy, qos.in.rtn); + } + syslog(LOG_INFO, "Output QoS [Interval %u us Latency %u " + "ms SDU %u PHY 0x%02x RTN %u]", qos.out.interval, + qos.out.latency, qos.out.sdu, qos.out.phy, qos.out.rtn); +} + +static int do_connect(char *peer) +{ + struct sockaddr_iso addr; + int sk; + + mgmt_set_experimental(); + + /* Create socket */ + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) { + syslog(LOG_ERR, "Can't create socket: %s (%d)", + strerror(errno), errno); + return -1; + } + + /* Bind to local address */ + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + bacpy(&addr.iso_bdaddr, mgmt_index != MGMT_INDEX_NONE ? + &bdaddr : BDADDR_ANY); + addr.iso_bdaddr_type = BDADDR_LE_PUBLIC; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + syslog(LOG_ERR, "Can't bind socket: %s (%d)", + strerror(errno), errno); + goto error; + } + + /* Set QoS if available */ + if (iso_qos) { + if (!inout || !strcmp(peer, "00:00:00:00:00:00")) { + iso_qos->in.phy = 0x00; + iso_qos->in.sdu = 0; + } + + if (setsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, iso_qos, + sizeof(*iso_qos)) < 0) { + syslog(LOG_ERR, "Can't set QoS socket option: " + "%s (%d)", strerror(errno), errno); + goto error; + } + } + + /* Enable deferred setup */ + if (defer_setup && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP, + &defer_setup, sizeof(defer_setup)) < 0) { + syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)", + strerror(errno), errno); + goto error; + } + + /* Connect to remote device */ + memset(&addr, 0, sizeof(addr)); + addr.iso_family = AF_BLUETOOTH; + str2ba(peer, &addr.iso_bdaddr); + addr.iso_bdaddr_type = bdaddr_type; + + syslog(LOG_INFO, "Connecting %s ...", peer); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + syslog(LOG_ERR, "Can't connect: %s (%d)", strerror(errno), + errno); + goto error; + } + + syslog(LOG_INFO, "Connected [%s]", peer); + + print_qos(sk, &addr); + + return sk; + +error: + close(sk); + return -1; +} + +static void do_listen(char *filename, void (*handler)(int fd, int sk), + char *peer) +{ + struct sockaddr_iso *addr = NULL; + socklen_t optlen; + int sk, nsk, fd = -1; + char ba[18]; + + if (filename) { + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (fd < 0) { + syslog(LOG_ERR, "Can't open file %s: %s\n", + filename, strerror(errno)); + exit(1); + } + } + + mgmt_set_experimental(); + + /* Create socket */ + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_ISO); + if (sk < 0) { + syslog(LOG_ERR, "Can't create socket: %s (%d)", + strerror(errno), errno); + if (fd >= 0) + close(fd); + exit(1); + } + + /* 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; + bacpy(&addr->iso_bdaddr, mgmt_index != MGMT_INDEX_NONE ? + &bdaddr : BDADDR_ANY); + addr->iso_bdaddr_type = BDADDR_LE_PUBLIC; + optlen = sizeof(*addr); + + if (peer) { + str2ba(peer, &addr->iso_bc->bc_bdaddr); + addr->iso_bc->bc_bdaddr_type = bdaddr_type; + addr->iso_bc->bc_num_bis = 1; + addr->iso_bc->bc_bis[0] = 1; + optlen += sizeof(*addr->iso_bc); + } + + if (bind(sk, (struct sockaddr *) addr, optlen) < 0) { + syslog(LOG_ERR, "Can't bind socket: %s (%d)", + strerror(errno), errno); + goto error; + } + + /* Enable deferred setup */ + if (defer_setup && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP, + &defer_setup, sizeof(defer_setup)) < 0) { + syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)", + strerror(errno), errno); + goto error; + } + + /* Listen for connections */ + if (listen(sk, 10)) { + syslog(LOG_ERR, "Can not listen on the socket: %s (%d)", + strerror(errno), errno); + goto error; + } + + syslog(LOG_INFO, "Waiting for connection %s...", peer ? peer : ""); + + while (1) { + memset(addr, 0, sizeof(*addr) + sizeof(*addr->iso_bc)); + optlen = sizeof(*addr); + + if (peer) + optlen += sizeof(*addr->iso_bc); + + nsk = accept(sk, (struct sockaddr *) addr, &optlen); + if (nsk < 0) { + syslog(LOG_ERR, "Accept failed: %s (%d)", + strerror(errno), errno); + goto error; + } + + if (fork()) { + /* Parent */ + close(nsk); + continue; + } + /* Child */ + close(sk); + + ba2str(&addr->iso_bdaddr, ba); + syslog(LOG_INFO, "Connected [%s]", ba); + + print_qos(nsk, addr); + + /* Handle deferred setup */ + if (defer_setup) { + syslog(LOG_INFO, "Waiting for %d seconds", + abs(defer_setup) - 1); + sleep(abs(defer_setup) - 1); + + if (defer_setup < 0) { + close(nsk); + exit(1); + } + } + + handler(fd, nsk); + + syslog(LOG_INFO, "Disconnect"); + exit(0); + } + +error: + free(addr); + + if (fd >= 0) + close(fd); + close(sk); + exit(1); +} + +static void dump_mode(int fd, int sk) +{ + int len; + + if (defer_setup) { + len = read(sk, buf, data_size); + if (len < 0) + syslog(LOG_ERR, "Initial read error: %s (%d)", + strerror(errno), errno); + else + syslog(LOG_INFO, "Initial bytes %d", len); + } + + syslog(LOG_INFO, "Receiving ..."); + while ((len = read(sk, buf, data_size)) > 0) { + if (fd >= 0) { + len = write(fd, buf, len); + if (len < 0) { + syslog(LOG_ERR, "Write failed: %s (%d)", + strerror(errno), errno); + return; + } + } else if (!quiet) + syslog(LOG_INFO, "Received %d bytes", len); + } +} + +static void recv_mode(int fd, int sk) +{ + struct timeval tv_beg, tv_end, tv_diff; + long total; + int len; + uint32_t seq; + + if (defer_setup) { + len = read(sk, buf, data_size); + if (len < 0) + syslog(LOG_ERR, "Initial read error: %s (%d)", + strerror(errno), errno); + else + syslog(LOG_INFO, "Initial bytes %d", len); + } + + syslog(LOG_INFO, "Receiving ..."); + + for (seq = 0; ; seq++) { + gettimeofday(&tv_beg, NULL); + total = 0; + while (total < data_size) { + int r; + + r = recv(sk, buf, data_size, 0); + if (r <= 0) { + if (r < 0) + syslog(LOG_ERR, "Read failed: %s (%d)", + strerror(errno), errno); + if (errno != ENOTCONN) + return; + r = 0; + } + + if (fd >= 0) { + r = write(fd, buf, r); + if (r < 0) { + syslog(LOG_ERR, "Write failed: %s (%d)", + strerror(errno), errno); + return; + } + } + + total += r; + } + gettimeofday(&tv_end, NULL); + + timersub(&tv_end, &tv_beg, &tv_diff); + + if (!quiet) + syslog(LOG_INFO, + "[seq %d] %ld bytes in %.2f sec speed %.2f " + "kb/s", seq, total, tv2fl(tv_diff), + (float)(total * 8 / tv2fl(tv_diff)) / 1024.0); + } +} + +static int open_file(const char *filename) +{ + int fd = -1; + + syslog(LOG_INFO, "Opening %s ...", filename); + + fd = open(filename, O_RDONLY); + if (fd <= 0) { + syslog(LOG_ERR, "Can't open file %s: %s\n", + filename, strerror(errno)); + } + + return fd; +} + +static void send_wait(struct timespec *t_start, uint32_t us) +{ + struct timespec t_now; + struct timespec t_diff; + int64_t delta_us; + + /* Skip sleep at start */ + if (!us) + return; + + if (clock_gettime(CLOCK_MONOTONIC, &t_now) < 0) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + t_diff.tv_sec = t_now.tv_sec - t_start->tv_sec; + t_diff.tv_nsec = t_now.tv_nsec - t_start->tv_nsec; + + delta_us = us - TS_USEC(&t_diff); + + if (delta_us < 0) { + syslog(LOG_INFO, "Send is behind: %zd us", delta_us); + delta_us = 1000; + } + + if (!quiet) + syslog(LOG_INFO, "Waiting (%zd us)...", delta_us); + + usleep(delta_us); + + if (clock_gettime(CLOCK_MONOTONIC, t_start) < 0) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } +} + +static int read_stream(int fd, ssize_t count) +{ + ssize_t len, ret = 0; + + while (ret < count) { + len = read(fd, buf + ret, count - ret); + if (len < 0) + return -errno; + + ret += len; + usleep(1000); + } + + return ret; +} + +static int read_file(int fd, ssize_t count, bool rewind) +{ + ssize_t len; + + if (fd == STDIN_FILENO) + return read_stream(fd, count); + + len = read(fd, buf, count); + if (len <= 0) { + if (!len) { + if (rewind) { + lseek(fd, 0, SEEK_SET); + return read_file(fd, count, rewind); + } + return len; + } + + return -errno; + } + + return len; +} + +static void do_send(int sk, int fd, struct bt_iso_qos *qos, uint32_t num, + bool repeat) +{ + uint32_t seq; + struct timespec t_start; + int len, used; + + if (clock_gettime(CLOCK_MONOTONIC, &t_start) < 0) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + for (seq = 0; ; seq++) { + if (fd >= 0) { + len = read_file(fd, qos->out.sdu, repeat); + if (len < 0) { + syslog(LOG_ERR, "read failed: %s (%d)", + strerror(-len), -len); + exit(1); + } + } else + len = qos->out.sdu; + + len = send(sk, buf, len, 0); + if (len <= 0) { + syslog(LOG_ERR, "send failed: %s (%d)", + strerror(errno), errno); + exit(1); + } + + ioctl(sk, TIOCOUTQ, &used); + + if (!quiet) + syslog(LOG_INFO, + "[seq %d] %d bytes buffered %d (%d bytes)", + seq, len, used / len, used); + + if (seq && !((seq + 1) % num)) + send_wait(&t_start, num * qos->out.interval); + } +} + +static void send_mode(char *filename, char *peer, int i, bool repeat) +{ + struct bt_iso_qos qos; + socklen_t len; + int sk, fd = -1; + uint32_t num; + + if (filename) { + char altername[PATH_MAX]; + struct stat st; + int err; + + snprintf(altername, PATH_MAX, "%s.%u", filename, i); + + err = stat(altername, &st); + if (!err) + fd = open_file(altername); + + if (fd <= 0) + fd = open_file(filename); + } + + sk = do_connect(peer); + if (sk < 0) { + syslog(LOG_ERR, "Can't connect to the server: %s (%d)", + strerror(errno), errno); + exit(1); + } + + if (defer_setup) { + syslog(LOG_INFO, "Waiting for %d seconds", + abs(defer_setup) - 1); + sleep(abs(defer_setup) - 1); + } + + syslog(LOG_INFO, "Sending ..."); + + /* Read QoS */ + memset(&qos, 0, sizeof(qos)); + len = sizeof(qos); + if (getsockopt(sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos, &len) < 0) { + syslog(LOG_ERR, "Can't get Output QoS socket option: %s (%d)", + strerror(errno), errno); + qos.out.sdu = ISO_DEFAULT_MTU; + } + + /* num of packets = latency (ms) / interval (us) */ + num = (qos.out.latency * 1000 / qos.out.interval); + + syslog(LOG_INFO, "Number of packets: %d", num); + + if (!sndbuf) + /* Use socket buffer as a jitter buffer for the entire buffer + * latency: + * jitter buffer = 2 * (SDU * subevents) + */ + sndbuf = 2 * ((qos.out.latency * 1000 / qos.out.interval) * + qos.out.sdu); + + len = sizeof(sndbuf); + if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &sndbuf, len) < 0) { + syslog(LOG_ERR, "Can't set socket SO_SNDBUF option: %s (%d)", + strerror(errno), errno); + } + + syslog(LOG_INFO, "Socket jitter buffer: %d buffer", sndbuf); + + if (sndto.tv_usec) { + len = sizeof(sndto); + if (setsockopt(sk, SOL_SOCKET, SO_SNDTIMEO, &sndto, len) < 0) { + syslog(LOG_ERR, "Can't set socket SO_SNDTIMEO option: " + "%s (%d)", strerror(errno), errno); + } else { + syslog(LOG_INFO, "Socket send timeout: %ld usec", + sndto.tv_usec); + } + } + + for (i = 6; i < qos.out.sdu; i++) + buf[i] = 0x7f; + + do_send(sk, fd, &qos, num, repeat); +} + +static void reconnect_mode(char *peer) +{ + while (1) { + int sk; + + sk = do_connect(peer); + if (sk < 0) { + syslog(LOG_ERR, "Can't connect to the server: %s (%d)", + strerror(errno), errno); + exit(1); + } + + close(sk); + + sleep(5); + } +} + +static void multy_connect_mode(char *peer) +{ + while (1) { + int i, sk; + + for (i = 0; i < 10; i++) { + if (fork()) + continue; + + /* Child */ + sk = do_connect(peer); + if (sk < 0) { + syslog(LOG_ERR, "Can't connect to the server: " + "%s (%d)", strerror(errno), errno); + } + close(sk); + exit(0); + } + + sleep(19); + } +} + +#define QOS_IO(_interval, _latency, _sdu, _phy, _rtn) \ +{ \ + .interval = _interval, \ + .latency = _latency, \ + .sdu = _sdu, \ + .phy = _phy, \ + .rtn = _rtn, \ +} + +#define QOS(_interval, _latency, _sdu, _phy, _rtn) \ +{ \ + .cig = BT_ISO_QOS_CIG_UNSET, \ + .cis = BT_ISO_QOS_CIS_UNSET, \ + .sca = 0x07, \ + .packing = 0x00, \ + .framing = 0x00, \ + .out = QOS_IO(_interval, _latency, _sdu, _phy, _rtn), \ +} + +#define QOS_PRESET(_name, _inout, _interval, _latency, _sdu, _phy, _rtn) \ +{ \ + .name = _name, \ + .inout = _inout, \ + .qos = QOS(_interval, _latency, _sdu, _phy, _rtn), \ +} + +static struct qos_preset { + const char *name; + bool inout; + struct bt_iso_qos qos; +} presets[] = { + /* QoS Configuration settings for low latency audio data */ + QOS_PRESET("8_1_1", true, 7500, 8, 26, 0x02, 2), + QOS_PRESET("8_2_1", true, 10000, 10, 30, 0x02, 2), + QOS_PRESET("16_1_1", true, 7500, 8, 30, 0x02, 2), + QOS_PRESET("16_2_1", true, 10000, 10, 40, 0x02, 2), + QOS_PRESET("24_1_1", true, 7500, 8, 45, 0x02, 2), + QOS_PRESET("24_2_1", true, 10000, 10, 60, 0x02, 2), + QOS_PRESET("32_1_1", true, 7500, 8, 60, 0x02, 2), + QOS_PRESET("32_2_1", true, 10000, 10, 80, 0x02, 2), + QOS_PRESET("44_1_1", false, 8163, 24, 98, 0x02, 5), + QOS_PRESET("44_2_1", false, 10884, 31, 130, 0x02, 5), + QOS_PRESET("48_1_1", false, 7500, 15, 75, 0x02, 5), + QOS_PRESET("48_2_1", false, 10000, 20, 100, 0x02, 5), + QOS_PRESET("48_3_1", false, 7500, 15, 90, 0x02, 5), + QOS_PRESET("48_4_1", false, 10000, 20, 120, 0x02, 5), + QOS_PRESET("48_5_1", false, 7500, 15, 117, 0x02, 5), + QOS_PRESET("44_6_1", false, 10000, 20, 155, 0x02, 5), + /* QoS Configuration settings for high reliability audio data */ + QOS_PRESET("8_1_2", true, 7500, 45, 26, 0x02, 41), + QOS_PRESET("8_2_2", true, 10000, 60, 30, 0x02, 53), + QOS_PRESET("16_1_2", true, 7500, 45, 30, 0x02, 41), + QOS_PRESET("16_2_2", true, 10000, 60, 40, 0x02, 47), + QOS_PRESET("24_1_2", true, 7500, 45, 45, 0x02, 35), + QOS_PRESET("24_2_2", true, 10000, 60, 60, 0x02, 41), + QOS_PRESET("32_1_2", true, 7500, 45, 60, 0x02, 29), + QOS_PRESET("32_2_1", true, 10000, 60, 80, 0x02, 35), + QOS_PRESET("44_1_2", false, 8163, 54, 98, 0x02, 23), + QOS_PRESET("44_2_2", false, 10884, 71, 130, 0x02, 23), + QOS_PRESET("48_1_2", false, 7500, 45, 75, 0x02, 23), + QOS_PRESET("48_2_2", false, 10000, 60, 100, 0x02, 23), + QOS_PRESET("48_3_2", false, 7500, 45, 90, 0x02, 23), + QOS_PRESET("48_4_2", false, 10000, 60, 120, 0x02, 23), + QOS_PRESET("48_5_2", false, 7500, 45, 117, 0x02, 23), + QOS_PRESET("44_6_2", false, 10000, 60, 155, 0x02, 23), +}; + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static void usage(void) +{ + printf("isotest - ISO testing\n" + "Usage:\n"); + printf("\tisotest [options] [bdaddr] [bdaddr1]...\n"); + printf("Modes:\n" + "\t-d, --dump [filename] dump (server)\n" + "\t-c, --reconnect reconnect (client)\n" + "\t-m, --multiple multiple connects (client)\n" + "\t-r, --receive [filename] receive (server)\n" + "\t-s, --send [filename,...] connect and send " + "(client/broadcaster)\n" + "\t-n, --silent connect and be silent (client)\n" + "Options:\n" + "\t[-b, --bytes ]\n" + "\t[-i, --device ]\n" + "\t[-j, --jitter socket/jitter buffer]\n" + "\t[-h, --help]\n" + "\t[-q, --quiet disable packet logging]\n" + "\t[-t, --timeout send timeout]\n" + "\t[-C, --continue]\n" + "\t[-W, --defer ] enable deferred setup\n" + "\t[-M, --mtu ]\n" + "\t[-S, --sca/adv-interval ]\n" + "\t[-P, --packing ]\n" + "\t[-F, --framing ]\n" + "\t[-I, --interval ]\n" + "\t[-L, --latency ]\n" + "\t[-Y, --phy ]\n" + "\t[-R, --rtn ]\n" + "\t[-B, --preset ]\n" + "\t[-G, --CIG/BIG ]\n" + "\t[-T, --CIS/BIS ]\n" + "\t[-V, --type ] address type (help for list)\n"); +} + +static const struct option main_options[] = { + { "dump", optional_argument, NULL, 'd'}, + { "reconnect", no_argument, NULL, 'c'}, + { "multiple", no_argument, NULL, 'm'}, + { "receive", optional_argument, NULL, 'r'}, + { "send", optional_argument, NULL, 's'}, + { "silent", no_argument, NULL, 'n'}, + { "bytes", required_argument, NULL, 'b'}, + { "index", required_argument, NULL, 'i'}, + { "jitter", required_argument, NULL, 'j'}, + { "help", no_argument, NULL, 'h'}, + { "quiet", no_argument, NULL, 'q'}, + { "timeout", required_argument, NULL, 't'}, + { "continue", no_argument, NULL, 'C'}, + { "defer", required_argument, NULL, 'W'}, + { "mtu", required_argument, NULL, 'M'}, + { "sca", required_argument, NULL, 'S'}, + { "packing", required_argument, NULL, 'P'}, + { "framing", required_argument, NULL, 'F'}, + { "interval", required_argument, NULL, 'I'}, + { "latency", required_argument, NULL, 'L'}, + { "phy", required_argument, NULL, 'Y'}, + { "rtn", required_argument, NULL, 'R'}, + { "preset", required_argument, NULL, 'B'}, + { "CIG/BIG", required_argument, NULL, 'G'}, + { "CIS/BIS", required_argument, NULL, 'T'}, + { "type", required_argument, NULL, 'V'}, + {} +}; + +int main(int argc, char *argv[]) +{ + struct sigaction sa; + int sk, mode = RECV; + char *filename = NULL; + bool repeat = false; + unsigned int i; + + iso_qos = malloc(sizeof(*iso_qos)); + /* Default to 16_2_1 */ + *iso_qos = presets[3].qos; + inout = true; + + while (1) { + int opt; + + opt = getopt_long(argc, argv, + "d::cmr::s::nb:i:j:hqt:CV:W:M:S:P:F:I:L:Y:R:B:G:T:", + main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'r': + mode = RECV; + if (optarg) + filename = strdup(optarg); + break; + + case 's': + mode = SEND; + if (optarg) + filename = strdup(optarg); + break; + + case 'd': + mode = DUMP; + if (optarg) + filename = strdup(optarg); + break; + + case 'c': + mode = RECONNECT; + break; + + case 'm': + mode = MULTY; + break; + + case 'n': + mode = CONNECT; + break; + + case 'b': + if (optarg) + data_size = atoi(optarg); + break; + + case 'i': + if (!optarg) + break; + + if (!strncasecmp(optarg, "hci", 3)) { + mgmt_index = atoi(optarg + 3); + hci_devba(mgmt_index, &bdaddr); + } else + str2ba(optarg, &bdaddr); + break; + + case 'j': + if (optarg) + sndbuf = atoi(optarg); + break; + + case 'q': + quiet = true; + break; + + case 't': + if (optarg) + sndto.tv_usec = atoi(optarg); + break; + + case 'C': + repeat = true; + break; + + case 'V': + if (optarg) + bdaddr_type = get_lookup_flag(bdaddr_types, + optarg); + + if (bdaddr_type == -1) { + print_lookup_values(bdaddr_types, + "List Address types:"); + exit(1); + } + + break; + + case 'W': + if (optarg) + defer_setup = atoi(optarg); + break; + + case 'M': + if (optarg) + iso_qos->out.sdu = atoi(optarg); + break; + + case 'S': + if (optarg) + iso_qos->sca = atoi(optarg); + break; + + + case 'P': + if (optarg) + iso_qos->packing = atoi(optarg); + break; + + case 'F': + if (optarg) + iso_qos->framing = atoi(optarg); + break; + + case 'I': + if (optarg) + iso_qos->out.interval = atoi(optarg); + break; + + case 'L': + if (optarg) + iso_qos->out.latency = atoi(optarg); + break; + + case 'Y': + if (optarg) + iso_qos->out.phy = atoi(optarg); + break; + + case 'R': + if (optarg) + iso_qos->out.rtn = atoi(optarg); + break; + + case 'B': + if (!optarg) + break; + + for (i = 0; i < ARRAY_SIZE(presets); i++) { + if (!strcmp(presets[i].name, optarg)) { + *iso_qos = presets[i].qos; + inout = presets[i].inout; + break; + } + } + + break; + + case 'G': + if (optarg) + iso_qos->cig = atoi(optarg); + break; + + case 'T': + if (optarg) + iso_qos->cis = atoi(optarg); + break; + + /* fall through */ + default: + usage(); + exit(1); + } + } + + if (inout) { + iso_qos->in = iso_qos->out; + } else { + /* Align interval and latency even if is unidirectional */ + iso_qos->in.interval = iso_qos->out.interval; + iso_qos->in.latency = iso_qos->out.latency; + } + + buf = malloc(data_size); + if (!buf) { + perror("Can't allocate data buffer"); + exit(1); + } + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDSTOP; + sigaction(SIGCHLD, &sa, NULL); + + openlog("isotest", LOG_PERROR | LOG_PID, LOG_LOCAL0); + + if (!(argc - optind)) { + switch (mode) { + case RECV: + do_listen(filename, recv_mode, NULL); + goto done; + + case DUMP: + do_listen(filename, dump_mode, NULL); + goto done; + default: + usage(); + exit(1); + } + } + + argc -= optind; + + for (i = 0; i < (unsigned int) argc; i++) { + pid_t pid; + + pid = fork(); + if (pid < 0) { + perror("Failed to fork new process"); + exit(1); + } + + if (!pid) + continue; + + switch (mode) { + case SEND: + send_mode(filename, argv[optind + i], i, repeat); + if (filename && strchr(filename, ',')) + filename = strchr(filename, ',') + 1; + break; + + case RECONNECT: + reconnect_mode(argv[optind + i]); + break; + + case MULTY: + multy_connect_mode(argv[optind + i]); + break; + + case CONNECT: + sk = do_connect(argv[optind + i]); + if (sk < 0) + exit(1); + dump_mode(-1, sk); + break; + + case RECV: + do_listen(filename, recv_mode, argv[optind + i]); + break; + + case DUMP: + do_listen(filename, dump_mode, argv[optind + i]); + break; + } + + break; + } + +done: + free(filename); + + syslog(LOG_INFO, "Exit"); + + closelog(); + + return 0; +} From patchwork Wed Jun 22 22:28:01 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 12891545 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BB52FC43334 for ; Wed, 22 Jun 2022 22:28:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1358655AbiFVW2X (ORCPT ); Wed, 22 Jun 2022 18:28:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59944 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1358311AbiFVW2Q (ORCPT ); Wed, 22 Jun 2022 18:28:16 -0400 Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EE38B26D6 for ; Wed, 22 Jun 2022 15:28:13 -0700 (PDT) Received: by mail-pj1-x1034.google.com with SMTP id b12-20020a17090a6acc00b001ec2b181c98so734828pjm.4 for ; Wed, 22 Jun 2022 15:28:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=pf1spZWRVVmzlCkdIoNQHP94yvKyL48SphUGzWr78oI=; b=jhxXc0Zq7LrLaA1EVaLU4OiAlj5SARtO1kd5eKLD3WdOPIMQVOIWbylHEevbjUlih0 s0iivKQ1HTE+fWMspWioD4auOyGNwbsAPwHavIaSfeO8jigiIqSp1oS8GwA/z2oFTkkS vbcxOxARxLqCnjPNqNlqdWT2Q9iFCoEGuqZRUUdX/0XhBcKOvP7ESEn0pe0jHZyr1phO NKMgTmbdRaDNZm4V9mWZWZ6wGtyIGcLOgBZ6NWnsMGPR5ZK7sLR1R+A2t3J4Pifu0dyu v9cSIhV061zHx7vfkMzGDhy8YDmTWmld+Ii1brie6LS6C3Qcs12czZPc2Vm3V/5jqazo JUvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pf1spZWRVVmzlCkdIoNQHP94yvKyL48SphUGzWr78oI=; b=aRNvG59v5m0ckxG2p5PaMTE4/302+ab441VVtyujbCzgfO157n57fe4wkrgDZD8URx VnusUBBa6wOf3y8xmdOHHSaPolpN/FR1eP3VoWFaXcbbQ4DoiSo2uotdnNYK7hzh1NAr tg8XqTmC3WdgRVgAfGOW1aCHfdLhb1v66CmAr0QN4vd5PSUSJuUMhLqXJbt5+xWHzLe5 uaG8tf5ZrEKwpoI6B0uMEArXvE/Px/16muCSKuwMLvYEGn/wKw1k2JS2Dvf2xb26prM7 oh5QFNmifviD51+8FwGQJ40mkJX7ARu0E3Kh6q97zB3j+UZ4SnEXiKUeB9lthAeV1ORg Ak9g== X-Gm-Message-State: AJIora/B2Fg8xKaPOTgZ/5yET+2lHnqe981Cf1Ul9tTH5YfD8ymPZI8B Pq0lpzp6PnbuZS631Ymw5qMz5nUYeLEKTw== X-Google-Smtp-Source: AGRyM1vKgL3RUF9NNTwCy8xgLGTZxdxynrmGZm9NKvf6jVNDOkpGZcpNnr8aii40R6GrCOfxmxSCPw== X-Received: by 2002:a17:90a:4f0a:b0:1ec:89bd:725c with SMTP id p10-20020a17090a4f0a00b001ec89bd725cmr630489pjh.172.1655936893091; Wed, 22 Jun 2022 15:28:13 -0700 (PDT) Received: from lvondent-mobl4.. (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id x10-20020a1709028eca00b0016368840c41sm11710482plo.14.2022.06.22.15.28.11 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jun 2022 15:28:12 -0700 (PDT) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [BlueZ PATCH v7 8/8] isotest: Add documentation Date: Wed, 22 Jun 2022 15:28:01 -0700 Message-Id: <20220622222801.2676431-8-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.35.3 In-Reply-To: <20220622222801.2676431-1-luiz.dentz@gmail.com> References: <20220622222801.2676431-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds isotest.rst which documents the modes and options of isotest(1) and is then converted isotest.1 manpage. --- Makefile.tools | 4 +- tools/isotest.rst | 202 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tools/isotest.rst diff --git a/Makefile.tools b/Makefile.tools index 4e5ff73b0..9412aed36 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -325,7 +325,7 @@ profiles_iap_iapd_SOURCES = profiles/iap/main.c profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la $(GLIB_LIBS) $(DBUS_LIBS) if MANPAGES -man_MANS += tools/rctest.1 tools/l2ping.1 tools/btattach.1 +man_MANS += tools/rctest.1 tools/l2ping.1 tools/btattach.1 tools/isotest.1 endif if MESH @@ -443,7 +443,7 @@ manual_pages += tools/hciattach.1 tools/hciconfig.1 \ tools/hcitool.1 tools/hcidump.1 \ tools/rfcomm.1 tools/sdptool.1 tools/ciptool.1 \ tools/rctest.1 tools/l2ping.1 tools/btattach.1 \ - tools/bdaddr.1 + tools/bdaddr.1 tools/isotest.1 if HID2HCI udevdir = $(UDEV_DIR) diff --git a/tools/isotest.rst b/tools/isotest.rst new file mode 100644 index 000000000..b2f4e4b38 --- /dev/null +++ b/tools/isotest.rst @@ -0,0 +1,202 @@ +======= +isotest +======= + +----------- +ISO testing +----------- + +:Authors: - Luiz Augusto Von Dentz +:Version: BlueZ +:Copyright: Free use of this software is granted under ther terms of the GNU + Lesser General Public Licenses (LGPL). +:Date: May 4, 2022 +:Manual section: 1 +:Manual group: Linux System Administration + +SYNOPSIS +======== + +**isotest** <*MODE*> [*OPTIONS*] [*bdaddr*] [*bdaddr1*]... + +DESCRIPTION +=========== + +**isotest(1)** is used to test Isochronous (CIS/BIS) communications on the +BlueZ stack + +MODES +===== + +-d, --dump=[FILE] Listen and dump incoming data + (CIS server/BIS broadcaster) and optionally save the + contents to *FILE*. + +-c, --reconnect Reconnect (CIS client). + +-m, --multiple Multiple connects (CIS client). + +-r, --receive=[FILE] Receive (CIS server/BIS broadcast receiver) and + optionally save the contents to *FILE*. + +-s, --send=[FILE] Connect and send (CIS client/BIS broadcaster), can + optionally use contents from *FILE*. + +-n, --silent Connect and be silent (CIS client/BIS broadcaster). + +OPTIONS +======= + +-b, --bytes= Send or Receive packet size + +-i, --index= Select the specified HCI device index. *hciNUM* is + also acceptable. + +-j, --jitter= Socket jitter buffer. + +-h, --help + +-q, --quiet Disables packet logging. + +-t, --timeout= Socket send timeout. + +-C, --continue Continuously send packets starting over in case of a + file. + +-W, --defer= Enable deferred setup. + +-M, --mtu= Socket QoS SDU. + +-S, --sca/adv-interval= + Socket QoS CIS SCA/BIS advertising interval. + +-P, --packing= Socket QoS Packing. + +.. list-table:: + :header-rows: 1 + :widths: auto + :stub-columns: 1 + :align: left + + * - *PACKING* + - Description + + * - **0x00** + - Sequential + + * - **0x01** + - Interleaved + +-F, --framing= Socket QoS Framing. + +.. list-table:: + :header-rows: 1 + :widths: auto + :stub-columns: 1 + :align: left + + * - *FRAMING* + - Description + + * - **0x00** + - Unframed + + * - **0x01** + - Framed + +-I, --interval= Socket QoS Interval. + +-L, --latency= Socket QoS Latency. + +-Y, --phy= Socket QoS PHY. + +.. list-table:: + :header-rows: 1 + :widths: auto + :stub-columns: 1 + :align: left + + * - *PHY* + - Description + + * - **0x01** + - LE 1M + + * - **0x02** + - LE 2M + + * - **0x03** + - LE Coded + +-R, --rtn= Socket QoS retransmissions. + +-B, --preset= Socket QoS preset. + +-G, --CIG/BIG= Socket QoS CIG/BIG ID. + +-T, --CIS/BIS= Socket QoS CIS/BIS ID. + +-V, --type= Socket destination address type: + +.. list-table:: + :header-rows: 1 + :widths: auto + :stub-columns: 1 + :align: left + + * - *TYPE* + - Description + + * - **le_public** + - LE Public Address + + * - **le_random** + - LE Random Address + +EXAMPLES +======== + +Unicast Central +--------------- + +.. code-block:: + + $ tools/isotest -s XX:XX:XX:XX:XX:XX + +Unicast Central connecting to 2 peers using CIG 0x01 +---------------------------------------------------- + +.. code-block:: + + $ tools/isotest -G 0x01 -s XX:XX:XX:XX:XX:XX YY:YY:YY:YY:YY:YY + +Unicast Peripheral +------------------ + +.. code-block:: + + $ tools/isotest -d + +Broadcaster +----------- + +.. code-block:: + + $ tools/isotest -s 00:00:00:00:00:00 + +Broadcast Receiver using hci1 +----------------------------- + +.. code-block:: + + $ tools/isotest -i hci1 -d XX:XX:XX:XX:XX:XX + +RESOURCES +========= + +http://www.bluez.org + +REPORTING BUGS +============== + +linux-bluetooth@vger.kernel.org