From patchwork Thu Mar 23 10:38:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185475 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 A2D07C6FD1D for ; Thu, 23 Mar 2023 10:41:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231438AbjCWKl4 (ORCPT ); Thu, 23 Mar 2023 06:41:56 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231206AbjCWKla (ORCPT ); Thu, 23 Mar 2023 06:41:30 -0400 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E71963609B for ; Thu, 23 Mar 2023 03:38:40 -0700 (PDT) Received: by mail-ed1-x52b.google.com with SMTP id w9so84434301edc.3 for ; Thu, 23 Mar 2023 03:38:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567919; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aDpTmnFPgLCUUWvLBnYjiNs0oMm44vUfcnwVasp4Px4=; b=vx9OK/COmF1WomGouYbeE8+sW2zI3CWPBGxZjXhaBijLl3DXDgZJEYQcwTnjpvOB/z /TuVEltl2vKmZGq3TD3k8ZMReb8VlyRKkNOu1v8XHZFeGL+cNgnmg/AtzjxSfykt/MFV JaPxpoXvBOmS/1bVktVoin4uegcYzFzIUqOik= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567919; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aDpTmnFPgLCUUWvLBnYjiNs0oMm44vUfcnwVasp4Px4=; b=lI2sQEiBv2GMEZcYZxAevyRUKHgkFCBwRGwol+wjFpiw7dvHiiWRPm3cZLX7Wnh64p Q0Lm4MVvBZGrv5sK42NKL/a1cQAXPTpTt8IeOk/iamdMHokCcjm6P13hHZqAaAKg7rmc RNyjPYKGn+GOCSVWE7Q6wvY0mPTcZkjUMaD5XeProuVbobAOjSmezTevJ2c9QEKYeUmM T83Npj5eZPPW+yrZmXwTWy7Dhiuhq+y2cvCj2CvMpKmWdGbuUmNxyn68RAwz71922yGf ZfNZXyHvboWSkSA1JRftEJwdH7xPVhVtnxQAKWAbu5f49AdjlSoQM2mOtHCtKJYR5Y7n FxFA== X-Gm-Message-State: AO0yUKU8UbvjkjLQID0zGlAuWaQC7pN6cxQGN1SWQXf7gtOoWdCF1teA n0vIqIwAMHdoN7SJj/0hqTeBdM3YCZI/PFJlWUg= X-Google-Smtp-Source: AK7set+/LSsdFUMO3x9R6S/hHeBCr9mqx0uIITLrGEGeI8s1VNuOKil/QT4E1pNzsvqpNINBsJrrYQ== X-Received: by 2002:a17:906:7008:b0:8b1:32b0:2a24 with SMTP id n8-20020a170906700800b008b132b02a24mr10932009ejj.47.1679567918855; Thu, 23 Mar 2023 03:38:38 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:38 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 1/9] gatt-db: Fix crash during calculating hash from ATT handles Date: Thu, 23 Mar 2023 11:38:27 +0100 Message-Id: <20230323103835.571037-2-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org It happens when next_handle is lower that discovered number of handles. Found by PTS test case: GATT/CL/GAD/BC-01-C --- src/shared/gatt-db.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c index b696fe33d..c9ffbfeed 100644 --- a/src/shared/gatt-db.c +++ b/src/shared/gatt-db.c @@ -297,6 +297,7 @@ static void handle_notify(void *data, void *user_data) struct hash_data { struct iovec *iov; uint16_t i; + size_t size; }; static void gen_hash_m(struct gatt_db_attribute *attr, void *user_data) @@ -327,7 +328,7 @@ static void gen_hash_m(struct gatt_db_attribute *attr, void *user_data) case GATT_CHARAC_AGREG_FMT_UUID: /* Allocate space for handle + type */ len = 2 + 2; - data = malloc(2 + 2 + attr->value_len); + data = malloc(2 + 2); put_le16(attr->handle, data); bt_uuid_to_le(&attr->uuid, data + 2); break; @@ -335,6 +336,13 @@ static void gen_hash_m(struct gatt_db_attribute *attr, void *user_data) return; } + if (hash->i >= hash->size) { + /* double the size of iov if we've run out of space */ + hash->iov = realloc(hash->iov, 2 * hash->size * sizeof(struct iovec)); + memset(hash->iov + hash->size, 0, hash->size * sizeof(struct iovec)); + hash->size *= 2; + } + hash->iov[hash->i].iov_base = data; hash->iov[hash->i].iov_len = len; @@ -361,9 +369,10 @@ static bool db_hash_update(void *user_data) hash.iov = new0(struct iovec, db->next_handle); hash.i = 0; + hash.size = db->next_handle; gatt_db_foreach_service(db, NULL, service_gen_hash_m, &hash); - bt_crypto_gatt_hash(db->crypto, hash.iov, db->next_handle, db->hash); + bt_crypto_gatt_hash(db->crypto, hash.iov, hash.i, db->hash); for (i = 0; i < hash.i; i++) free(hash.iov[i].iov_base); From patchwork Thu Mar 23 10:38:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185477 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 68187C7619A for ; Thu, 23 Mar 2023 10:41:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231186AbjCWKl6 (ORCPT ); Thu, 23 Mar 2023 06:41:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230410AbjCWKlh (ORCPT ); Thu, 23 Mar 2023 06:41:37 -0400 Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 50DDB3B213 for ; Thu, 23 Mar 2023 03:38:41 -0700 (PDT) Received: by mail-ed1-x52d.google.com with SMTP id b20so51427363edd.1 for ; Thu, 23 Mar 2023 03:38:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567919; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IAjtHHkZIjXfehI7jKJJnTD5plU363jFhdvoTH8SNNQ=; b=UD1Y+30oZ0mDaqMoWRiCL7UWo+td3EIS/bc8rC+uvRfMOqDPKmZVEDpVztnL+0gOMX XE2fyThsKd1WgWsb8zvEZarM6le2af/rBjwBCyAH/ximXGdzgHLad+2gc3LvmMEdwYNv LjVovedNpM4H3VmHkwwTrWaTvtRR/s9SPZaUE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567919; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=IAjtHHkZIjXfehI7jKJJnTD5plU363jFhdvoTH8SNNQ=; b=tQWCH0y0cplKTv54mPl8QWRJG6m8xkdXLjkYwBoRbfgP62XwZG3hvmm+7m1VLy5PNM tWZ/5rgx5ZbrIStM+a+3mSayRXLKVBV4754h/hU+7iOcGeJXd6lTg4TI+j1CKcQ6Y5q8 RmoWKIuZfgOGktRmC4XVFmUzxJhLlOHrP/HkMHgp05cpK0FffUI/ia5kS2zo3GCNT5rQ KIOh2AwO78joYD82guqHOxQq3HnH6t2D0W4cTksQ5bajezBqLUeO+06CHHQ0aQlAD2GJ HXSbbqEWCIPPOmfDmN0Mb/kGS0HrQ41uYppyjPbTOZK4IlyS9wedSR/84ycKZ5lWKgwW DgGg== X-Gm-Message-State: AO0yUKX7I9wwltukC/7MEtRpucpY3UmiobHJFTtvMsjnZAk4nJO3sDkZ BMfu0fOUztkZz9l9DBXkpf9f45OC6qDVLUurlSA= X-Google-Smtp-Source: AK7set9ZEbOm4QWvfW1CW499GZmIXw19gzL2RZ3N20O6HaIZjian97SBB4jGZFZldB6AHaTcbul6Tw== X-Received: by 2002:a17:906:3391:b0:932:748a:f0ea with SMTP id v17-20020a170906339100b00932748af0eamr10670022eja.63.1679567919528; Thu, 23 Mar 2023 03:38:39 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:39 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 2/9] btgatt-client: Add option to connect to ATT over BR/EDR Date: Thu, 23 Mar 2023 11:38:28 +0100 Message-Id: <20230323103835.571037-3-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 58a03bd48..cce978869 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -35,6 +35,7 @@ #include "src/shared/gatt-client.h" #define ATT_CID 4 +#define ATT_PSM 31 #define PRLOG(...) \ printf(__VA_ARGS__); print_prompt(); @@ -1406,7 +1407,7 @@ static void signal_cb(int signum, void *user_data) } } -static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, +static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, int sec) { int sock; @@ -1419,8 +1420,9 @@ static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, ba2str(src, srcaddr_str); ba2str(dst, dstaddr_str); - printf("btgatt-client: Opening L2CAP LE connection on ATT " + printf("btgatt-client: Opening L2CAP %s connection on ATT " "channel:\n\t src: %s\n\tdest: %s\n", + (dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"), srcaddr_str, dstaddr_str); } @@ -1433,7 +1435,10 @@ static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, /* Set up source address */ memset(&srcaddr, 0, sizeof(srcaddr)); srcaddr.l2_family = AF_BLUETOOTH; - srcaddr.l2_cid = htobs(ATT_CID); + if (dst_type == BDADDR_BREDR) + srcaddr.l2_psm = htobs(ATT_PSM); + else + srcaddr.l2_cid = htobs(ATT_CID); srcaddr.l2_bdaddr_type = 0; bacpy(&srcaddr.l2_bdaddr, src); @@ -1456,7 +1461,10 @@ static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, /* Set up destination address */ memset(&dstaddr, 0, sizeof(dstaddr)); dstaddr.l2_family = AF_BLUETOOTH; - dstaddr.l2_cid = htobs(ATT_CID); + if (dst_type == BDADDR_BREDR) + dstaddr.l2_psm = htobs(ATT_PSM); + else + dstaddr.l2_cid = htobs(ATT_CID); dstaddr.l2_bdaddr_type = dst_type; bacpy(&dstaddr.l2_bdaddr, dst); @@ -1482,7 +1490,7 @@ static void usage(void) printf("Options:\n" "\t-i, --index \t\tSpecify adapter index, e.g. hci0\n" "\t-d, --dest \t\tSpecify the destination address\n" - "\t-t, --type [random|public] \tSpecify the LE address type\n" + "\t-t, --type [random|public|bredr] \tSpecify the address type\n" "\t-m, --mtu \t\tThe ATT MTU to use\n" "\t-s, --security-level \tSet security level (low|medium|" "high|fips)\n" @@ -1558,9 +1566,11 @@ int main(int argc, char *argv[]) dst_type = BDADDR_LE_RANDOM; else if (strcmp(optarg, "public") == 0) dst_type = BDADDR_LE_PUBLIC; + else if (strcmp(optarg, "bredr") == 0) + dst_type = BDADDR_BREDR; else { fprintf(stderr, - "Allowed types: random, public\n"); + "Allowed types: random, public, bredr\n"); return EXIT_FAILURE; } break; @@ -1616,7 +1626,7 @@ int main(int argc, char *argv[]) mainloop_init(); - fd = l2cap_le_att_connect(&src_addr, &dst_addr, dst_type, sec); + fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec); if (fd < 0) return EXIT_FAILURE; From patchwork Thu Mar 23 10:38:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185478 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 86F9DC6FD1D for ; Thu, 23 Mar 2023 10:42:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231495AbjCWKl7 (ORCPT ); Thu, 23 Mar 2023 06:41:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33492 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231215AbjCWKli (ORCPT ); Thu, 23 Mar 2023 06:41:38 -0400 Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 502903B236 for ; Thu, 23 Mar 2023 03:38:43 -0700 (PDT) Received: by mail-ed1-x530.google.com with SMTP id b20so51427459edd.1 for ; Thu, 23 Mar 2023 03:38:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567920; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5lb9C5GpQN4g3bfCkMJYSdXclZJQqIUaDfGvJziBEQI=; b=qZo4COgzFy5TXbZEm9KL0WRz0YiTwXQyRsOdZdsZyT+A7d0PI92wuiqdDcFmMoIiiq B47/hJpRBJskD1K6CBnkIS7nBJh3eHDIuaCf/+RIcr/YBEcyTnMHxtWPhJ8BeBeg7KXx nn3d1yfo7qOyBJo/DnOhWsuz/jsSKGvu9sxUE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567920; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5lb9C5GpQN4g3bfCkMJYSdXclZJQqIUaDfGvJziBEQI=; b=C4GmCeeM944X3ZwdanBtRq5NaIdEpnq0Ku5KH6U22120pYHd1T2zoOORqoK1pjMSp8 SGVwvotM8tXht8oVHy6xKgGcmZLRxxVoT7vOTz7vq0q6o5ZD1+xiLv8k5QzN4IjCX+c/ 8gYNHNH1KFINS8qJRXealZvReQVsp+Vx44WuFMEHdEg79TewZ+wg4tIzdyj0VpaKjj/E QSvuzJNDDG4kM7+kztX25+m/HLK3uphT35lLqMXroyrnPMLsFui43IISkgPizqqHUWvO WukDxLiZVWSSDHcXXE46L4saEHAQF6x0o3gORmx7FYj6e0BaBqY2hxBG10gIHhY3nAzr 0NJw== X-Gm-Message-State: AO0yUKUphyA+QOirBcdB2dRn+Ns9AVgRdkbq66ukA4Hyo4Jph8EI0R1D o9uScr3By3n66ECOsAfqT3DmQ1H97Tmp0FVx1Rg= X-Google-Smtp-Source: AK7set/xzgjzHn+dX5Kt7KTw08xGVM2wM0rQLPhLQFDbFKxYSmxOF38ndiprRebzHArIQ5MB8vGiQQ== X-Received: by 2002:a17:907:2162:b0:92d:44ca:1137 with SMTP id rl2-20020a170907216200b0092d44ca1137mr10313652ejb.43.1679567920236; Thu, 23 Mar 2023 03:38:40 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:40 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 3/9] btgatt-client: Don't wait for discovery on read/write commands Date: Thu, 23 Mar 2023 11:38:29 +0100 Message-Id: <20230323103835.571037-4-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 45 ------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index cce978869..2a0cb5181 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -490,11 +490,6 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str) int i; char *endptr = NULL; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) { read_multiple_usage(); return; @@ -560,11 +555,6 @@ static void cmd_read_value(struct client *cli, char *cmd_str) uint16_t handle; char *endptr = NULL; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) { read_value_usage(); return; @@ -594,11 +584,6 @@ static void cmd_read_long_value(struct client *cli, char *cmd_str) uint16_t offset; char *endptr = NULL; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) { read_long_value_usage(); return; @@ -661,11 +646,6 @@ static void cmd_write_value(struct client *cli, char *cmd_str) bool without_response = false; bool signed_write = false; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { printf("Too many arguments\n"); write_value_usage(); @@ -791,11 +771,6 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) uint8_t *value = NULL; bool reliable_writes = false; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { printf("Too many arguments\n"); write_value_usage(); @@ -901,11 +876,6 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str) unsigned int length; uint8_t *value = NULL; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { printf("Too many arguments\n"); write_value_usage(); @@ -1026,11 +996,6 @@ static void cmd_write_execute(struct client *cli, char *cmd_str) unsigned int session_id; bool execute; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 514, argv, &argc)) { printf("Too many arguments\n"); write_value_usage(); @@ -1193,11 +1158,6 @@ static void cmd_set_security(struct client *cli, char *cmd_str) char *endptr = NULL; int level; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - if (!parse_args(cmd_str, 1, argv, &argc)) { printf("Too many arguments\n"); set_security_usage(); @@ -1225,11 +1185,6 @@ static void cmd_get_security(struct client *cli, char *cmd_str) { int level; - if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - level = bt_gatt_client_get_security(cli->gatt); if (level < 0) printf("Could not set sec level\n"); From patchwork Thu Mar 23 10:38:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185484 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 76621C6FD1D for ; Thu, 23 Mar 2023 10:42:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230486AbjCWKmF (ORCPT ); Thu, 23 Mar 2023 06:42:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33636 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231264AbjCWKlo (ORCPT ); Thu, 23 Mar 2023 06:41:44 -0400 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E10EC2F065 for ; Thu, 23 Mar 2023 03:38:45 -0700 (PDT) Received: by mail-ed1-x52b.google.com with SMTP id h8so84355105ede.8 for ; Thu, 23 Mar 2023 03:38:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mVU42B4Dr0BnvC5xEKXdM1x0vLNCEDJsJBUynT6eTLE=; b=Vb/f/BlTQu9/NtCC1vaXn0y7PfFNpXfxG29jrOBjRicYxnPWxZ5LpDyEy+k/RxhM/z MoYlpUmGUfaqa3FpDRzuTDW7OdeL67D4wcHbJkAUnhPcZfR2XFbjkV2AOs2uP1EPjHIG WtnN/F5ALNjZKUJhrwrWrrErt8wwZwV4ywzvI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mVU42B4Dr0BnvC5xEKXdM1x0vLNCEDJsJBUynT6eTLE=; b=N6avEMpCkq01w43COid+2jp+JgIL/bLXpV0DHkvN3DQCaoOhtPosyJVSf3RoCP2dtm ir8dEbokmYRlVUcDhAfeEa/TgL9dy4xkz1URGqCFk2bnvNQ7VQlH1EaD0gFpUR5/Uy+1 jDuSa9o38PTonwr7yQ/sMkhGA5w/1yugDJb36IT214hgYSqukz5l09IzJLsKaB5BiNg2 uUinp9TfUg0HkdlSiIUoCf6ITklTHi7Av8O+zEwlQJzwTsAGzj/Y2VcAAHXBGHCUg4yb w508h32z/oEG/1LVSbMxqybY/D0DMMG7FTEyADtRqRqb7KTm/JCY3cycKmLg5hhrdhpY s2lA== X-Gm-Message-State: AO0yUKX6r5ilw3xPmENCCH42z3JgY7sLGe/SqTAGYGSrpX9pG3h8fAhx VvpWOGJCV6bSyEqWFS7gcjwGRrXmS/2xu6XrbWg= X-Google-Smtp-Source: AK7set/+59+Hz/c5j9Klcx+0Ma90xbOMSVJ8FxncdH212IPi9ZxX/rJ9kF16iUNPhEfIGhOI8MCQsQ== X-Received: by 2002:a05:6402:1013:b0:4fb:98e2:3df8 with SMTP id c19-20020a056402101300b004fb98e23df8mr9887656edu.27.1679567921018; Thu, 23 Mar 2023 03:38:41 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:40 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 4/9] btgatt-client: Rewrite to use bt_shell Date: Thu, 23 Mar 2023 11:38:30 +0100 Message-Id: <20230323103835.571037-5-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- Makefile.tools | 2 +- src/shared/shell.h | 1 + tools/btgatt-client.c | 1087 ++++++++++++++++------------------------- 3 files changed, 412 insertions(+), 678 deletions(-) diff --git a/Makefile.tools b/Makefile.tools index df4cad065..6125e57f7 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -315,7 +315,7 @@ tools_ibeacon_LDADD = src/libshared-mainloop.la tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c tools_btgatt_client_LDADD = src/libshared-mainloop.la \ - lib/libbluetooth-internal.la + lib/libbluetooth-internal.la -lreadline tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c tools_btgatt_server_LDADD = src/libshared-mainloop.la \ diff --git a/src/shared/shell.h b/src/shared/shell.h index 87fb5c415..8793835c0 100644 --- a/src/shared/shell.h +++ b/src/shared/shell.h @@ -15,6 +15,7 @@ #define COLOR_GREEN "\001\x1B[0;92m\002" #define COLOR_YELLOW "\001\x1B[0;93m\002" #define COLOR_BLUE "\001\x1B[0;94m\002" +#define COLOR_MAGENTA "\x1B[0;95m" #define COLOR_BOLDGRAY "\001\x1B[1;30m\002" #define COLOR_BOLDWHITE "\001\x1B[1;37m\002" #define COLOR_HIGHLIGHT "\001\x1B[1;39m\002" diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 2a0cb5181..ecfe3f3f1 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -28,6 +28,7 @@ #include "lib/uuid.h" #include "src/shared/mainloop.h" +#include "src/shared/shell.h" #include "src/shared/util.h" #include "src/shared/att.h" #include "src/shared/queue.h" @@ -37,19 +38,29 @@ #define ATT_CID 4 #define ATT_PSM 31 -#define PRLOG(...) \ - printf(__VA_ARGS__); print_prompt(); - -#define COLOR_OFF "\x1B[0m" -#define COLOR_RED "\x1B[0;91m" -#define COLOR_GREEN "\x1B[0;92m" -#define COLOR_YELLOW "\x1B[0;93m" -#define COLOR_BLUE "\x1B[0;94m" -#define COLOR_MAGENTA "\x1B[0;95m" -#define COLOR_BOLDGRAY "\x1B[1;30m" -#define COLOR_BOLDWHITE "\x1B[1;37m" +#define MAX_LEN_LINE 512 +struct client *cli; static bool verbose = false; +static bool shell_running = false; + +#define print(fmt, arg...) do { \ + if (shell_running) \ + bt_shell_printf(fmt "\n", ## arg); \ + else \ + printf(fmt "\n", ## arg); \ +} while (0) + +#define error(fmt, arg...) do { \ + if (shell_running) \ + bt_shell_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \ + else \ + fprintf(stderr, COLOR_RED fmt "\n" COLOR_OFF, ## arg); \ +} while (0) + +#define append(str, fmt, arg...) do { \ + sprintf(strchr(str, '\0'), fmt, ## arg); \ +} while (0) struct client { int fd; @@ -60,10 +71,11 @@ struct client { unsigned int reliable_session_id; }; -static void print_prompt(void) +static void update_prompt(void) { - printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# "); - fflush(stdout); + char str[32]; + sprintf(str, COLOR_BLUE "[GATT client]" COLOR_OFF "# "); + bt_shell_set_prompt(str); } static const char *ecode_to_string(uint8_t ecode) @@ -116,7 +128,7 @@ static const char *ecode_to_string(uint8_t ecode) static void att_disconnect_cb(int err, void *user_data) { - printf("Device disconnected: %s\n", strerror(err)); + print("Device disconnected: %s", strerror(err)); mainloop_quit(); } @@ -125,14 +137,14 @@ static void att_debug_cb(const char *str, void *user_data) { const char *prefix = user_data; - PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str); + print(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s" COLOR_OFF, prefix, str); } static void gatt_debug_cb(const char *str, void *user_data) { const char *prefix = user_data; - PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str); + print(COLOR_GREEN "%s%s" COLOR_OFF, prefix, str); } static void ready_cb(bool success, uint8_t att_ecode, void *user_data); @@ -150,7 +162,7 @@ static void log_service_event(struct gatt_db_attribute *attr, const char *str) gatt_db_attribute_get_service_handles(attr, &start, &end); - PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str, + print("%s - UUID: %s start: 0x%04x end: 0x%04x", str, uuid_str, start, end); } @@ -170,20 +182,20 @@ static struct client *client_create(int fd, uint16_t mtu) cli = new0(struct client, 1); if (!cli) { - fprintf(stderr, "Failed to allocate memory for client\n"); + error("Failed to allocate memory for client"); return NULL; } cli->att = bt_att_new(fd, false); if (!cli->att) { - fprintf(stderr, "Failed to initialze ATT transport layer\n"); + error("Failed to initialze ATT transport layer"); bt_att_unref(cli->att); free(cli); return NULL; } if (!bt_att_set_close_on_unref(cli->att, true)) { - fprintf(stderr, "Failed to set up ATT transport layer\n"); + error("Failed to set up ATT transport layer"); bt_att_unref(cli->att); free(cli); return NULL; @@ -191,7 +203,7 @@ static struct client *client_create(int fd, uint16_t mtu) if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL, NULL)) { - fprintf(stderr, "Failed to set ATT disconnect handler\n"); + error("Failed to set ATT disconnect handler"); bt_att_unref(cli->att); free(cli); return NULL; @@ -200,7 +212,7 @@ static struct client *client_create(int fd, uint16_t mtu) cli->fd = fd; cli->db = gatt_db_new(); if (!cli->db) { - fprintf(stderr, "Failed to create GATT database\n"); + error("Failed to create GATT database"); bt_att_unref(cli->att); free(cli); return NULL; @@ -208,7 +220,7 @@ static struct client *client_create(int fd, uint16_t mtu) cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0); if (!cli->gatt) { - fprintf(stderr, "Failed to create GATT client\n"); + error("Failed to create GATT client"); gatt_db_unref(cli->db); bt_att_unref(cli->att); free(cli); @@ -225,8 +237,8 @@ static struct client *client_create(int fd, uint16_t mtu) NULL); } - bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL); - bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli, + bt_gatt_client_ready_register(cli->gatt, ready_cb, NULL, NULL); + bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, NULL, NULL); /* bt_gatt_client already holds a reference */ @@ -242,7 +254,7 @@ static void client_destroy(struct client *cli) free(cli); } -static void print_uuid(const bt_uuid_t *uuid) +static void append_uuid(char *str, const bt_uuid_t *uuid) { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_t uuid128; @@ -250,15 +262,15 @@ static void print_uuid(const bt_uuid_t *uuid) bt_uuid_to_uuid128(uuid, &uuid128); bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str)); - printf("%s\n", uuid_str); + append(str, "%s", uuid_str); } static void print_incl(struct gatt_db_attribute *attr, void *user_data) { - struct client *cli = user_data; uint16_t handle, start, end; struct gatt_db_attribute *service; bt_uuid_t uuid; + char line[MAX_LEN_LINE] = {0}; if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end)) return; @@ -269,18 +281,21 @@ static void print_incl(struct gatt_db_attribute *attr, void *user_data) gatt_db_attribute_get_service_uuid(service, &uuid); - printf("\t " COLOR_GREEN "include" COLOR_OFF " - handle: " + append(line, "\t " COLOR_GREEN "include" COLOR_OFF " - handle: " "0x%04x, - start: 0x%04x, end: 0x%04x," "uuid: ", handle, start, end); - print_uuid(&uuid); + append_uuid(line, &uuid); + print("%s", line); } static void print_desc(struct gatt_db_attribute *attr, void *user_data) { - printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF + char line[MAX_LEN_LINE] = {0}; + append(line, "\t\t " COLOR_MAGENTA "descr" COLOR_OFF " - handle: 0x%04x, uuid: ", gatt_db_attribute_get_handle(attr)); - print_uuid(gatt_db_attribute_get_type(attr)); + append_uuid(line, gatt_db_attribute_get_type(attr)); + print("%s", line); } static void print_chrc(struct gatt_db_attribute *attr, void *user_data) @@ -289,6 +304,7 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data) uint8_t properties; uint16_t ext_prop; bt_uuid_t uuid; + char line[MAX_LEN_LINE] = {0}; if (!gatt_db_attribute_get_char_data(attr, &handle, &value_handle, @@ -297,170 +313,133 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data) &uuid)) return; - printf("\t " COLOR_YELLOW "charac" COLOR_OFF + append(line, "\t " COLOR_YELLOW "charac" COLOR_OFF " - start: 0x%04x, value: 0x%04x, " "props: 0x%02x, ext_props: 0x%04x, uuid: ", handle, value_handle, properties, ext_prop); - print_uuid(&uuid); + append_uuid(line, &uuid); + print("%s", line); gatt_db_service_foreach_desc(attr, print_desc, NULL); } static void print_service(struct gatt_db_attribute *attr, void *user_data) { - struct client *cli = user_data; uint16_t start, end; bool primary; bt_uuid_t uuid; + char line[MAX_LEN_LINE] = {0}; if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary, &uuid)) return; - printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, " + append(line, COLOR_RED "service" COLOR_OFF " - start: 0x%04x, " "end: 0x%04x, type: %s, uuid: ", start, end, primary ? "primary" : "secondary"); - print_uuid(&uuid); + append_uuid(line, &uuid); + print("%s", line); - gatt_db_service_foreach_incl(attr, print_incl, cli); + gatt_db_service_foreach_incl(attr, print_incl, NULL); gatt_db_service_foreach_char(attr, print_chrc, NULL); - - printf("\n"); } static void print_services(struct client *cli) { - printf("\n"); - - gatt_db_foreach_service(cli->db, NULL, print_service, cli); + gatt_db_foreach_service(cli->db, NULL, print_service, NULL); } -static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid) +static void print_services_by_uuid(const bt_uuid_t *uuid) { - printf("\n"); - - gatt_db_foreach_service(cli->db, uuid, print_service, cli); + gatt_db_foreach_service(cli->db, uuid, print_service, NULL); } -static void print_services_by_handle(struct client *cli, uint16_t handle) +static void print_services_by_handle(uint16_t handle) { - printf("\n"); - /* TODO: Filter by handle */ gatt_db_foreach_service(cli->db, NULL, print_service, cli); } static void ready_cb(bool success, uint8_t att_ecode, void *user_data) { - struct client *cli = user_data; - if (!success) { - PRLOG("GATT discovery procedures failed - error code: 0x%02x\n", + error("GATT discovery procedures failed - error code: 0x%02x", att_ecode); return; } - PRLOG("GATT discovery procedures complete\n"); + print("GATT discovery procedures complete"); print_services(cli); - print_prompt(); } static void service_changed_cb(uint16_t start_handle, uint16_t end_handle, void *user_data) { - struct client *cli = user_data; - - printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n", + print("Service Changed handled - start: 0x%04x end: 0x%04x", start_handle, end_handle); - gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli, + gatt_db_foreach_service_in_range(cli->db, NULL, print_service, NULL, start_handle, end_handle); - print_prompt(); -} - -static void services_usage(void) -{ - printf("Usage: services [options]\nOptions:\n" - "\t -u, --uuid \tService UUID\n" - "\t -a, --handle \tService start handle\n" - "\t -h, --help\t\tShow help message\n" - "e.g.:\n" - "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n"); } -static bool parse_args(char *str, int expected_argc, char **argv, int *argc) -{ - char **ap; - - for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) { - if (**ap == '\0') - continue; - - (*argc)++; - ap++; - - if (*argc > expected_argc) - return false; - } - - return true; -} +static struct option services_options[] = { + { "uuid", 1, 0, 'u' }, + { "handle", 1, 0, 'a' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; -static void cmd_services(struct client *cli, char *cmd_str) +static void cmd_services(int argc, char **argv) { - char *argv[3]; - int argc = 0; + int opt; + bool use_uuid = false; + bt_uuid_t tmp, uuid; + uint16_t handle = 0; + char *endptr = NULL; if (!bt_gatt_client_is_ready(cli->gatt)) { - printf("GATT client not initialized\n"); - return; - } - - if (!parse_args(cmd_str, 2, argv, &argc)) { - services_usage(); - return; - } - - if (!argc) { - print_services(cli); + print("GATT client not initialized"); return; } - if (argc != 2) { - services_usage(); - return; - } - - if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) { - bt_uuid_t tmp, uuid; - - if (bt_string_to_uuid(&tmp, argv[1]) < 0) { - printf("Invalid UUID: %s\n", argv[1]); - return; - } - - bt_uuid_to_uuid128(&tmp, &uuid); - - print_services_by_uuid(cli, &uuid); - } else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) { - uint16_t handle; - char *endptr = NULL; - - handle = strtol(argv[1], &endptr, 0); - if (!endptr || *endptr != '\0') { - printf("Invalid start handle: %s\n", argv[1]); - return; + while ((opt = getopt_long(argc, argv, "u:a:", services_options, + NULL)) != -1) { + switch (opt) { + case 'u': + if (bt_string_to_uuid(&tmp, optarg) < 0) { + error("Invalid UUID: %s", optarg); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + bt_uuid_to_uuid128(&tmp, &uuid); + use_uuid = true; + break; + case 'a': + handle = strtol(optarg, &endptr, 0); + if (!endptr || *endptr != '\0') { + error("Invalid start handle: %s", optarg); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + break; + case 'h': + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_SUCCESS); + default: + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); } + } - print_services_by_handle(cli, handle); - } else - services_usage(); -} + optind = 0; -static void read_multiple_usage(void) -{ - printf("Usage: read-multiple ...\n"); + if (use_uuid) + print_services_by_uuid(&uuid); + else + print_services_by_handle(handle); } static void read_multiple_cb(bool success, uint8_t att_ecode, @@ -468,43 +447,37 @@ static void read_multiple_cb(bool success, uint8_t att_ecode, void *user_data) { int i; + char line[MAX_LEN_LINE] = {0}; if (!success) { - PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode); + error("Read multiple request failed: 0x%02x", att_ecode); return; } - printf("\nRead multiple value (%u bytes):", length); + append(line, "Read multiple value (%u bytes):", length); for (i = 0; i < length; i++) - printf("%02x ", value[i]); + append(line, "%02x ", value[i]); - PRLOG("\n"); + print("%s", line); } -static void cmd_read_multiple(struct client *cli, char *cmd_str) +static void cmd_read_multiple(int argc, char **argv) { - int argc = 0; uint16_t *value; - char *argv[512]; int i; char *endptr = NULL; - if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) { - read_multiple_usage(); - return; - } - value = malloc(sizeof(uint16_t) * argc); if (!value) { - printf("Failed to construct value\n"); + error("Failed to construct value"); return; } - for (i = 0; i < argc; i++) { + for (i = 1; i < argc; i++) { value[i] = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' || !value[i]) { - printf("Invalid value byte: %s\n", argv[i]); + error("Invalid value byte: %s", argv[i]); free(value); return; } @@ -512,133 +485,98 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str) if (!bt_gatt_client_read_multiple(cli->gatt, value, argc, read_multiple_cb, NULL, NULL)) - printf("Failed to initiate read multiple procedure\n"); + error("Failed to initiate read multiple procedure"); free(value); } -static void read_value_usage(void) -{ - printf("Usage: read-value \n"); -} - static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { int i; + char line[MAX_LEN_LINE] = {0}; if (!success) { - PRLOG("\nRead request failed: %s (0x%02x)\n", + error("Read request failed: %s (0x%02x)", ecode_to_string(att_ecode), att_ecode); return; } - printf("\nRead value"); + append(line, "Read value"); if (length == 0) { - PRLOG(": 0 bytes\n"); + print("%s: 0 bytes", line); return; } - printf(" (%u bytes): ", length); + append(line, " (%u bytes): ", length); for (i = 0; i < length; i++) - printf("%02x ", value[i]); + append(line, "%02x ", value[i]); - PRLOG("\n"); + print("%s", line); } -static void cmd_read_value(struct client *cli, char *cmd_str) +static void cmd_read_value(int argc, char **argv) { - char *argv[2]; - int argc = 0; uint16_t handle; char *endptr = NULL; - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) { - read_value_usage(); - return; - } - - handle = strtol(argv[0], &endptr, 0); + handle = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { - printf("Invalid value handle: %s\n", argv[0]); + error("Invalid value handle: %s", argv[1]); return; } if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb, NULL, NULL)) - printf("Failed to initiate read value procedure\n"); -} - -static void read_long_value_usage(void) -{ - printf("Usage: read-long-value \n"); + error("Failed to initiate read value procedure"); } -static void cmd_read_long_value(struct client *cli, char *cmd_str) +static void cmd_read_long_value(int argc, char **argv) { - char *argv[3]; - int argc = 0; uint16_t handle; uint16_t offset; char *endptr = NULL; - if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) { - read_long_value_usage(); - return; - } - - handle = strtol(argv[0], &endptr, 0); + handle = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { - printf("Invalid value handle: %s\n", argv[0]); + error("Invalid value handle: %s", argv[1]); return; } endptr = NULL; - offset = strtol(argv[1], &endptr, 0); + offset = strtol(argv[2], &endptr, 0); if (!endptr || *endptr != '\0') { - printf("Invalid offset: %s\n", argv[1]); + error("Invalid offset: %s", argv[2]); return; } if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb, NULL, NULL)) - printf("Failed to initiate read long value procedure\n"); -} - -static void write_value_usage(void) -{ - printf("Usage: write-value [options] \n" - "Options:\n" - "\t-w, --without-response\tWrite without response\n" - "\t-s, --signed-write\tSigned write command\n" - "e.g.:\n" - "\twrite-value 0x0001 00 01 00\n"); + error("Failed to initiate read long value procedure"); } static struct option write_value_options[] = { { "without-response", 0, 0, 'w' }, { "signed-write", 0, 0, 's' }, + { "help", 0, 0, 'h' }, { } }; static void write_cb(bool success, uint8_t att_ecode, void *user_data) { if (success) { - PRLOG("\nWrite successful\n"); + print("Write successful"); } else { - PRLOG("\nWrite failed: %s (0x%02x)\n", + error("Write failed: %s (0x%02x)", ecode_to_string(att_ecode), att_ecode); } } -static void cmd_write_value(struct client *cli, char *cmd_str) +static void cmd_write_value(int argc, char **argv) { int opt, i, val; - char *argvbuf[516]; - char **argv = argvbuf; - int argc = 1; uint16_t handle; char *endptr = NULL; int length; @@ -646,14 +584,6 @@ static void cmd_write_value(struct client *cli, char *cmd_str) bool without_response = false; bool signed_write = false; - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { - printf("Too many arguments\n"); - write_value_usage(); - return; - } - - optind = 0; - argv[0] = "write-value"; while ((opt = getopt_long(argc, argv, "+ws", write_value_options, NULL)) != -1) { switch (opt) { @@ -663,23 +593,24 @@ static void cmd_write_value(struct client *cli, char *cmd_str) case 's': signed_write = true; break; + case 'h': + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: - write_value_usage(); - return; + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; - - if (argc < 1) { - write_value_usage(); - return; - } + optind = 0; handle = strtol(argv[0], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { - printf("Invalid handle: %s\n", argv[0]); + error("Invalid handle: %s", argv[1]); return; } @@ -687,13 +618,13 @@ static void cmd_write_value(struct client *cli, char *cmd_str) if (length > 0) { if (length > UINT16_MAX) { - printf("Write value too long\n"); + error("Write value too long"); return; } value = malloc(length); if (!value) { - printf("Failed to construct write value\n"); + error("Failed to construct write value"); return; } @@ -701,7 +632,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str) val = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE || val < 0 || val > 255) { - printf("Invalid value byte: %s\n", + error("Invalid value byte: %s", argv[i]); goto done; } @@ -712,36 +643,27 @@ static void cmd_write_value(struct client *cli, char *cmd_str) if (without_response) { if (!bt_gatt_client_write_without_response(cli->gatt, handle, signed_write, value, length)) { - printf("Failed to initiate write without response " - "procedure\n"); + error("Failed to initiate write without response " + "procedure"); goto done; } - printf("Write command sent\n"); + print("Write command sent"); goto done; } if (!bt_gatt_client_write_value(cli->gatt, handle, value, length, write_cb, NULL, NULL)) - printf("Failed to initiate write procedure\n"); + error("Failed to initiate write procedure"); done: free(value); } -static void write_long_value_usage(void) -{ - printf("Usage: write-long-value [options] " - "\n" - "Options:\n" - "\t-r, --reliable-write\tReliable write\n" - "e.g.:\n" - "\twrite-long-value 0x0001 0 00 01 00\n"); -} - static struct option write_long_value_options[] = { { "reliable-write", 0, 0, 'r' }, + { "help", 0, 0, 'h' }, { } }; @@ -749,21 +671,18 @@ static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode, void *user_data) { if (success) { - PRLOG("Write successful\n"); + print("Write successful"); } else if (reliable_error) { - PRLOG("Reliable write not verified\n"); + error("Reliable write not verified"); } else { - PRLOG("\nWrite failed: %s (0x%02x)\n", + error("Write failed: %s (0x%02x)", ecode_to_string(att_ecode), att_ecode); } } -static void cmd_write_long_value(struct client *cli, char *cmd_str) +static void cmd_write_long_value(int argc, char **argv) { int opt, i, val; - char *argvbuf[516]; - char **argv = argvbuf; - int argc = 1; uint16_t handle; uint16_t offset; char *endptr = NULL; @@ -771,44 +690,44 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) uint8_t *value = NULL; bool reliable_writes = false; - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { - printf("Too many arguments\n"); - write_value_usage(); - return; - } - - optind = 0; - argv[0] = "write-long-value"; while ((opt = getopt_long(argc, argv, "+r", write_long_value_options, NULL)) != -1) { switch (opt) { case 'r': reliable_writes = true; break; + case 'h': + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: - write_long_value_usage(); - return; + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; + optind = 0; - if (argc < 2) { - write_long_value_usage(); + if (argc > 514) { + error("Too many arguments"); + bt_shell_usage(); + optind = 0; return; } handle = strtol(argv[0], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { - printf("Invalid handle: %s\n", argv[0]); + error("Invalid handle: %s", argv[1]); return; } endptr = NULL; offset = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || errno == ERANGE) { - printf("Invalid offset: %s\n", argv[1]); + error("Invalid offset: %s", argv[2]); return; } @@ -816,13 +735,13 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) if (length > 0) { if (length > UINT16_MAX) { - printf("Write value too long\n"); + error("Write value too long"); return; } value = malloc(length); if (!value) { - printf("Failed to construct write value\n"); + error("Failed to construct write value"); return; } @@ -830,7 +749,7 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) val = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE || val < 0 || val > 255) { - printf("Invalid value byte: %s\n", + error("Invalid value byte: %s", argv[i]); free(value); return; @@ -843,32 +762,20 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) offset, value, length, write_long_cb, NULL, NULL)) - printf("Failed to initiate long write procedure\n"); + error("Failed to initiate long write procedure"); free(value); } -static void write_prepare_usage(void) -{ - printf("Usage: write-prepare [options] " - "\n" - "Options:\n" - "\t-s, --session-id\tSession id\n" - "e.g.:\n" - "\twrite-prepare -s 1 0x0001 00 01 00\n"); -} - static struct option write_prepare_options[] = { { "session-id", 1, 0, 's' }, + { "help", 0, 0, 'h' }, { } }; -static void cmd_write_prepare(struct client *cli, char *cmd_str) +static void cmd_write_prepare(int argc, char **argv) { int opt, i, val; - char *argvbuf[516]; - char **argv = argvbuf; - int argc = 0; unsigned int id = 0; uint16_t handle; uint16_t offset; @@ -876,59 +783,50 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str) unsigned int length; uint8_t *value = NULL; - if (!parse_args(cmd_str, 514, argv + 1, &argc)) { - printf("Too many arguments\n"); - write_value_usage(); - return; - } - - /* Add command name for getopt_long */ - argc++; - argv[0] = "write-prepare"; - - optind = 0; while ((opt = getopt_long(argc, argv , "s:", write_prepare_options, NULL)) != -1) { switch (opt) { case 's': - if (!optarg) { - write_prepare_usage(); - return; - } - id = atoi(optarg); - break; + case 'h': + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_SUCCESS); default: - write_prepare_usage(); - return; + bt_shell_usage(); + optind = 0; + return bt_shell_noninteractive_quit(EXIT_FAILURE); } } argc -= optind; argv += optind; + optind = 0; - if (argc < 3) { - write_prepare_usage(); + if (argc > 514) { + error("Too many arguments"); + bt_shell_usage(); + optind = 0; return; } if (cli->reliable_session_id != id) { - printf("Session id != Ongoing session id (%u!=%u)\n", id, + error("Session id != Ongoing session id (%u!=%u)", id, cli->reliable_session_id); return; } handle = strtol(argv[0], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { - printf("Invalid handle: %s\n", argv[0]); + error("Invalid handle: %s", argv[1]); return; } endptr = NULL; offset = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || errno == ERANGE) { - printf("Invalid offset: %s\n", argv[1]); + error("Invalid offset: %s", argv[2]); return; } @@ -942,13 +840,13 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str) goto done; if (length > UINT16_MAX) { - printf("Write value too long\n"); + error("Write value too long"); return; } value = malloc(length); if (!value) { - printf("Failed to allocate memory for value\n"); + error("Failed to allocate memory for value"); return; } @@ -956,7 +854,7 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str) val = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE || val < 0 || val > 255) { - printf("Invalid value byte: %s\n", argv[i]); + error("Invalid value byte: %s", argv[i]); free(value); return; } @@ -971,64 +869,43 @@ done: write_long_cb, NULL, NULL); if (!cli->reliable_session_id) - printf("Failed to proceed prepare write\n"); + error("Failed to proceed prepare write"); else - printf("Prepare write success.\n" - "Session id: %d to be used on next write\n", + print("Prepare write success." + "Session id: %d to be used on next write", cli->reliable_session_id); free(value); } -static void write_execute_usage(void) -{ - printf("Usage: write-execute \n" - "e.g.:\n" - "\twrite-execute 1 0\n"); -} - -static void cmd_write_execute(struct client *cli, char *cmd_str) +static void cmd_write_execute(int argc, char **argv) { - char *argvbuf[516]; - char **argv = argvbuf; - int argc = 0; char *endptr = NULL; unsigned int session_id; bool execute; - if (!parse_args(cmd_str, 514, argv, &argc)) { - printf("Too many arguments\n"); - write_value_usage(); - return; - } - - if (argc < 2) { - write_execute_usage(); - return; - } - - session_id = strtol(argv[0], &endptr, 0); + session_id = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0') { - printf("Invalid session id: %s\n", argv[0]); + error("Invalid session id: %s", argv[1]); return; } if (session_id != cli->reliable_session_id) { - printf("Invalid session id: %u != %u\n", session_id, + error("Invalid session id: %u != %u", session_id, cli->reliable_session_id); return; } - execute = !!strtol(argv[1], &endptr, 0); + execute = !!strtol(argv[2], &endptr, 0); if (!endptr || *endptr != '\0') { - printf("Invalid execute: %s\n", argv[1]); + error("Invalid execute: %s", argv[2]); return; } if (execute) { if (!bt_gatt_client_write_execute(cli->gatt, session_id, write_cb, NULL, NULL)) - printf("Failed to proceed write execute\n"); + error("Failed to proceed write execute"); } else { bt_gatt_client_cancel(cli->gatt, session_id); } @@ -1036,46 +913,40 @@ static void cmd_write_execute(struct client *cli, char *cmd_str) cli->reliable_session_id = 0; } -static void register_notify_usage(void) -{ - printf("Usage: register-notify \n"); -} - static void notify_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { int i; + char line[MAX_LEN_LINE] = {0}; - printf("\n\tHandle Value Not/Ind: 0x%04x - ", value_handle); + append(line, "\tHandle Value Not/Ind: 0x%04x - ", value_handle); if (length == 0) { - PRLOG("(0 bytes)\n"); + print("%s(0 bytes)", line); return; } - printf("(%u bytes): ", length); + append(line, "(%u bytes): ", length); for (i = 0; i < length; i++) - printf("%02x ", value[i]); + append(line, "%02x ", value[i]); - PRLOG("\n"); + print("%s", line); } static void register_notify_cb(uint16_t att_ecode, void *user_data) { if (att_ecode) { - PRLOG("Failed to register notify handler " - "- error code: 0x%02x\n", att_ecode); + error("Failed to register notify handler " + "- error code: 0x%02x", att_ecode); return; } - PRLOG("Registered notify handler!\n"); + print("Registered notify handler!"); } -static void cmd_register_notify(struct client *cli, char *cmd_str) +static void cmd_register_notify(int argc, char **argv) { - char *argv[2]; - int argc = 0; uint16_t value_handle; unsigned int id; char *endptr = NULL; @@ -1085,14 +956,9 @@ static void cmd_register_notify(struct client *cli, char *cmd_str) return; } - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) { - register_notify_usage(); - return; - } - - value_handle = strtol(argv[0], &endptr, 0); + value_handle = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || !value_handle) { - printf("Invalid value handle: %s\n", argv[0]); + error("Invalid value handle: %s", argv[1]); return; } @@ -1100,22 +966,15 @@ static void cmd_register_notify(struct client *cli, char *cmd_str) register_notify_cb, notify_cb, NULL, NULL); if (!id) { - printf("Failed to register notify handler\n"); + error("Failed to register notify handler"); return; } - printf("Registering notify handler with id: %u\n", id); + print("Registering notify handler with id: %u", id); } -static void unregister_notify_usage(void) +static void cmd_unregister_notify(int argc, char **argv) { - printf("Usage: unregister-notify \n"); -} - -static void cmd_unregister_notify(struct client *cli, char *cmd_str) -{ - char *argv[2]; - int argc = 0; unsigned int id; char *endptr = NULL; @@ -1124,72 +983,46 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str) return; } - if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) { - unregister_notify_usage(); - return; - } - - id = strtol(argv[0], &endptr, 0); + id = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || !id) { - printf("Invalid notify id: %s\n", argv[0]); + error("Invalid notify id: %s", argv[1]); return; } if (!bt_gatt_client_unregister_notify(cli->gatt, id)) { - printf("Failed to unregister notify handler with id: %u\n", id); + error("Failed to unregister notify handler with id: %u", id); return; } - printf("Unregistered notify handler with id: %u\n", id); -} - -static void set_security_usage(void) -{ - printf("Usage: set-security \n" - "level: 1-3\n" - "e.g.:\n" - "\tset-security 2\n"); + print("Unregistered notify handler with id: %u", id); } -static void cmd_set_security(struct client *cli, char *cmd_str) +static void cmd_set_security(int argc, char **argv) { - char *argv[2]; - int argc = 0; char *endptr = NULL; int level; - if (!parse_args(cmd_str, 1, argv, &argc)) { - printf("Too many arguments\n"); - set_security_usage(); - return; - } - - if (argc < 1) { - set_security_usage(); - return; - } - - level = strtol(argv[0], &endptr, 0); + level = strtol(argv[1], &endptr, 0); if (!endptr || *endptr != '\0' || level < 1 || level > 3) { - printf("Invalid level: %s\n", argv[0]); + error("Invalid level: %s", argv[1]); return; } if (!bt_gatt_client_set_security(cli->gatt, level)) - printf("Could not set sec level\n"); + error("Could not set sec level"); else - printf("Setting security level %d success\n", level); + print("Setting security level %d success", level); } -static void cmd_get_security(struct client *cli, char *cmd_str) +static void cmd_get_security(int argc, char **argv) { int level; level = bt_gatt_client_get_security(cli->gatt); if (level < 0) - printf("Could not set sec level\n"); + error("Could not get sec level"); else - printf("Security level: %u\n", level); + print("Security level: %u", level); } static bool convert_sign_key(char *optarg, uint8_t key[16]) @@ -1197,7 +1030,7 @@ static bool convert_sign_key(char *optarg, uint8_t key[16]) int i; if (strlen(optarg) != 32) { - printf("sign-key length is invalid\n"); + error("sign-key length is invalid"); return false; } @@ -1209,14 +1042,6 @@ static bool convert_sign_key(char *optarg, uint8_t key[16]) return true; } -static void set_sign_key_usage(void) -{ - printf("Usage: set-sign-key [options]\nOptions:\n" - "\t -c, --sign-key \tCSRK\n" - "e.g.:\n" - "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n"); -} - static bool local_counter(uint32_t *sign_cnt, void *user_data) { static uint32_t cnt = 0; @@ -1226,141 +1051,74 @@ static bool local_counter(uint32_t *sign_cnt, void *user_data) return true; } -static void cmd_set_sign_key(struct client *cli, char *cmd_str) +static void cmd_set_sign_key(int argc, char **argv) { - char *argv[3]; - int argc = 0; uint8_t key[16]; memset(key, 0, 16); - if (!parse_args(cmd_str, 2, argv, &argc)) { - set_sign_key_usage(); - return; - } - - if (argc != 2) { - set_sign_key_usage(); - return; - } - - if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) { - if (convert_sign_key(argv[1], key)) + if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--sign-key")) { + if (convert_sign_key(argv[2], key)) bt_att_set_local_key(cli->att, key, local_counter, cli); - } else - set_sign_key_usage(); -} - -static void cmd_help(struct client *cli, char *cmd_str); - -typedef void (*command_func_t)(struct client *cli, char *cmd_str); - -static struct { - char *cmd; - command_func_t func; - char *doc; -} command[] = { - { "help", cmd_help, "\tDisplay help message" }, - { "services", cmd_services, "\tShow discovered services" }, - { "read-value", cmd_read_value, - "\tRead a characteristic or descriptor value" }, - { "read-long-value", cmd_read_long_value, - "\tRead a long characteristic or desctriptor value" }, - { "read-multiple", cmd_read_multiple, "\tRead Multiple" }, - { "write-value", cmd_write_value, - "\tWrite a characteristic or descriptor value" }, - { "write-long-value", cmd_write_long_value, - "Write long characteristic or descriptor value" }, - { "write-prepare", cmd_write_prepare, - "\tWrite prepare characteristic or descriptor value" }, - { "write-execute", cmd_write_execute, - "\tExecute already prepared write" }, - { "register-notify", cmd_register_notify, - "\tSubscribe to not/ind from a characteristic" }, - { "unregister-notify", cmd_unregister_notify, - "Unregister a not/ind session"}, - { "set-security", cmd_set_security, - "\tSet security level on le connection"}, - { "get-security", cmd_get_security, - "\tGet security level on le connection"}, - { "set-sign-key", cmd_set_sign_key, - "\tSet signing key for signed write command"}, - { } -}; - -static void cmd_help(struct client *cli, char *cmd_str) -{ - int i; - - printf("Commands:\n"); - for (i = 0; command[i].cmd; i++) - printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc); -} - -static void prompt_read_cb(int fd, uint32_t events, void *user_data) -{ - ssize_t read; - size_t len = 0; - char *line = NULL; - char *cmd = NULL, *args; - struct client *cli = user_data; - int i; - - if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) { - mainloop_quit(); - return; - } - - read = getline(&line, &len, stdin); - if (read < 0) { - free(line); - return; - } - - if (read <= 1) { - cmd_help(cli, NULL); - print_prompt(); - free(line); - return; - } - - line[read-1] = '\0'; - args = line; - - while ((cmd = strsep(&args, " \t"))) - if (*cmd != '\0') - break; - - if (!cmd) - goto failed; - - for (i = 0; command[i].cmd; i++) { - if (strcmp(command[i].cmd, cmd) == 0) - break; + } else { + bt_shell_usage(); + optind = 0; } - - if (command[i].cmd) - command[i].func(cli, args); - else - fprintf(stderr, "Unknown command: %s\n", line); - -failed: - print_prompt(); - - free(line); } -static void signal_cb(int signum, void *user_data) -{ - switch (signum) { - case SIGINT: - case SIGTERM: - mainloop_quit(); - break; - default: - break; - } -} +static const struct bt_shell_menu main_menu = { + .name = "main", + .entries = { + { "services", "[options...]", cmd_services, + "Show discovered services\n" + "Options:\n" + "\t -u, --uuid \tService UUID\n" + "\t -a, --handle \tService start handle\n" + "e.g.:\n" + "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009" + }, + { "read-value", "", + cmd_read_value, "Read a characteristic or descriptor value" }, + { "read-long-value", " ", + cmd_read_long_value, "Read a long characteristic or desctriptor value" }, + { "read-multiple", "", + cmd_read_multiple, "Read Multiple" }, + { "write-value", " [-w|-s] ", + cmd_write_value, "Write a characteristic or descriptor value\n" + "Options:\n" + "\t-w, --without-response\tWrite without response\n" + "\t-s, --signed-write\tSigned write command\n" + "e.g.:\n" + "\twrite-value 0x0001 00 01 00" + }, + { "write-long-value", "[-r] ", + cmd_write_long_value, "Write long characteristic or descriptor value\n" + "Options:\n" + "\t-r, --reliable-write\tReliable write\n" + "e.g.:\n" + "\twrite-long-value 0x0001 0 00 01 00" + }, + { "write-prepare", " [options...] ", + cmd_write_prepare, "Write prepare characteristic or descriptor value\n" + "Options:\n" + "\t-s, --session-id\tSession id\n" + "e.g.:\n" + "\twrite-prepare -s 1 0x0001 00 01 00" + }, + { "write-execute", " ", + cmd_write_execute, "Execute already prepared write" }, + { "register-notify", "", + cmd_register_notify, "Subscribe to not/ind from a characteristic" }, + { "unregister-notify", "", + cmd_unregister_notify, "Unregister a not/ind session"}, + { "set-security", "", + cmd_set_security, "Set security level on connection"}, + { "get-security", NULL, + cmd_get_security, "Get security level on connection"}, + { "set-sign-key", "", + cmd_set_sign_key, "Set signing key for signed write command"}, + {} }, +}; static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, int sec) @@ -1375,15 +1133,15 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, ba2str(src, srcaddr_str); ba2str(dst, dstaddr_str); - printf("btgatt-client: Opening L2CAP %s connection on ATT " - "channel:\n\t src: %s\n\tdest: %s\n", + print("btgatt-client: Opening L2CAP %s connection on ATT " + "channel:\n\t src: %s\n\tdest: %s", (dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"), srcaddr_str, dstaddr_str); } sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { - perror("Failed to create L2CAP socket"); + error("Failed to create L2CAP socket"); return -1; } @@ -1398,7 +1156,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, bacpy(&srcaddr.l2_bdaddr, src); if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) { - perror("Failed to bind L2CAP socket"); + error("Failed to bind L2CAP socket"); close(sock); return -1; } @@ -1408,7 +1166,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, btsec.level = sec; if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) { - fprintf(stderr, "Failed to set L2CAP security level\n"); + error("Failed to set L2CAP security level"); close(sock); return -1; } @@ -1423,164 +1181,144 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, dstaddr.l2_bdaddr_type = dst_type; bacpy(&dstaddr.l2_bdaddr, dst); - printf("Connecting to device..."); + print("Connecting to device..."); fflush(stdout); if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) { - perror(" Failed to connect"); + error("Failed to connect"); close(sock); return -1; } - printf(" Done\n"); + print("Done"); return sock; } -static void usage(void) -{ - printf("btgatt-client\n"); - printf("Usage:\n\tbtgatt-client [options]\n"); - - printf("Options:\n" - "\t-i, --index \t\tSpecify adapter index, e.g. hci0\n" - "\t-d, --dest \t\tSpecify the destination address\n" - "\t-t, --type [random|public|bredr] \tSpecify the address type\n" - "\t-m, --mtu \t\tThe ATT MTU to use\n" - "\t-s, --security-level \tSet security level (low|medium|" - "high|fips)\n" - "\t-v, --verbose\t\t\tEnable extra logging\n" - "\t-h, --help\t\t\tDisplay help\n"); -} - static struct option main_options[] = { - { "index", 1, 0, 'i' }, - { "dest", 1, 0, 'd' }, - { "type", 1, 0, 't' }, - { "mtu", 1, 0, 'm' }, - { "security-level", 1, 0, 's' }, - { "verbose", 0, 0, 'v' }, - { "help", 0, 0, 'h' }, + { "index", required_argument, NULL, 'i' }, + { "dst-addr", required_argument, NULL, 'd' }, + { "type", required_argument, NULL, 'T' }, + { "mtu", required_argument, NULL, 'M' }, + { "sec-level", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'V' }, { } }; +static const char *index_option; +static const char *dst_addr_option; +static const char *type_option; +static const char *mtu_option; +static const char *security_level_option; +static const char *verbose_option; + +static const char **optargs[] = { + &index_option, + &dst_addr_option, + &type_option, + &mtu_option, + &security_level_option, + &verbose_option, +}; + +static const char *help[] = { + "Specify adapter index, e.g. hci0", + "Specify the destination address", + "Specify the address type (random|public|bredr)", + "The ATT MTU to use", + "Set security level (low|medium|high|fips)", + "Enable extra logging" +}; + +static const struct bt_shell_opt opt = { + .options = main_options, + .optno = sizeof(main_options) / sizeof(struct option), + .optstr = "i:d:T:M:s:V", + .optarg = optargs, + .help = help, +}; + int main(int argc, char *argv[]) { - int opt; int sec = BT_SECURITY_LOW; uint16_t mtu = 0; uint8_t dst_type = BDADDR_LE_PUBLIC; - bool dst_addr_given = false; bdaddr_t src_addr, dst_addr; int dev_id = -1; int fd; - struct client *cli; - - while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:", - main_options, NULL)) != -1) { - switch (opt) { - case 'h': - usage(); - return EXIT_SUCCESS; - case 'v': - verbose = true; - break; - case 's': - if (strcmp(optarg, "low") == 0) - sec = BT_SECURITY_LOW; - else if (strcmp(optarg, "medium") == 0) - sec = BT_SECURITY_MEDIUM; - else if (strcmp(optarg, "high") == 0) - sec = BT_SECURITY_HIGH; - else if (strcmp(optarg, "fips") == 0) - sec = BT_SECURITY_FIPS; - else { - fprintf(stderr, "Invalid security level\n"); - return EXIT_FAILURE; - } - break; - case 'm': { - int arg; - - arg = atoi(optarg); - if (arg <= 0) { - fprintf(stderr, "Invalid MTU: %d\n", arg); - return EXIT_FAILURE; - } - - if (arg > UINT16_MAX) { - fprintf(stderr, "MTU too large: %d\n", arg); - return EXIT_FAILURE; - } - - mtu = (uint16_t)arg; - break; + int status; + + bt_shell_init(argc, argv, &opt); + bt_shell_set_menu(&main_menu); + + if (verbose_option) + verbose = true; + if (security_level_option) { + if (strcmp(security_level_option, "low") == 0) + sec = BT_SECURITY_LOW; + else if (strcmp(security_level_option, "medium") == 0) + sec = BT_SECURITY_MEDIUM; + else if (strcmp(security_level_option, "high") == 0) + sec = BT_SECURITY_HIGH; + else if (strcmp(security_level_option, "fips") == 0) + sec = BT_SECURITY_FIPS; + else { + error("Invalid security level"); + return EXIT_FAILURE; } - case 't': - if (strcmp(optarg, "random") == 0) - dst_type = BDADDR_LE_RANDOM; - else if (strcmp(optarg, "public") == 0) - dst_type = BDADDR_LE_PUBLIC; - else if (strcmp(optarg, "bredr") == 0) - dst_type = BDADDR_BREDR; - else { - fprintf(stderr, - "Allowed types: random, public, bredr\n"); - return EXIT_FAILURE; - } - break; - case 'd': - if (str2ba(optarg, &dst_addr) < 0) { - fprintf(stderr, "Invalid remote address: %s\n", - optarg); - return EXIT_FAILURE; - } + } + if (mtu_option) { + int arg; - dst_addr_given = true; - break; + arg = atoi(mtu_option); + if (arg <= 0) { + error("Invalid MTU: %d", arg); + return EXIT_FAILURE; + } - case 'i': - dev_id = hci_devid(optarg); - if (dev_id < 0) { - perror("Invalid adapter"); - return EXIT_FAILURE; - } + if (arg > UINT16_MAX) { + error("MTU too large: %d", arg); + return EXIT_FAILURE; + } - break; - default: - fprintf(stderr, "Invalid option: %c\n", opt); + mtu = (uint16_t)arg; + } + if (type_option) { + if (strcmp(type_option, "random") == 0) + dst_type = BDADDR_LE_RANDOM; + else if (strcmp(type_option, "public") == 0) + dst_type = BDADDR_LE_PUBLIC; + else if (strcmp(type_option, "bredr") == 0) + dst_type = BDADDR_BREDR; + else { + error("Allowed types: random, public, bredr"); return EXIT_FAILURE; } } - - if (!argc) { - usage(); - return EXIT_SUCCESS; + if (dst_addr_option) { + if (str2ba(dst_addr_option, &dst_addr) < 0) { + error("Invalid remote address: %s", dst_addr_option); + return EXIT_FAILURE; + } + } else { + error("Destination address required!"); + return EXIT_FAILURE; } - - argc -= optind; - argv += optind; - optind = 0; - - if (argc) { - usage(); - return EXIT_SUCCESS; + if (index_option) { + dev_id = hci_devid(index_option); + if (dev_id < 0) { + error("Invalid adapter"); + return EXIT_FAILURE; + } } if (dev_id == -1) bacpy(&src_addr, BDADDR_ANY); else if (hci_devba(dev_id, &src_addr) < 0) { - perror("Adapter not available"); + error("Adapter not available"); return EXIT_FAILURE; } - if (!dst_addr_given) { - fprintf(stderr, "Destination address required!\n"); - return EXIT_FAILURE; - } - - mainloop_init(); - fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec); if (fd < 0) return EXIT_FAILURE; @@ -1591,20 +1329,15 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (mainloop_add_fd(fileno(stdin), - EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR, - prompt_read_cb, cli, NULL) < 0) { - fprintf(stderr, "Failed to initialize console\n"); - return EXIT_FAILURE; - } - - print_prompt(); - - mainloop_run_with_signal(signal_cb, NULL); + bt_shell_attach(fileno(stdin)); + update_prompt(); + shell_running = true; + status = bt_shell_run(); + shell_running = false; - printf("\n\nShutting down...\n"); + print("Shutting down..."); client_destroy(cli); - return EXIT_SUCCESS; + return status; } From patchwork Thu Mar 23 10:38:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185480 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 CFA5CC6FD1C for ; Thu, 23 Mar 2023 10:42:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231583AbjCWKmC (ORCPT ); Thu, 23 Mar 2023 06:42:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231256AbjCWKln (ORCPT ); Thu, 23 Mar 2023 06:41:43 -0400 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3CAAA35EF7 for ; Thu, 23 Mar 2023 03:38:47 -0700 (PDT) Received: by mail-ed1-x52c.google.com with SMTP id ew6so21390407edb.7 for ; Thu, 23 Mar 2023 03:38:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=px4mnZzR8LoF4aom5eqQZBlh4g7OwxQ32xwWn2plVSU=; b=oN2OfDjfIEE+A2TiyruPnnuFibRItdbnOybgOzwJX8jpQq+lkRM7V7VXf5k35JIHpy 1i4a586zCp//b9NVxirmCNZgGyI4aWa9w0W5xrDCfQhLkSMXq6FpUh4qPhEWdxH2KC3u GZzv9DGNNK1Z4adDoV/PLrf0dgp6unq7Bi9Vo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=px4mnZzR8LoF4aom5eqQZBlh4g7OwxQ32xwWn2plVSU=; b=JuLxkzHJOeU+UOFYn3Go++zJBG59nvu2KimO7KVAoePR34YvVp0ZVgWK+OWKvr9BEN h8XPpDh6AaowXQCGU0xKaJoMw+lZ5ky1o+w8AbvqttCC6n6T/P3NblP5hlMXrheJhxX7 NCgK3Zg4jLwL+75ngmvyOudy8K86iACOWRtsHLRixinBGvXEZNeqQhaLhqNmTN5Q4PSD Kknkt+HnGBGC3f8Fz6/jrNTPGr8/SHz+b8+QIHCRs0WHWVbWoyhtDudNkVrsEknL0APx cOKeV6+DeLZHksxo/qITuLpQ8IutjD2LdvADdj+NBia8+WKMuGhoKAc+4q3rw33qkccR SnfA== X-Gm-Message-State: AO0yUKULGzSei8xLMbz+3in6oyGNXw379096AZPmHwPWDkY2SAvGOc9B omJMIMSR+DJoOyTmHAiQ7Npcd8OPSfWi0mqqrvU= X-Google-Smtp-Source: AK7set9Acckpq6Dik1PHf72AaNp2XARPzM/J37I148tb9tMagBIQmKY/JfZNxokC50c3xbGWM/y/LA== X-Received: by 2002:a17:906:f0c1:b0:931:6641:9897 with SMTP id dk1-20020a170906f0c100b0093166419897mr9847937ejb.44.1679567921442; Thu, 23 Mar 2023 03:38:41 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:41 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 5/9] btgatt-client: Implement disconnect and connect commands Date: Thu, 23 Mar 2023 11:38:31 +0100 Message-Id: <20230323103835.571037-6-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 137 +++++++++++++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 34 deletions(-) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index ecfe3f3f1..152e6ee70 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -40,9 +40,13 @@ #define MAX_LEN_LINE 512 -struct client *cli; +struct client *cli = NULL; static bool verbose = false; static bool shell_running = false; +static uint8_t dst_type = BDADDR_LE_PUBLIC; +static bdaddr_t src_addr, dst_addr; +static int security_level = BT_SECURITY_LOW; +static uint16_t mtu = 0; #define print(fmt, arg...) do { \ if (shell_running) \ @@ -71,10 +75,22 @@ struct client { unsigned int reliable_session_id; }; +static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, + int sec); + static void update_prompt(void) { - char str[32]; - sprintf(str, COLOR_BLUE "[GATT client]" COLOR_OFF "# "); + char str[64], addr[18], type[3]; + if (!bacmp(&dst_addr, BDADDR_ANY)) + sprintf(str, "[GATT client]# "); + else { + ba2str(&dst_addr, addr); + sprintf(type, dst_type == BDADDR_BREDR ? "BR" : "LE"); + if (cli) + sprintf(str, COLOR_BLUE "[%s][%s]" COLOR_OFF "# ", addr, type); + else + sprintf(str, "[%s][%s]# ", addr, type); + } bt_shell_set_prompt(str); } @@ -126,11 +142,22 @@ static const char *ecode_to_string(uint8_t ecode) } } +static void client_destroy() +{ + if (cli) { + bt_gatt_client_unref(cli->gatt); + bt_att_unref(cli->att); + free(cli); + cli = NULL; + } +} + static void att_disconnect_cb(int err, void *user_data) { print("Device disconnected: %s", strerror(err)); - mainloop_quit(); + client_destroy(); + update_prompt(); } static void att_debug_cb(const char *str, void *user_data) @@ -247,13 +274,6 @@ static struct client *client_create(int fd, uint16_t mtu) return cli; } -static void client_destroy(struct client *cli) -{ - bt_gatt_client_unref(cli->gatt); - bt_att_unref(cli->att); - free(cli); -} - static void append_uuid(char *str, const bt_uuid_t *uuid) { char uuid_str[MAX_LEN_UUID_STR]; @@ -1066,6 +1086,66 @@ static void cmd_set_sign_key(int argc, char **argv) } } +static void connect_device() +{ + int fd; + fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, security_level); + if (fd < 0) { + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + cli = client_create(fd, mtu); + if (!cli) { + close(fd); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } +} + +static void cmd_connect(int argc, char **argv) +{ + char addr[18]; + + if (cli) { + error("Already connected"); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + if (argc > 1) { + if (str2ba(argv[1], &addr) < 0) { + error("Invalid remote address: %s", argv[1]); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + bacpy(&dst_addr, &addr); + } + if (argc > 2) { + if (strcmp(argv[2], "random") == 0) + dst_type = BDADDR_LE_RANDOM; + else if (strcmp(argv[2], "public") == 0) + dst_type = BDADDR_LE_PUBLIC; + else if (strcmp(argv[2], "bredr") == 0) + dst_type = BDADDR_BREDR; + else { + error("Allowed types: random, public, bredr"); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + } + if (!bacmp(&dst_addr, BDADDR_ANY)) { + error("Destination address required!"); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + connect_device(); + update_prompt(); +} + +static void cmd_disconnect(int argc, char **argv) +{ + if (!cli) { + error("Already disconnected"); + return bt_shell_noninteractive_quit(EXIT_FAILURE); + } + close(cli->fd); + client_destroy(); + update_prompt(); +} + static const struct bt_shell_menu main_menu = { .name = "main", .entries = { @@ -1117,6 +1197,10 @@ static const struct bt_shell_menu main_menu = { cmd_get_security, "Get security level on connection"}, { "set-sign-key", "", cmd_set_sign_key, "Set signing key for signed write command"}, + { "connect", "[address] [public|random|bredr]", + cmd_connect, "Connect to device" }, + { "disconnect", NULL, + cmd_disconnect, "Disconnect from connected device" }, {} }, }; @@ -1240,12 +1324,7 @@ static const struct bt_shell_opt opt = { int main(int argc, char *argv[]) { - int sec = BT_SECURITY_LOW; - uint16_t mtu = 0; - uint8_t dst_type = BDADDR_LE_PUBLIC; - bdaddr_t src_addr, dst_addr; int dev_id = -1; - int fd; int status; bt_shell_init(argc, argv, &opt); @@ -1255,13 +1334,13 @@ int main(int argc, char *argv[]) verbose = true; if (security_level_option) { if (strcmp(security_level_option, "low") == 0) - sec = BT_SECURITY_LOW; + security_level = BT_SECURITY_LOW; else if (strcmp(security_level_option, "medium") == 0) - sec = BT_SECURITY_MEDIUM; + security_level = BT_SECURITY_MEDIUM; else if (strcmp(security_level_option, "high") == 0) - sec = BT_SECURITY_HIGH; + security_level = BT_SECURITY_HIGH; else if (strcmp(security_level_option, "fips") == 0) - sec = BT_SECURITY_FIPS; + security_level = BT_SECURITY_FIPS; else { error("Invalid security level"); return EXIT_FAILURE; @@ -1301,8 +1380,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } } else { - error("Destination address required!"); - return EXIT_FAILURE; + bacpy(&dst_addr, BDADDR_ANY); } if (index_option) { dev_id = hci_devid(index_option); @@ -1319,15 +1397,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec); - if (fd < 0) - return EXIT_FAILURE; - - cli = client_create(fd, mtu); - if (!cli) { - close(fd); - return EXIT_FAILURE; - } + if (bacmp(&dst_addr, BDADDR_ANY)) + connect_device(); bt_shell_attach(fileno(stdin)); update_prompt(); @@ -1335,9 +1406,7 @@ int main(int argc, char *argv[]) status = bt_shell_run(); shell_running = false; - print("Shutting down..."); - - client_destroy(cli); + client_destroy(); return status; } From patchwork Thu Mar 23 10:38:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185479 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 8DC9FC76195 for ; Thu, 23 Mar 2023 10:42:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231252AbjCWKmB (ORCPT ); Thu, 23 Mar 2023 06:42:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33576 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229993AbjCWKln (ORCPT ); Thu, 23 Mar 2023 06:41:43 -0400 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C7A163B877 for ; Thu, 23 Mar 2023 03:38:47 -0700 (PDT) Received: by mail-ed1-x52a.google.com with SMTP id w9so84434817edc.3 for ; Thu, 23 Mar 2023 03:38:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567922; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rSw/9A8e25iNNUIuoQ6diM5sPI659B2koAayn4VceGY=; b=REOa65XR2YHqqQCs+cLPW7MhKLJCGArzuJDQDSLjECJIHQZd+iYZeWHxI+E+UqQ8yX mSb8e4PoKpVw4xzqyy/RFTI1BODS8xketkhiRPnVtbNqU9M2OX1K49jmoHPlowNzWkux E2QjuRFU3KiLXLei0HSCWOHNYC69g5N1+AcCo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567922; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rSw/9A8e25iNNUIuoQ6diM5sPI659B2koAayn4VceGY=; b=egfJL8NwEPuPp3enn/RwIxCfgZ7Si8e7bnQDHbK8LU1eM2eelYHIVG52iLogq7zb4q iuiUY75QFm6f9Z4Z7cxm3u47poinXp752Yd25oCbJrg7DyzOOuxfKipW5Qqz2jdjM/7K 7NtqEjmDwHTsbA68DlymaPzq4o7WbL6UXZ3ptRh3O1/+8whpDeXSRDk3v+2bbsz1/fuO ODkyWx+N4plrsvkzoD2H4FIYM001OAvmZNN493iysF0AoTRtwaTl7iQ0IRkOxt4LANZj rKIiW1wnS+xi90g41baej7YX99WADmGUaBDhHCuAtjH+D8bjC725aqsUSscBU2+mn7wo pF+Q== X-Gm-Message-State: AO0yUKULtW0z9miQ0JcUv0tCj6y1mdhYAHblfw5G9gCy/KDcEedRMmR5 vi4EHpoMqjv/6CCe4AoU8e15K3jJNIjk7T5L1pM= X-Google-Smtp-Source: AK7set90vrKTX/ikTOmoV2IDjrzE5CE39SXTuOQgBhJo+3Xovbeqwuvq3RDwe0yttj/wWV6EipnkbQ== X-Received: by 2002:a17:907:6e17:b0:902:874:9c31 with SMTP id sd23-20020a1709076e1700b0090208749c31mr5985518ejc.35.1679567922303; Thu, 23 Mar 2023 03:38:42 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:41 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 6/9] btgatt-client: Implement read by type Date: Thu, 23 Mar 2023 11:38:32 +0100 Message-Id: <20230323103835.571037-7-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 152e6ee70..0681074f9 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -34,6 +34,7 @@ #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" +#include "src/shared/gatt-helpers.h" #define ATT_CID 4 #define ATT_PSM 31 @@ -510,6 +511,75 @@ static void cmd_read_multiple(int argc, char **argv) free(value); } +void read_by_type_cb(bool success, uint8_t att_ecode, + struct bt_gatt_result *result, + void *user_data) +{ + const uint8_t *value; + uint16_t length, handle; + struct bt_gatt_iter iter; + char line[MAX_LEN_LINE]; + int i; + + if (!success) { + error("Read by type request failed: %s (0x%02x)", + ecode_to_string(att_ecode), att_ecode); + return; + } + + bt_gatt_iter_init(&iter, result); + while (bt_gatt_iter_next_read_by_type(&iter, &handle, &length, &value)) { + line[0] = '\0'; + append(line, "\tValue handle 0x%04x", handle); + + if (length == 0) { + print("%s: 0 bytes", line); + return; + } + + append(line, " (%u bytes): ", length); + + for (i = 0; i < length; i++) + append(line, "%02x ", value[i]); + + print("%s", line); + } +} + +static void cmd_read_by_type(int argc, char **argv) +{ + bt_uuid_t uuid; + uint16_t start_handle = 0x0001, end_handle = 0xFFFF; + char *endptr = NULL; + + if (bt_string_to_uuid(&uuid, argv[1]) < 0) { + error("Invalid UUID: %s", optarg); + return; + } + if (argc > 2) { + start_handle = strtol(argv[2], &endptr, 0); + if (!endptr || *endptr != '\0' || !start_handle) { + error("Invalid start_handle : %s", argv[1]); + return; + } + } + if (argc > 3) { + end_handle = strtol(argv[3], &endptr, 0); + if (!endptr || *endptr != '\0' || !end_handle) { + error("Invalid end_handle : %s", argv[1]); + return; + } + } + if (start_handle > end_handle) { + error("start_handle cannot by larger than end_handle"); + return; + } + + if (!bt_gatt_read_by_type(cli->att, start_handle, end_handle, + &uuid, read_by_type_cb, NULL, NULL)) + error("Failed to initiate read value procedure"); +} + static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { @@ -1163,6 +1233,8 @@ static const struct bt_shell_menu main_menu = { cmd_read_long_value, "Read a long characteristic or desctriptor value" }, { "read-multiple", "", cmd_read_multiple, "Read Multiple" }, + { "read-by-type", " [start_handle] [end_handle]", + cmd_read_by_type, "Read a value by UUID" }, { "write-value", " [-w|-s] ", cmd_write_value, "Write a characteristic or descriptor value\n" "Options:\n" From patchwork Thu Mar 23 10:38:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185482 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 7D02DC76195 for ; Thu, 23 Mar 2023 10:42:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230357AbjCWKmE (ORCPT ); Thu, 23 Mar 2023 06:42:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33642 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230366AbjCWKlo (ORCPT ); Thu, 23 Mar 2023 06:41:44 -0400 Received: from mail-ed1-x536.google.com (mail-ed1-x536.google.com [IPv6:2a00:1450:4864:20::536]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AFB0D36095 for ; Thu, 23 Mar 2023 03:38:49 -0700 (PDT) Received: by mail-ed1-x536.google.com with SMTP id x3so84329110edb.10 for ; Thu, 23 Mar 2023 03:38:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=joJx6HhFwXvoHrCIHTeLaTTrK8R0VHAtHSi1abn1ul8=; b=npW8+DGdWsIlmJyLIVyBFlMzhsaZ61rW+nOlmLqaSDeS+Ol35dwcJADPazMGe57Ebw SIgRyzxO8WFKRDwr80qPEO6xJ1kZORdNyfuAVF4VST2lMX5zETAeg2dfpmcszcvklHuD SvW2SQsODTbHMEvWBnJUvHuk/c7q8gvuz9WFs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=joJx6HhFwXvoHrCIHTeLaTTrK8R0VHAtHSi1abn1ul8=; b=QGH+Ze/jAwYrfbnJahfMMm7taoQTDYGsu7NcKDnpj/Ks+DL8IWVOPoq/+vMSSXHv2Y HIa1bT4u51dPghqcVJsjY62exPqfO3igEEqFxyIS6hOFRLTmjsQNq2xPGR9IOS3hkGk7 qbK28Wnk0joxNPbvZkuWV72EE7hzRhC4B+ZKun79j1wxyycSjjS8p2PHNhuJX5j09Gb4 0kaB7Oj5kNVWhUq8vDGB4Oxb9UiW09/YYQOk5JtJpOX+u0kVLgu5Hd92VGf+nX7Zcoz4 s4hOG/CDMVL1rivLO/7AGGX4SAxvmc3iw451UpD3JkJOgXlcPdvY6+prFKQxyeCS4XMI q+mg== X-Gm-Message-State: AO0yUKU7pE1i42F4YrM//vK9QLOFnWyXlC/EdgOD5IwlkhQ+9IOMoqN4 Qnsc1mp550Gt0+5AO8/imbDFcpsm0HzRG2S4VPg= X-Google-Smtp-Source: AK7set9mYG5dOm82ezU05Bf76zo9Ey2oeP1y+r5wk0YIl59A9kUTWld4IksREtyVrFXpH9RpJ/8N4w== X-Received: by 2002:a17:907:987c:b0:92f:de0d:104b with SMTP id ko28-20020a170907987c00b0092fde0d104bmr10613520ejc.9.1679567923146; Thu, 23 Mar 2023 03:38:43 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:42 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 7/9] btgatt-client: Implement common read bytes procedure Date: Thu, 23 Mar 2023 11:38:33 +0100 Message-Id: <20230323103835.571037-8-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 130 ++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 88 deletions(-) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 0681074f9..c4311d067 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -664,6 +664,40 @@ static void write_cb(bool success, uint8_t att_ecode, void *user_data) } } +static uint8_t *read_bytes(char **argv, int *length) +{ + int i, byte; + uint8_t *value; + char *endptr = NULL; + + if (*length <= 0) { + error("Nothing to write"); + return NULL; + } + if (*length > BT_ATT_MAX_VALUE_LEN) { + error("Write value too long"); + return NULL; + } + + value = malloc(*length); + if (!value) { + error("Failed to construct write value"); + return NULL; + } + + for (i = 0; i < *length; i++) { + byte = strtol(argv[i], &endptr, 0); + if (endptr == argv[i] || *endptr != '\0' + || errno == ERANGE || byte < 0 || byte > 255) { + error("Invalid value byte: %s", argv[i]); + free(value); + return NULL; + } + value[i] = byte; + } + return value; +} + static void cmd_write_value(int argc, char **argv) { int opt, i, val; @@ -705,30 +739,9 @@ static void cmd_write_value(int argc, char **argv) } length = argc - 1; - - if (length > 0) { - if (length > UINT16_MAX) { - error("Write value too long"); - return; - } - - value = malloc(length); - if (!value) { - error("Failed to construct write value"); - return; - } - - for (i = 1; i < argc; i++) { - val = strtol(argv[i], &endptr, 0); - if (endptr == argv[i] || *endptr != '\0' - || errno == ERANGE || val < 0 || val > 255) { - error("Invalid value byte: %s", - argv[i]); - goto done; - } - value[i-1] = val; - } - } + value = read_bytes(argv + 1, &length); + if (!value) + return; if (without_response) { if (!bt_gatt_client_write_without_response(cli->gatt, handle, @@ -801,13 +814,6 @@ static void cmd_write_long_value(int argc, char **argv) argv += optind; optind = 0; - if (argc > 514) { - error("Too many arguments"); - bt_shell_usage(); - optind = 0; - return; - } - handle = strtol(argv[0], &endptr, 0); if (!endptr || *endptr != '\0' || !handle) { error("Invalid handle: %s", argv[1]); @@ -822,31 +828,9 @@ static void cmd_write_long_value(int argc, char **argv) } length = argc - 2; - - if (length > 0) { - if (length > UINT16_MAX) { - error("Write value too long"); - return; - } - - value = malloc(length); - if (!value) { - error("Failed to construct write value"); - return; - } - - for (i = 2; i < argc; i++) { - val = strtol(argv[i], &endptr, 0); - if (endptr == argv[i] || *endptr != '\0' - || errno == ERANGE || val < 0 || val > 255) { - error("Invalid value byte: %s", - argv[i]); - free(value); - return; - } - value[i-2] = val; - } - } + value = read_bytes(argv + 2, &length); + if (!value) + return; if (!bt_gatt_client_write_long_value(cli->gatt, reliable_writes, handle, offset, value, length, @@ -894,13 +878,6 @@ static void cmd_write_prepare(int argc, char **argv) argv += optind; optind = 0; - if (argc > 514) { - error("Too many arguments"); - bt_shell_usage(); - optind = 0; - return; - } - if (cli->reliable_session_id != id) { error("Session id != Ongoing session id (%u!=%u)", id, cli->reliable_session_id); @@ -925,33 +902,10 @@ static void cmd_write_prepare(int argc, char **argv) * length */ length = argc - 2; - - if (length == 0) - goto done; - - if (length > UINT16_MAX) { - error("Write value too long"); - return; - } - - value = malloc(length); - if (!value) { - error("Failed to allocate memory for value"); + value = read_bytes(argv + 2, &length); + if (!value) return; - } - - for (i = 2; i < argc; i++) { - val = strtol(argv[i], &endptr, 0); - if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE - || val < 0 || val > 255) { - error("Invalid value byte: %s", argv[i]); - free(value); - return; - } - value[i-2] = val; - } -done: cli->reliable_session_id = bt_gatt_client_prepare_write(cli->gatt, id, handle, offset, From patchwork Thu Mar 23 10:38:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185481 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 95F04C7619A for ; Thu, 23 Mar 2023 10:42:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231591AbjCWKmD (ORCPT ); Thu, 23 Mar 2023 06:42:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44250 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229734AbjCWKlo (ORCPT ); Thu, 23 Mar 2023 06:41:44 -0400 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C8FC121A3E for ; Thu, 23 Mar 2023 03:38:50 -0700 (PDT) Received: by mail-ed1-x52a.google.com with SMTP id h8so84355438ede.8 for ; Thu, 23 Mar 2023 03:38:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uryBnvx4D/luK4MX+zSL+EQy1EEM2ITqQU+jTpoum7E=; b=H7XFPMHH2wrEU7ilGkWv1Ff2r9Pgr3vh2CEmrSmM4DJdayy3trH9um8yLDS0lBpo4F zoyHLwjPj4XC2/w5QUxbfOThkYI1hYvZATV1Ig/yMIpc1HpAOujz3D00vATRs6cXxL3M ae2zzvHORhq1v6t2Jcth48O2XsClv8q4IYvyA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uryBnvx4D/luK4MX+zSL+EQy1EEM2ITqQU+jTpoum7E=; b=VQAzVXyza5yHSjwDOhB5P4za/6Br8AK5faoZ6yCQJ7R6Se+55P0Mbk3CmBBLaITF80 WPXlVN6OV1DBmB2SWr2g8z7IIy/fxiVtH68QzHRcA8JZ9Y9HJJbjcz41uODPE/vTwXRY 4cCFrUzJT0dyxh/pd8wZR9AWOSwe8dloaCBeAyIkr0TyLzpGlhgJwET7THLu5qxmrec0 22kaiYHUP/4b3CJvgAm/sj/bWCMWarAu3l4sHbCvGlxSTuKHAx18nKLdftAKC9VQ4I25 vg8hMVO3aMLMaaNDK+GNq2tUS880JgO7YZiiqTrd3lcPjVH27RnO2X9TkVJn70LCqFYO 3dEQ== X-Gm-Message-State: AO0yUKWNtPSgyRM8gpZH4tAnnDmStRsfkleFyODMsq8ItnwtdBHx7K5D G30guEuXeCj9IhN51LfhL3t8puJq+b0IcUHoJrI= X-Google-Smtp-Source: AK7set+0RR9r9kwxioQHwJvDXHR6ljJkvnkciN2Grpwseey6lcvFc5beRCBUrUWqCg6KJ7qQiSqn2A== X-Received: by 2002:a17:906:d117:b0:925:8bc0:b19 with SMTP id b23-20020a170906d11700b009258bc00b19mr4779887ejz.20.1679567923820; Thu, 23 Mar 2023 03:38:43 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:43 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 8/9] btgatt-client: Add 'bytes ' option Date: Thu, 23 Mar 2023 11:38:34 +0100 Message-Id: <20230323103835.571037-9-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org This command is available for write requests command and it can specify bytes count with specific value. e.g. command: write-value 0x0001 bytes 0 100 will write 100 zero bytes to handle 0x0001 --- tools/btgatt-client.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index c4311d067..b7a23ac9b 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -669,6 +669,23 @@ static uint8_t *read_bytes(char **argv, int *length) int i, byte; uint8_t *value; char *endptr = NULL; + bool use_bytes = false; + + if (*length == 3 && !strcmp(argv[i], "bytes")) { + byte = strtol(argv[i+1], &endptr, 0); + if (endptr == argv[i+1] || *endptr != '\0' + || errno == ERANGE || byte < 0 || byte > 255) { + error("Invalid bytes value: %s", argv[i+1]); + return NULL; + } + *length = strtol(argv[i+2], &endptr, 0); + if (endptr == argv[i+2] || *endptr != '\0' + || errno == ERANGE) { + error("Invalid bytes count: %s", argv[i+2]); + return NULL; + } + use_bytes = true; + } if (*length <= 0) { error("Nothing to write"); @@ -685,6 +702,11 @@ static uint8_t *read_bytes(char **argv, int *length) return NULL; } + if (use_bytes) { + memset(value, byte, *length); + return value; + } + for (i = 0; i < *length; i++) { byte = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' @@ -1194,22 +1216,28 @@ static const struct bt_shell_menu main_menu = { "Options:\n" "\t-w, --without-response\tWrite without response\n" "\t-s, --signed-write\tSigned write command\n" + "\tbytes \tWrite specified number of bytes with value\n" "e.g.:\n" "\twrite-value 0x0001 00 01 00" + "\twrite-value 0x0001 bytes 0 100" }, { "write-long-value", "[-r] ", cmd_write_long_value, "Write long characteristic or descriptor value\n" "Options:\n" "\t-r, --reliable-write\tReliable write\n" + "\tbytes \tWrite specified number of bytes with value\n" "e.g.:\n" "\twrite-long-value 0x0001 0 00 01 00" + "\twrite-long-value 0x0001 0 bytes 0 100" }, { "write-prepare", " [options...] ", cmd_write_prepare, "Write prepare characteristic or descriptor value\n" "Options:\n" "\t-s, --session-id\tSession id\n" + "\tbytes \tWrite specified number of bytes with value\n" "e.g.:\n" "\twrite-prepare -s 1 0x0001 00 01 00" + "\twrite-prepare -s 1 0x0001 bytes 0 100" }, { "write-execute", " ", cmd_write_execute, "Execute already prepared write" }, From patchwork Thu Mar 23 10:38:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Mikuda X-Patchwork-Id: 13185483 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 77A00C761AF for ; Thu, 23 Mar 2023 10:42:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231264AbjCWKmG (ORCPT ); Thu, 23 Mar 2023 06:42:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33650 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230409AbjCWKlp (ORCPT ); Thu, 23 Mar 2023 06:41:45 -0400 Received: from mail-ed1-x536.google.com (mail-ed1-x536.google.com [IPv6:2a00:1450:4864:20::536]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F03B8367E1 for ; Thu, 23 Mar 2023 03:38:51 -0700 (PDT) Received: by mail-ed1-x536.google.com with SMTP id eh3so84363845edb.11 for ; Thu, 23 Mar 2023 03:38:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=streamunlimited.com; s=google; t=1679567924; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=qTiNn5ElAqSIAm568k6v0bazI47/CuUlIG5hKkmFQbE=; b=PPnXU9P0ZKmXfN17axYUb+xWhSjqik3AHPGvbND3tcf5hr0mjIBZnuH5YDVKPC4GeT 6mqie7P6JnDduouaHDusHW8eakANhIVk8X3Yzjih+uFmkh06yaVBF4BpYFRVa2957kXr MbVrpV1jIRpENZg+fybLA8Q4Ep1jrtsUSARzc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679567924; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qTiNn5ElAqSIAm568k6v0bazI47/CuUlIG5hKkmFQbE=; b=Rch8sSJfWi3EC72Kk63zww5bb2Hstropzqejaa5sMT/7OkCqCFxIL8zwyoiAa+UE34 d3GXbDT0jbmk+g2YCcrZEuupocYLrgFnUVqZuwvxlrOyldDrcM5j9O6I0DVTYx0r891y Ty5POMsF/wUGVKl5k/UUmjAqaVuf4dDopDxwdGXyDZApk2Hfmg+Xkqe++9s87fA4B2Gl 9rcXulth+5TlJ+XZ5lbdnNEJZ46/GLDFXqOkWJMos9cldu0JbQBCfDy19n5RdxnMmiV/ mk4Hb/eV46HBR/bHfERGJHB8gqfQuz7rg+nZurR6Wte1KSun1LxK/c8JgxEHUj66XgfK 39qg== X-Gm-Message-State: AO0yUKU2sLhVsDAFMow9obFcv8MXePt2YmFjSehq6djJN1fa3IQ7Cypb 6DmpgAmRIozfMmyV/P3OJvaPT+6KFd+D6WUwNco= X-Google-Smtp-Source: AK7set+9xomHPozhhDGz6bkEKNxGqxkxVJ3r+6E7SUWBzWKFGFdYO3/z8AJgUv7pfFwqUUgncCCPyg== X-Received: by 2002:a17:906:a4b:b0:92b:2389:46bd with SMTP id x11-20020a1709060a4b00b0092b238946bdmr10239655ejf.21.1679567924329; Thu, 23 Mar 2023 03:38:44 -0700 (PDT) Received: from smi-ubuntu.sueba ([2a01:390:0:101:4f8c:7da4:48b2:8bd2]) by smtp.gmail.com with ESMTPSA id bv1-20020a170906b1c100b00939e76a0cabsm3513208ejb.111.2023.03.23.03.38.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 03:38:44 -0700 (PDT) From: Simon Mikuda To: linux-bluetooth@vger.kernel.org Cc: Simon Mikuda Subject: [PATCH BlueZ 9/9] btgatt-client: Implement filter service by start handle Date: Thu, 23 Mar 2023 11:38:35 +0100 Message-Id: <20230323103835.571037-10-simon.mikuda@streamunlimited.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> References: <20230323103835.571037-1-simon.mikuda@streamunlimited.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org --- tools/btgatt-client.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index b7a23ac9b..629063d5c 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -377,8 +377,13 @@ static void print_services_by_uuid(const bt_uuid_t *uuid) static void print_services_by_handle(uint16_t handle) { - /* TODO: Filter by handle */ - gatt_db_foreach_service(cli->db, NULL, print_service, cli); + uint16_t start = 0x0001, end = 0xFFFF; + if (handle) { + start = handle; + end = handle; + } + gatt_db_foreach_service_in_range(cli->db, NULL, print_service, NULL, + start, end); } static void ready_cb(bool success, uint8_t att_ecode, void *user_data)