From patchwork Mon Mar 2 21:32:33 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: 11416399 X-Patchwork-Delegate: marcel@holtmann.org 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 D97E092A for ; Mon, 2 Mar 2020 21:32:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B807922B48 for ; Mon, 2 Mar 2020 21:32:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="T49ptd1p" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726740AbgCBVcl (ORCPT ); Mon, 2 Mar 2020 16:32:41 -0500 Received: from mail-pj1-f67.google.com ([209.85.216.67]:51922 "EHLO mail-pj1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725781AbgCBVcl (ORCPT ); Mon, 2 Mar 2020 16:32:41 -0500 Received: by mail-pj1-f67.google.com with SMTP id l8so364340pjy.1 for ; Mon, 02 Mar 2020 13:32:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:mime-version :content-transfer-encoding; bh=PsYqvu4OP5OhY0v0HTAxks1Yq6VOsFTAMVNjMdSYWj4=; b=T49ptd1pHjYPNVWkw55M8uCdvaU+JP2tLirXOFS3u3p6ZXUQHrV2v1sGyXCEW3cCPn U3ghD6b1RN8A/xezVJ/nrLpHKPAI1YVC2rx5OTRb5FAWdfxKGEGkUh0Qn3HUoNRxaGkw vw6E3ilCDlspPZRsKlhX8ZJ223H/7TFGXBg/S6rSugbN8Ze6VOYquoa7fFk6Nv3CT35v fpAMEyHNP9tXd8TAAwSAE2NpfKHbcy0rwdrYe7BaFKnT0tp/UtpxISFk+o8PEApzVVlG qW6hZNKZV0F38GwRsbRjxI++R/95fk7Eah7YoeLXXi9nioLae71ZXIh/wtUwVE6sXTw9 SifA== 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:mime-version :content-transfer-encoding; bh=PsYqvu4OP5OhY0v0HTAxks1Yq6VOsFTAMVNjMdSYWj4=; b=nKNCQ6E6CtV/WRo4OaJFgNaYOPdSVpWk4opQEITZ9cLe8Ws+RnvJZ39MbVau6EDneB fxiwFBtDCfy+AXFRYxA6931Fy2DCq7FYYgEMce/ZE81aRBEYKdBsmKn4rd9jZ4/nnS2I ymjDmYtcf7BCdVBwwgdxuivzI0k5RVOuQ/52DktcqvOMxBpxlUEqXm1Hy6J7TmrxpxoY ZcTn6R1b99kRnA2p9VNiqQbXE9htU7GHJN/nUmD0tVVR7i+nBnBWKMz/ZUwi4DwtpYYp aufvDH5WFSN8cKvY+GmaNUtK3Ne8LHNf0ysX8b9J5EJ6dotFNALBHkG/wqSGIezBs/W3 TKbw== X-Gm-Message-State: ANhLgQ2N4V6eVO/nLGdqJBHZ19kdcJjQZqwj8KiIhiiGMarY8FmCLWan PgXKJjTt7Uu0aTGK/rfOIJz9mwdk4pk= X-Google-Smtp-Source: ADFU+vuYz+mN/RcHCCWgZhdb9eEZrUlMmrrOYid2hK1kwMdpxp211Y34eIVyVXkZE9q+sEpNZDlmFA== X-Received: by 2002:a17:902:8d85:: with SMTP id v5mr1099500plo.146.1583184759520; Mon, 02 Mar 2020 13:32:39 -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 i22sm22183749pgi.69.2020.03.02.13.32.38 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2020 13:32:38 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 1/5] Bluetooth: Add BT_MODE socket option Date: Mon, 2 Mar 2020 13:32:33 -0800 Message-Id: <20200302213237.19540-1-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 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_MODE socket option which can be used to set L2CAP modes, including modes only supported over LE which were not supported using the L2CAP_OPTIONS. Signed-off-by: Luiz Augusto von Dentz --- v2: Make BT_MODE conditional to enable_ecred. include/net/bluetooth/bluetooth.h | 2 + net/bluetooth/l2cap_sock.c | 64 ++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 1576353a2773..c361ec7b06aa 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -139,6 +139,8 @@ struct bt_voice { #define BT_PHY_LE_CODED_TX 0x00002000 #define BT_PHY_LE_CODED_RX 0x00004000 +#define BT_MODE 15 + __printf(1, 2) void bt_info(const char *fmt, ...); __printf(1, 2) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 305710446e66..640cb461af9d 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -616,6 +616,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, err = -EFAULT; break; + case BT_MODE: + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { + err = -EINVAL; + break; + } + + if (put_user(chan->mode, (u8 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; @@ -641,6 +651,28 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) return true; } +static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode) +{ + switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + break; + case L2CAP_MODE_BASIC: + clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + return -EINVAL; + } + + chan->mode = mode; + + return 0; +} + static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) { @@ -690,19 +722,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, break; } - chan->mode = opts.mode; - switch (chan->mode) { - case L2CAP_MODE_LE_FLOWCTL: - break; - case L2CAP_MODE_BASIC: - clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: + if (l2cap_set_mode(chan, opts.mode)) { err = -EINVAL; break; } @@ -952,6 +972,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, chan->imtu = opt; break; + case BT_MODE: + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { + err = -EINVAL; + break; + } + + if (get_user(opt, (u8 __user *) optval)) { + err = -EFAULT; + break; + } + + if (l2cap_set_mode(chan, opt)) { + err = -EINVAL; + break; + } + + break; + default: err = -ENOPROTOOPT; break; From patchwork Mon Mar 2 21:32:34 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: 11416401 X-Patchwork-Delegate: marcel@holtmann.org 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 6234C14E3 for ; Mon, 2 Mar 2020 21:32:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 400772465D for ; Mon, 2 Mar 2020 21:32:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="TuGDoORD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726758AbgCBVcm (ORCPT ); Mon, 2 Mar 2020 16:32:42 -0500 Received: from mail-pg1-f195.google.com ([209.85.215.195]:46722 "EHLO mail-pg1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726720AbgCBVcl (ORCPT ); Mon, 2 Mar 2020 16:32:41 -0500 Received: by mail-pg1-f195.google.com with SMTP id y30so429970pga.13 for ; Mon, 02 Mar 2020 13:32:41 -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=Yc9twyfo2ofnnZ2ALGcCp/mhrgMLu+tyCCMaRloUOOY=; b=TuGDoORDgdYpEUZkmoa6yL29eO0sz1Lj4DPT7WTpAA9Nx3rZdaA/GO0DVBFrZ1D0mW e8W/EL6eZwLWyU6g2iP55baan6ECAJSmbUTPOKj39QpfftyQ9Z1LPDzDllwLysxq1sbp pzeoR1zXTbAJ5dEFsylCgsKEh4mtilaZ79SbPjL86BGWilPQMc/gKdgAk3aQVGQBQGDo SDdcVI3w3UJGlY5ke1CU4e29iiAgcg/gIsbgFnMm+6xugN7Vj8lEsVTW4IG03ZLfpsDy Iv7qfCit6jhaov6UnGqkX7ykmSrqkBcvjGW3GY5XHPFCtyXigujsEQoSUvIrdTfnM9GT 25JQ== 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=Yc9twyfo2ofnnZ2ALGcCp/mhrgMLu+tyCCMaRloUOOY=; b=S9rEgeRCqvtWpr+M4fYUAX80aGRc64ElcYlUqb5lmfrZS8hGIDhGxS/UKLeFZnyZjW rYlX5VmVBzt6qNxTeSZzgen2E425NgEO6gp5UVwFjWLbF5vR1duQ5Nb3DefnfKLhq5/f R8YYIVSXlhEhvsSvmGR0GJtwzY2HQZbmXJ/6+7RzZCyArat+p7nmDsdLDhaSj4v+YiNy 9YuW10J02S8+LbmOIUD08C8KwjeI8i8FnSnnMlVpZWpRjELMtyO8rBNNZwC1A+Vr85gi MVMgVhyxmfY9k3Q6YxErXL0yrOGePbGyntSrq6XEtjeU5qzuXdzDL2VudQP4h3lm9ous rtTw== X-Gm-Message-State: ANhLgQ1cu88nkZsNLMYIm3laT/ZP2mAjtq/abA0zittVN1bwLrH8TB0e DNGbxMvgE4karPJh6n7wxIFlzO0nCu0= X-Google-Smtp-Source: ADFU+vs+tCi+BednGzslt1wC7A2BoVdFIFUvLyjJNUaOojFIgXRo0eZ8IguSOSL0zjhfpkI9Bn4mcQ== X-Received: by 2002:a63:1e06:: with SMTP id e6mr894213pge.134.1583184760374; Mon, 02 Mar 2020 13:32:40 -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 i22sm22183749pgi.69.2020.03.02.13.32.39 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2020 13:32:39 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 2/5] Bluetooth: L2CAP: Add definitions for Enhanced Credit Based Mode Date: Mon, 2 Mar 2020 13:32:34 -0800 Message-Id: <20200302213237.19540-2-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200302213237.19540-1-luiz.dentz@gmail.com> References: <20200302213237.19540-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 introduces the definitions for the new L2CAP mode called Enhanced Credit Based Mode. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 093aedebdf0c..3e6544e53516 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -119,6 +119,10 @@ struct l2cap_conninfo { #define L2CAP_LE_CONN_REQ 0x14 #define L2CAP_LE_CONN_RSP 0x15 #define L2CAP_LE_CREDITS 0x16 +#define L2CAP_ECRED_CONN_REQ 0x17 +#define L2CAP_ECRED_CONN_RSP 0x18 +#define L2CAP_ECRED_RECONF_REQ 0x19 +#define L2CAP_ECRED_RECONF_RSP 0x1a /* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 @@ -359,6 +363,7 @@ struct l2cap_conf_rfc { * ever be used in the BR/EDR configuration phase. */ #define L2CAP_MODE_LE_FLOWCTL 0x80 +#define L2CAP_MODE_EXT_FLOWCTL 0x81 struct l2cap_conf_efs { __u8 id; @@ -483,6 +488,39 @@ struct l2cap_le_credits { __le16 credits; } __packed; +#define L2CAP_ECRED_MIN_MTU 64 +#define L2CAP_ECRED_MIN_MPS 64 + +struct l2cap_ecred_conn_req { + __le16 psm; + __le16 mtu; + __le16 mps; + __le16 credits; + __le16 scid[0]; +} __packed; + +struct l2cap_ecred_conn_rsp { + __le16 mtu; + __le16 mps; + __le16 credits; + __le16 result; + __le16 dcid[0]; +}; + +struct l2cap_ecred_reconf_req { + __le16 mtu; + __le16 mps; + __le16 scid[0]; +} __packed; + +#define L2CAP_RECONF_SUCCESS 0x0000 +#define L2CAP_RECONF_INVALID_MTU 0x0001 +#define L2CAP_RECONF_INVALID_MPS 0x0002 + +struct l2cap_ecred_reconf_rsp { + __le16 result; +} __packed; + /* ----- L2CAP channels and connections ----- */ struct l2cap_seq_list { __u16 head; @@ -724,6 +762,7 @@ enum { FLAG_EFS_ENABLE, FLAG_DEFER_SETUP, FLAG_LE_CONN_REQ_SENT, + FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, }; From patchwork Mon Mar 2 21:32:35 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: 11416405 X-Patchwork-Delegate: marcel@holtmann.org 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 B3BF192A for ; Mon, 2 Mar 2020 21:32:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7EAC82465D for ; Mon, 2 Mar 2020 21:32:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PJaKvIfS" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726838AbgCBVcp (ORCPT ); Mon, 2 Mar 2020 16:32:45 -0500 Received: from mail-pj1-f46.google.com ([209.85.216.46]:51411 "EHLO mail-pj1-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726720AbgCBVco (ORCPT ); Mon, 2 Mar 2020 16:32:44 -0500 Received: by mail-pj1-f46.google.com with SMTP id l8so364397pjy.1 for ; Mon, 02 Mar 2020 13:32:43 -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=QXMCo7VDz8JVh/LVZ/FXFSw9DItOuDHI5Tis8ugq5TU=; b=PJaKvIfScXuejlhR9cHcTNXNfqEcxat0r/40wONlJpIeBotC7+HOpjCXflCSEc61R9 VwFK56zYmXDcaBcXxAPqcHO9TpibRiS3pPWSrAQXTB9OSkoUdTNu2rk0AAK7Xc8mMJS8 Ejnwi4vmvXiwvrW4HalKRx54ZwEvh3RJO89bJgVbQSzNkfLQQgjOLkTuVBZtnvklHj1K FtQhmQLsqxaifvbfUksp5Y5n2K9erX3GM7Cf5AeCqn/MWiF0tJZ3U+Aurqpehu23Avg9 D+/6ZptqP808YzhxFoFYVnz7wJey0PA2QuVkVXo/ADELWmF08MrVpS+vcum/kXMlGZKG cjCA== 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=QXMCo7VDz8JVh/LVZ/FXFSw9DItOuDHI5Tis8ugq5TU=; b=tjwNwQ2Q7MmiBiUryKW0qPOtaRmS0OhSSPePLFGQSLZeb9UEZ3L28SB9IUlSFFdWcj 12AwzFng3rdDNNRwEfAwibdzvM1CHLramWGTjR5LM/W/VSC4GOAzBTw7ykMy+7wt0OWi EiOr212BexztTcgC+iFOJDgM2NVrxZNf2g91hEBAWE10UhTMe3IlPZ7cnnLu7BmoKIQQ o23jJFe65P3fTnMJf/fIIqNF7HWiYW0E2xR3mhccsthugZDStwiXARSOWqJVlf1nAkfb EpqkKzv2/D38XZ7vPepoL3+03xGnYPQvJqg+JJbsJS5Ttf0b7UZ6p8mA6QC5YA32oJGX aGIA== X-Gm-Message-State: ANhLgQ3Z3uqBLGYY2QIf5Lxc/+DbhC7MRBxM56speSfLk/k0XuSs8K41 mPNz8sM+I/IhLfPIcXJXhQqEDqM9oNA= X-Google-Smtp-Source: ADFU+vt5WiVyyvjjYkFov7hc+TwcWEqx9Vqun5rQcz4LPoM3A/JotcCqycMvkjuOb7Gv63wwYZ1MCw== X-Received: by 2002:a17:902:ed12:: with SMTP id b18mr1052712pld.45.1583184761493; Mon, 02 Mar 2020 13:32:41 -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 i22sm22183749pgi.69.2020.03.02.13.32.40 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2020 13:32:40 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 3/5] Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode Date: Mon, 2 Mar 2020 13:32:35 -0800 Message-Id: <20200302213237.19540-3-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200302213237.19540-1-luiz.dentz@gmail.com> References: <20200302213237.19540-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 the initial code for Enhanced Credit Based Mode which introduces a new socket mode called L2CAP_MODE_EXT_FLOWCTL, which for the most part work the same as L2CAP_MODE_LE_FLOWCTL but uses different PDUs to setup the connections and also works over BR/EDR. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 4 + net/bluetooth/l2cap_core.c | 545 +++++++++++++++++++++++++++++++++- net/bluetooth/l2cap_sock.c | 26 +- 3 files changed, 554 insertions(+), 21 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3e6544e53516..dbe68b06d4bb 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -294,6 +294,8 @@ struct l2cap_conn_rsp { #define L2CAP_CR_LE_ENCRYPTION 0x0008 #define L2CAP_CR_LE_INVALID_SCID 0x0009 #define L2CAP_CR_LE_SCID_IN_USE 0X000A +#define L2CAP_CR_LE_UNACCEPT_PARAMS 0X000B +#define L2CAP_CR_LE_INVALID_PARAMS 0X000C /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 @@ -962,6 +964,7 @@ void l2cap_cleanup_sockets(void); bool l2cap_is_socket(struct socket *sock); void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan); +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); @@ -971,6 +974,7 @@ struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); +int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index dd2021270b8a..5c03baffc82c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -535,6 +535,17 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) skb_queue_head_init(&chan->tx_q); } +static void l2cap_ecred_init(struct l2cap_chan *chan, u16 tx_credits) +{ + l2cap_le_flowctl_init(chan, tx_credits); + + /* L2CAP implementations shall support a minimum MPS of 64 octets */ + if (chan->mps < L2CAP_ECRED_MIN_MPS) { + chan->mps = L2CAP_ECRED_MIN_MPS; + chan->rx_credits = (chan->imtu / chan->mps) + 1; + } +} + void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, @@ -641,6 +652,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) break; case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: skb_queue_purge(&chan->tx_q); break; @@ -707,6 +719,27 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) &rsp); } +static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct l2cap_ecred_conn_rsp rsp; + u16 result; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + result = L2CAP_CR_LE_AUTHORIZATION; + else + result = L2CAP_CR_LE_BAD_PSM; + + l2cap_state_change(chan, BT_DISCONN); + + memset(&rsp, 0, sizeof(rsp)); + + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), + &rsp); +} + static void l2cap_chan_connect_reject(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -752,8 +785,16 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { if (conn->hcon->type == ACL_LINK) l2cap_chan_connect_reject(chan); - else if (conn->hcon->type == LE_LINK) - l2cap_chan_le_connect_reject(chan); + else if (conn->hcon->type == LE_LINK) { + switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + l2cap_chan_le_connect_reject(chan); + break; + case L2CAP_MODE_EXT_FLOWCTL: + l2cap_chan_ecred_connect_reject(chan); + break; + } + } } l2cap_chan_del(chan, reason); @@ -1276,8 +1317,13 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) chan->conf_state = 0; __clear_chan_timer(chan); - if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits) - chan->ops->suspend(chan); + switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: + if (!chan->tx_credits) + chan->ops->suspend(chan); + break; + } chan->state = BT_CONNECTED; @@ -1309,6 +1355,31 @@ static void l2cap_le_connect(struct l2cap_chan *chan) sizeof(req), &req); } +static void l2cap_ecred_connect(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct { + struct l2cap_ecred_conn_req req; + __le16 scid; + } __packed pdu; + + if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) + return; + + l2cap_ecred_init(chan, 0); + + pdu.req.psm = chan->psm; + pdu.req.mtu = cpu_to_le16(chan->imtu); + pdu.req.mps = cpu_to_le16(chan->mps); + pdu.req.credits = cpu_to_le16(chan->rx_credits); + pdu.scid = cpu_to_le16(chan->scid); + + chan->ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_CONN_REQ, + sizeof(pdu), &pdu); +} + static void l2cap_le_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -1321,8 +1392,12 @@ static void l2cap_le_start(struct l2cap_chan *chan) return; } - if (chan->state == BT_CONNECT) - l2cap_le_connect(chan); + if (chan->state == BT_CONNECT) { + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) + l2cap_ecred_connect(chan); + else + l2cap_le_connect(chan); + } } static void l2cap_start_connection(struct l2cap_chan *chan) @@ -2508,6 +2583,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) switch (chan->mode) { case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: /* Check outgoing MTU */ if (len > chan->omtu) return -EMSGSIZE; @@ -3776,6 +3852,45 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) &rsp); } +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan) +{ + struct { + struct l2cap_ecred_conn_rsp rsp; + __le16 dcid[5]; + } __packed pdu; + struct l2cap_conn *conn = chan->conn; + u16 ident = chan->ident; + int i = 0; + + if (!ident) + return; + + BT_DBG("chan %p ident %d", chan, ident); + + pdu.rsp.mtu = cpu_to_le16(chan->imtu); + pdu.rsp.mps = cpu_to_le16(chan->mps); + pdu.rsp.credits = cpu_to_le16(chan->rx_credits); + pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS); + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + if (chan->ident != ident) + continue; + + /* Reset ident so only one response is sent */ + chan->ident = 0; + + /* Include all channels pending with the same ident */ + pdu.dcid[i++] = cpu_to_le16(chan->scid); + } + + mutex_unlock(&conn->chan_lock); + + l2cap_send_cmd(conn, ident, L2CAP_ECRED_CONN_RSP, + sizeof(pdu.rsp) + i * sizeof(__le16), &pdu); +} + void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) { struct l2cap_conn_rsp rsp; @@ -5718,6 +5833,351 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn, return 0; } +static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_conn_req *req = (void *) data; + struct { + struct l2cap_ecred_conn_rsp rsp; + __le16 dcid[5]; + } __packed pdu; + struct l2cap_chan *chan, *pchan; + u16 credits, mtu, mps; + __le16 psm; + u8 result, len = 0; + int i, num_scid; + bool defer = false; + + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { + result = L2CAP_CR_LE_INVALID_PARAMS; + goto response; + } + + mtu = __le16_to_cpu(req->mtu); + mps = __le16_to_cpu(req->mps); + + if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MPS) { + result = L2CAP_CR_LE_UNACCEPT_PARAMS; + goto response; + } + + psm = req->psm; + credits = 0; + + BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps); + + memset(&pdu, 0, sizeof(pdu)); + + /* Check if we have socket listening on psm */ + pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, + &conn->hcon->dst, LE_LINK); + if (!pchan) { + result = L2CAP_CR_LE_BAD_PSM; + goto response; + } + + mutex_lock(&conn->chan_lock); + l2cap_chan_lock(pchan); + + if (!smp_sufficient_security(conn->hcon, pchan->sec_level, + SMP_ALLOW_STK)) { + result = L2CAP_CR_LE_AUTHENTICATION; + goto unlock; + } + + result = L2CAP_CR_LE_SUCCESS; + cmd_len -= sizeof(req); + num_scid = cmd_len / sizeof(u16); + + for (i = 0; i < num_scid; i++) { + u16 scid = __le16_to_cpu(req->scid[i]); + + BT_DBG("scid[%d] 0x%4.4x", i, scid); + + pdu.dcid[i] = 0x0000; + len += sizeof(*pdu.dcid); + + /* Check for valid dynamic CID range */ + if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) { + result = L2CAP_CR_LE_INVALID_SCID; + continue; + } + + /* Check if we already have channel with that dcid */ + if (__l2cap_get_chan_by_dcid(conn, scid)) { + result = L2CAP_CR_LE_SCID_IN_USE; + continue; + } + + chan = pchan->ops->new_connection(pchan); + if (!chan) { + result = L2CAP_CR_LE_NO_MEM; + continue; + } + + bacpy(&chan->src, &conn->hcon->src); + bacpy(&chan->dst, &conn->hcon->dst); + chan->src_type = bdaddr_src_type(conn->hcon); + chan->dst_type = bdaddr_dst_type(conn->hcon); + chan->psm = psm; + chan->dcid = scid; + chan->omtu = mtu; + chan->remote_mps = mps; + + __l2cap_chan_add(conn, chan); + + l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); + + /* Init response */ + if (!pdu.rsp.credits) { + pdu.rsp.mtu = cpu_to_le16(chan->imtu); + pdu.rsp.mps = cpu_to_le16(chan->mps); + pdu.rsp.credits = cpu_to_le16(chan->rx_credits); + } + + pdu.dcid[i] = cpu_to_le16(chan->scid); + + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); + + chan->ident = cmd->ident; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { + l2cap_state_change(chan, BT_CONNECT2); + defer = true; + chan->ops->defer(chan); + } else { + l2cap_chan_ready(chan); + } + } + +unlock: + l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); + l2cap_chan_put(pchan); + +response: + pdu.rsp.result = cpu_to_le16(result); + + if (defer) + return 0; + + l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_CONN_RSP, + sizeof(pdu.rsp) + len, &pdu); + + return 0; +} + +static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_conn_rsp *rsp = (void *) data; + struct hci_conn *hcon = conn->hcon; + u16 mtu, mps, credits, result; + struct l2cap_chan *chan; + int err = 0, sec_level; + int i = 0; + + if (cmd_len < sizeof(*rsp)) + return -EPROTO; + + mtu = __le16_to_cpu(rsp->mtu); + mps = __le16_to_cpu(rsp->mps); + credits = __le16_to_cpu(rsp->credits); + result = __le16_to_cpu(rsp->result); + + BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits, + result); + + mutex_lock(&conn->chan_lock); + + cmd_len -= sizeof(*rsp); + + list_for_each_entry(chan, &conn->chan_l, list) { + u16 dcid; + + if (chan->ident != cmd->ident || + chan->mode != L2CAP_MODE_EXT_FLOWCTL || + chan->state == BT_CONNECTED) + continue; + + l2cap_chan_lock(chan); + + /* Check that there is a dcid for each pending channel */ + if (cmd_len < sizeof(dcid)) { + l2cap_chan_del(chan, ECONNREFUSED); + l2cap_chan_unlock(chan); + continue; + } + + dcid = __le16_to_cpu(rsp->dcid[i++]); + cmd_len -= sizeof(u16); + + BT_DBG("dcid[%d] 0x%4.4x", i, dcid); + + /* Check if dcid is already in use */ + if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) { + /* If a device receives a + * L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an + * already-assigned Destination CID, then both the + * original channel and the new channel shall be + * immediately discarded and not used. + */ + l2cap_chan_del(chan, ECONNREFUSED); + l2cap_chan_unlock(chan); + chan = __l2cap_get_chan_by_dcid(conn, dcid); + l2cap_chan_lock(chan); + l2cap_chan_del(chan, ECONNRESET); + l2cap_chan_unlock(chan); + continue; + } + + switch (result) { + case L2CAP_CR_LE_AUTHENTICATION: + case L2CAP_CR_LE_ENCRYPTION: + /* If we already have MITM protection we can't do + * anything. + */ + if (hcon->sec_level > BT_SECURITY_MEDIUM) { + l2cap_chan_del(chan, ECONNREFUSED); + break; + } + + sec_level = hcon->sec_level + 1; + if (chan->sec_level < sec_level) + chan->sec_level = sec_level; + + /* We'll need to send a new Connect Request */ + clear_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags); + + smp_conn_security(hcon, chan->sec_level); + break; + + case L2CAP_CR_LE_BAD_PSM: + l2cap_chan_del(chan, ECONNREFUSED); + break; + + default: + /* If dcid was not set it means channels was refused */ + if (!dcid) { + l2cap_chan_del(chan, ECONNREFUSED); + break; + } + + chan->ident = 0; + chan->dcid = dcid; + chan->omtu = mtu; + chan->remote_mps = mps; + chan->tx_credits = credits; + l2cap_chan_ready(chan); + break; + } + + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); + + return err; +} + +static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_reconf_req *req = (void *) data; + struct l2cap_ecred_reconf_rsp rsp; + u16 mtu, mps, result; + struct l2cap_chan *chan; + int i, num_scid; + + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { + result = L2CAP_CR_LE_INVALID_PARAMS; + goto respond; + } + + mtu = __le16_to_cpu(req->mtu); + mps = __le16_to_cpu(req->mps); + + BT_DBG("mtu %u mps %u", mtu, mps); + + if (mtu < L2CAP_ECRED_MIN_MTU) { + result = L2CAP_RECONF_INVALID_MTU; + goto respond; + } + + if (mps < L2CAP_ECRED_MIN_MPS) { + result = L2CAP_RECONF_INVALID_MPS; + goto respond; + } + + cmd_len -= sizeof(*req); + num_scid = cmd_len / sizeof(u16); + result = L2CAP_RECONF_SUCCESS; + + for (i = 0; i < num_scid; i++) { + u16 scid; + + scid = __le16_to_cpu(req->scid[i]); + if (!scid) + return -EPROTO; + + chan = __l2cap_get_chan_by_dcid(conn, scid); + if (!chan) + continue; + + /* If the MTU value is decreased for any of the included + * channels, then the receiver shall disconnect all + * included channels. + */ + if (chan->omtu > mtu) { + BT_ERR("chan %p decreased MTU %u -> %u", chan, + chan->omtu, mtu); + result = L2CAP_RECONF_INVALID_MTU; + } + + chan->omtu = mtu; + chan->remote_mps = mps; + } + +respond: + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_RECONF_RSP, sizeof(rsp), + &rsp); + + return 0; +} + +static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_chan *chan; + struct l2cap_ecred_conn_rsp *rsp = (void *) data; + u16 result; + + if (cmd_len < sizeof(*rsp)) + return -EPROTO; + + result = __le16_to_cpu(rsp->result); + + BT_DBG("result 0x%4.4x", rsp->result); + + if (!result) + return 0; + + list_for_each_entry(chan, &conn->chan_l, list) { + if (chan->ident != cmd->ident) + continue; + + l2cap_chan_del(chan, ECONNRESET); + } + + return 0; +} + static inline int l2cap_le_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -5773,6 +6233,22 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, err = l2cap_le_credits(conn, cmd, cmd_len, data); break; + case L2CAP_ECRED_CONN_REQ: + err = l2cap_ecred_conn_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_CONN_RSP: + err = l2cap_ecred_conn_rsp(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_RECONF_REQ: + err = l2cap_ecred_reconf_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_RECONF_RSP: + err = l2cap_ecred_reconf_rsp(conn, cmd, cmd_len, data); + break; + case L2CAP_DISCONN_REQ: err = l2cap_disconnect_req(conn, cmd, cmd_len, data); break; @@ -6818,11 +7294,13 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_le_credits pkt; u16 return_credits; - return_credits = ((chan->imtu / chan->mps) + 1) - chan->rx_credits; + return_credits = (chan->imtu / chan->mps) + 1; - if (!return_credits) + if (chan->rx_credits >= return_credits) return; + return_credits -= chan->rx_credits; + BT_DBG("chan %p returning %u credits to sender", chan, return_credits); chan->rx_credits += return_credits; @@ -6835,7 +7313,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); } -static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb) +static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6850,7 +7328,7 @@ static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb) return err; } -static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) +static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6898,7 +7376,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) } if (skb->len == sdu_len) - return l2cap_le_recv(chan, skb); + return l2cap_ecred_recv(chan, skb); chan->sdu = skb; chan->sdu_len = sdu_len; @@ -6930,7 +7408,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) skb = NULL; if (chan->sdu->len == chan->sdu_len) { - err = l2cap_le_recv(chan, chan->sdu); + err = l2cap_ecred_recv(chan, chan->sdu); if (!err) { chan->sdu = NULL; chan->sdu_last_frag = NULL; @@ -6991,7 +7469,8 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, switch (chan->mode) { case L2CAP_MODE_LE_FLOWCTL: - if (l2cap_le_data_rcv(chan, skb) < 0) + case L2CAP_MODE_EXT_FLOWCTL: + if (l2cap_ecred_data_rcv(chan, skb) < 0) goto drop; goto done; @@ -7218,8 +7697,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, struct hci_dev *hdev; int err; - BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst, - dst_type, __le16_to_cpu(psm)); + BT_DBG("%pMR -> %pMR (type %u) psm 0x%4.4x mode 0x%2.2x", &chan->src, + dst, dst_type, __le16_to_cpu(psm), chan->mode); hdev = hci_get_route(dst, &chan->src, chan->src_type); if (!hdev) @@ -7247,6 +7726,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, case L2CAP_MODE_BASIC: break; case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -7372,6 +7852,38 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, } EXPORT_SYMBOL_GPL(l2cap_chan_connect); +static void l2cap_ecred_reconfigure(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct { + struct l2cap_ecred_reconf_req req; + __le16 scid; + } pdu; + + pdu.req.mtu = cpu_to_le16(chan->imtu); + pdu.req.mps = cpu_to_le16(chan->mps); + pdu.scid = cpu_to_le16(chan->scid); + + chan->ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ, + sizeof(pdu), &pdu); +} + +int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu) +{ + if (chan->imtu > mtu) + return -EINVAL; + + BT_DBG("chan %p mtu 0x%4.4x", chan, mtu); + + chan->imtu = mtu; + + l2cap_ecred_reconfigure(chan); + + return 0; +} + /* ---- L2CAP interface with lower layer (HCI) ---- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) @@ -7583,7 +8095,8 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) else __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } else if (chan->state == BT_CONNECT2 && - chan->mode != L2CAP_MODE_LE_FLOWCTL) { + !(chan->mode == L2CAP_MODE_EXT_FLOWCTL || + chan->mode == L2CAP_MODE_LE_FLOWCTL)) { struct l2cap_conn_rsp rsp; __u16 res, stat; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 640cb461af9d..19123c268ce3 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -232,7 +232,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; } - if (chan->psm && bdaddr_type_is_le(chan->src_type)) + if (chan->psm && bdaddr_type_is_le(chan->src_type) && !chan->mode) chan->mode = L2CAP_MODE_LE_FLOWCTL; err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), @@ -273,6 +273,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) switch (chan->mode) { case L2CAP_MODE_BASIC: case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -408,7 +409,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, switch (optname) { case L2CAP_OPTIONS: - /* LE sockets should use BT_SNDMTU/BT_RCVMTU, but since + /* LE sockets should use BT_SNDMTU/BT_RCVMTU/BT_MODE, but since * legacy ATT code depends on getsockopt for * L2CAP_OPTIONS we need to let this pass. */ @@ -427,6 +428,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, opts.max_tx = chan->max_tx; opts.txwin_size = chan->tx_win; + BT_DBG("mode 0x%2.2x", chan->mode); + len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) err = -EFAULT; @@ -655,6 +658,7 @@ static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode) { switch (chan->mode) { case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: break; case L2CAP_MODE_BASIC: clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); @@ -727,6 +731,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, break; } + BT_DBG("mode 0x%2.2x", chan->mode); + chan->imtu = opts.imtu; chan->omtu = opts.omtu; chan->fcs = opts.fcs; @@ -959,7 +965,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; } - if (sk->sk_state == BT_CONNECTED) { + if (chan->mode == L2CAP_MODE_LE_FLOWCTL && + sk->sk_state == BT_CONNECTED) { err = -EISCONN; break; } @@ -969,7 +976,12 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; } - chan->imtu = opt; + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL && + sk->sk_state == BT_CONNECTED) + err = l2cap_chan_reconfigure(chan, opt); + else + chan->imtu = opt; + break; case BT_MODE: @@ -1042,7 +1054,11 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - if (bdaddr_type_is_le(pi->chan->src_type)) { + if (pi->chan->mode == L2CAP_MODE_EXT_FLOWCTL) { + sk->sk_state = BT_CONNECTED; + pi->chan->state = BT_CONNECTED; + __l2cap_ecred_conn_rsp_defer(pi->chan); + } if (bdaddr_type_is_le(pi->chan->src_type)) { sk->sk_state = BT_CONNECTED; pi->chan->state = BT_CONNECTED; __l2cap_le_connect_rsp_defer(pi->chan); From patchwork Mon Mar 2 21:32:36 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: 11416403 X-Patchwork-Delegate: marcel@holtmann.org 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 D803714E3 for ; Mon, 2 Mar 2020 21:32:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B7D402465D for ; Mon, 2 Mar 2020 21:32:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kHY0hnln" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726831AbgCBVco (ORCPT ); Mon, 2 Mar 2020 16:32:44 -0500 Received: from mail-pl1-f193.google.com ([209.85.214.193]:39568 "EHLO mail-pl1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726773AbgCBVco (ORCPT ); Mon, 2 Mar 2020 16:32:44 -0500 Received: by mail-pl1-f193.google.com with SMTP id g6so316031plp.6 for ; Mon, 02 Mar 2020 13:32:43 -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=iiBww5T0liLTQo9idkZdPdBXUSr8GhdCY90/l+BLa+Q=; b=kHY0hnlnB9hvsgsiQbMoC9IxhvXEHruv1ZLgzFCtBtAYum1XEEo3xwjmE6GwzwCYHV hrxSeBuOI7jplANKQfsP/C+/KLUsWQQmi9j5HXnibVzV6BT94ym0oyhid37XcaVLwzxl 6mX3Yt83LcC1JHGAtdG0d/yTV6V9H+2KM4zrzLrPtoIzMci/qbhe6HkPZ4fYtPPe24oD hraly7794AJgYu7DjwE0Tc+KgV0UxUlt6lATWpZi9DLBkuUUhe80//s7v19KyKeHruGK HIG1Lbo2k3r6Q7e5DeWSGRIsoiA7M0AfTULFXk+EwUbHsKH1vGfr9cDPcXXCo/cBuW05 yqVA== 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=iiBww5T0liLTQo9idkZdPdBXUSr8GhdCY90/l+BLa+Q=; b=lDVKQrqp3hvU8rAVBq77VlwWfrS5mmPmnhwHP1VcoB33lDqhU+sG6JgqD7XQ6UKCpc zoWQW7uk4VOK0x7OfxYxPf56iq3wvqwHBc79xnhPZzLpaZKRGHYY0DdzcQl51/nI0ETU fz8wTg3G5H04xPMjUMV3dtvGkxOMJFZEHk+GLSdTQh14lHlbxv/kWDppwhWdizEqarXu 2//T0prh3ydnnf4DDP2aBnun8TuVRf0OtmfQRNDIAMG2e4zQCdpsuFLnmhBUIEKkdfAP rDDfv5whlfEPjfODaTDqd+e1TpUudb2Fu5M3TeOXXay+CE5O6IWX1BYP7Isi29+DrYoh G+Rg== X-Gm-Message-State: ANhLgQ177avmrrnssG16cZrld8Q+PVhfdJ/B7xNXGey5veC8hxsQaad+ YQaQDqHTCISoOSQBkwqvrbRZSH94E44= X-Google-Smtp-Source: ADFU+vs5Lj5nbz/4j3/oliajS5Ov32GvEeIRV+ohKL0d3mmH0sEKnFRUFyUD0YZeuuZag4sXDqaGuA== X-Received: by 2002:a17:90b:3612:: with SMTP id ml18mr488215pjb.148.1583184762451; Mon, 02 Mar 2020 13:32:42 -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 i22sm22183749pgi.69.2020.03.02.13.32.41 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2020 13:32:41 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 4/5] Bluetooth: L2CAP: Add module option to enable ECRED mode Date: Mon, 2 Mar 2020 13:32:36 -0800 Message-Id: <20200302213237.19540-4-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200302213237.19540-1-luiz.dentz@gmail.com> References: <20200302213237.19540-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 should make it safe to have the code upstream without affecting stable systems since there are a few details not sort out with ECRED mode e.g: how to initiate multiple connections at once. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 15 +++++++++++++++ net/bluetooth/l2cap_sock.c | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index dbe68b06d4bb..1644c9651a35 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -958,6 +958,7 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) } extern bool disable_ertm; +extern bool enable_ecred; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5c03baffc82c..badfbbcc0de4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -45,6 +45,7 @@ #define LE_FLOWCTL_MAX_CREDITS 65535 bool disable_ertm; +bool enable_ecred; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; @@ -5849,6 +5850,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, int i, num_scid; bool defer = false; + if (!enable_ecred) + return -EINVAL; + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { result = L2CAP_CR_LE_INVALID_PARAMS; goto response; @@ -6092,6 +6096,9 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, struct l2cap_chan *chan; int i, num_scid; + if (!enable_ecred) + return -EINVAL; + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { result = L2CAP_CR_LE_INVALID_PARAMS; goto respond; @@ -7726,7 +7733,12 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, case L2CAP_MODE_BASIC: break; case L2CAP_MODE_LE_FLOWCTL: + break; case L2CAP_MODE_EXT_FLOWCTL: + if (!enable_ecred) { + err = -EOPNOTSUPP; + goto done; + } break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -8304,3 +8316,6 @@ void l2cap_exit(void) module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); + +module_param(enable_ecred, bool, 0644); +MODULE_PARM_DESC(enable_ecred, "Enable enhanced credit flow control mode"); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 19123c268ce3..2a9fa588e554 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -273,7 +273,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) switch (chan->mode) { case L2CAP_MODE_BASIC: case L2CAP_MODE_LE_FLOWCTL: + break; case L2CAP_MODE_EXT_FLOWCTL: + if (!enable_ecred) { + err = -EOPNOTSUPP; + goto done; + } break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -985,6 +990,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; case BT_MODE: + if (!enable_ecred) { + err = -ENOPROTOOPT; + break; + } + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { err = -EINVAL; break; From patchwork Mon Mar 2 21:32:37 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: 11416407 X-Patchwork-Delegate: marcel@holtmann.org 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 E4F3E17E0 for ; Mon, 2 Mar 2020 21:32:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C250A22B48 for ; Mon, 2 Mar 2020 21:32:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Z6e6mezg" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726843AbgCBVcp (ORCPT ); Mon, 2 Mar 2020 16:32:45 -0500 Received: from mail-pl1-f194.google.com ([209.85.214.194]:43961 "EHLO mail-pl1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726778AbgCBVcp (ORCPT ); Mon, 2 Mar 2020 16:32:45 -0500 Received: by mail-pl1-f194.google.com with SMTP id x17so304416plm.10 for ; Mon, 02 Mar 2020 13:32:44 -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=vAo0yyTRXJIRV2+8svlp67yebltVIuoxDS7qYeS2Pk4=; b=Z6e6mezggr5RLBe9H9LX4YFOXOvOz2ThJ/K0t3V3U7uAWSAm6VVbnIGFBJAOCD7L6M MdY+KnnjkT/hghW7np4xSkq4cUEKLJFAv+XQR2o8oSu+eC9fbUI7HOraKDxV7o+9NDpn ezCIVVbC02t7k72a4fwVaR/DXQWvgKvRntGRwKkVSc6KlMq0i3s+JONaUgQMBW9ZgG+k /GhzTGvzrHSZPG/9pvYc1lVuxzjD+Tf20oSxa0iqbPKHxWls2/b3rxAnBOnHn9D+avEb bJ4TMUS3nPmInqg8ViBvx/b+l+E9AVVNiphDvgONTFvGh1vKg3C9kbcYiVx6QgHMvzgf 4kvg== 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=vAo0yyTRXJIRV2+8svlp67yebltVIuoxDS7qYeS2Pk4=; b=E049UAHEJErxlKjxpRokU9TIc1oJ51+ki/cdXmRn9OqsFfsmyvS5ApVYiI7laoVRho h/M0G9YJNpI1V6pmNpiJtuTZVVWfkNdkGwYUjbG25vO3C8t6pT3n8rlcrZnwoCpH0PJb EyOJs8tiJP8WmoQjHtPT89DYVaDAtVd0ufs7vDu9HBEh7C+Y99t9EhdUwNVtYOhWplhK 3h2DNhw9IBCRRo/2Eyx3pZZLx8rCLzmip/BQbzLsFGnA903VprQ6XAGIKV4E1bQojKnG bHyts87/Kato64QhkKnBCUbqoAuEBP2Zvm2nsHC/ol6xTlxSe/UkwGL9gsJIoB7SM7sT Nuwg== X-Gm-Message-State: ANhLgQ2UxzHPJg0dLNhxM9YfpJLACOd0pDevG29IZHP6U78Pmzf1aYaV LOYyNEG32gNbARfV31THmrlfucBZ93k= X-Google-Smtp-Source: ADFU+vuGhnEDVmBhVYb/+wJ45Ii2e6EmujdRclbCNMi+sqKvsFuX+2FVNijQTaF8iPIlsLWi8hfGyQ== X-Received: by 2002:a17:90a:da01:: with SMTP id e1mr529971pjv.100.1583184763384; Mon, 02 Mar 2020 13:32:43 -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 i22sm22183749pgi.69.2020.03.02.13.32.42 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Mar 2020 13:32:42 -0800 (PST) From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH v2 5/5] Bluetooth: L2CAP: Use DEFER_SETUP to group ECRED connections Date: Mon, 2 Mar 2020 13:32:37 -0800 Message-Id: <20200302213237.19540-5-luiz.dentz@gmail.com> X-Mailer: git-send-email 2.21.1 In-Reply-To: <20200302213237.19540-1-luiz.dentz@gmail.com> References: <20200302213237.19540-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 uses the DEFER_SETUP flag to group channels with L2CAP_CREDIT_BASED_CONNECTION_REQ. Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/l2cap.h | 5 +++ net/bluetooth/l2cap_core.c | 81 ++++++++++++++++++++++++++++++----- net/bluetooth/l2cap_sock.c | 49 ++++++++++++++++++--- 3 files changed, 120 insertions(+), 15 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1644c9651a35..1b7be429f63c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -47,6 +47,7 @@ #define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF #define L2CAP_BREDR_MAX_PAYLOAD 1019 /* 3-DH5 packet */ #define L2CAP_LE_MIN_MTU 23 +#define L2CAP_ECRED_CONN_SCID_MAX 5 #define L2CAP_DISC_TIMEOUT msecs_to_jiffies(100) #define L2CAP_DISC_REJ_TIMEOUT msecs_to_jiffies(5000) @@ -660,6 +661,7 @@ struct l2cap_ops { void (*suspend) (struct l2cap_chan *chan); void (*set_shutdown) (struct l2cap_chan *chan); long (*get_sndtimeo) (struct l2cap_chan *chan); + struct pid *(*get_peer_pid) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long hdr_len, unsigned long len, int nb); @@ -983,6 +985,9 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); +void l2cap_chan_list(struct l2cap_conn *conn, + void (*func)(struct l2cap_chan *chan, void *data), + void *data); void l2cap_chan_del(struct l2cap_chan *chan, int err); void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_move_start(struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index badfbbcc0de4..8eb483948565 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -678,6 +678,23 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) } EXPORT_SYMBOL_GPL(l2cap_chan_del); +void l2cap_chan_list(struct l2cap_conn *conn, + void (*func)(struct l2cap_chan *chan, void *data), + void *data) +{ + struct l2cap_chan *chan; + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + func(chan, data); + } + + mutex_unlock(&conn->chan_lock); +} + +EXPORT_SYMBOL_GPL(l2cap_chan_list); + static void l2cap_conn_update_id_addr(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, @@ -1356,29 +1373,73 @@ static void l2cap_le_connect(struct l2cap_chan *chan) sizeof(req), &req); } -static void l2cap_ecred_connect(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; +struct l2cap_ecred_conn_data { struct { struct l2cap_ecred_conn_req req; - __le16 scid; + __le16 scid[5]; } __packed pdu; + struct l2cap_chan *chan; + struct pid *pid; + int count; +}; + +static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data) +{ + struct l2cap_ecred_conn_data *conn = data; + + if (chan == conn->chan) + return; + + /* Only add deferred channels with the same PID/PSM */ + if (conn->pid != chan->ops->get_peer_pid(chan) || + chan->psm != conn->chan->psm || chan->ident || + chan->mode != L2CAP_MODE_EXT_FLOWCTL || + chan->state != BT_CONNECT) + return; + + if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags)) + return; + + /* Set the same ident so we can match on the rsp */ + chan->ident = conn->chan->ident; + + /* Include all channels deferred */ + conn->pdu.scid[conn->count] = cpu_to_le16(chan->scid); + + conn->count++; +} + +static void l2cap_ecred_connect(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct l2cap_ecred_conn_data data; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + return; if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) return; l2cap_ecred_init(chan, 0); - pdu.req.psm = chan->psm; - pdu.req.mtu = cpu_to_le16(chan->imtu); - pdu.req.mps = cpu_to_le16(chan->mps); - pdu.req.credits = cpu_to_le16(chan->rx_credits); - pdu.scid = cpu_to_le16(chan->scid); + data.pdu.req.psm = chan->psm; + data.pdu.req.mtu = cpu_to_le16(chan->imtu); + data.pdu.req.mps = cpu_to_le16(chan->mps); + data.pdu.req.credits = cpu_to_le16(chan->rx_credits); + data.pdu.scid[0] = cpu_to_le16(chan->scid); chan->ident = l2cap_get_ident(conn); + data.pid = chan->ops->get_peer_pid(chan); + + data.count = 1; + data.chan = chan; + data.pid = chan->ops->get_peer_pid(chan); + + l2cap_chan_list(chan->conn, l2cap_ecred_defer_connect, &data); l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_CONN_REQ, - sizeof(pdu), &pdu); + sizeof(data.pdu.req) + data.count * sizeof(__le16), + &data.pdu); } static void l2cap_le_start(struct l2cap_chan *chan) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2a9fa588e554..592dadd11f15 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -172,6 +172,29 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) return err; } +struct l2cap_chan_data { + struct l2cap_chan *chan; + struct pid *pid; + int count; +}; + +static void l2cap_chan_by_pid(struct l2cap_chan *chan, void *data) +{ + struct l2cap_chan_data *d = data; + + if (chan == d->chan) + return; + + /* Only count deferred channels with the same PID/PSM */ + if (d->pid != chan->ops->get_peer_pid(chan) || + !test_bit(FLAG_DEFER_SETUP, &chan->flags) || + chan->psm != d->chan->psm || chan->ident || + chan->state != BT_CONNECT) + return; + + d->count++; +} + static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { @@ -235,6 +258,19 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, if (chan->psm && bdaddr_type_is_le(chan->src_type) && !chan->mode) chan->mode = L2CAP_MODE_LE_FLOWCTL; + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) { + struct l2cap_chan_data data; + + data.chan = chan; + data.pid = chan->ops->get_peer_pid(chan); + data.count = 0; + + l2cap_chan_list(chan->conn, l2cap_chan_by_pid, &data); + /* Check if there isn't too many channels being connected */ + if (!(data.count < L2CAP_ECRED_CONN_SCID_MAX - 1)) + return -EINVAL; + } + err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), &la.l2_bdaddr, la.l2_bdaddr_type); if (err) @@ -549,11 +585,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), (u32 __user *) optval)) err = -EFAULT; @@ -1548,6 +1579,13 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan) return sk->sk_sndtimeo; } +static struct pid *l2cap_sock_get_peer_pid_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + + return sk->sk_peer_pid; +} + static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; @@ -1569,6 +1607,7 @@ static const struct l2cap_ops l2cap_chan_ops = { .suspend = l2cap_sock_suspend_cb, .set_shutdown = l2cap_sock_set_shutdown_cb, .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, + .get_peer_pid = l2cap_sock_get_peer_pid_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, };