From patchwork Fri Feb 28 23:46:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413591 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A3FA314B7 for ; Fri, 28 Feb 2020 23:47:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 77FAF246B6 for ; Fri, 28 Feb 2020 23:47:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JAE41cfz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726589AbgB1XrG (ORCPT ); Fri, 28 Feb 2020 18:47:06 -0500 Received: from mail-pj1-f45.google.com ([209.85.216.45]:33922 "EHLO mail-pj1-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726525AbgB1XrF (ORCPT ); Fri, 28 Feb 2020 18:47:05 -0500 Received: by mail-pj1-f45.google.com with SMTP id f2so4702733pjq.1 for ; Fri, 28 Feb 2020 15:47:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=a4mt3tuBEtrcFQaO04FkgRzkU5NWsbNKqrXQXndd3Lo=; b=JAE41cfzDEddUoF89z01cmKJCa1ICMR9riB04GVa9TLtSknXNHkpIhyyppzRG53vzz oDDlUJSbi00CAGUrQYi6DafximPtDrQ1fWeMxJK8vCs4CY8jk8qHkt8J4XWnaJOUu251 8GtmkHypZ1exTEl3lYL9KVrymahP1MJI2ePCtuIXsOAcxZfeBB2wgNUPk+wAqRb9uztF BHwqk6++kEmceWxEyDaGrxTF2kFefVKSExvVLIf0RK63Qd7QRLDc7xClGDrmaUCWw9aq 3kam9VAO5+FAoO5VjIiEAhLyXuyaBSbg6Tjt8T4jIg9hl5yPoYew9qdMFDduvaaq1zx0 WNSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=a4mt3tuBEtrcFQaO04FkgRzkU5NWsbNKqrXQXndd3Lo=; b=HbGTZ4yrGL0yPwPbwcBfuZfiDz0++fZeHgM2k1y55RYmj6cmfS0cxITModReyGvh5j JAFQ9dVNCFKF82N7BRKtYQbVrfesRVwIDBod7Ymoa0vQajO+3NxV8XpyPc7Ca4qJoSw9 mK4msGW74E/AIfqCC2MOjibRspowaFcCv+IyMLPASxNdw/6pf6neIW/3IF2ijNJrTVhA aCam9+Bi+kp9MapQEEkHScS5YbZ9Wc5Ya/wD4BohEyy6S1OJHe/5GznQkYTWDZqPlk6U 2FGn0q8SlnJyW1VKeR+VTwwaHPG67shQE4wlZontk5siw6ZaKSv1x8b6DPSnX2eEghcA c4SA== X-Gm-Message-State: APjAAAVOH2dOk50cmmjkjoWIcB7GkGyyWRjxT1jxxWap9ou8jqctC9Pk GY5FIXX+cwrMTg2hFome8ppxVUaRAkk= X-Google-Smtp-Source: APXvYqwblYvciv7f7kHmDiW0jcNlt2mK1qRYN2plv6ZAr9Rs3xy9LrI88P8X2YIIRAdtYLuJZPMc4Q== X-Received: by 2002:a17:90a:17e5:: with SMTP id q92mr7484160pja.28.1582933624183; Fri, 28 Feb 2020 15:47:04 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.03 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:03 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 01/12] lib: Add definitions for Enhanced Credits Based Mode Date: Fri, 28 Feb 2020 15:46:50 -0800 Message-Id: <20200228234701.14614-2-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz --- lib/bluetooth.h | 2 ++ lib/l2cap.h | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/bluetooth.h b/lib/bluetooth.h index d14217eac..47521d50e 100644 --- a/lib/bluetooth.h +++ b/lib/bluetooth.h @@ -141,6 +141,8 @@ struct bt_voice { #define BT_PHY_LE_CODED_TX 0x00002000 #define BT_PHY_LE_CODED_RX 0x00004000 +#define BT_MODE 15 + /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ diff --git a/lib/l2cap.h b/lib/l2cap.h index 5ce94c4ee..f9ceb2f33 100644 --- a/lib/l2cap.h +++ b/lib/l2cap.h @@ -197,6 +197,7 @@ typedef struct { #define L2CAP_MODE_FLOWCTL 0x02 #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 +#define L2CAP_MODE_EXT_FLOWCTL 0x81 #define L2CAP_SERVTYPE_NOTRAFFIC 0x00 #define L2CAP_SERVTYPE_BESTEFFORT 0x01 From patchwork Fri Feb 28 23:46:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413595 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9114117E0 for ; Fri, 28 Feb 2020 23:47:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 70533246BA for ; Fri, 28 Feb 2020 23:47:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CVarktRL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726658AbgB1XrH (ORCPT ); Fri, 28 Feb 2020 18:47:07 -0500 Received: from mail-pl1-f172.google.com ([209.85.214.172]:44861 "EHLO mail-pl1-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726621AbgB1XrG (ORCPT ); Fri, 28 Feb 2020 18:47:06 -0500 Received: by mail-pl1-f172.google.com with SMTP id d9so1827783plo.11 for ; Fri, 28 Feb 2020 15:47:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=XGWE9lkXskeRh9mJU7EOBpH5ngFpikJQY3KMiOnIcZA=; b=CVarktRLphIVsaupESJaDJPI+GeH5pN9S1eJJtuTaUIwhvvdjuPBOnshRQkp8Wt79B 3GRDcnAeq4E2wn2duzDr482x9iwR37Ak0oaDuOa8WxQ8in+8QqLcSlBsLQqPRIUtzfJ/ 8NYQB9luotpofWUyZUfN1Um/A34Ba0zC1b7cwlKmzzj2amf1hcO1ug/OnEdsjmur9f/5 d8WgjtNnDMqu3uzu++Y/AfE6tXK/BdH59mbBnOBIhJJeFWDaSsevFlPxi4AvDy0UIWT7 ih+RsQjXzlZzyCUTpSITkypXIkHVdv/+R75POcwJ4/UHqFgNSXArZlYv0CMfylc14FoS ViEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=XGWE9lkXskeRh9mJU7EOBpH5ngFpikJQY3KMiOnIcZA=; b=Co+AKcbDL/GhPGlgfrxQ2mTHthILgIO9Rahm1qlgvfsoQOpQ5Cx/RtCjbhwL6WQHtk uQwyAy0EUn0MAwpYDYbVgqbuRDDmVaNpjuK5lLvkNS1BSf6k94RyvQe18wfuywT8wnGU 5dzgNlbTmpiKTRldxN5QmNg4lUB7E/RcY2QEQ3Oi9HGFcZVSXeCJkYh8rmDHsmJa+646 gawLa9YTtXvkFLFHqTuMJUqNDrworCPABzdOuMi/+jQFTAVatrIefZBFXfagDbBWuCAz m2xoVRXx1wNzSCbeHvhr2bEKh52p6XuLzjRE1vT1y8AwBSYk/5gAW5qD7tJUG9WdXfwj mr/w== X-Gm-Message-State: APjAAAWv8Bc4XVIWpy0kbvtlUpgFodWsUKrcHTJ6WHKZUi8LZC438IfC GnNaUdbOnwFFKOsdHrr0HOe6CbAEge0= X-Google-Smtp-Source: APXvYqzxouOEocRjtAd84wyirrhsFWVMy42AjkdaOacpoeWN117HE9Az+aByaDJvxb/M/J7bDOJD/g== X-Received: by 2002:a17:902:c113:: with SMTP id 19mr6390408pli.138.1582933625315; Fri, 28 Feb 2020 15:47:05 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.04 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:04 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 02/12] btio: Add mode to for Enhanced Credit Mode Date: Fri, 28 Feb 2020 15:46:51 -0800 Message-Id: <20200228234701.14614-3-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds BT_IO_MODE_ECRED which directly maps to L2CAP_MODE_ECRED. --- btio/btio.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ btio/btio.h | 3 ++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/btio/btio.c b/btio/btio.c index db37b99da..4c84da0ee 100644 --- a/btio/btio.c +++ b/btio/btio.c @@ -630,18 +630,34 @@ static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err) return TRUE; } +static gboolean set_le_mode(int sock, uint8_t mode, GError **err) +{ + if (setsockopt(sock, SOL_BLUETOOTH, BT_MODE, &mode, + sizeof(mode)) < 0) { + ERROR_FAILED(err, "setsockopt(BT_MODE)", errno); + return FALSE; + } + + return TRUE; +} + static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level, uint16_t imtu, uint16_t omtu, uint8_t mode, int master, int flushable, uint32_t priority, GError **err) { if (imtu || omtu || mode) { - gboolean ret; + gboolean ret = FALSE; if (src_type == BDADDR_BREDR) ret = set_l2opts(sock, imtu, omtu, mode, err); - else - ret = set_le_imtu(sock, imtu, err); + else { + if (imtu) + ret = set_le_imtu(sock, imtu, err); + + if (ret && mode) + ret = set_le_mode(sock, mode, err); + } if (!ret) return ret; @@ -980,6 +996,30 @@ static int get_phy(int sock, uint32_t *phy) return 0; } +static int get_le_imtu(int sock, uint16_t *mtu) +{ + socklen_t len; + + len = sizeof(*mtu); + + if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, mtu, &len) < 0) + return -errno; + + return 0; +} + +static int get_le_mode(int sock, uint8_t *mode) +{ + socklen_t len; + + len = sizeof(*mode); + + if (getsockopt(sock, SOL_BLUETOOTH, BT_MODE, mode, &len) < 0) + return -errno; + + return 0; +} + static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, va_list args) { @@ -999,10 +1039,11 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1, memset(&l2o, 0, sizeof(l2o)); if (src.l2_bdaddr_type != BDADDR_BREDR) { - len = sizeof(l2o.imtu); - if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, - &l2o.imtu, &len) == 0) + if (get_le_imtu(sock, &l2o.imtu) == 0) { + /* Older kernels may not support BT_MODE */ + get_le_mode(sock, &l2o.mode); goto parse_opts; + } /* Non-LE CoC enabled kernels will return one of these * in which case we need to fall back to L2CAP_OPTIONS. diff --git a/btio/btio.h b/btio/btio.h index 41a017acb..5ebfb85c6 100644 --- a/btio/btio.h +++ b/btio/btio.h @@ -71,7 +71,8 @@ typedef enum { BT_IO_MODE_RETRANS, BT_IO_MODE_FLOWCTL, BT_IO_MODE_ERTM, - BT_IO_MODE_STREAMING + BT_IO_MODE_STREAMING, + BT_IO_MODE_EXT_FLOWCTL = 0x81 } BtIOMode; typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data); From patchwork Fri Feb 28 23:46:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413597 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A55E414B7 for ; Fri, 28 Feb 2020 23:47:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 80A8E246B6 for ; Fri, 28 Feb 2020 23:47:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aqnxoDJj" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726688AbgB1XrI (ORCPT ); Fri, 28 Feb 2020 18:47:08 -0500 Received: from mail-pf1-f194.google.com ([209.85.210.194]:37928 "EHLO mail-pf1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726621AbgB1XrH (ORCPT ); Fri, 28 Feb 2020 18:47:07 -0500 Received: by mail-pf1-f194.google.com with SMTP id x185so2499080pfc.5 for ; Fri, 28 Feb 2020 15:47:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=FXwvTnQykoPpkweMgQAgCKJtBtzeHaiXypRA5dAMTdI=; b=aqnxoDJjH15ML2P2FXmR6AliXPmUtllDOyfQEq9aIkvm0Wi69baU+MPXeMPguA3wYV OrZ8WJvb3bAsJ0GH4NKDx29yskNQqEY8gIia85rFeXX2VMJLtopwHL1PUnfuq7jQMVdm QoIb6MIvptd2gaXxc6PRE6pHD9mEwBmJnbJNUuR2Ssw499GPr/lde7WMO5qQqnhawPWi 1lRknvP3hYWRh/WgCPFwTukIqDr+k7C7CqsBtRy1FKBZgxbIlzMaSPGwzWF8X0BATMlT PFSq2UMtLmD53B1iT0ZKzGQo+s2GQjtnTVPRp/pAq4ajc2V5NkhvNEjZ/NTpoIPum3qd D1xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FXwvTnQykoPpkweMgQAgCKJtBtzeHaiXypRA5dAMTdI=; b=MyDWxzRvRMT9RNxpTjmQKxXpf8NAvPEG/DV90QQOxOhJ5ibcG7zo3NE32dneeTbkZU nI/onh1yOZFEiaATYvrXatQ/ZPOJN/iOfxyTy7eFaQE86mNLiX3BKCQKBtpaY2IHtHLF t8d482pO7Ervn1jOgnxs+qFUDywsbYFTe/L7ZAuPuMQkfFTpjHbg3OFcOBVZI5Gl72zK RuSk2RolVi1o/n8FVpwGCexrtN6T6kTVgRr/l4yr1y78a9EeymkSeWuSVq//4RNPTehA KZJ0b/KNJ5jaGlrJCJ3Vd5458gw9Jm19bG5WO5yVEshgV8+g1LsOsdiyTbGJktNK6NW3 qdBg== X-Gm-Message-State: APjAAAULGE6SUHWOm98iKBNCSVwv6CDye0ZjYSqT15SwVIsMLakQzo6k mkiQU6+BtU5eyq9Rhg9AVsa3F4w+VoA= X-Google-Smtp-Source: APXvYqwAi+0VqygDjpA6CCRD2qsD6Kjlf7e1k1VikdoaIRHDd3O00D+C2iahrxmGG5A8v4vEzg/d4g== X-Received: by 2002:a62:b604:: with SMTP id j4mr6735453pff.93.1582933626463; Fri, 28 Feb 2020 15:47:06 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.05 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:05 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 03/12] l2test: Add support for L2CAP_EXT_FLOWCTL_MODE Date: Fri, 28 Feb 2020 15:46:52 -0800 Message-Id: <20200228234701.14614-4-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This enables using l2test to connect or listen with L2CAP_EXT_FLOWCTL_MODE. --- tools/l2test.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/l2test.c b/tools/l2test.c index 8c6e08646..ee3637699 100644 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -150,6 +150,7 @@ static struct lookup_table l2cap_modes[] = { */ { "ertm", L2CAP_MODE_ERTM }, { "streaming", L2CAP_MODE_STREAMING }, + { "ext-flowctl",L2CAP_MODE_EXT_FLOWCTL }, { 0 } }; @@ -283,7 +284,7 @@ static int getopts(int sk, struct l2cap_options *opts, bool connected) memset(opts, 0, sizeof(*opts)); - if (bdaddr_type == BDADDR_BREDR) { + if (bdaddr_type == BDADDR_BREDR || rfcmode) { optlen = sizeof(*opts); return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen); } @@ -303,6 +304,13 @@ static int setopts(int sk, struct l2cap_options *opts) return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, sizeof(*opts)); + if (opts->mode) { + if (setsockopt(sk, SOL_BLUETOOTH, BT_MODE, &opts->mode, + sizeof(opts->mode)) < 0) { + return -errno; + } + } + return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, sizeof(opts->imtu)); } From patchwork Fri Feb 28 23:46:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413599 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4E50D14B7 for ; Fri, 28 Feb 2020 23:47:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 10482246B7 for ; Fri, 28 Feb 2020 23:47:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="juUoUVo+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726695AbgB1XrK (ORCPT ); Fri, 28 Feb 2020 18:47:10 -0500 Received: from mail-pf1-f195.google.com ([209.85.210.195]:37931 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbgB1XrK (ORCPT ); Fri, 28 Feb 2020 18:47:10 -0500 Received: by mail-pf1-f195.google.com with SMTP id x185so2499100pfc.5 for ; Fri, 28 Feb 2020 15:47:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=P7v08WPCJmgOE+zN+pZI071+aDIMN916uP0KQpxtxkc=; b=juUoUVo+l9t1SMSbRzM6G/q3idaKgZFJ0Ubfj+QxqGwT1Co/62YGVThvZyNnnYYcyb s9djdwHuxuioji4Ju8BFckrDIurLFrsgpg3qtwy22B1DW2/YBBxU7/2sCiKEzpqUSWOP pncJIsodxz3c0x70sXc91X5JBH7TfvW8h6FAShQgBkKIn+ojYomVxl0XHhXtZvumoIUj RYyv4w6trQLOMb3OpI18gCzE7o8KRNeVBRfI7azdWNaJowssdJgVuTA8DZR6P2bE0YwL kTxiLDB9LUF+Em4QVbBhQcKWU/7PER5rhCvpoIIyNFOnS2q9RyxO57OigDKj9iSEkZ8h 9UZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=P7v08WPCJmgOE+zN+pZI071+aDIMN916uP0KQpxtxkc=; b=hOP14/hF3LIiPJpAjDQZ+95q9QMVzHh3/GDWtDaM9OCFjdw/YYKmSCLMlTzKhP+QvT jaHUmnQfK9ql/kjEcXj2GYb137pBsArArIUHxFjqHXI/YbqMWrYxcs6wdCYyJTkin8is SdDfx51jYZL8i26Ldw+cZH1e5svmTB1lwh11zvzUGnMUApbgIIUwahFzsfbRBJ7SeSyy mKpsZgI09tZuiEkBgR7DFIuqGxzUP2R85Dd9N8oqd1vPoIFs7IqcNnPpS1jVDxioeR3P af+oDkZF2tSWkHyA3xy0XgguAGy1UIC/QfIYElQoEVhp7R8IaJms2jvBdoB1c3iuM/Ha jnog== X-Gm-Message-State: APjAAAX531REcS34xKbkDDow+SjJgl04gxast2GZxFa5FsVWMQ7crM7W I1KJRwMpR8lpi6BUs0eR/engV9KFM+A= X-Google-Smtp-Source: APXvYqwgbDlOWZX8XcbO5NU1uTistB/cMXNdOoVtHipYJgkMuEirzo26t82MGWUIto78tnCV9CkJSw== X-Received: by 2002:a62:768d:: with SMTP id r135mr6463474pfc.108.1582933627648; Fri, 28 Feb 2020 15:47:07 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.06 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:06 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 04/12] share/att: Add EATT support Date: Fri, 28 Feb 2020 15:46:53 -0800 Message-Id: <20200228234701.14614-5-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds EATT support to bt_att, EATT bearers are handled as additional channels which auto allocated for queued requests. --- src/gatt-database.c | 4 +- src/shared/att-types.h | 16 +- src/shared/att.c | 566 +++++++++++++++++++++++++-------------- src/shared/att.h | 4 + src/shared/gatt-client.c | 2 +- 5 files changed, 389 insertions(+), 203 deletions(-) diff --git a/src/gatt-database.c b/src/gatt-database.c index 2bae8711a..419e4f9e1 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -2102,10 +2102,10 @@ static void append_options(DBusMessageIter *iter, void *user_data) uint16_t mtu; switch (op->link_type) { - case BT_ATT_LINK_BREDR: + case BT_ATT_BREDR: link = "BR/EDR"; break; - case BT_ATT_LINK_LE: + case BT_ATT_LE: link = "LE"; break; default: diff --git a/src/shared/att-types.h b/src/shared/att-types.h index 8a2658de3..7b88e7d92 100644 --- a/src/shared/att-types.h +++ b/src/shared/att-types.h @@ -27,6 +27,10 @@ #define __packed __attribute__((packed)) #endif +#define BT_ATT_CID 4 +#define BT_ATT_PSM 31 +#define BT_ATT_EATT_PSM 0x27 + #define BT_ATT_SECURITY_AUTO 0 #define BT_ATT_SECURITY_LOW 1 #define BT_ATT_SECURITY_MEDIUM 2 @@ -37,9 +41,10 @@ #define BT_ATT_MAX_LE_MTU 517 #define BT_ATT_MAX_VALUE_LEN 512 -#define BT_ATT_LINK_BREDR 0x00 -#define BT_ATT_LINK_LE 0x01 -#define BT_ATT_LINK_LOCAL 0xff +#define BT_ATT_BREDR 0x00 +#define BT_ATT_LE 0x01 +#define BT_ATT_EATT 0x02 +#define BT_ATT_LOCAL 0xff /* ATT protocol opcodes */ #define BT_ATT_OP_ERROR_RSP 0x01 @@ -159,3 +164,8 @@ struct bt_att_pdu_error_rsp { /* GATT Characteristic Client Features Bitfield values */ #define BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING 0x01 +#define BT_GATT_CHRC_CLI_FEAT_EATT 0x02 +#define BT_GATT_CHRC_CLI_FEAT_NFY_MULTI 0x04 + +/* GATT Characteristic Server Features Bitfield values */ +#define BT_GATT_CHRC_SERVER_FEAT_EATT 0x01 diff --git a/src/shared/att.c b/src/shared/att.c index 0ea6d55bd..1313703f9 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -49,32 +49,40 @@ struct att_send_op; -struct bt_att { - int ref_count; +struct bt_att_chan { + struct bt_att *att; int fd; struct io *io; - bool io_on_l2cap; - int io_sec_level; /* Only used for non-L2CAP */ - uint8_t enc_size; + uint8_t type; + int sec_level; /* Only used for non-L2CAP */ - struct queue *req_queue; /* Queued ATT protocol requests */ struct att_send_op *pending_req; - struct queue *ind_queue; /* Queued ATT protocol indications */ struct att_send_op *pending_ind; - struct queue *write_queue; /* Queue of PDUs ready to send */ bool writer_active; - struct queue *notify_list; /* List of registered callbacks */ - struct queue *disconn_list; /* List of disconnect handlers */ - bool in_req; /* There's a pending incoming request */ uint8_t *buf; uint16_t mtu; +}; + +struct bt_att { + int ref_count; + bool close_on_unref; + struct queue *chans; + uint8_t enc_size; + uint16_t mtu; /* Biggest possible MTU */ + + struct queue *notify_list; /* List of registered callbacks */ + struct queue *disconn_list; /* List of disconnect handlers */ unsigned int next_send_id; /* IDs for "send" ops */ unsigned int next_reg_id; /* IDs for registered callbacks */ + struct queue *req_queue; /* Queued ATT protocol requests */ + struct queue *ind_queue; /* Queued ATT protocol indications */ + struct queue *write_queue; /* Queue of PDUs ready to send */ + bt_att_timeout_func_t timeout_callback; bt_att_destroy_func_t timeout_destroy; void *timeout_data; @@ -362,8 +370,9 @@ static struct att_send_op *create_att_send_op(struct bt_att *att, return op; } -static struct att_send_op *pick_next_send_op(struct bt_att *att) +static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan) { + struct bt_att *att = chan->att; struct att_send_op *op; /* See if any operations are already in the write queue */ @@ -374,7 +383,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att) /* If there is no pending request, pick an operation from the * request queue. */ - if (!att->pending_req) { + if (!chan->pending_req) { op = queue_pop_head(att->req_queue); if (op) return op; @@ -383,7 +392,7 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att) /* There is either a request pending or no requests queued. If there is * no pending indication, pick an operation from the indication queue. */ - if (!att->pending_ind) { + if (!chan->pending_ind) { op = queue_pop_head(att->ind_queue); if (op) return op; @@ -393,22 +402,23 @@ static struct att_send_op *pick_next_send_op(struct bt_att *att) } struct timeout_data { - struct bt_att *att; + struct bt_att_chan *chan; unsigned int id; }; static bool timeout_cb(void *user_data) { struct timeout_data *timeout = user_data; - struct bt_att *att = timeout->att; + struct bt_att_chan *chan = timeout->chan; + struct bt_att *att = chan->att; struct att_send_op *op = NULL; - if (att->pending_req && att->pending_req->id == timeout->id) { - op = att->pending_req; - att->pending_req = NULL; - } else if (att->pending_ind && att->pending_ind->id == timeout->id) { - op = att->pending_ind; - att->pending_ind = NULL; + if (chan->pending_req && chan->pending_req->id == timeout->id) { + op = chan->pending_req; + chan->pending_req = NULL; + } else if (chan->pending_ind && chan->pending_ind->id == timeout->id) { + op = chan->pending_ind; + chan->pending_ind = NULL; } if (!op) @@ -428,27 +438,28 @@ static bool timeout_cb(void *user_data) * This should trigger an io disconnect event which will clean up the * io and notify the upper layer. */ - io_shutdown(att->io); + io_shutdown(chan->io); return false; } static void write_watch_destroy(void *user_data) { - struct bt_att *att = user_data; + struct bt_att_chan *chan = user_data; - att->writer_active = false; + chan->writer_active = false; } static bool can_write_data(struct io *io, void *user_data) { - struct bt_att *att = user_data; + struct bt_att_chan *chan = user_data; + struct bt_att *att = chan->att; struct att_send_op *op; struct timeout_data *timeout; ssize_t ret; struct iovec iov; - op = pick_next_send_op(att); + op = pick_next_send_op(chan); if (!op) return false; @@ -478,14 +489,14 @@ static bool can_write_data(struct io *io, void *user_data) */ switch (op->type) { case ATT_OP_TYPE_REQ: - att->pending_req = op; + chan->pending_req = op; break; case ATT_OP_TYPE_IND: - att->pending_ind = op; + chan->pending_ind = op; break; case ATT_OP_TYPE_RSP: /* Set in_req to false to indicate that no request is pending */ - att->in_req = false; + chan->in_req = false; /* fall through */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NOT: @@ -497,7 +508,7 @@ static bool can_write_data(struct io *io, void *user_data) } timeout = new0(struct timeout_data, 1); - timeout->att = att; + timeout->chan = chan; timeout->id = op->id; op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb, timeout, free); @@ -506,25 +517,33 @@ static bool can_write_data(struct io *io, void *user_data) return true; } -static void wakeup_writer(struct bt_att *att) +static void wakeup_chan_writer(void *data, void *user_data) { - if (att->writer_active) + struct bt_att_chan *chan = data; + struct bt_att *att = chan->att; + + if (chan->writer_active) return; /* Set the write handler only if there is anything that can be sent * at all. */ if (queue_isempty(att->write_queue)) { - if ((att->pending_req || queue_isempty(att->req_queue)) && - (att->pending_ind || queue_isempty(att->ind_queue))) + if ((chan->pending_req || queue_isempty(att->req_queue)) && + (chan->pending_ind || queue_isempty(att->ind_queue))) return; } - if (!io_set_write_handler(att->io, can_write_data, att, + if (!io_set_write_handler(chan->io, can_write_data, chan, write_watch_destroy)) return; - att->writer_active = true; + chan->writer_active = true; +} + +static void wakeup_writer(struct bt_att *att) +{ + queue_foreach(att->chans, wakeup_chan_writer, NULL); } static void disconn_handler(void *data, void *user_data) @@ -549,44 +568,66 @@ static void disc_att_send_op(void *data) destroy_att_send_op(op); } +static void bt_att_chan_free(void *data) +{ + struct bt_att_chan *chan = data; + + if (chan->pending_req) + destroy_att_send_op(chan->pending_req); + + if (chan->pending_ind) + destroy_att_send_op(chan->pending_ind); + + io_destroy(chan->io); + + free(chan->buf); + free(chan); +} + static bool disconnect_cb(struct io *io, void *user_data) { - struct bt_att *att = user_data; + struct bt_att_chan *chan = user_data; + struct bt_att *att = chan->att; int err; socklen_t len; len = sizeof(err); - if (getsockopt(att->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { - util_debug(att->debug_callback, att->debug_data, + if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { + util_debug(chan->att->debug_callback, chan->att->debug_data, "Failed to obtain disconnect error: %s", strerror(errno)); err = 0; } - util_debug(att->debug_callback, att->debug_data, - "Physical link disconnected: %s", - strerror(err)); + util_debug(chan->att->debug_callback, chan->att->debug_data, + "Channel %p disconnected: %s", + chan, strerror(err)); - io_destroy(att->io); - att->io = NULL; - att->fd = -1; + /* Dettach channel */ + queue_remove(att->chans, chan); /* Notify request callbacks */ queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op); queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op); queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op); - if (att->pending_req) { - disc_att_send_op(att->pending_req); - att->pending_req = NULL; + if (chan->pending_req) { + disc_att_send_op(chan->pending_req); + chan->pending_req = NULL; } - if (att->pending_ind) { - disc_att_send_op(att->pending_ind); - att->pending_ind = NULL; + if (chan->pending_ind) { + disc_att_send_op(chan->pending_ind); + chan->pending_ind = NULL; } + bt_att_chan_free(chan); + + /* Don't run disconnect callback if there are channels left */ + if (!queue_isempty(att->chans)) + return false; + bt_att_ref(att); queue_foreach(att->disconn_list, disconn_handler, INT_TO_PTR(err)); @@ -597,14 +638,49 @@ static bool disconnect_cb(struct io *io, void *user_data) return false; } -static bool change_security(struct bt_att *att, uint8_t ecode) +static int bt_att_chan_get_security(struct bt_att_chan *chan) +{ + struct bt_security sec; + socklen_t len; + + if (chan->type == BT_ATT_LOCAL) + return chan->sec_level; + + memset(&sec, 0, sizeof(sec)); + len = sizeof(sec); + if (getsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0) + return -EIO; + + return sec.level; +} + +static bool bt_att_chan_set_security(struct bt_att_chan *chan, int level) +{ + struct bt_security sec; + + if (chan->type == BT_ATT_LOCAL) { + chan->sec_level = level; + return true; + } + + memset(&sec, 0, sizeof(sec)); + sec.level = level; + + if (setsockopt(chan->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, + sizeof(sec)) < 0) + return false; + + return true; +} + +static bool change_security(struct bt_att_chan *chan, uint8_t ecode) { int security; - if (att->io_sec_level != BT_ATT_SECURITY_AUTO) + if (chan->sec_level != BT_ATT_SECURITY_AUTO) return false; - security = bt_att_get_security(att, NULL); + security = bt_att_chan_get_security(chan); if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION && security < BT_ATT_SECURITY_MEDIUM) { @@ -622,14 +698,15 @@ static bool change_security(struct bt_att *att, uint8_t ecode) return false; } - return bt_att_set_security(att, security); + return bt_att_chan_set_security(chan, security); } -static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu, +static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len, uint8_t *opcode) { + struct bt_att *att = chan->att; const struct bt_att_pdu_error_rsp *rsp; - struct att_send_op *op = att->pending_req; + struct att_send_op *op = chan->pending_req; if (pdu_len != sizeof(*rsp)) { *opcode = 0; @@ -641,7 +718,7 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu, *opcode = rsp->opcode; /* Attempt to change security */ - if (!change_security(att, rsp->ecode)) + if (!change_security(chan, rsp->ecode)) return false; /* Remove timeout_id if outstanding */ @@ -653,16 +730,17 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu, util_debug(att->debug_callback, att->debug_data, "Retrying operation %p", op); - att->pending_req = NULL; + chan->pending_req = NULL; /* Push operation back to request queue */ return queue_push_head(att->req_queue, op); } -static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu, +static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, ssize_t pdu_len) { - struct att_send_op *op = att->pending_req; + struct bt_att *att = chan->att; + struct att_send_op *op = chan->pending_req; uint8_t req_opcode; uint8_t rsp_opcode; uint8_t *rsp_pdu = NULL; @@ -675,7 +753,7 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu, if (!op) { util_debug(att->debug_callback, att->debug_data, "Received unexpected ATT response"); - io_shutdown(att->io); + io_shutdown(chan->io); return; } @@ -685,8 +763,8 @@ static void handle_rsp(struct bt_att *att, uint8_t opcode, uint8_t *pdu, */ if (opcode == BT_ATT_OP_ERROR_RSP) { /* Return if error response cause a retry */ - if (handle_error_rsp(att, pdu, pdu_len, &req_opcode)) { - wakeup_writer(att); + if (handle_error_rsp(chan, pdu, pdu_len, &req_opcode)) { + wakeup_chan_writer(chan, NULL); return; } } else if (!(req_opcode = get_req_opcode(opcode))) @@ -715,14 +793,15 @@ done: op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data); destroy_att_send_op(op); - att->pending_req = NULL; + chan->pending_req = NULL; - wakeup_writer(att); + wakeup_chan_writer(chan, NULL); } -static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len) +static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) { - struct att_send_op *op = att->pending_ind; + struct bt_att *att = chan->att; + struct att_send_op *op = chan->pending_ind; /* * Disconnect the bearer if the confirmation is unexpected or the PDU is @@ -731,7 +810,7 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len) if (!op || pdu_len) { util_debug(att->debug_callback, att->debug_data, "Received unexpected/invalid ATT confirmation"); - io_shutdown(att->io); + io_shutdown(chan->io); return; } @@ -739,9 +818,9 @@ static void handle_conf(struct bt_att *att, uint8_t *pdu, ssize_t pdu_len) op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data); destroy_att_send_op(op); - att->pending_ind = NULL; + chan->pending_ind = NULL; - wakeup_writer(att); + wakeup_chan_writer(chan, NULL); } struct notify_data { @@ -811,9 +890,10 @@ fail: return false; } -static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu, - ssize_t pdu_len) +static void handle_notify(struct bt_att_chan *chan, uint8_t opcode, + uint8_t *pdu, ssize_t pdu_len) { + struct bt_att *att = chan->att; const struct queue_entry *entry; bool found; @@ -845,7 +925,7 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu, * link since the MTU size is negotiated using L2CAP channel * configuration procedures. */ - if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR) { + if (bt_att_get_link_type(att) == BT_ATT_BREDR) { switch (opcode) { case BT_ATT_OP_MTU_REQ: goto not_supported; @@ -876,22 +956,23 @@ not_supported: static bool can_read_data(struct io *io, void *user_data) { - struct bt_att *att = user_data; + struct bt_att_chan *chan = user_data; + struct bt_att *att = chan->att; uint8_t opcode; uint8_t *pdu; ssize_t bytes_read; - bytes_read = read(att->fd, att->buf, att->mtu); + bytes_read = read(chan->fd, chan->buf, chan->mtu); if (bytes_read < 0) return false; - util_hexdump('>', att->buf, bytes_read, - att->debug_callback, att->debug_data); + util_hexdump('>', chan->buf, bytes_read, + att->debug_callback, att->debug_data); if (bytes_read < ATT_MIN_PDU_LEN) return true; - pdu = att->buf; + pdu = chan->buf; opcode = pdu[0]; bt_att_ref(att); @@ -901,12 +982,12 @@ static bool can_read_data(struct io *io, void *user_data) case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, "ATT response received: 0x%02x", opcode); - handle_rsp(att, opcode, pdu + 1, bytes_read - 1); + handle_rsp(chan, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: util_debug(att->debug_callback, att->debug_data, "ATT confirmation received: 0x%02x", opcode); - handle_conf(att, pdu + 1, bytes_read - 1); + handle_conf(chan, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: /* @@ -914,17 +995,17 @@ static bool can_read_data(struct io *io, void *user_data) * protocol was violated. Disconnect the bearer, which will * promptly notify the upper layer via disconnect handlers. */ - if (att->in_req) { + if (chan->in_req) { util_debug(att->debug_callback, att->debug_data, "Received request while another is " "pending: 0x%02x", opcode); - io_shutdown(att->io); - bt_att_unref(att); + io_shutdown(chan->io); + bt_att_unref(chan->att); return false; } - att->in_req = true; + chan->in_req = true; /* fall through */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NOT: @@ -937,7 +1018,7 @@ static bool can_read_data(struct io *io, void *user_data) */ util_debug(att->debug_callback, att->debug_data, "ATT PDU received: 0x%02x", opcode); - handle_notify(att, opcode, pdu + 1, bytes_read - 1); + handle_notify(chan, opcode, pdu + 1, bytes_read - 1); break; } @@ -973,21 +1054,8 @@ static bool is_io_l2cap_based(int fd) static void bt_att_free(struct bt_att *att) { - if (att->pending_req) - destroy_att_send_op(att->pending_req); - - if (att->pending_ind) - destroy_att_send_op(att->pending_ind); - - io_destroy(att->io); bt_crypto_unref(att->crypto); - queue_destroy(att->req_queue, NULL); - queue_destroy(att->ind_queue, NULL); - queue_destroy(att->write_queue, NULL); - queue_destroy(att->notify_list, NULL); - queue_destroy(att->disconn_list, NULL); - if (att->timeout_destroy) att->timeout_destroy(att->timeout_data); @@ -997,7 +1065,12 @@ static void bt_att_free(struct bt_att *att) free(att->local_sign); free(att->remote_sign); - free(att->buf); + queue_destroy(att->req_queue, NULL); + queue_destroy(att->ind_queue, NULL); + queue_destroy(att->write_queue, NULL); + queue_destroy(att->notify_list, NULL); + queue_destroy(att->disconn_list, NULL); + queue_destroy(att->chans, bt_att_chan_free); free(att); } @@ -1014,60 +1087,101 @@ static uint16_t get_l2cap_mtu(int fd) return l2o.omtu; } -struct bt_att *bt_att_new(int fd, bool ext_signed) +static uint8_t io_get_type(int fd) { - struct bt_att *att; + struct sockaddr_l2 src; + socklen_t len; - if (fd < 0) - return NULL; + if (!is_io_l2cap_based(fd)) + return BT_ATT_LOCAL; - att = new0(struct bt_att, 1); - att->fd = fd; + len = sizeof(src); + memset(&src, 0, len); + if (getsockname(fd, (void *)&src, &len) < 0) + return -errno; - att->io = io_new(fd); - if (!att->io) - goto fail; + if (src.l2_bdaddr_type == BDADDR_BREDR) + return BT_ATT_BREDR; - /* crypto is optional, if not available leave it NULL */ - if (!ext_signed) - att->crypto = bt_crypto_new(); + return BT_ATT_LE; +} - att->req_queue = queue_new(); - att->ind_queue = queue_new(); - att->write_queue = queue_new(); - att->notify_list = queue_new(); - att->disconn_list = queue_new(); +static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) +{ + struct bt_att_chan *chan; - if (!io_set_read_handler(att->io, can_read_data, att, NULL)) + if (fd < 0) + return NULL; + + chan = new0(struct bt_att_chan, 1); + chan->fd = fd; + + chan->io = io_new(fd); + if (!chan->io) goto fail; - if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL)) + if (!io_set_read_handler(chan->io, can_read_data, chan, NULL)) goto fail; - att->io_on_l2cap = is_io_l2cap_based(att->fd); - if (!att->io_on_l2cap) - att->io_sec_level = BT_ATT_SECURITY_LOW; + if (!io_set_disconnect_handler(chan->io, disconnect_cb, chan, NULL)) + goto fail; - if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR) - att->mtu = get_l2cap_mtu(att->fd); - else - att->mtu = BT_ATT_DEFAULT_LE_MTU; + chan->type = type; + switch (chan->type) { + case BT_ATT_LOCAL: + chan->sec_level = BT_ATT_SECURITY_LOW; + /* fall through */ + case BT_ATT_LE: + chan->mtu = BT_ATT_DEFAULT_LE_MTU; + break; + default: + chan->mtu = get_l2cap_mtu(chan->fd); + } - if (att->mtu < BT_ATT_DEFAULT_LE_MTU) + if (chan->mtu < BT_ATT_DEFAULT_LE_MTU) goto fail; - att->buf = malloc(att->mtu); - if (!att->buf) + chan->buf = malloc(chan->mtu); + if (!chan->buf) goto fail; - return bt_att_ref(att); + return chan; fail: - bt_att_free(att); + bt_att_chan_free(chan); return NULL; } +struct bt_att *bt_att_new(int fd, bool ext_signed) +{ + struct bt_att *att; + struct bt_att_chan *chan; + + chan = bt_att_chan_new(fd, io_get_type(fd)); + if (!chan) + return NULL; + + att = new0(struct bt_att, 1); + att->chans = queue_new(); + att->mtu = chan->mtu; + + queue_push_head(att->chans, chan); + chan->att = att; + + /* crypto is optional, if not available leave it NULL */ + if (!ext_signed) + att->crypto = bt_crypto_new(); + + att->req_queue = queue_new(); + att->ind_queue = queue_new(); + att->write_queue = queue_new(); + att->notify_list = queue_new(); + att->disconn_list = queue_new(); + + return bt_att_ref(att); +} + struct bt_att *bt_att_ref(struct bt_att *att) { if (!att) @@ -1094,18 +1208,67 @@ void bt_att_unref(struct bt_att *att) bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close) { - if (!att || !att->io) + const struct queue_entry *entry; + + if (!att) return false; - return io_set_close_on_destroy(att->io, do_close); + att->close_on_unref = do_close; + + for (entry = queue_get_entries(att->chans); entry; + entry = entry->next) { + struct bt_att_chan *chan = entry->data; + + if (!io_set_close_on_destroy(chan->io, do_close)) + return false; + } + + return true; +} + +int bt_att_attach_fd(struct bt_att *att, int fd) +{ + struct bt_att_chan *chan; + + if (!att || fd < 0) + return -EINVAL; + + chan = bt_att_chan_new(fd, BT_ATT_EATT); + if (!chan) + return -EINVAL; + + queue_push_tail(att->chans, chan); + chan->att = att; + + if (chan->mtu > att->mtu) + att->mtu = chan->mtu; + + io_set_close_on_destroy(chan->io, att->close_on_unref); + + return 0; } int bt_att_get_fd(struct bt_att *att) { + struct bt_att_chan *chan; + if (!att) return -1; - return att->fd; + if (queue_isempty(att->chans)) + return -ENOTCONN; + + chan = queue_peek_head(att->chans); + + return chan->fd; +} + +int bt_att_get_channels(struct bt_att *att) +{ + if (!att) + return 0; + + return queue_length(att->chans); } bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback, @@ -1134,6 +1297,7 @@ uint16_t bt_att_get_mtu(struct bt_att *att) bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu) { + struct bt_att_chan *chan; void *buf; if (!att) @@ -1142,38 +1306,37 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu) if (mtu < BT_ATT_DEFAULT_LE_MTU) return false; + chan = queue_peek_head(att->chans); + if (!chan) + return -ENOTCONN; + buf = malloc(mtu); if (!buf) return false; - free(att->buf); + free(chan->buf); + + chan->mtu = mtu; + chan->buf = buf; - att->mtu = mtu; - att->buf = buf; + if (chan->mtu > att->mtu) + att->mtu = chan->mtu; return true; } uint8_t bt_att_get_link_type(struct bt_att *att) { - struct sockaddr_l2 src; - socklen_t len; + struct bt_att_chan *chan; if (!att) return -EINVAL; - if (!att->io_on_l2cap) - return BT_ATT_LINK_LOCAL; + chan = queue_peek_head(att->chans); + if (!chan) + return -ENOTCONN; - len = sizeof(src); - memset(&src, 0, len); - if (getsockname(att->fd, (void *)&src, &len) < 0) - return -errno; - - if (src.l2_bdaddr_type == BDADDR_BREDR) - return BT_ATT_LINK_BREDR; - - return BT_ATT_LINK_LE; + return chan->type; } bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback, @@ -1200,7 +1363,7 @@ unsigned int bt_att_register_disconnect(struct bt_att *att, { struct att_disconn *disconn; - if (!att || !att->io) + if (!att || queue_isempty(att->chans)) return 0; disconn = new0(struct att_disconn, 1); @@ -1229,7 +1392,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id) return false; /* Check if disconnect is running */ - if (!att->io) { + if (!queue_isempty(att->chans)) { disconn = queue_find(att->disconn_list, match_disconn_id, UINT_TO_PTR(id)); if (!disconn) @@ -1256,7 +1419,7 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, struct att_send_op *op; bool result; - if (!att || !att->io) + if (!att || queue_isempty(att->chans)) return 0; op = create_att_send_op(att, opcode, pdu, length, callback, user_data, @@ -1308,21 +1471,31 @@ static bool match_op_id(const void *a, const void *b) bool bt_att_cancel(struct bt_att *att, unsigned int id) { + const struct queue_entry *entry; struct att_send_op *op; if (!att || !id) return false; - if (att->pending_req && att->pending_req->id == id) { - /* Don't cancel the pending request; remove it's handlers */ - cancel_att_send_op(att->pending_req); - return true; - } + for (entry = queue_get_entries(att->chans); entry; + entry = entry->next) { + struct bt_att_chan *chan = entry->data; - if (att->pending_ind && att->pending_ind->id == id) { - /* Don't cancel the pending indication; remove it's handlers */ - cancel_att_send_op(att->pending_ind); - return true; + if (chan->pending_req && chan->pending_req->id == id) { + /* Don't cancel the pending request; remove it's + * handlers + */ + cancel_att_send_op(chan->pending_req); + return true; + } + + if (chan->pending_ind && chan->pending_ind->id == id) { + /* Don't cancel the pending indication; remove it's + * handlers. + */ + cancel_att_send_op(chan->pending_ind); + return true; + } } op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id)); @@ -1350,6 +1523,8 @@ done: bool bt_att_cancel_all(struct bt_att *att) { + const struct queue_entry *entry; + if (!att) return false; @@ -1357,13 +1532,22 @@ bool bt_att_cancel_all(struct bt_att *att) queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op); queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op); - if (att->pending_req) - /* Don't cancel the pending request; remove it's handlers */ - cancel_att_send_op(att->pending_req); - - if (att->pending_ind) - /* Don't cancel the pending request; remove it's handlers */ - cancel_att_send_op(att->pending_ind); + for (entry = queue_get_entries(att->chans); entry; + entry = entry->next) { + struct bt_att_chan *chan = entry->data; + + if (chan->pending_req) + /* Don't cancel the pending request; remove it's + * handlers + */ + cancel_att_send_op(chan->pending_req); + + if (chan->pending_ind) + /* Don't cancel the pending request; remove it's + * handlers + */ + cancel_att_send_op(chan->pending_ind); + } return true; } @@ -1424,7 +1608,7 @@ unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, { struct att_notify *notify; - if (!att || !callback || !att->io) + if (!att || !callback || queue_isempty(att->chans)) return 0; notify = new0(struct att_notify, 1); @@ -1475,51 +1659,39 @@ bool bt_att_unregister_all(struct bt_att *att) int bt_att_get_security(struct bt_att *att, uint8_t *enc_size) { - struct bt_security sec; - socklen_t len; + struct bt_att_chan *chan; + int ret; if (!att) return -EINVAL; - if (!att->io_on_l2cap) { - if (enc_size) - *enc_size = att->enc_size; + chan = queue_peek_head(att->chans); + if (!chan) + return -ENOTCONN; - return att->io_sec_level; - } - - memset(&sec, 0, sizeof(sec)); - len = sizeof(sec); - if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0) - return -EIO; + ret = bt_att_chan_get_security(chan); + if (ret < 0) + return ret; if (enc_size) *enc_size = att->enc_size; - return sec.level; + return ret; } bool bt_att_set_security(struct bt_att *att, int level) { - struct bt_security sec; + struct bt_att_chan *chan; if (!att || level < BT_ATT_SECURITY_AUTO || level > BT_ATT_SECURITY_HIGH) return false; - if (!att->io_on_l2cap) { - att->io_sec_level = level; - return true; - } + chan = queue_peek_head(att->chans); + if (!chan) + return -ENOTCONN; - memset(&sec, 0, sizeof(sec)); - sec.level = level; - - if (setsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, - sizeof(sec)) < 0) - return false; - - return true; + return bt_att_chan_set_security(chan, level); } void bt_att_set_enc_key_size(struct bt_att *att, uint8_t enc_size) diff --git a/src/shared/att.h b/src/shared/att.h index 49d93269b..110700846 100644 --- a/src/shared/att.h +++ b/src/shared/att.h @@ -37,6 +37,10 @@ bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close); int bt_att_get_fd(struct bt_att *att); +int bt_att_attach_fd(struct bt_att *att, int fd); + +int bt_att_get_channels(struct bt_att *att); + typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu, diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 29254cb61..3ce126485 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -1909,7 +1909,7 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu) * the MTU size is negotiated using L2CAP channel configuration * procedures. */ - if (bt_att_get_link_type(client->att) == BT_ATT_LINK_BREDR) + if (bt_att_get_link_type(client->att) == BT_ATT_BREDR) goto discover; /* Check if MTU needs to be send */ From patchwork Fri Feb 28 23:46:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413603 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CB94614B7 for ; Fri, 28 Feb 2020 23:47:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7B192246B6 for ; Fri, 28 Feb 2020 23:47:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UsZ6TMow" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726785AbgB1XrN (ORCPT ); Fri, 28 Feb 2020 18:47:13 -0500 Received: from mail-pj1-f67.google.com ([209.85.216.67]:55334 "EHLO mail-pj1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726733AbgB1XrM (ORCPT ); Fri, 28 Feb 2020 18:47:12 -0500 Received: by mail-pj1-f67.google.com with SMTP id a18so1900791pjs.5 for ; Fri, 28 Feb 2020 15:47:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=M0G9TugDn5OKwPo71nYWIjdIsvuqwMBKvnlVGuB2yEw=; b=UsZ6TMow/PrA4nvRmAzfXjFQP6rF3wMrGr8U0C7ChZg6j+DBDNAAktfT3ejO67YjCB m2j29tJM+2yt2K7w0WjviyP3faF7lOaqb5GnCWx5CZekXkdBUNZiIGMha13L2qiY+sQ+ /jfSh5ZH9ZowAFK9Uu+3+PuZAMqs7pr7J69E4wkCmAc3tGIqlAQq5idSVYZxtuklCQRW Ro0ilkj4g/ml293nvAOTrrTtNTAKT4MgX6aDSWQYM9zP6jS5SrXRtW2EELmVoJdLauNB QwqA7tLQeAJWfXoqnYyQ3eW/2S7VPwxRDZQYxmvjFVs8ltbef2IxAWGT2RrYBtA8euOO 3q+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=M0G9TugDn5OKwPo71nYWIjdIsvuqwMBKvnlVGuB2yEw=; b=hZ0vvTbbxhuqENT7t+vt2DDC0Lg4FEfdPnRR0cHsHs3y8Z+HcYU6fZyPBrD/abjBVt 3M6Eo1fPCvTfKYuPnv9cgKVcr64aAzc4IXbhiHAhKoYALrUseB/DtdMfNGaPTL1SscvP 2du+pIWBJepUeW121EZ6bGCCbDjoNs6V10TvoECKnWbKVnbsMCNqhSxyLIlWUSkI/gln c2NBZjkgEPVbTcXfay+dpL21mVTL3QbyYxM/fhN4ql6d2Up/LXGzjf9R3Rx9d5LmXkUS w9TV8GOBdm54181KVa+mozBBHKHt5Sb7FQ8Qk69c68v9+5BM55QNXURLqsDv5jm+VpWF spnw== X-Gm-Message-State: APjAAAVWOmUoeZJcbme9EMEw/tEoIIR7cdqkRYxZfKxeNNLqxTDTcf7z hi5wFR4x6cO50ovy0RP3L61YC/Lkr4U= X-Google-Smtp-Source: APXvYqzWMvaQtN+2vfd4o1isRYf5z/jkWXUTCCQMu/flwEyQlsiKnWJUrGIGd4MCB7Ob95qJM9xfyQ== X-Received: by 2002:a17:902:8f91:: with SMTP id z17mr6340710plo.234.1582933628802; Fri, 28 Feb 2020 15:47:08 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.07 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:08 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 05/12] shared/gatt-client: Add support for EATT features Date: Fri, 28 Feb 2020 15:46:54 -0800 Message-Id: <20200228234701.14614-6-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This enables EATT in the Client Features if the EATT characteristic is present in the database. --- attrib/gattrib.c | 5 +- lib/uuid.h | 3 + peripheral/gatt.c | 2 +- src/device.c | 2 +- src/shared/att.c | 271 ++++++++++++++++++++++++++------------- src/shared/att.h | 14 +- src/shared/gatt-client.c | 167 ++++++++++++++++++++++-- src/shared/gatt-client.h | 5 +- src/shared/gatt-server.c | 205 ++++++++++++++++------------- tools/btgatt-client.c | 2 +- unit/test-gatt.c | 23 ++-- 11 files changed, 494 insertions(+), 205 deletions(-) diff --git a/attrib/gattrib.c b/attrib/gattrib.c index 57ca01541..8aa0f5eff 100644 --- a/attrib/gattrib.c +++ b/attrib/gattrib.c @@ -275,8 +275,9 @@ static void attrib_callback_result(uint8_t opcode, const void *pdu, free(buf); } -static void attrib_callback_notify(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void attrib_callback_notify(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { uint8_t *buf; struct attrib_callbacks *cb = user_data; diff --git a/lib/uuid.h b/lib/uuid.h index fbc08f51e..ebdcf729c 100644 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -154,6 +154,9 @@ extern "C" { #define GATT_CHARAC_CLI_FEAT 0x2B29 #define GATT_CHARAC_DB_HASH 0x2B2A +/* GATT Server Supported features */ +#define GATT_CHARAC_SERVER_FEAT 0x2B3A + typedef struct { enum { BT_UUID_UNSPEC = 0, diff --git a/peripheral/gatt.c b/peripheral/gatt.c index 08541c424..bbbf3f59f 100644 --- a/peripheral/gatt.c +++ b/peripheral/gatt.c @@ -136,7 +136,7 @@ static struct gatt_conn *gatt_conn_new(int fd) return NULL; } - conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu); + conn->client = bt_gatt_client_new(gatt_cache, conn->att, mtu, 0); if (!conn->gatt) { fprintf(stderr, "Failed to create GATT client\n"); bt_gatt_server_unref(conn->gatt); diff --git a/src/device.c b/src/device.c index a8f4c22f3..3f4afa281 100644 --- a/src/device.c +++ b/src/device.c @@ -4939,7 +4939,7 @@ static void gatt_client_init(struct btd_device *device) } device->client = bt_gatt_client_new(device->db, device->att, - device->att_mtu); + device->att_mtu, 0); if (!device->client) { DBG("Failed to initialize"); return; diff --git a/src/shared/att.c b/src/shared/att.c index 1313703f9..56ea40c46 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -56,6 +56,8 @@ struct bt_att_chan { uint8_t type; int sec_level; /* Only used for non-L2CAP */ + struct queue *queue; /* Channel dedicated queue */ + struct att_send_op *pending_req; struct att_send_op *pending_ind; bool writer_active; @@ -375,32 +377,47 @@ static struct att_send_op *pick_next_send_op(struct bt_att_chan *chan) struct bt_att *att = chan->att; struct att_send_op *op; - /* See if any operations are already in the write queue */ - op = queue_pop_head(att->write_queue); + /* Check if there is anything queued on the channel */ + op = queue_pop_head(chan->queue); if (op) return op; + /* See if any operations are already in the write queue */ + op = queue_peek_head(att->write_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->write_queue); + /* If there is no pending request, pick an operation from the * request queue. */ if (!chan->pending_req) { - op = queue_pop_head(att->req_queue); - if (op) - return op; + op = queue_peek_head(att->req_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->req_queue); } /* There is either a request pending or no requests queued. If there is * no pending indication, pick an operation from the indication queue. */ if (!chan->pending_ind) { - op = queue_pop_head(att->ind_queue); - if (op) - return op; + op = queue_peek_head(att->ind_queue); + if (op && op->len <= chan->mtu) + return queue_pop_head(att->ind_queue); } return NULL; } +static void disc_att_send_op(void *data) +{ + struct att_send_op *op = data; + + if (op->callback) + op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); + + destroy_att_send_op(op); +} + struct timeout_data { struct bt_att_chan *chan; unsigned int id; @@ -425,13 +442,14 @@ static bool timeout_cb(void *user_data) return false; util_debug(att->debug_callback, att->debug_data, - "Operation timed out: 0x%02x", op->opcode); + "(chan %p) Operation timed out: 0x%02x", + chan, op->opcode); if (att->timeout_callback) att->timeout_callback(op->id, op->opcode, att->timeout_data); op->timeout_id = 0; - destroy_att_send_op(op); + disc_att_send_op(op); /* * Directly terminate the connection as required by the ATT protocol. @@ -450,39 +468,52 @@ static void write_watch_destroy(void *user_data) chan->writer_active = false; } +static ssize_t bt_att_chan_write(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len) +{ + struct bt_att *att = chan->att; + ssize_t ret; + struct iovec iov; + + iov.iov_base = (void *) pdu; + iov.iov_len = len; + + util_debug(att->debug_callback, att->debug_data, + "(chan %p) ATT op 0x%02x", + chan, opcode); + + ret = io_send(chan->io, &iov, 1); + if (ret < 0) { + util_debug(att->debug_callback, att->debug_data, + "(chan %p) write failed: %s", + chan, strerror(-ret)); + + return ret; + } + + util_hexdump('<', pdu, ret, att->debug_callback, att->debug_data); + + return ret; +} + static bool can_write_data(struct io *io, void *user_data) { struct bt_att_chan *chan = user_data; - struct bt_att *att = chan->att; struct att_send_op *op; struct timeout_data *timeout; - ssize_t ret; - struct iovec iov; op = pick_next_send_op(chan); if (!op) return false; - iov.iov_base = op->pdu; - iov.iov_len = op->len; - - ret = io_send(io, &iov, 1); - if (ret < 0) { - util_debug(att->debug_callback, att->debug_data, - "write failed: %s", strerror(-ret)); + if (!bt_att_chan_write(chan, op->opcode, op->pdu, op->len)) { if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); - destroy_att_send_op(op); return true; } - util_debug(att->debug_callback, att->debug_data, - "ATT op 0x%02x", op->opcode); - - util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data); - /* Based on the operation type, set either the pending request or the * pending indication. If it came from the write queue, then there is * no need to keep it around. @@ -528,7 +559,7 @@ static void wakeup_chan_writer(void *data, void *user_data) /* Set the write handler only if there is anything that can be sent * at all. */ - if (queue_isempty(att->write_queue)) { + if (queue_isempty(chan->queue) && queue_isempty(att->write_queue)) { if ((chan->pending_req || queue_isempty(att->req_queue)) && (chan->pending_ind || queue_isempty(att->ind_queue))) return; @@ -558,16 +589,6 @@ static void disconn_handler(void *data, void *user_data) disconn->callback(err, disconn->user_data); } -static void disc_att_send_op(void *data) -{ - struct att_send_op *op = data; - - if (op->callback) - op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); - - destroy_att_send_op(op); -} - static void bt_att_chan_free(void *data) { struct bt_att_chan *chan = data; @@ -578,6 +599,8 @@ static void bt_att_chan_free(void *data) if (chan->pending_ind) destroy_att_send_op(chan->pending_ind); + queue_destroy(chan->queue, destroy_att_send_op); + io_destroy(chan->io); free(chan->buf); @@ -595,8 +618,8 @@ static bool disconnect_cb(struct io *io, void *user_data) if (getsockopt(chan->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { util_debug(chan->att->debug_callback, chan->att->debug_data, - "Failed to obtain disconnect error: %s", - strerror(errno)); + "(chan %p) Failed to obtain disconnect" + " error: %s", chan, strerror(errno)); err = 0; } @@ -728,7 +751,8 @@ static bool handle_error_rsp(struct bt_att_chan *chan, uint8_t *pdu, } util_debug(att->debug_callback, att->debug_data, - "Retrying operation %p", op); + "(chan %p) Retrying operation " + "%p", chan, op); chan->pending_req = NULL; @@ -752,7 +776,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, */ if (!op) { util_debug(att->debug_callback, att->debug_data, - "Received unexpected ATT response"); + "(chan %p) Received unexpected ATT " + "response", chan); io_shutdown(chan->io); return; } @@ -784,7 +809,8 @@ static void handle_rsp(struct bt_att_chan *chan, uint8_t opcode, uint8_t *pdu, fail: util_debug(att->debug_callback, att->debug_data, - "Failed to handle response PDU; opcode: 0x%02x", opcode); + "(chan %p) Failed to handle response PDU; opcode: " + "0x%02x", chan, opcode); rsp_opcode = BT_ATT_OP_ERROR_RSP; @@ -809,7 +835,8 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) */ if (!op || pdu_len) { util_debug(att->debug_callback, att->debug_data, - "Received unexpected/invalid ATT confirmation"); + "(chan %p) Received unexpected/invalid ATT " + "confirmation", chan); io_shutdown(chan->io); return; } @@ -935,7 +962,7 @@ static void handle_notify(struct bt_att_chan *chan, uint8_t opcode, found = true; if (notify->callback) - notify->callback(opcode, pdu, pdu_len, + notify->callback(chan, opcode, pdu, pdu_len, notify->user_data); /* callback could remove all entries from notify list */ @@ -966,6 +993,10 @@ static bool can_read_data(struct io *io, void *user_data) if (bytes_read < 0) return false; + util_debug(att->debug_callback, att->debug_data, + "(chan %p) ATT received: %zd", + chan, bytes_read); + util_hexdump('>', chan->buf, bytes_read, att->debug_callback, att->debug_data); @@ -981,12 +1012,14 @@ static bool can_read_data(struct io *io, void *user_data) switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, - "ATT response received: 0x%02x", opcode); + "(chan %p) ATT response received: 0x%02x", + chan, opcode); handle_rsp(chan, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: util_debug(att->debug_callback, att->debug_data, - "ATT confirmation received: 0x%02x", opcode); + "(chan %p) ATT confirmation received: 0x%02x", + chan, opcode); handle_conf(chan, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: @@ -997,8 +1030,9 @@ static bool can_read_data(struct io *io, void *user_data) */ if (chan->in_req) { util_debug(att->debug_callback, att->debug_data, - "Received request while another is " - "pending: 0x%02x", opcode); + "(chan %p) Received request while " + "another is pending: 0x%02x", + chan, opcode); io_shutdown(chan->io); bt_att_unref(chan->att); @@ -1017,7 +1051,8 @@ static bool can_read_data(struct io *io, void *user_data) * let them act on it. */ util_debug(att->debug_callback, att->debug_data, - "ATT PDU received: 0x%02x", opcode); + "(chan %p) ATT PDU received: 0x%02x", + chan, opcode); handle_notify(chan, opcode, pdu + 1, bytes_read - 1); break; } @@ -1075,16 +1110,19 @@ static void bt_att_free(struct bt_att *att) free(att); } -static uint16_t get_l2cap_mtu(int fd) +static uint16_t io_get_mtu(int fd) { socklen_t len; struct l2cap_options l2o; len = sizeof(l2o); - if (getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) - return 0; + if (!getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len)) + return l2o.omtu; - return l2o.omtu; + if (!getsockopt(fd, SOL_BLUETOOTH, BT_SNDMTU, &l2o.omtu, &len)) + return l2o.omtu; + + return 0; } static uint8_t io_get_type(int fd) @@ -1135,7 +1173,7 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) chan->mtu = BT_ATT_DEFAULT_LE_MTU; break; default: - chan->mtu = get_l2cap_mtu(chan->fd); + chan->mtu = io_get_mtu(chan->fd); } if (chan->mtu < BT_ATT_DEFAULT_LE_MTU) @@ -1145,6 +1183,8 @@ static struct bt_att_chan *bt_att_chan_new(int fd, uint8_t type) if (!chan->buf) goto fail; + chan->queue = queue_new(); + return chan; fail: @@ -1153,6 +1193,23 @@ fail: return NULL; } +static void bt_att_attach_chan(struct bt_att *att, struct bt_att_chan *chan) +{ + /* Push to head as EATT channels have higher priority */ + queue_push_head(att->chans, chan); + chan->att = att; + + if (chan->mtu > att->mtu) + att->mtu = chan->mtu; + + io_set_close_on_destroy(chan->io, att->close_on_unref); + + util_debug(att->debug_callback, att->debug_data, "Channel %p attached", + chan); + + wakeup_chan_writer(chan, NULL); +} + struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; @@ -1166,9 +1223,6 @@ struct bt_att *bt_att_new(int fd, bool ext_signed) att->chans = queue_new(); att->mtu = chan->mtu; - queue_push_head(att->chans, chan); - chan->att = att; - /* crypto is optional, if not available leave it NULL */ if (!ext_signed) att->crypto = bt_crypto_new(); @@ -1179,6 +1233,8 @@ struct bt_att *bt_att_new(int fd, bool ext_signed) att->notify_list = queue_new(); att->disconn_list = queue_new(); + bt_att_attach_chan(att, chan); + return bt_att_ref(att); } @@ -1237,13 +1293,7 @@ int bt_att_attach_fd(struct bt_att *att, int fd) if (!chan) return -EINVAL; - queue_push_tail(att->chans, chan); - chan->att = att; - - if (chan->mtu > att->mtu) - att->mtu = chan->mtu; - - io_set_close_on_destroy(chan->io, att->close_on_unref); + bt_att_attach_chan(att, chan); return 0; } @@ -1258,7 +1308,7 @@ int bt_att_get_fd(struct bt_att *att) if (queue_isempty(att->chans)) return -ENOTCONN; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); return chan->fd; } @@ -1306,7 +1356,8 @@ bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu) if (mtu < BT_ATT_DEFAULT_LE_MTU) return false; - chan = queue_peek_head(att->chans); + /* Original channel is always the last */ + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1332,7 +1383,7 @@ uint8_t bt_att_get_link_type(struct bt_att *att) if (!att) return -EINVAL; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1392,7 +1443,7 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id) return false; /* Check if disconnect is running */ - if (!queue_isempty(att->chans)) { + if (queue_isempty(att->chans)) { disconn = queue_find(att->disconn_list, match_disconn_id, UINT_TO_PTR(id)); if (!disconn) @@ -1461,6 +1512,33 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, return op->id; } +unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len, + bt_att_response_func_t callback, + void *user_data, + bt_att_destroy_func_t destroy) +{ + struct att_send_op *op; + + if (!chan || !chan->att) + return -EINVAL; + + op = create_att_send_op(chan->att, opcode, pdu, len, callback, + user_data, destroy); + if (!op) + return -EINVAL; + + if (!queue_push_tail(chan->queue, op)) { + free(op->pdu); + free(op); + return 0; + } + + wakeup_chan_writer(chan, NULL); + + return op->id; +} + static bool match_op_id(const void *a, const void *b) { const struct att_send_op *op = a; @@ -1469,6 +1547,33 @@ static bool match_op_id(const void *a, const void *b) return op->id == id; } +bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id) +{ + struct att_send_op *op; + + if (chan->pending_req && chan->pending_req->id == id) { + /* Don't cancel the pending request; remove it's handlers */ + cancel_att_send_op(chan->pending_req); + return true; + } + + if (chan->pending_ind && chan->pending_ind->id == id) { + /* Don't cancel the pending indication; remove it's handlers. */ + cancel_att_send_op(chan->pending_ind); + return true; + } + + op = queue_remove_if(chan->queue, match_op_id, UINT_TO_PTR(id)); + if (!op) + return false; + + destroy_att_send_op(op); + + wakeup_chan_writer(chan, NULL); + + return true; +} + bool bt_att_cancel(struct bt_att *att, unsigned int id) { const struct queue_entry *entry; @@ -1477,25 +1582,13 @@ bool bt_att_cancel(struct bt_att *att, unsigned int id) if (!att || !id) return false; + /* Lookuo request on each channel first */ for (entry = queue_get_entries(att->chans); entry; entry = entry->next) { struct bt_att_chan *chan = entry->data; - if (chan->pending_req && chan->pending_req->id == id) { - /* Don't cancel the pending request; remove it's - * handlers - */ - cancel_att_send_op(chan->pending_req); - return true; - } - - if (chan->pending_ind && chan->pending_ind->id == id) { - /* Don't cancel the pending indication; remove it's - * handlers. - */ - cancel_att_send_op(chan->pending_ind); + if (bt_att_chan_cancel(chan, id)) return true; - } } op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id)); @@ -1580,14 +1673,14 @@ static uint8_t att_ecode_from_error(int err) return BT_ATT_ERROR_UNLIKELY; } -unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, +int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error) { struct bt_att_pdu_error_rsp pdu; uint8_t ecode; - if (!att || !opcode) - return 0; + if (!chan || !chan->att || !opcode) + return -EINVAL; ecode = att_ecode_from_error(error); @@ -1597,8 +1690,8 @@ unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, put_le16(handle, &pdu.handle); pdu.ecode = ecode; - return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), - NULL, NULL, NULL); + return bt_att_chan_send_rsp(chan, BT_ATT_OP_ERROR_RSP, &pdu, + sizeof(pdu)); } unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, @@ -1665,7 +1758,7 @@ int bt_att_get_security(struct bt_att *att, uint8_t *enc_size) if (!att) return -EINVAL; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; @@ -1687,7 +1780,7 @@ bool bt_att_set_security(struct bt_att *att, int level) level > BT_ATT_SECURITY_HIGH) return false; - chan = queue_peek_head(att->chans); + chan = queue_peek_tail(att->chans); if (!chan) return -ENOTCONN; diff --git a/src/shared/att.h b/src/shared/att.h index 110700846..ed20bb5b8 100644 --- a/src/shared/att.h +++ b/src/shared/att.h @@ -27,6 +27,7 @@ #include "src/shared/att-types.h" struct bt_att; +struct bt_att_chan; struct bt_att *bt_att_new(int fd, bool ext_signed); @@ -43,7 +44,8 @@ int bt_att_get_channels(struct bt_att *att); typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu, uint16_t length, void *user_data); -typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu, +typedef void (*bt_att_notify_func_t)(struct bt_att_chan *chan, + uint8_t opcode, const void *pdu, uint16_t length, void *user_data); typedef void (*bt_att_destroy_func_t)(void *user_data); typedef void (*bt_att_debug_func_t)(const char *str, void *user_data); @@ -68,10 +70,18 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, bt_att_response_func_t callback, void *user_data, bt_att_destroy_func_t destroy); +unsigned int bt_att_chan_send(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t len, + bt_att_response_func_t callback, + void *user_data, + bt_att_destroy_func_t destroy); +#define bt_att_chan_send_rsp(chan, opcode, pdu, len) \ + bt_att_chan_send(chan, opcode, pdu, len, NULL, NULL, NULL) +bool bt_att_chan_cancel(struct bt_att_chan *chan, unsigned int id); bool bt_att_cancel(struct bt_att *att, unsigned int id); bool bt_att_cancel_all(struct bt_att *att); -unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode, +int bt_att_chan_send_error_rsp(struct bt_att_chan *chan, uint8_t opcode, uint16_t handle, int error); unsigned int bt_att_register(struct bt_att *att, uint8_t opcode, diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 3ce126485..2c5fe14dc 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -60,6 +60,7 @@ struct ready_cb { struct bt_gatt_client { struct bt_att *att; int ref_count; + uint8_t features; struct bt_gatt_client *parent; struct queue *clones; @@ -326,6 +327,7 @@ struct discovery_op { struct queue *ext_prop_desc; struct gatt_db_attribute *cur_svc; struct gatt_db_attribute *hash; + uint8_t server_feat; bool success; uint16_t start; uint16_t end; @@ -1278,6 +1280,9 @@ static void notify_client_ready(struct bt_gatt_client *client, bool success, bt_gatt_client_ref(client); client->ready = success; + if (client->parent) + client->features = client->parent->features; + for (entry = queue_get_entries(client->ready_cbs); entry; entry = entry->next) { struct ready_cb *ready = entry->data; @@ -1381,7 +1386,7 @@ static void db_hash_read_cb(bool success, uint8_t att_ecode, util_hexdump(' ', value, len, client->debug_callback, client->debug_data); - /* Store the new hash in the db */ + /* Store ithe new hash in the db */ gatt_db_attribute_write(op->hash, 0, value, len, 0, NULL, db_hash_write_value_cb, client); @@ -1431,6 +1436,67 @@ static bool read_db_hash(struct discovery_op *op) return true; } +static void db_server_feat_read(bool success, uint8_t att_ecode, + struct bt_gatt_result *result, void *user_data) +{ + struct discovery_op *op = user_data; + struct bt_gatt_client *client = op->client; + const uint8_t *value; + uint16_t len, handle; + struct bt_gatt_iter iter; + + if (!result) + return; + + bt_gatt_iter_init(&iter, result); + bt_gatt_iter_next_read_by_type(&iter, &handle, &len, &value); + + util_debug(client->debug_callback, client->debug_data, + "Server Features found: handle 0x%04x " + "length 0x%04x value 0x%02x", handle, len, + value[0]); + + op->server_feat = value[0]; +} + +static void server_feat_read_value(struct gatt_db_attribute *attrib, + int err, const uint8_t *value, + size_t length, void *user_data) +{ + const uint8_t **feat = user_data; + + if (err) + return; + + *feat = value; +} + +static void read_server_feat(struct discovery_op *op) +{ + struct bt_gatt_client *client = op->client; + struct gatt_db_attribute *attr = NULL; + const uint8_t *feat = NULL; + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (attr) { + /* Read stored value in the db */ + gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, NULL, + server_feat_read_value, &feat); + if (feat) + return; + } + + if (!bt_gatt_read_by_type(client->att, 0x0001, 0xffff, &uuid, + db_server_feat_read, + discovery_op_ref(op), + discovery_op_unref)) + discovery_op_unref(op); +} + static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data) { struct discovery_op *op = user_data; @@ -1464,6 +1530,8 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data) bt_att_get_mtu(client->att)); discover: + read_server_feat(op); + if (read_db_hash(op)) { op->success = false; return; @@ -1839,12 +1907,41 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value, queue_push_tail(client->svc_chngd_queue, op); } +static void server_feat_write_value(struct gatt_db_attribute *attrib, + int err, void *user_data) +{ + struct bt_gatt_client *client = user_data; + + util_debug(client->debug_callback, client->debug_data, + "Server Features Value set status: %d", err); +} + +static void write_server_features(struct bt_gatt_client *client, uint8_t feat) +{ + bt_uuid_t uuid; + struct gatt_db_attribute *attr = NULL; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (!attr) + return; + + /* Store value in the DB */ + if (!gatt_db_attribute_write(attr, 0, &feat, sizeof(feat), + 0, NULL, server_feat_write_value, + client)) + util_debug(client->debug_callback, client->debug_data, + "Unable to store Server Features"); +} + static void write_client_features(struct bt_gatt_client *client) { bt_uuid_t uuid; struct gatt_db_attribute *attr = NULL; uint16_t handle; - uint8_t value; + const uint8_t *feat = NULL; bt_uuid16_create(&uuid, GATT_CHARAC_CLI_FEAT); @@ -1854,10 +1951,28 @@ static void write_client_features(struct bt_gatt_client *client) return; handle = gatt_db_attribute_get_handle(attr); - value = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; - bt_gatt_client_write_value(client, handle, &value, sizeof(value), NULL, - NULL, NULL); + client->features = BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + + attr = NULL; + gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, + get_first_attribute, &attr); + if (attr) { + /* Read stored value in the db */ + gatt_db_attribute_read(attr, 0, BT_ATT_OP_READ_REQ, + NULL, server_feat_read_value, + &feat); + if (feat && feat[0] & BT_GATT_CHRC_SERVER_FEAT_EATT) + client->features |= BT_GATT_CHRC_CLI_FEAT_EATT; + } + + util_debug(client->debug_callback, client->debug_data, + "Writing Client Features 0x%02x", client->features); + + bt_gatt_client_write_value(client, handle, &client->features, + sizeof(client->features), NULL, NULL, NULL); } static void init_complete(struct discovery_op *op, bool success, @@ -1870,6 +1985,9 @@ static void init_complete(struct discovery_op *op, bool success, if (!success) goto fail; + if (op->server_feat) + write_server_features(client, op->server_feat); + write_client_features(client); if (register_service_changed(client)) @@ -1932,6 +2050,8 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu) return true; discover: + read_server_feat(op); + if (read_db_hash(op)) { op->success = false; goto done; @@ -2026,8 +2146,9 @@ static void notify_handler(void *data, void *user_data) notify_data->user_data); } -static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, - void *user_data) +static void notify_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_client *client = user_data; struct pdu_data pdu_data; @@ -2041,7 +2162,7 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, queue_foreach(client->notify_list, notify_handler, &pdu_data); if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent) - bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, + bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, NULL, NULL, NULL); bt_gatt_client_unref(client); @@ -2099,7 +2220,8 @@ static void att_disconnect_cb(int err, void *user_data) } static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, - struct bt_att *att) + struct bt_att *att, + uint8_t features) { struct bt_gatt_client *client; @@ -2129,6 +2251,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, client->att = bt_att_ref(att); client->db = gatt_db_ref(db); + client->features = features; return client; @@ -2140,14 +2263,15 @@ fail: struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, - uint16_t mtu) + uint16_t mtu, + uint8_t features) { struct bt_gatt_client *client; if (!att || !db) return NULL; - client = gatt_client_new(db, att); + client = gatt_client_new(db, att, features); if (!client) return NULL; @@ -2166,7 +2290,7 @@ struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client) if (!client) return NULL; - clone = gatt_client_new(client->db, client->att); + clone = gatt_client_new(client->db, client->att, client->features); if (!clone) return NULL; @@ -2284,6 +2408,14 @@ uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client) return bt_att_get_mtu(client->att); } +struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client) +{ + if (!client) + return NULL; + + return client->att; +} + struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client) { if (!client || !client->db) @@ -2292,6 +2424,17 @@ struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client) return client->db; } +uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client) +{ + if (!client) + return 0; + + if (client->parent) + return client->parent->features; + + return client->features; +} + static bool match_req_id(const void *a, const void *b) { const struct request *req = a; diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h index 6d8bf8043..10900168b 100644 --- a/src/shared/gatt-client.h +++ b/src/shared/gatt-client.h @@ -31,7 +31,8 @@ struct bt_gatt_client; struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, - uint16_t mtu); + uint16_t mtu, + uint8_t features); struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client); struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client); @@ -73,7 +74,9 @@ bool bt_gatt_client_set_debug(struct bt_gatt_client *client, bt_gatt_client_destroy_func_t destroy); uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client); +struct bt_att *bt_gatt_client_get_att(struct bt_gatt_client *client); struct gatt_db *bt_gatt_client_get_db(struct bt_gatt_client *client); +uint8_t bt_gatt_client_get_features(struct bt_gatt_client *client); bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id); bool bt_gatt_client_cancel_all(struct bt_gatt_client *client); diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 0d9bb0762..ee0058486 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -52,6 +52,7 @@ #define DEFAULT_MAX_PREP_QUEUE_LEN 30 struct async_read_op { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; bool done; @@ -62,6 +63,7 @@ struct async_read_op { }; struct async_write_op { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint8_t opcode; }; @@ -239,8 +241,9 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, return true; } -static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_by_grp_type_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -308,15 +311,14 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, queue_destroy(q, NULL); - bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, - rsp_pdu, rsp_len, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, + rsp_pdu, rsp_len); return; error: queue_destroy(q, NULL); - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } static void async_read_op_destroy(struct async_read_op *op) @@ -350,7 +352,7 @@ static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr, /* Terminate the operation if there was an error */ if (err) { - bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, + bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, handle, err); async_read_op_destroy(op); return; @@ -451,10 +453,8 @@ static void process_read_by_type(struct async_read_op *op) attr = queue_pop_head(op->db_data); if (op->done || !attr) { - bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, - op->pdu_len, - NULL, NULL, - NULL); + bt_att_chan_send_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_RSP, + op->pdu, op->pdu_len); async_read_op_destroy(op); return; } @@ -472,13 +472,14 @@ static void process_read_by_type(struct async_read_op *op) ecode = BT_ATT_ERROR_UNLIKELY; error: - bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, + bt_att_chan_send_error_rsp(op->chan, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); } -static void read_by_type_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_by_type_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -535,6 +536,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu, goto error; } + op->chan = chan; op->opcode = opcode; op->server = server; op->db_data = q; @@ -545,7 +547,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu, return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } @@ -603,8 +605,9 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q, return true; } -static void find_info_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void find_info_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; @@ -653,14 +656,14 @@ static void find_info_cb(uint8_t opcode, const void *pdu, goto error; } - bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len); + queue_destroy(q, NULL); return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); queue_destroy(q, NULL); } @@ -702,8 +705,9 @@ static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib, data->len += 4; } -static void find_by_type_val_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void find_by_type_val_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end, uuid16; @@ -748,13 +752,13 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu, if (data.ecode) goto error; - bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu, - data.len, NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_FIND_BY_TYPE_RSP, + data.pdu, data.len); return; error: - bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, data.ecode); } static void async_write_op_destroy(struct async_write_op *op) @@ -772,6 +776,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err, struct bt_gatt_server *server = op->server; uint16_t handle; + util_debug(server->debug_callback, server->debug_data, + "Write Complete: err %d", err); + if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) { async_write_op_destroy(op); return; @@ -780,10 +787,9 @@ static void write_complete_cb(struct gatt_db_attribute *attr, int err, handle = gatt_db_attribute_get_handle(attr); if (err) - bt_att_send_error_rsp(server->att, op->opcode, handle, err); + bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); else - bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(op->chan, BT_ATT_OP_WRITE_RSP, NULL, 0); async_write_op_destroy(op); } @@ -798,7 +804,7 @@ static uint8_t authorize_req(struct bt_gatt_server *server, server->authorize_data); } -static void write_cb(uint8_t opcode, const void *pdu, +static void write_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; @@ -840,6 +846,7 @@ static void write_cb(uint8_t opcode, const void *pdu, } op = new0(struct async_write_op, 1); + op->chan = chan; op->server = server; op->opcode = opcode; server->pending_write_op = op; @@ -857,7 +864,7 @@ error: if (opcode == BT_ATT_OP_WRITE_CMD) return; - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } static uint8_t get_read_rsp_opcode(uint8_t opcode) @@ -893,6 +900,9 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err, uint16_t mtu; uint16_t handle; + util_debug(server->debug_callback, server->debug_data, + "Read Complete: err %d", err); + if (!server) { async_read_op_destroy(op); return; @@ -902,22 +912,21 @@ static void read_complete_cb(struct gatt_db_attribute *attr, int err, handle = gatt_db_attribute_get_handle(attr); if (err) { - bt_att_send_error_rsp(server->att, op->opcode, handle, err); + bt_att_chan_send_error_rsp(op->chan, op->opcode, handle, err); async_read_op_destroy(op); return; } rsp_opcode = get_read_rsp_opcode(op->opcode); - bt_att_send(server->att, rsp_opcode, len ? value : NULL, - MIN((unsigned) mtu - 1, len), - NULL, NULL, NULL); + bt_att_chan_send_rsp(op->chan, rsp_opcode, len ? value : NULL, + MIN((unsigned int) mtu - 1, len)); async_read_op_destroy(op); } -static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, - uint16_t handle, - uint16_t offset) +static void handle_read_req(struct bt_att_chan *chan, + struct bt_gatt_server *server, uint8_t opcode, + uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; @@ -950,6 +959,7 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, } op = new0(struct async_read_op, 1); + op->chan = chan; op->opcode = opcode; op->server = server; server->pending_read_op = op; @@ -964,34 +974,35 @@ error: if (op) async_read_op_destroy(op); - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } -static void read_cb(uint8_t opcode, const void *pdu, +static void read_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle; if (length != 2) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } handle = get_le16(pdu); - handle_read_req(server, opcode, handle, 0); + handle_read_req(chan, server, opcode, handle, 0); } -static void read_blob_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle, offset; if (length != 4) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } @@ -999,10 +1010,11 @@ static void read_blob_cb(uint8_t opcode, const void *pdu, handle = get_le16(pdu); offset = get_le16(pdu + 2); - handle_read_req(server, opcode, handle, offset); + handle_read_req(chan, server, opcode, handle, offset); } struct read_multiple_resp_data { + struct bt_att_chan *chan; struct bt_gatt_server *server; uint16_t *handles; size_t cur_handle; @@ -1029,7 +1041,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, uint8_t ecode; if (err != 0) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; @@ -1039,7 +1051,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, handle, ecode); read_multiple_resp_data_free(data); return; @@ -1054,8 +1066,8 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { - bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, - data->rsp_data, data->length, NULL, NULL, NULL); + bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP, + data->rsp_data, data->length); read_multiple_resp_data_free(data); return; } @@ -1069,7 +1081,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, data->handles[data->cur_handle]); if (!next_attr) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); @@ -1080,7 +1092,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { - bt_att_send_error_rsp(data->server->att, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); @@ -1088,8 +1100,9 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, } } -static void read_multiple_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; @@ -1103,6 +1116,7 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, } data = new0(struct read_multiple_resp_data, 1); + data->chan = chan; data->handles = NULL; data->rsp_data = NULL; data->server = server; @@ -1139,7 +1153,7 @@ error: if (data) read_multiple_resp_data_free(data); - bt_att_send_error_rsp(server->att, opcode, 0, ecode); + bt_att_chan_send_error_rsp(chan, opcode, 0, ecode); } static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle, @@ -1230,6 +1244,7 @@ static bool store_prep_data(struct bt_gatt_server *server, } struct prep_write_complete_data { + struct bt_att_chan *chan; void *pdu; uint16_t length; struct bt_gatt_server *server; @@ -1245,8 +1260,8 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, handle = get_le16(pwcd->pdu); if (err) { - bt_att_send_error_rsp(pwcd->server->att, - BT_ATT_OP_PREP_WRITE_REQ, handle, err); + bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_REQ, + handle, err); free(pwcd->pdu); free(pwcd); @@ -1257,19 +1272,20 @@ static void prep_write_complete_cb(struct gatt_db_attribute *attr, int err, if (!store_prep_data(pwcd->server, handle, offset, pwcd->length - 4, &((uint8_t *) pwcd->pdu)[4])) - bt_att_send_error_rsp(pwcd->server->att, - BT_ATT_OP_PREP_WRITE_RSP, handle, + bt_att_chan_send_error_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, + handle, BT_ATT_ERROR_INSUFFICIENT_RESOURCES); - bt_att_send(pwcd->server->att, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, - pwcd->length, NULL, NULL, NULL); + bt_att_chan_send_rsp(pwcd->chan, BT_ATT_OP_PREP_WRITE_RSP, pwcd->pdu, + pwcd->length); free(pwcd->pdu); free(pwcd); } -static void prep_write_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void prep_write_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = 0; @@ -1307,6 +1323,7 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, goto error; pwcd = new0(struct prep_write_complete_data, 1); + pwcd->chan = chan; pwcd->pdu = malloc(length); memcpy(pwcd->pdu, pdu, length); pwcd->length = length; @@ -1323,23 +1340,28 @@ static void prep_write_cb(uint8_t opcode, const void *pdu, ecode = BT_ATT_ERROR_UNLIKELY; error: - bt_att_send_error_rsp(server->att, opcode, handle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, handle, ecode); } -static void exec_next_prep_write(struct bt_gatt_server *server, - uint16_t ehandle, int err); +struct exec_data { + struct bt_att_chan *chan; + struct bt_gatt_server *server; +}; + +static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, + int err); static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { - struct bt_gatt_server *server = user_data; + struct exec_data *data = user_data; uint16_t handle = gatt_db_attribute_get_handle(attr); - exec_next_prep_write(server, handle, err); + exec_next_prep_write(data, handle, err); } -static void exec_next_prep_write(struct bt_gatt_server *server, - uint16_t ehandle, int err) +static void exec_next_prep_write(struct exec_data *data, uint16_t ehandle, + int err) { struct prep_write_data *next = NULL; struct gatt_db_attribute *attr; @@ -1348,14 +1370,15 @@ static void exec_next_prep_write(struct bt_gatt_server *server, if (err) goto error; - next = queue_pop_head(server->prep_queue); + next = queue_pop_head(data->server->prep_queue); if (!next) { - bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_RSP, + NULL, 0); + free(data); return; } - attr = gatt_db_get_attribute(server->db, next->handle); + attr = gatt_db_get_attribute(data->server->db, next->handle); if (!attr) { err = BT_ATT_ERROR_UNLIKELY; goto error; @@ -1364,8 +1387,8 @@ static void exec_next_prep_write(struct bt_gatt_server *server, status = gatt_db_attribute_write(attr, next->offset, next->value, next->length, BT_ATT_OP_EXEC_WRITE_REQ, - server->att, - exec_write_complete_cb, server); + data->server->att, + exec_write_complete_cb, data); prep_write_data_destroy(next); @@ -1375,11 +1398,12 @@ static void exec_next_prep_write(struct bt_gatt_server *server, err = BT_ATT_ERROR_UNLIKELY; error: - queue_remove_all(server->prep_queue, NULL, NULL, + queue_remove_all(data->server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ, + bt_att_chan_send_error_rsp(data->chan, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); + free(data); } static bool find_no_reliable_characteristic(const void *data, @@ -1390,10 +1414,12 @@ static bool find_no_reliable_characteristic(const void *data, return !prep_data->reliable_supported; } -static void exec_write_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void exec_write_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; + struct exec_data *data; uint8_t flags; uint8_t ecode; bool write; @@ -1421,8 +1447,7 @@ static void exec_write_cb(uint8_t opcode, const void *pdu, if (!write) { queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, - NULL, NULL, NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0); return; } @@ -1439,18 +1464,23 @@ static void exec_write_cb(uint8_t opcode, const void *pdu, } } - exec_next_prep_write(server, 0, 0); + data = new0(struct exec_data, 1); + data->chan = chan; + data->server = server; + + exec_next_prep_write(data, 0, 0); return; error: queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); - bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); + bt_att_chan_send_error_rsp(chan, opcode, ehandle, ecode); } -static void exchange_mtu_cb(uint8_t opcode, const void *pdu, - uint16_t length, void *user_data) +static void exchange_mtu_cb(struct bt_att_chan *chan, uint8_t opcode, + const void *pdu, uint16_t length, + void *user_data) { struct bt_gatt_server *server = user_data; uint16_t client_rx_mtu; @@ -1458,7 +1488,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, uint8_t rsp_pdu[2]; if (length != 2) { - bt_att_send_error_rsp(server->att, opcode, 0, + bt_att_chan_send_error_rsp(chan, opcode, 0, BT_ATT_ERROR_INVALID_PDU); return; } @@ -1468,8 +1498,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, /* Respond with the server MTU */ put_le16(server->mtu, rsp_pdu); - bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL, - NULL); + bt_att_chan_send_rsp(chan, BT_ATT_OP_MTU_RSP, rsp_pdu, 2); /* Set MTU to be the minimum */ server->mtu = final_mtu; diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 7df659747..82a9e3fe0 100644 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -218,7 +218,7 @@ static struct client *client_create(int fd, uint16_t mtu) return NULL; } - cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu); + cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0); if (!cli->gatt) { fprintf(stderr, "Failed to create GATT client\n"); gatt_db_unref(cli->db); diff --git a/unit/test-gatt.c b/unit/test-gatt.c index e35271b61..d94993b9c 100644 --- a/unit/test-gatt.c +++ b/unit/test-gatt.c @@ -124,8 +124,16 @@ struct context { raw_pdu(0x02, 0x00, 0x02), \ raw_pdu(0x03, 0x00, 0x02) -#define SERVICE_DATA_1_PDUS \ +#define READ_SERVER_FEAT_PDUS \ + raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x3a, 0x2b), \ + raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a) + +#define CLIENT_INIT_PDUS \ MTU_EXCHANGE_CLIENT_PDUS, \ + READ_SERVER_FEAT_PDUS + +#define SERVICE_DATA_1_PDUS \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ @@ -150,7 +158,7 @@ struct context { raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29) #define SERVICE_DATA_2_PDUS \ - MTU_EXCHANGE_CLIENT_PDUS, \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \ @@ -175,7 +183,7 @@ struct context { raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29) #define SERVICE_DATA_3_PDUS \ - MTU_EXCHANGE_CLIENT_PDUS, \ + CLIENT_INIT_PDUS, \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \ 0x00, 0x02, 0x00, 0x02, 0x01, 0x18), \ @@ -683,7 +691,7 @@ static struct context *create_context(uint16_t mtu, gconstpointer data) g_assert(context->client_db); context->client = bt_gatt_client_new(context->client_db, - context->att, mtu); + context->att, mtu, 0); g_assert(context->client); bt_gatt_client_set_debug(context->client, print_debug, @@ -2371,8 +2379,7 @@ int main(int argc, char *argv[]) * Discovery of Services and Service Characteristics. */ define_test_att("/TP/GAD/CL/BV-01-C", test_search_primary, NULL, NULL, - raw_pdu(0x02, 0x00, 0x02), - raw_pdu(0x03, 0x00, 0x02), + MTU_EXCHANGE_CLIENT_PDUS, raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x00, 0x18, 0x20, 0x00, 0x29, 0x00, 0xb0, 0x68, @@ -3245,7 +3252,7 @@ int main(int argc, char *argv[]) define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db, &test_notification_1, - MTU_EXCHANGE_CLIENT_PDUS, + CLIENT_INIT_PDUS, SMALL_DB_DISCOVERY_PDUS, raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00), raw_pdu(0x13), @@ -3271,7 +3278,7 @@ int main(int argc, char *argv[]) define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db, &test_indication_1, - MTU_EXCHANGE_CLIENT_PDUS, + CLIENT_INIT_PDUS, SMALL_DB_DISCOVERY_PDUS, raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00), raw_pdu(0x13), From patchwork Fri Feb 28 23:46:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413601 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1011814B7 for ; Fri, 28 Feb 2020 23:47:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DA3C6246B6 for ; Fri, 28 Feb 2020 23:47:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MswAdl4h" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726758AbgB1XrM (ORCPT ); Fri, 28 Feb 2020 18:47:12 -0500 Received: from mail-pf1-f195.google.com ([209.85.210.195]:37824 "EHLO mail-pf1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbgB1XrM (ORCPT ); Fri, 28 Feb 2020 18:47:12 -0500 Received: by mail-pf1-f195.google.com with SMTP id p14so2499002pfn.4 for ; Fri, 28 Feb 2020 15:47:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=ogjWjETBYwo566sKocT/kttAKA3EKZNShflLoYgN4s4=; b=MswAdl4hwvViY4ov+Zvzbcatv89gW/aMrmjkuXUITtImIOt1nGpc7WwxRbQFMNnpfk hUt5jYMyRvRytO9KZIHbGXlEFwGxisKAe63Ys2cgFTMqsabJTM3NlOYkE71A+Ygyksm5 nPra7URGHMZDgplW85S/sqty+iQYM8ni+dXFYYVC3hRczUA24nxfq+qLXHLYiAepK/pj MHkURiFqpiHVkrcjt4TUbFlURhCMpxHYUoI47iLc4uRje3MVGI5gLjpASg6klzsmNY6R ltXIeqVuFpfEoG5S0bQS6LclrK8DVLqp+Efd1/F1sDNfT7wbk0KEcheHlJ8eAjGuKJhm MDdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ogjWjETBYwo566sKocT/kttAKA3EKZNShflLoYgN4s4=; b=HSQFkuZGoXgNFGPJb11YMSAfjeYVPCNTLhW1QfKXB02n/A5U4mhV0aspXWTu7p8r76 k8+WlytREE9BJ3/VOqXg8n4k/Jls8Uq2dqwXiRxNmhUSworZptfSJgXbjC0RKjQUPcIP 03N3PWAlKVN4NXYwp/12A6DMq49tmQYJEIU7muWqOIrm0QjBhyyOlH96+GrhOTQYji27 DAGSdW7TEBwrEuVjxrHEVqiaRry+E4ijWcgAyzzVPAbHwEJCvKsGl8IsqQZVm8WyqbLY Ytwg5zyfJAlZpB4+0VoxEJet1Og3VWPVSo8PoGmAKmhQZidR+GRMq0oCLFGOiAa6HsVU LTjA== X-Gm-Message-State: APjAAAWENwP3ksVKJbuLPhC1vFiovvFUhp1zqmlXfcJU8lrOMi/G9fNG gHbBKbfNbBA5zJs1UStN29mscM48ST4= X-Google-Smtp-Source: APXvYqz+IAbMYdpT0zJm1LrLVjc2jHDTJ/6S0bv753ij5VWw4pNC7KB5qMVHllUHY7Di8od/8WCDXQ== X-Received: by 2002:aa7:96b6:: with SMTP id g22mr6815686pfk.206.1582933629688; Fri, 28 Feb 2020 15:47:09 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.08 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:09 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 06/12] gatt: Enable EATT bearer support Date: Fri, 28 Feb 2020 15:46:55 -0800 Message-Id: <20200228234701.14614-7-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds support for EATT connections. --- src/device.c | 11 +++++ src/gatt-client.c | 83 ++++++++++++++++++++++++++++++++++ src/gatt-database.c | 107 +++++++++++++++++++++++++++++++++----------- 3 files changed, 175 insertions(+), 26 deletions(-) diff --git a/src/device.c b/src/device.c index 3f4afa281..c86e9a64d 100644 --- a/src/device.c +++ b/src/device.c @@ -5048,6 +5048,17 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) return false; } + if (dev->att) { + if (!bt_att_attach_fd(dev->att, g_io_channel_unix_get_fd(io))) { + DBG("EATT channel connected"); + g_io_channel_set_close_on_unref(io, FALSE); + return true; + } + + error("Failed to attach EATT channel"); + return false; + } + if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) { DBG("Elevating security level since LTK is available"); diff --git a/src/gatt-client.c b/src/gatt-client.c index 6bcdecf09..aa77661ad 100644 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -35,9 +35,11 @@ #include "lib/uuid.h" #include "gdbus/gdbus.h" +#include "btio/btio.h" #include "log.h" #include "error.h" +#include "hcid.h" #include "adapter.h" #include "device.h" #include "src/shared/io.h" @@ -57,8 +59,11 @@ #define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1" #define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1" +#define EATT_MAX_BEARERS 2 + struct btd_gatt_client { struct btd_device *device; + uint8_t features; bool ready; char devaddr[18]; struct gatt_db *db; @@ -2154,6 +2159,70 @@ static void register_notify(void *data, void *user_data) notify_client_free(notify_client); } +static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) +{ + struct btd_gatt_client *client = user_data; + + if (gerr) + return; + + device_attach_att(client->device, io); +} + +static void eatt_connect(struct btd_gatt_client *client) +{ + struct btd_device *dev = client->device; + struct btd_adapter *adapter = device_get_adapter(dev); + GIOChannel *io; + GError *gerr = NULL; + char addr[18]; + int i; + + ba2str(device_get_address(dev), addr); + + DBG("Connection attempt to: %s", addr); + + for (i = 0; i < EATT_MAX_BEARERS; i++) { + io = bt_io_connect(eatt_connect_cb, client, NULL, NULL, + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(adapter), + BT_IO_OPT_SOURCE_TYPE, + btd_adapter_get_address_type(adapter), + BT_IO_OPT_DEST_BDADDR, device_get_address(dev), + BT_IO_OPT_DEST_TYPE, + device_get_le_address_type(dev), + BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL, + BT_IO_OPT_PSM, BT_ATT_EATT_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_MTU, main_opts.gatt_mtu, + BT_IO_OPT_INVALID); + if (io == NULL) { + /* Fallback to regular LE mode */ + io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(adapter), + BT_IO_OPT_SOURCE_TYPE, + btd_adapter_get_address_type(adapter), + BT_IO_OPT_DEST_BDADDR, + device_get_address(dev), + BT_IO_OPT_DEST_TYPE, + device_get_le_address_type(dev), + BT_IO_OPT_PSM, BT_ATT_EATT_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_MTU, main_opts.gatt_mtu, + BT_IO_OPT_INVALID); + if (!io) { + error("EATT bt_io_connect(%s): %s", addr, + gerr->message); + g_error_free(gerr); + return; + } + } + + g_io_channel_unref(io); + } +} + void btd_gatt_client_ready(struct btd_gatt_client *client) { if (!client) @@ -2175,6 +2244,15 @@ void btd_gatt_client_ready(struct btd_gatt_client *client) DBG("GATT client ready"); create_services(client); + + DBG("Features 0x%02x", client->features); + + if (!client->features) { + client->features = bt_gatt_client_get_features(client->gatt); + DBG("Update Features 0x%02x", client->features); + if (client->features & BT_GATT_CHRC_CLI_FEAT_EATT) + eatt_connect(client); + } } void btd_gatt_client_connected(struct btd_gatt_client *client) @@ -2197,6 +2275,11 @@ void btd_gatt_client_connected(struct btd_gatt_client *client) * for any pre-registered notification sessions. */ queue_foreach(client->all_notify_clients, register_notify, client); + + if (!(client->features & BT_GATT_CHRC_CLI_FEAT_EATT)) + return; + + eatt_connect(client); } void btd_gatt_client_service_added(struct btd_gatt_client *client, diff --git a/src/gatt-database.c b/src/gatt-database.c index 419e4f9e1..2445d1fa5 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -48,14 +48,6 @@ #include "profile.h" #include "service.h" -#ifndef ATT_CID -#define ATT_CID 4 -#endif - -#ifndef ATT_PSM -#define ATT_PSM 31 -#endif - #define GATT_MANAGER_IFACE "org.bluez.GattManager1" #define GATT_PROFILE_IFACE "org.bluez.GattProfile1" #define GATT_SERVICE_IFACE "org.bluez.GattService1" @@ -80,7 +72,8 @@ struct btd_gatt_database { struct gatt_db *db; unsigned int db_id; GIOChannel *le_io; - GIOChannel *l2cap_io; + GIOChannel *eatt_io; + GIOChannel *bredr_io; struct queue *records; struct queue *device_states; struct queue *ccc_callbacks; @@ -88,6 +81,7 @@ struct btd_gatt_database { struct gatt_db_attribute *svc_chngd_ccc; struct gatt_db_attribute *cli_feat; struct gatt_db_attribute *db_hash; + struct gatt_db_attribute *eatt; struct queue *apps; struct queue *profiles; }; @@ -594,9 +588,14 @@ static void gatt_database_free(void *data) g_io_channel_unref(database->le_io); } - if (database->l2cap_io) { - g_io_channel_shutdown(database->l2cap_io, FALSE, NULL); - g_io_channel_unref(database->l2cap_io); + if (database->eatt_io) { + g_io_channel_shutdown(database->eatt_io, FALSE, NULL); + g_io_channel_unref(database->eatt_io); + } + + if (database->bredr_io) { + g_io_channel_shutdown(database->bredr_io, FALSE, NULL); + g_io_channel_unref(database->bredr_io); } /* TODO: Persistently store CCC states before freeing them */ @@ -717,7 +716,7 @@ static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end) uuid_t root_uuid, proto_uuid, l2cap; sdp_record_t *record; sdp_data_t *psm, *sh, *eh; - uint16_t lp = ATT_PSM; + uint16_t lp = BT_ATT_PSM; if (uuid == NULL) return NULL; @@ -1098,7 +1097,10 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib, { struct btd_gatt_database *database = user_data; struct device_state *state; + uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING, + BT_GATT_CHRC_CLI_FEAT_EATT }; uint8_t ecode = 0; + unsigned int i; DBG("Client Features write"); @@ -1113,13 +1115,12 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib, goto done; } - /* A client shall never clear a bit it has set. - * TODO: make it generic to any bits. - */ - if (state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING && - !(value[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) { - ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED; - goto done; + for (i = 0; i < sizeof(bits); i++) { + /* A client shall never clear a bit it has set */ + if (state->cli_feat[0] & (1 << i) && !(value[0] & (1 << i))) { + ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED; + goto done; + } } /* Shall we reallocate the feat array if bigger? */ @@ -1129,7 +1130,7 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib, len--; } - state->cli_feat[0] &= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; + state->cli_feat[0] &= ((1 << sizeof(bits)) - 1); state->change_aware = true; done: @@ -1161,6 +1162,28 @@ static void db_hash_read_cb(struct gatt_db_attribute *attrib, state->change_aware = true; } +static void server_feat_read_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct btd_gatt_database *database = user_data; + struct device_state *state; + uint8_t ecode = 0; + uint8_t value = 0; + + state = get_device_state(database, att); + if (!state) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + value |= BT_GATT_CHRC_SERVER_FEAT_EATT; + +done: + gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value)); +} + static void populate_gatt_service(struct btd_gatt_database *database) { bt_uuid_t uuid; @@ -1168,7 +1191,7 @@ static void populate_gatt_service(struct btd_gatt_database *database) /* Add the GATT service */ bt_uuid16_create(&uuid, UUID_GATT); - service = gatt_db_add_service(database->db, &uuid, true, 8); + service = gatt_db_add_service(database->db, &uuid, true, 10); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid, @@ -1191,6 +1214,11 @@ static void populate_gatt_service(struct btd_gatt_database *database) &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, db_hash_read_cb, NULL, database); + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + database->eatt = gatt_db_service_add_characteristic(service, + &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, + server_feat_read_cb, NULL, database); + gatt_db_service_set_active(service, true); database_add_record(database, service); @@ -3525,7 +3553,7 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) BT_IO_OPT_SOURCE_BDADDR, addr, BT_IO_OPT_SOURCE_TYPE, btd_adapter_get_address_type(adapter), - BT_IO_OPT_CID, ATT_CID, + BT_IO_OPT_CID, BT_ATT_CID, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (!database->le_io) { @@ -3534,14 +3562,41 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) goto fail; } + /* EATT socket */ + database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, NULL, + BT_IO_OPT_SOURCE_BDADDR, addr, + BT_IO_OPT_SOURCE_TYPE, + btd_adapter_get_address_type(adapter), + BT_IO_OPT_MODE, BT_IO_MODE_EXT_FLOWCTL, + BT_IO_OPT_PSM, BT_ATT_EATT_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_MTU, main_opts.gatt_mtu, + BT_IO_OPT_INVALID); + if (!database->eatt_io) { + /* Fallback to regular LE mode */ + database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, + &gerr, + BT_IO_OPT_SOURCE_BDADDR, addr, + BT_IO_OPT_SOURCE_TYPE, + btd_adapter_get_address_type(adapter), + BT_IO_OPT_PSM, BT_ATT_EATT_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_MTU, main_opts.gatt_mtu, + BT_IO_OPT_INVALID); + if (!database->eatt_io) { + g_error_free(gerr); + goto fail; + } + } + /* BR/EDR socket */ - database->l2cap_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr, + database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, - BT_IO_OPT_PSM, ATT_PSM, + BT_IO_OPT_PSM, BT_ATT_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_MTU, main_opts.gatt_mtu, BT_IO_OPT_INVALID); - if (database->l2cap_io == NULL) { + if (database->bredr_io == NULL) { error("Failed to start listening: %s", gerr->message); g_error_free(gerr); goto fail; From patchwork Fri Feb 28 23:46:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413605 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 707F617E0 for ; Fri, 28 Feb 2020 23:47:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4F1D2246B6 for ; Fri, 28 Feb 2020 23:47:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XZl4iA/b" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726788AbgB1XrN (ORCPT ); Fri, 28 Feb 2020 18:47:13 -0500 Received: from mail-pg1-f193.google.com ([209.85.215.193]:41293 "EHLO mail-pg1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbgB1XrN (ORCPT ); Fri, 28 Feb 2020 18:47:13 -0500 Received: by mail-pg1-f193.google.com with SMTP id b1so2291633pgm.8 for ; Fri, 28 Feb 2020 15:47:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=F0EYRJheUMHXrROxyDRP4RYQ+UKzYUGuYTVdPdCLH+E=; b=XZl4iA/b7jylrHnuYNeX0LqAHO/fD+P4tLBeMhHPgUe04qJ0O2hI6nACzmiOmACb2v nMhKHMHAT3YZ1+5gzxLljUIyPX4kTYLCaijQ81Q0wVxB74Ep9wxotLI0KBBD+vYttWX1 gheI6aDQOCV0rCd9tcAfyEhgST5lZFgCuZpbzGmxjceEriHp+SxjwHTKt4plvaQSDNmV dO2QVFju0Jg8+dUDq7zs6UgKRRpFHiP3RIubDbPqg+hAKkbIznmu2Bwr0vHRj3/ZjJjH WLCsLazVTseADV9YbbgX8UtzMxgrzPYBVeUwSGqyZ45C81X2CNpDb08wDDVRUAcWEyBK +KgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=F0EYRJheUMHXrROxyDRP4RYQ+UKzYUGuYTVdPdCLH+E=; b=MS4YaETaFBHtp/NrRIDB6jPuilOHGkl+4b7ayFHTOVznrJk86hmcHDFYoCUIhr/rjU bArh4cJxNOz8oLlNAd/RfQ/2Jlt5Fvp5pyufL6uYP0GXlTJ+Eb+ciSAqYg1LFJw7mhgL m7/HuE9LJ8sRzMHLmqAYp8Pb/weobcNBn58NqSB7b5BM9lCyoGsCf9YqzWFdY2ddiHyW H4RV98zR35hAQ+lSXD2VekBNoQU528zSCQfDvEjAMVyuO4ffbTqossJOJHMgnfuwVu7g 4AjMojVyIdjWVhNKmS8t7E/vEVOLwMQLPHHifCReSXLBP8ZvAfgITZaAwgJWFnnq9rdJ XESQ== X-Gm-Message-State: APjAAAWMLXoATE5w6+2r69iSC6OHYqvtTn5+lWQsQOwFl4/2A7AvLq8P /NAFyKDJasD37IuEQ8vgrgKh71DNvHk= X-Google-Smtp-Source: APXvYqxb3gumvOZHNpm1UqtRR0Q9BN0T2DlO3pmYUnAX3L/hS+V0LGQ/0KGpnVDYrwzPfDtQ43Tkog== X-Received: by 2002:a63:e20a:: with SMTP id q10mr5725388pgh.331.1582933630570; Fri, 28 Feb 2020 15:47:10 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.09 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:10 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 07/12] shared/gatt-server: Add support for Read Multiple Variable Length Date: Fri, 28 Feb 2020 15:46:56 -0800 Message-Id: <20200228234701.14614-8-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz The Read Multiple Variable Length Request is used to request that the server read two or more values of a set of attributes that have a variable or unknown value length and return their values in a Read Multiple Variable Length Response. --- src/shared/att-types.h | 2 + src/shared/gatt-server.c | 113 ++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/shared/att-types.h b/src/shared/att-types.h index 7b88e7d92..cc9cc9fd6 100644 --- a/src/shared/att-types.h +++ b/src/shared/att-types.h @@ -75,6 +75,8 @@ #define BT_ATT_OP_HANDLE_VAL_NOT 0x1B #define BT_ATT_OP_HANDLE_VAL_IND 0x1D #define BT_ATT_OP_HANDLE_VAL_CONF 0x1E +#define BT_ATT_OP_READ_MULT_VL_REQ 0x20 +#define BT_ATT_OP_READ_MULT_VL_RSP 0x21 /* Packed struct definitions for ATT protocol PDUs */ /* TODO: Complete these definitions for all opcodes */ diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index ee0058486..8b18cb21f 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -102,6 +102,7 @@ struct bt_gatt_server { unsigned int read_id; unsigned int read_blob_id; unsigned int read_multiple_id; + unsigned int read_multiple_vl_id; unsigned int prep_write_id; unsigned int exec_write_id; @@ -136,6 +137,7 @@ static void bt_gatt_server_free(struct bt_gatt_server *server) bt_att_unregister(server->att, server->read_id); bt_att_unregister(server->att, server->read_blob_id); bt_att_unregister(server->att, server->read_multiple_id); + bt_att_unregister(server->att, server->read_multiple_vl_id); bt_att_unregister(server->att, server->prep_write_id); bt_att_unregister(server->att, server->exec_write_id); @@ -1013,9 +1015,10 @@ static void read_blob_cb(struct bt_att_chan *chan, uint8_t opcode, handle_read_req(chan, server, opcode, handle, offset); } -struct read_multiple_resp_data { +struct read_mult_data { struct bt_att_chan *chan; struct bt_gatt_server *server; + uint8_t opcode; uint16_t *handles; size_t cur_handle; size_t num_handles; @@ -1024,7 +1027,7 @@ struct read_multiple_resp_data { size_t mtu; }; -static void read_multiple_resp_data_free(struct read_multiple_resp_data *data) +static void read_mult_data_free(struct read_mult_data *data) { free(data->handles); free(data->rsp_data); @@ -1035,15 +1038,16 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { - struct read_multiple_resp_data *data = user_data; + struct read_mult_data *data = user_data; struct gatt_db_attribute *next_attr; uint16_t handle = gatt_db_attribute_get_handle(attr); uint8_t ecode; + uint16_t length; if (err != 0) { - bt_att_chan_send_error_rsp(data->chan, - BT_ATT_OP_READ_MULT_REQ, handle, err); - read_multiple_resp_data_free(data); + bt_att_chan_send_error_rsp(data->chan, data->opcode, handle, + err); + read_mult_data_free(data); return; } @@ -1051,29 +1055,45 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) { - bt_att_chan_send_error_rsp(data->chan, - BT_ATT_OP_READ_MULT_REQ, handle, ecode); - read_multiple_resp_data_free(data); + bt_att_chan_send_error_rsp(data->chan, data->opcode, handle, + ecode); + read_mult_data_free(data); return; } - len = MIN(len, data->mtu - data->length - 1); + length = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ? + MIN(len, data->mtu - data->length - 3) : + MIN(len, data->mtu - data->length - 1); - memcpy(data->rsp_data + data->length, value, len); - data->length += len; + if (data->opcode == BT_ATT_OP_READ_MULT_VL_REQ) { + /* The Length Value Tuple List may be truncated within the first + * two octets of a tuple due to the size limits of the current + * ATT_MTU. + */ + put_le16(len, data->rsp_data + data->length); + data->length += 2; + } + + memcpy(data->rsp_data + data->length, value, length); + data->length += length; data->cur_handle++; - if ((data->length >= data->mtu - 1) || - (data->cur_handle == data->num_handles)) { - bt_att_chan_send_rsp(data->chan, BT_ATT_OP_READ_MULT_RSP, + len = data->opcode == BT_ATT_OP_READ_MULT_VL_REQ ? + data->mtu - data->length - 3 : data->mtu - data->length - 1; + + if (!len || (data->cur_handle == data->num_handles)) { + bt_att_chan_send_rsp(data->chan, data->opcode + 1, data->rsp_data, data->length); - read_multiple_resp_data_free(data); + read_mult_data_free(data); return; } util_debug(data->server->debug_callback, data->server->debug_data, - "Read Multiple Req - #%zu of %zu: 0x%04x", + "%s Req - #%zu of %zu: 0x%04x", + data->opcode == BT_ATT_OP_READ_MULT_REQ ? + "Read Multiple" : + "Read Multiple Variable Length", data->cur_handle + 1, data->num_handles, data->handles[data->cur_handle]); @@ -1085,7 +1105,7 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); - read_multiple_resp_data_free(data); + read_mult_data_free(data); return; } @@ -1096,17 +1116,39 @@ static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); - read_multiple_resp_data_free(data); + read_mult_data_free(data); } } +static struct read_mult_data *read_mult_data_new(struct bt_gatt_server *server, + struct bt_att_chan *chan, + uint8_t opcode, + uint16_t num_handles) +{ + struct read_mult_data *data; + + data = new0(struct read_mult_data, 1); + data->chan = chan; + data->opcode = opcode; + data->handles = new0(uint16_t, num_handles); + data->rsp_data = NULL; + data->server = server; + data->num_handles = num_handles; + data->cur_handle = 0; + data->mtu = bt_att_get_mtu(server->att); + data->length = 0; + data->rsp_data = new0(uint8_t, data->mtu - 1); + + return data; +} + static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; - struct read_multiple_resp_data *data = NULL; + struct read_mult_data *data = NULL; uint8_t ecode = BT_ATT_ERROR_UNLIKELY; size_t i = 0; @@ -1115,27 +1157,17 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode, goto error; } - data = new0(struct read_multiple_resp_data, 1); - data->chan = chan; - data->handles = NULL; - data->rsp_data = NULL; - data->server = server; - data->num_handles = length / 2; - data->cur_handle = 0; - data->mtu = bt_att_get_mtu(server->att); - data->length = 0; - data->rsp_data = malloc(data->mtu - 1); - - if (!data->rsp_data) + data = read_mult_data_new(server, chan, opcode, length / 2); + if (!data) goto error; - data->handles = new0(uint16_t, data->num_handles); - for (i = 0; i < data->num_handles; i++) data->handles[i] = get_le16(pdu + i * 2); util_debug(server->debug_callback, server->debug_data, - "Read Multiple Req - %zu handles, 1st: 0x%04x", + "%s Req - %zu handles, 1st: 0x%04x", + data->opcode == BT_ATT_OP_READ_MULT_REQ ? + "Read Multiple" : "Read Multiple Variable Length", data->num_handles, data->handles[0]); attr = gatt_db_get_attribute(server->db, data->handles[0]); @@ -1151,7 +1183,7 @@ static void read_multiple_cb(struct bt_att_chan *chan, uint8_t opcode, error: if (data) - read_multiple_resp_data_free(data); + read_mult_data_free(data); bt_att_chan_send_error_rsp(chan, opcode, 0, ecode); } @@ -1588,6 +1620,15 @@ static bool gatt_server_register_att_handlers(struct bt_gatt_server *server) if (!server->read_multiple_id) return false; + /* Read Multiple Variable Length Request */ + server->read_multiple_vl_id = bt_att_register(server->att, + BT_ATT_OP_READ_MULT_VL_REQ, + read_multiple_cb, + server, NULL); + + if (!server->read_multiple_vl_id) + return false; + /* Prepare Write Request */ server->prep_write_id = bt_att_register(server->att, BT_ATT_OP_PREP_WRITE_REQ, From patchwork Fri Feb 28 23:46:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413607 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8E40F14B7 for ; Fri, 28 Feb 2020 23:47:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6D7F4246B6 for ; Fri, 28 Feb 2020 23:47:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mUcOj83b" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726838AbgB1XrP (ORCPT ); Fri, 28 Feb 2020 18:47:15 -0500 Received: from mail-pl1-f196.google.com ([209.85.214.196]:42992 "EHLO mail-pl1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726798AbgB1XrO (ORCPT ); Fri, 28 Feb 2020 18:47:14 -0500 Received: by mail-pl1-f196.google.com with SMTP id u3so1826775plr.9 for ; Fri, 28 Feb 2020 15:47:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=QWDfGgp0E1rqcdY0x4qxjaOMAPigahCylDGC8zfCPCs=; b=mUcOj83btugphxtskZuyFh/hffkA+yTaBHTdo1pYOYxrveUsrI78LYQsl1ANX1bqjD ZSnXJk+mjnsu5FKRl9J59jHMgTKEP7r04rlLuAV6TUH0+rufAHpXLR2hNek9Sn5wbcIq 5rJNI2pH2TSew9zcJ1xg1PSkksbEn/SW7Nvgnk8htv5OAAY6OedHoTk2DZfj4j+uEBW5 NQWQctazIwcUQpb3/4ZCoHGsi7XoASdqkE5X6fCV6a/0Jt6pK9JHZIzqz93TUtNuzKjy 9ASu5ozPFdDFy9d5se7RIN2H3L7mFCu9/wySExJl36ynWPeNwv5ta5mL3SvMzjKxPp3e vlkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QWDfGgp0E1rqcdY0x4qxjaOMAPigahCylDGC8zfCPCs=; b=bgLQtyYwvwqYQaTGmsQM/xXa33ClXrhDZvP4C2U3X4GXpfnXiEzG1TZz4r6NqumV+J Ftkl+evZTtZ5LBsLmHMW+73A12r2jKbZYuLPRmKztRdqNVlGJBPtUqg7NX+fVuNMy8ew Z6ZXSwvzfmCfrF23dpiQx78iuPSHui8cYjz1646iFh3nABTaLmMLIZZhjNIZuejKO827 GajnsFOVzFRi7a4K5bT7Djvdfgm2X/4iKBnKBGA41dugFmHQC2AWJ7D9tXlKCzbC5Zjd ho+fNt3UPhme6WZIXAfCbw5+nerWen1qUfhfq72w9eIjzTHRsU+KUp2N3s8OdzN8A/ZS 8LYw== X-Gm-Message-State: ANhLgQ00aooePaG3hrR5PS0Y3VmZU5jm9RLdeF6QyzgCkZ4AvG0vY+dI fIYz/VMHYOe4RtRDqiGsvem5QNnzxPI= X-Google-Smtp-Source: ADFU+vu9JfHadDKuXms5KfoT3PsGOmDgWr/jxwHK07pZ+EYpqbwHtk+s+CHx2vhumVk6j4bakIndwQ== X-Received: by 2002:a17:90a:3a86:: with SMTP id b6mr1403835pjc.96.1582933631765; Fri, 28 Feb 2020 15:47:11 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.10 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:11 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 08/12] shared/gatt-client: Add support for Read Multiple Variable Length Date: Fri, 28 Feb 2020 15:46:57 -0800 Message-Id: <20200228234701.14614-9-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz The Read Multiple Variable Length Request is used to request that the server read two or more values of a set of attributes that have a variable or unknown value length and return their values in a Read Multiple Variable Length Response. --- src/shared/gatt-client.c | 42 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 2c5fe14dc..5b6723f97 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -2642,7 +2642,9 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length, uint8_t att_ecode; bool success; - if (opcode != BT_ATT_OP_READ_MULT_RSP || (!pdu && length)) { + if ((opcode != BT_ATT_OP_READ_MULT_RSP && + opcode != BT_ATT_OP_READ_MULT_VL_RSP) || + (!pdu && length)) { success = false; if (opcode == BT_ATT_OP_ERROR_RSP) @@ -2657,8 +2659,36 @@ static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length, att_ecode = 0; } - if (op->callback) + if (!op->callback) + return; + + if (opcode == BT_ATT_OP_READ_MULT_RSP || att_ecode) { op->callback(success, att_ecode, pdu, length, op->user_data); + return; + } + + if (length < 2) { + op->callback(success, att_ecode, pdu, length, op->user_data); + return; + } + + /* Parse response */ + while (length >= 2) { + uint16_t len; + + len = get_le16(pdu); + length -= 2; + pdu += 2; + + /* The Length Value Tuple List may be truncated within the + * first two octets of a tuple due to the size limits of the + * current ATT_MTU. + */ + if (len > length) + length = len; + + op->callback(success, att_ecode, pdu, len, op->user_data); + } } unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, @@ -2670,6 +2700,7 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, uint8_t pdu[num_handles * 2]; struct request *req; struct read_op *op; + uint8_t opcode; int i; if (!client) @@ -2699,8 +2730,11 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, for (i = 0; i < num_handles; i++) put_le16(handles[i], pdu + (2 * i)); - req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_MULT_REQ, - pdu, sizeof(pdu), + opcode = bt_gatt_client_get_features(client) & + BT_GATT_CHRC_CLI_FEAT_EATT ? BT_ATT_OP_READ_MULT_VL_REQ : + BT_ATT_OP_READ_MULT_REQ; + + req->att_id = bt_att_send(client->att, opcode, pdu, sizeof(pdu), read_multiple_cb, req, request_unref); if (!req->att_id) { From patchwork Fri Feb 28 23:46:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413609 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E383E17E0 for ; Fri, 28 Feb 2020 23:47:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C24E3246B7 for ; Fri, 28 Feb 2020 23:47:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CgvRXxk9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726843AbgB1XrP (ORCPT ); Fri, 28 Feb 2020 18:47:15 -0500 Received: from mail-pg1-f194.google.com ([209.85.215.194]:34448 "EHLO mail-pg1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbgB1XrO (ORCPT ); Fri, 28 Feb 2020 18:47:14 -0500 Received: by mail-pg1-f194.google.com with SMTP id t3so2308023pgn.1 for ; Fri, 28 Feb 2020 15:47:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=fCeTaywODgjO8kmhS2KT8NC14kXxNHPxWVs5X0bXanE=; b=CgvRXxk9Fc9Qp/LaliGQtyUt6vmv+1gt9oiBAmaErQPzEfeBcLD2PFzgsTVW79mXpm 5R06+WwoHN87tD9wvzuAmOHsyGHQJQVmZ4xd7M91suZLy0ZJVReCW8XfnvAu6nAoAIAO J4ZNj6qRfgtYasBaEK5qjHG39kk5lUzyBA3cr374LSilquqHgeoCU/DzNuZ94gbIFdWE h2/YagpAgvnY5PA9o+yXANX5ABwv59T1OIXirbtmE2dtbxe1muS5ntacZUGz0AgH+Hs1 jp9SVSagkGZqbllzpULR52Fbaf68tgVUt3B4VexUseii8w8uVzpsF/FFUKLCUVvJOVer 0BwA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fCeTaywODgjO8kmhS2KT8NC14kXxNHPxWVs5X0bXanE=; b=BiAjKMhCY2crh5MdZe0JIGO190ZM8gSsUU77gXVhPYJlTbJHq8+6ZW/SFa2BTXhD0j tJwsPpdNG1pUV2iMX54krk0BBfjFd9lYwHPlPFfUbbasPnGXwUkRL0XWTpDpesg0zr3c Tc0WT4VLCVcVa4OWpkDf5fGREkIaHc2d2zDN1N5Wo0Km8aK/KOtSRih/F0jLeYzMy7vr TCCSIvhaNokSxxxUfo/krRs8AMAecxF0wpW3bCNparAXK/SmLppwhJSKUbZqSgEpfasy l/5Dza7vP/I5ydGKmpoJgPU6TwNGINkjXGx1GXH4l25bG7yx9mIu6I6tF0b16RBJ3foX ty8Q== X-Gm-Message-State: APjAAAVG7w0l5njRaBGqLL3XOrISXFu7Q6KHqaqRKgjPTIydZQpQUGwB cy98GFGay/POgoqx9egn3r/uIrohwQI= X-Google-Smtp-Source: APXvYqyZvhR4V+GfmBc5XQgLy4gzf7Rczqc3R6p9qFvARX/fMErkJAC8XZ1zaFQY4zHdVWHEtWXS1w== X-Received: by 2002:a63:a062:: with SMTP id u34mr7178395pgn.286.1582933633043; Fri, 28 Feb 2020 15:47:13 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.11 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:12 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 09/12] shared/gatt: Add support for Handle Value Multiple Notifications Date: Fri, 28 Feb 2020 15:46:58 -0800 Message-Id: <20200228234701.14614-10-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz Handle Value Multiple Notification can be used to notify multiple values at once. --- src/shared/att-types.h | 7 ++-- src/shared/att.c | 16 ++++----- src/shared/gatt-client.c | 76 ++++++++++++++++++++++++++-------------- src/shared/gatt-server.c | 4 +-- 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/src/shared/att-types.h b/src/shared/att-types.h index cc9cc9fd6..99b108990 100644 --- a/src/shared/att-types.h +++ b/src/shared/att-types.h @@ -72,11 +72,12 @@ #define BT_ATT_OP_PREP_WRITE_RSP 0x17 #define BT_ATT_OP_EXEC_WRITE_REQ 0x18 #define BT_ATT_OP_EXEC_WRITE_RSP 0x19 -#define BT_ATT_OP_HANDLE_VAL_NOT 0x1B -#define BT_ATT_OP_HANDLE_VAL_IND 0x1D -#define BT_ATT_OP_HANDLE_VAL_CONF 0x1E +#define BT_ATT_OP_HANDLE_NFY 0x1B +#define BT_ATT_OP_HANDLE_IND 0x1D +#define BT_ATT_OP_HANDLE_CONF 0x1E #define BT_ATT_OP_READ_MULT_VL_REQ 0x20 #define BT_ATT_OP_READ_MULT_VL_RSP 0x21 +#define BT_ATT_OP_HANDLE_NFY_MULT 0x23 /* Packed struct definitions for ATT protocol PDUs */ /* TODO: Complete these definitions for all opcodes */ diff --git a/src/shared/att.c b/src/shared/att.c index 56ea40c46..6fa16e422 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -110,7 +110,7 @@ enum att_op_type { ATT_OP_TYPE_RSP, ATT_OP_TYPE_CMD, ATT_OP_TYPE_IND, - ATT_OP_TYPE_NOT, + ATT_OP_TYPE_NFY, ATT_OP_TYPE_CONF, ATT_OP_TYPE_UNKNOWN, }; @@ -144,9 +144,9 @@ static const struct { { BT_ATT_OP_PREP_WRITE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP }, - { BT_ATT_OP_HANDLE_VAL_NOT, ATT_OP_TYPE_NOT }, - { BT_ATT_OP_HANDLE_VAL_IND, ATT_OP_TYPE_IND }, - { BT_ATT_OP_HANDLE_VAL_CONF, ATT_OP_TYPE_CONF }, + { BT_ATT_OP_HANDLE_NFY, ATT_OP_TYPE_NFY }, + { BT_ATT_OP_HANDLE_IND, ATT_OP_TYPE_IND }, + { BT_ATT_OP_HANDLE_CONF, ATT_OP_TYPE_CONF }, { } }; @@ -530,7 +530,7 @@ static bool can_write_data(struct io *io, void *user_data) chan->in_req = false; /* fall through */ case ATT_OP_TYPE_CMD: - case ATT_OP_TYPE_NOT: + case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_CONF: case ATT_OP_TYPE_UNKNOWN: default: @@ -842,7 +842,7 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) } if (op->callback) - op->callback(BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, op->user_data); + op->callback(BT_ATT_OP_HANDLE_NFY, NULL, 0, op->user_data); destroy_att_send_op(op); chan->pending_ind = NULL; @@ -1042,7 +1042,7 @@ static bool can_read_data(struct io *io, void *user_data) chan->in_req = true; /* fall through */ case ATT_OP_TYPE_CMD: - case ATT_OP_TYPE_NOT: + case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_IND: /* fall through */ @@ -1492,7 +1492,7 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode, result = queue_push_tail(att->ind_queue, op); break; case ATT_OP_TYPE_CMD: - case ATT_OP_TYPE_NOT: + case ATT_OP_TYPE_NFY: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_RSP: case ATT_OP_TYPE_CONF: diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 5b6723f97..7381f96e0 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -95,7 +95,7 @@ struct bt_gatt_client { struct queue *notify_list; struct queue *notify_chrcs; int next_reg_id; - unsigned int disc_id, notify_id, ind_id; + unsigned int disc_id, nfy_id, nfy_mult_id, ind_id; /* * Handles of the GATT Service and the Service Changed characteristic @@ -2072,9 +2072,10 @@ done: return true; } -struct pdu_data { - const void *pdu; - uint16_t length; +struct value_data { + uint16_t handle; + uint16_t len; + const void *data; }; static void disable_ccc_callback(uint8_t opcode, const void *pdu, @@ -2125,25 +2126,18 @@ done: static void notify_handler(void *data, void *user_data) { struct notify_data *notify_data = data; - struct pdu_data *pdu_data = user_data; - uint16_t value_handle; - const uint8_t *value = NULL; - - value_handle = get_le16(pdu_data->pdu); + struct value_data *value_data = user_data; - if (notify_data->chrc->value_handle != value_handle) + if (notify_data->chrc->value_handle != value_data->handle) return; - if (pdu_data->length > 2) - value = pdu_data->pdu + 2; - /* * Even if the notify data has a pending ATT request to write to the * CCC, there is really no reason not to notify the handlers. */ if (notify_data->notify) - notify_data->notify(value_handle, value, pdu_data->length - 2, - notify_data->user_data); + notify_data->notify(value_data->handle, value_data->data, + value_data->len, notify_data->user_data); } static void notify_cb(struct bt_att_chan *chan, uint8_t opcode, @@ -2151,18 +2145,42 @@ static void notify_cb(struct bt_att_chan *chan, uint8_t opcode, void *user_data) { struct bt_gatt_client *client = user_data; - struct pdu_data pdu_data; + struct value_data data; bt_gatt_client_ref(client); - memset(&pdu_data, 0, sizeof(pdu_data)); - pdu_data.pdu = pdu; - pdu_data.length = length; + memset(&data, 0, sizeof(data)); + + if (opcode == BT_ATT_OP_HANDLE_NFY_MULT) { + while (length >= 4) { + data.handle = get_le16(pdu); + length -= 2; + pdu += 2; + + data.len = get_le16(pdu); + length -= 2; + pdu += 2; + + data.data = pdu; + + queue_foreach(client->notify_list, notify_handler, + &data); - queue_foreach(client->notify_list, notify_handler, &pdu_data); + length -= data.len; + } + } else { + data.handle = get_le16(pdu); + length -= 2; + pdu += 2; + + data.len = length; + data.data = pdu; - if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent) - bt_att_chan_send(chan, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, + queue_foreach(client->notify_list, notify_handler, &data); + } + + if (opcode == BT_ATT_OP_HANDLE_IND && !client->parent) + bt_att_chan_send(chan, BT_ATT_OP_HANDLE_CONF, NULL, 0, NULL, NULL, NULL); bt_gatt_client_unref(client); @@ -2181,7 +2199,8 @@ static void bt_gatt_client_free(struct bt_gatt_client *client) if (client->att) { bt_att_unregister_disconnect(client->att, client->disc_id); - bt_att_unregister(client->att, client->notify_id); + bt_att_unregister(client->att, client->nfy_id); + bt_att_unregister(client->att, client->nfy_mult_id); bt_att_unregister(client->att, client->ind_id); bt_att_unref(client->att); } @@ -2239,12 +2258,17 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, client->notify_chrcs = queue_new(); client->pending_requests = queue_new(); - client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT, + client->nfy_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY, + notify_cb, client, NULL); + if (!client->nfy_id) + goto fail; + + client->nfy_mult_id = bt_att_register(att, BT_ATT_OP_HANDLE_NFY_MULT, notify_cb, client, NULL); - if (!client->notify_id) + if (!client->nfy_mult_id) goto fail; - client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_IND, + client->ind_id = bt_att_register(att, BT_ATT_OP_HANDLE_IND, notify_cb, client, NULL); if (!client->ind_id) goto fail; diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 8b18cb21f..8e81d6e0e 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -1745,7 +1745,7 @@ bool bt_gatt_server_send_notification(struct bt_gatt_server *server, put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); - result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu, + result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, pdu, pdu_len, NULL, NULL, NULL); free(pdu); @@ -1806,7 +1806,7 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server, put_le16(handle, pdu); memcpy(pdu + 2, value, pdu_len - 2); - result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu, + result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_IND, pdu, pdu_len, conf_cb, data, destroy_ind_data); if (!result) From patchwork Fri Feb 28 23:46:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413613 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3AABD17E0 for ; Fri, 28 Feb 2020 23:47:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0FA79246B9 for ; Fri, 28 Feb 2020 23:47:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d9YE6H/v" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726860AbgB1XrR (ORCPT ); Fri, 28 Feb 2020 18:47:17 -0500 Received: from mail-pj1-f53.google.com ([209.85.216.53]:52738 "EHLO mail-pj1-f53.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725957AbgB1XrR (ORCPT ); Fri, 28 Feb 2020 18:47:17 -0500 Received: by mail-pj1-f53.google.com with SMTP id ep11so1910637pjb.2 for ; Fri, 28 Feb 2020 15:47:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=9MeUOiYNiKf6MLQmBNl/ygbA8CVOmtoSr1PMVUY3JEs=; b=d9YE6H/v/l/C1sK5nneHEp60x/ifEApV+tzj4vjjmiz2eu3kay8oe0dOU00tF37Qkt fWXkJGDJrE+sV78q3EwfhupUxyJK7MU2Zdhs2vi8k2tdQSi9SJ0Gnw7NClRs5iRrGUls a1qv6k6iZ98mnV3kC4/epNgN6gO6QFuCU8yXB1312YVDfp9CsFIlzY/9p1lPBfMwHyXs BkCpLMoRC3I6ONZhp1OcTKoN3JnkPU53fs0qvlpEODW0gN6uP1+wC3JblU2/qtagcJTK 4si8hw56wuVqpuM6kF6rpgDqh5nxPgaxN9ew7KsoeY3Z3rIan9IWim2fYKPt2hb+Pk3K MgEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=9MeUOiYNiKf6MLQmBNl/ygbA8CVOmtoSr1PMVUY3JEs=; b=o54I69hwOUGMGikud7cYaR8HoreMQz7ODR2HlqTsO48525WGCUYMYWwS/YFByYrl4j RxMQPpppgWg6PDoRWuTNKROO07+GLk9fO8B4drBJOj1QRdunXoPsnf/gOX7vwMFSvsia WaaoiQ+nnx6PW0sKatUBcUudKT9swU75vL7gE2iQ6mr/fzPaOi/0ni6ClAGWrQlwR6E/ EM++yl3XeI7HnF9OOYWSQl7MgKUYFf0e8AbwLZjxUuRPpMu2wbxToNsp93uo6ZwXGyja xxWH2D19xRka3k3t3lzo2QsD5BuYoC2Ic4FS3Ghz0OBQZ8bkr3U5I2gg3sV96G5PgGDN rT+A== X-Gm-Message-State: APjAAAXj98ze6pWyzIXmyoKwhhuuJhuhQWyJ2t3kHVf7+JHisgRivhi/ zaLAV39YDDsyq7z3iYNPJzfdaRZriv8= X-Google-Smtp-Source: APXvYqwK3x3btGe2qQmUeWAVC7CuXBmRWM2gyJOMl+7hxT/8qj2GYwfcQ/sBcS0FequPAE18+Bk3LA== X-Received: by 2002:a17:90a:a617:: with SMTP id c23mr7485620pjq.32.1582933634471; Fri, 28 Feb 2020 15:47:14 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.13 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:13 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 10/12] gatt: Add support for Notify Multiple Date: Fri, 28 Feb 2020 15:46:59 -0800 Message-Id: <20200228234701.14614-11-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds support for Notify Multiple procedure marking its bit as supported in the Client Features. --- src/gatt-database.c | 8 +++-- src/shared/att.c | 3 +- src/shared/gatt-client.c | 2 ++ src/shared/gatt-server.c | 77 +++++++++++++++++++++++++++++++++------- src/shared/gatt-server.h | 2 +- tools/btgatt-server.c | 4 +-- unit/test-gatt.c | 2 +- 7 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/gatt-database.c b/src/gatt-database.c index 2445d1fa5..1b514aa4f 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -1011,7 +1011,7 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib, if (ccc_cb->callback) { struct pending_op *op; - op = pending_ccc_new(att, attrib, get_le16(value), + op = pending_ccc_new(att, attrib, val, bt_att_get_link_type(att)); if (!op) { ecode = BT_ATT_ERROR_UNLIKELY; @@ -1098,7 +1098,8 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib, struct btd_gatt_database *database = user_data; struct device_state *state; uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING, - BT_GATT_CHRC_CLI_FEAT_EATT }; + BT_GATT_CHRC_CLI_FEAT_EATT, + BT_GATT_CHRC_CLI_FEAT_NFY_MULTI }; uint8_t ecode = 0; unsigned int i; @@ -1330,7 +1331,8 @@ static void send_notification_to_device(void *data, void *user_data) DBG("GATT server sending notification"); bt_gatt_server_send_notification(server, notify->handle, notify->value, - notify->len); + notify->len, device_state->cli_feat[0] & + BT_GATT_CHRC_CLI_FEAT_NFY_MULTI); return; } diff --git a/src/shared/att.c b/src/shared/att.c index 6fa16e422..948a5548b 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -145,6 +145,7 @@ static const struct { { BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ }, { BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP }, { BT_ATT_OP_HANDLE_NFY, ATT_OP_TYPE_NFY }, + { BT_ATT_OP_HANDLE_NFY_MULT, ATT_OP_TYPE_NFY }, { BT_ATT_OP_HANDLE_IND, ATT_OP_TYPE_IND }, { BT_ATT_OP_HANDLE_CONF, ATT_OP_TYPE_CONF }, { } @@ -842,7 +843,7 @@ static void handle_conf(struct bt_att_chan *chan, uint8_t *pdu, ssize_t pdu_len) } if (op->callback) - op->callback(BT_ATT_OP_HANDLE_NFY, NULL, 0, op->user_data); + op->callback(BT_ATT_OP_HANDLE_CONF, NULL, 0, op->user_data); destroy_att_send_op(op); chan->pending_ind = NULL; diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 7381f96e0..963ad619f 100644 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -1968,6 +1968,8 @@ static void write_client_features(struct bt_gatt_client *client) client->features |= BT_GATT_CHRC_CLI_FEAT_EATT; } + client->features |= BT_GATT_CHRC_CLI_FEAT_NFY_MULTI; + util_debug(client->debug_callback, client->debug_data, "Writing Client Features 0x%02x", client->features); diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 8e81d6e0e..7e5d652e4 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -36,6 +36,7 @@ #include "src/shared/gatt-server.h" #include "src/shared/gatt-helpers.h" #include "src/shared/util.h" +#include "src/shared/timeout.h" #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -51,6 +52,8 @@ */ #define DEFAULT_MAX_PREP_QUEUE_LEN 30 +#define NFY_MULT_TIMEOUT 10 + struct async_read_op { struct bt_att_chan *chan; struct bt_gatt_server *server; @@ -86,6 +89,13 @@ static void prep_write_data_destroy(void *user_data) free(data); } +struct nfy_mult_data { + unsigned int id; + uint8_t *pdu; + uint16_t offset; + uint16_t len; +}; + struct bt_gatt_server { struct gatt_db *db; struct bt_att *att; @@ -120,6 +130,8 @@ struct bt_gatt_server { bt_gatt_server_authorize_cb_t authorize; void *authorize_data; + + struct nfy_mult_data *nfy_mult; }; static void bt_gatt_server_free(struct bt_gatt_server *server) @@ -1726,28 +1738,69 @@ bool bt_gatt_server_set_debug(struct bt_gatt_server *server, return true; } +static bool notify_multiple(void *user_data) +{ + struct bt_gatt_server *server = user_data; + + bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY_MULT, + server->nfy_mult->pdu, server->nfy_mult->offset, NULL, + NULL, NULL); + + free(server->nfy_mult->pdu); + free(server->nfy_mult); + server->nfy_mult = NULL; + + return false; +} + bool bt_gatt_server_send_notification(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, - uint16_t length) + uint16_t length, bool multiple) { - uint16_t pdu_len; - uint8_t *pdu; + struct nfy_mult_data *data = NULL; bool result; if (!server || (length && !value)) return false; - pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2); - pdu = malloc(pdu_len); - if (!pdu) - return false; + if (multiple) + data = server->nfy_mult; - put_le16(handle, pdu); - memcpy(pdu + 2, value, pdu_len - 2); + if (!data) { + data = new0(struct nfy_mult_data, 1); + data->len = bt_att_get_mtu(server->att) - 1; + data->pdu = malloc(data->len); + } - result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, pdu, - pdu_len, NULL, NULL, NULL); - free(pdu); + put_le16(handle, data->pdu + data->offset); + data->offset += 2; + + length = MIN(data->len - data->offset, length); + + if (multiple) { + put_le16(length, data->pdu + data->offset); + data->offset += 2; + } + + memcpy(data->pdu + data->offset, value, length); + data->offset += length; + + if (multiple) { + if (!server->nfy_mult) + server->nfy_mult = data; + + if (!server->nfy_mult->id) + server->nfy_mult->id = timeout_add(NFY_MULT_TIMEOUT, + notify_multiple, server, + NULL); + + return true; + } + + result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_NFY, + data->pdu, data->offset, NULL, NULL, NULL); + free(data->pdu); + free(data); return result; } diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h index c3d83f225..a2492d275 100644 --- a/src/shared/gatt-server.h +++ b/src/shared/gatt-server.h @@ -52,7 +52,7 @@ bool bt_gatt_server_set_authorize(struct bt_gatt_server *server, bool bt_gatt_server_send_notification(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, - uint16_t length); + uint16_t length, bool multiple); bool bt_gatt_server_send_indication(struct bt_gatt_server *server, uint16_t handle, const uint8_t *value, diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c index d9d96e691..5b7857b00 100644 --- a/tools/btgatt-server.c +++ b/tools/btgatt-server.c @@ -305,7 +305,7 @@ static bool hr_msrmt_cb(void *user_data) bt_gatt_server_send_notification(server->gatt, server->hr_msrmt_handle, - pdu, len); + pdu, len, false); cur_ee = server->hr_energy_expended; @@ -831,7 +831,7 @@ static void cmd_notify(struct server *server, char *cmd_str) conf_cb, NULL, NULL)) printf("Failed to initiate indication\n"); } else if (!bt_gatt_server_send_notification(server->gatt, handle, - value, length)) + value, length, false)) printf("Failed to initiate notification\n"); done: diff --git a/unit/test-gatt.c b/unit/test-gatt.c index d94993b9c..36dd2847c 100644 --- a/unit/test-gatt.c +++ b/unit/test-gatt.c @@ -2271,7 +2271,7 @@ static void test_server_notification(struct context *context) const struct test_step *step = context->data->step; bt_gatt_server_send_notification(context->server, step->handle, - step->value, step->length); + step->value, step->length, false); } static const struct test_step test_notification_server_1 = { From patchwork Fri Feb 28 23:47:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413611 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B0D4914BC for ; Fri, 28 Feb 2020 23:47:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8FB21246B5 for ; Fri, 28 Feb 2020 23:47:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e1MPxMf/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726857AbgB1XrR (ORCPT ); Fri, 28 Feb 2020 18:47:17 -0500 Received: from mail-pg1-f196.google.com ([209.85.215.196]:41301 "EHLO mail-pg1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726845AbgB1XrQ (ORCPT ); Fri, 28 Feb 2020 18:47:16 -0500 Received: by mail-pg1-f196.google.com with SMTP id b1so2291721pgm.8 for ; Fri, 28 Feb 2020 15:47:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=71j6KIh4O1bMSaDqDl28Qe8IFycvbdYvPA383k/CA+I=; b=e1MPxMf/ShhmI4JzT0UYt3+mUh0qpkuHA4nqMhnSFZ6WlSIj3DGK/v69QPw/6J4/KJ 2i7QjCk1R57r+/bZDZcwnqhpHGHEBqByq2FChrD0Ehi7WOUW56Me5OlJ8mL1nZhPhFyV 7y16OBR75snJapXp7KwZRdibMvFDV488bOUk4wy1ARpltuB+/af1kEL/WOwg5MrBM9eT 1L7EQszOdP4LuJPytFeYt4zpUqIU5TWPepYKBQG1okRjtZjfoRiVEtjkHQRdGsvKCFqD TXoMizG947axpiBI7qvanEfCuJLaAxU7X+MayvSGRlsmg9gI7NtnZHKu6bLEz+12ZYMX 529A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=71j6KIh4O1bMSaDqDl28Qe8IFycvbdYvPA383k/CA+I=; b=eYKGRFY+dVrVDr6TgYxxP4GFl4GothxShUP72GaK4ZxRWgjiVpp3Gjk4M3C8sB/oar 0ao9vJdywZ4VRNM9K7nhqE5GcOH+G/Wp73dpGn2jIsHJnvk0zw0wtAQyirBQdvqzrnex D5rVe9kLloEzyZeolBHusJDPb3PVorkuPFPjLtwyWzPkrP0kobt3MB8LPi30VmV+zCjr eTsI3Sm0dK2mDhZKitc8fTsLbpS5sX+uP+ayvTGXnj8rIyNW6Hi+Q7N4aGUpBpjxNNfz xyuemfOLjNZ0DAYeAfvOl0EGC5iGgOD6K/FfW2xxSXTbMFsSHpGcbX2s8hTn1v6LUhxh AFMA== X-Gm-Message-State: APjAAAVkgJSLfVCUDTEaO36QpTd7Ipy/JZRMhhwtpcDoZaKPEqqQNqct QRyGD+UXFbb4l7QTofeAmjrs6GEErt4= X-Google-Smtp-Source: APXvYqy3JCtxwvKMG1akJpnWwJCfitFbk23IBwhatCcu0SyP6sI/a9XoymcAWZCNa6sb6UFLcoL1VQ== X-Received: by 2002:a62:3808:: with SMTP id f8mr6868661pfa.30.1582933635323; Fri, 28 Feb 2020 15:47:15 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:14 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 11/12] core: Add support for setting the number of GATT bearers Date: Fri, 28 Feb 2020 15:47:00 -0800 Message-Id: <20200228234701.14614-12-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This adds option to set the numbers of GATT Channels/Bearers to be connected in main.conf. --- src/device.c | 5 +++++ src/gatt-client.c | 8 +++++--- src/gatt-database.c | 12 ++++++++++-- src/hcid.h | 1 + src/main.c | 14 ++++++++++++++ src/main.conf | 5 +++++ 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/device.c b/src/device.c index c86e9a64d..7e3c2b215 100644 --- a/src/device.c +++ b/src/device.c @@ -5049,6 +5049,11 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) } if (dev->att) { + if (main_opts.gatt_channels == bt_att_get_channels(dev->att)) { + DBG("EATT channel limit reached"); + return false; + } + if (!bt_att_attach_fd(dev->att, g_io_channel_unix_get_fd(io))) { DBG("EATT channel connected"); g_io_channel_set_close_on_unref(io, FALSE); diff --git a/src/gatt-client.c b/src/gatt-client.c index aa77661ad..958ce5f9b 100644 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -59,8 +59,6 @@ #define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1" #define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1" -#define EATT_MAX_BEARERS 2 - struct btd_gatt_client { struct btd_device *device; uint8_t features; @@ -2171,6 +2169,7 @@ static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) static void eatt_connect(struct btd_gatt_client *client) { + struct bt_att *att = bt_gatt_client_get_att(client->gatt); struct btd_device *dev = client->device; struct btd_adapter *adapter = device_get_adapter(dev); GIOChannel *io; @@ -2178,11 +2177,14 @@ static void eatt_connect(struct btd_gatt_client *client) char addr[18]; int i; + if (bt_att_get_channels(att) == main_opts.gatt_channels) + return; + ba2str(device_get_address(dev), addr); DBG("Connection attempt to: %s", addr); - for (i = 0; i < EATT_MAX_BEARERS; i++) { + for (i = bt_att_get_channels(att); i < main_opts.gatt_channels; i++) { io = bt_io_connect(eatt_connect_cb, client, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(adapter), diff --git a/src/gatt-database.c b/src/gatt-database.c index 1b514aa4f..d0ab4da99 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -1215,10 +1215,13 @@ static void populate_gatt_service(struct btd_gatt_database *database) &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, db_hash_read_cb, NULL, database); - bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); - database->eatt = gatt_db_service_add_characteristic(service, + /* Only enable EATT if there is a socket listening */ + if (database->eatt_io) { + bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT); + database->eatt = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, server_feat_read_cb, NULL, database); + } gatt_db_service_set_active(service, true); @@ -3564,6 +3567,10 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) goto fail; } + /* If just just 1 channel is enabled EATT is not required */ + if (main_opts.gatt_channels == 1) + goto bredr; + /* EATT socket */ database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, addr, @@ -3591,6 +3598,7 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) } } +bredr: /* BR/EDR socket */ database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, addr, diff --git a/src/hcid.h b/src/hcid.h index ca405fde4..1bf93784d 100644 --- a/src/hcid.h +++ b/src/hcid.h @@ -62,6 +62,7 @@ struct main_opts { bt_mode_t mode; bt_gatt_cache_t gatt_cache; uint16_t gatt_mtu; + uint8_t gatt_channels; uint8_t key_size; diff --git a/src/main.c b/src/main.c index fc8c869fc..7b927ac79 100644 --- a/src/main.c +++ b/src/main.c @@ -109,6 +109,7 @@ static const char *gatt_options[] = { "Cache", "KeySize", "ExchangeMTU", + "EATTChannels", NULL }; @@ -471,6 +472,18 @@ static void parse_config(GKeyFile *config) DBG("ExchangeMTU=%d", val); main_opts.gatt_mtu = val; } + + val = g_key_file_get_integer(config, "GATT", "Channels", &err); + if (err) { + DBG("%s", err->message); + g_clear_error(&err); + } else { + DBG("Channels=%d", val); + /* Ensure the channels is within a valid range. */ + val = MIN(val, 5); + val = MAX(val, 1); + main_opts.gatt_channels = val; + } } static void init_defaults(void) @@ -497,6 +510,7 @@ static void init_defaults(void) main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS; main_opts.gatt_mtu = BT_ATT_MAX_LE_MTU; + main_opts.gatt_channels = 3; } static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, diff --git a/src/main.conf b/src/main.conf index bb5ff5b15..16701ebe4 100644 --- a/src/main.conf +++ b/src/main.conf @@ -99,6 +99,11 @@ # Defaults to 517 #ExchangeMTU = 517 +# Number of ATT channels +# Possible values: 1-5 (1 disables EATT) +# Default to 3 +#Channels = 3 + [Policy] # # The ReconnectUUIDs defines the set of remote services that should try From patchwork Fri Feb 28 23:47:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Augusto von Dentz X-Patchwork-Id: 11413615 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EFA3C14B7 for ; Fri, 28 Feb 2020 23:47:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CE24B246B6 for ; Fri, 28 Feb 2020 23:47:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BomLSKbx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726589AbgB1XrS (ORCPT ); Fri, 28 Feb 2020 18:47:18 -0500 Received: from mail-pg1-f194.google.com ([209.85.215.194]:39197 "EHLO mail-pg1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726859AbgB1XrS (ORCPT ); Fri, 28 Feb 2020 18:47:18 -0500 Received: by mail-pg1-f194.google.com with SMTP id s2so1438852pgv.6 for ; Fri, 28 Feb 2020 15:47:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=Ihpr9Y1VTzQzgPw9VEZXNNYIhYzl+fh8ZyruBbafTqs=; b=BomLSKbxqm14l62RVdci9cGy5/vAT/By4PN2LjJaYz6s6lm2yhB3U10E8fqfTgkg29 mB2vT0dPdnqg5TC+C3EXfiPT3ER1OJERjub5x8iBt4NLtEfZ7qYKcEzJq0S1VCNeAPUP ire4Eq99v9QqFSjffMUYjUrr73p4F3bqrxy5omdrcBTxeNCBVw1Kd5mlVsJWscEEThjO uBNqDy68mumYqaY5k4tlIjQtrzkiiH0/ryT5z3hxntEftCFPc31ecMWgWb8uq4GkI6oT vJLYGgToXxKHBvOM3rTwtV3LInUyB/z3R2of7WfpyaNUdIT+x9zJiYHvX5ordLdYXtWq WceA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Ihpr9Y1VTzQzgPw9VEZXNNYIhYzl+fh8ZyruBbafTqs=; b=aBVFvNMsd7Hncqwjsyfk2BVUJFxpyPe2oCuYknxcikBpcz+/tES5B7XwJp/8r9FN6R BbncSuFz4uVCM50vgVUL2ccGWYmRtoZ3YoLjqT2rXm7D/cKfifm+bJe6T4+Jlv79yBNV 98NwLT1pFl6zo4xxpPXtt4q11Rp5oPKVx2KP2eCzTQsso1mzwwZ71AYp4WgB/vUrCiZB LJoX72IuTXp37G8wyulsTyMni+5N7mZqJhdoncbm0aVySCKtHTE/kybrb7jAAyZj2ZMl 4l9atD76iP2pJfV3L0l0IsQ6sYk20LpyooNp6S1PLF1sYC9mlwLCk/FWIGRTRUhBq6Rj PzFA== X-Gm-Message-State: APjAAAWQbG2ODstB2r2jnhJkhN4iWkSdLgaZynstnbpo47VRyJlh9TPg sUKt+f30A8kBadRxrh/A5KFF2c7nHqI= X-Google-Smtp-Source: APXvYqyW3v57MPE2tPOo3vYqBeHnxM5MQSErh4bz4Wa62RLiXiq+jeJ93cy9oafsSBTQagqoc8389w== X-Received: by 2002:a63:6cc6:: with SMTP id h189mr6781156pgc.201.1582933636601; Fri, 28 Feb 2020 15:47:16 -0800 (PST) Received: from localhost.localdomain (c-71-56-157-77.hsd1.or.comcast.net. [71.56.157.77]) by smtp.gmail.com with ESMTPSA id q13sm13643954pfn.162.2020.02.28.15.47.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 15:47:15 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 12/12] monitor: Add support for decoding EATT Date: Fri, 28 Feb 2020 15:47:01 -0800 Message-Id: <20200228234701.14614-13-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200228234701.14614-1-luiz.dentz@gmail.com> References: <20200228234701.14614-1-luiz.dentz@gmail.com> MIME-Version: 1.0 Sender: linux-bluetooth-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org From: Luiz Augusto von Dentz This decodes packets received over EATT PSM. --- monitor/l2cap.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/monitor/l2cap.c b/monitor/l2cap.c index 3b2b25f24..9409604c3 100644 --- a/monitor/l2cap.c +++ b/monitor/l2cap.c @@ -2573,6 +2573,36 @@ static void att_handle_value_conf(const struct l2cap_frame *frame) { } +static void att_multiple_vl_rsp(const struct l2cap_frame *frame) +{ + struct l2cap_frame *f = (void *) frame; + + while (frame->size) { + uint16_t handle; + uint16_t len; + + if (!l2cap_frame_get_le16(f, &handle)) + return; + + print_field("Handle: 0x%4.4x", handle); + + if (!l2cap_frame_get_le16(f, &len)) + return; + + print_field("Length: 0x%4.4x", len); + + print_hex_field(" Data", f->data, + len < f->size ? len : f->size); + + if (len > f->size) { + print_text(COLOR_ERROR, "invalid size"); + return; + } + + l2cap_frame_pull(f, f, len); + } +} + static void att_write_command(const struct l2cap_frame *frame) { print_field("Handle: 0x%4.4x", get_le16(frame->data)); @@ -2645,6 +2675,12 @@ static const struct att_opcode_data att_opcode_table[] = { att_handle_value_ind, 2, false }, { 0x1e, "Handle Value Confirmation", att_handle_value_conf, 0, true }, + { 0x20, "Read Multiple Request Variable Length", + att_read_multiple_req, 4, false }, + { 0x21, "Read Multiple Response Variable Length", + att_multiple_vl_rsp, 4, false }, + { 0x23, "Handle Multiple Value Notification", + att_multiple_vl_rsp, 4, false }, { 0x52, "Write Command", att_write_command, 2, false }, { 0xd2, "Signed Write Command", att_signed_write_command, 14, false }, @@ -3287,6 +3323,9 @@ void l2cap_frame(uint16_t index, bool in, uint16_t handle, uint16_t cid, case 0x001f: att_packet(index, in, handle, cid, data, size); break; + case 0x0027: + att_packet(index, in, handle, cid, data + 2, size - 2); + break; case 0x0017: case 0x001B: avctp_packet(&frame);