* Re: [RFC v3 00/12] LE auto connection and connection parameters
From: Andre Guedes @ 2013-12-04 14:35 UTC (permalink / raw)
To: linux-bluetooth@vger.kernel.org
In-Reply-To: <1384985340-2902-1-git-send-email-andre.guedes@openbossa.org>
Ping.
On Wed, Nov 20, 2013 at 7:08 PM, Andre Guedes
<andre.guedes@openbossa.org> wrote:
> Hi all,
>
> The main changes from the previous version are:
> * Debugfs interface to add auto connect address instead of new mgmt
> commands.
> * We always stop LE scanning in favor of connection establishment even if
> the controller supports scanning and connection at the same time.
> * Background scanning is now controlled in one single place (hci_update_
> background_scan function).
> * RCU was replaced by hdev->lock to protect hdev->le_conn_params list. After
> all the changes since the original version of this patch set, I realized
> we always operate on hdev->le_conn_params with hdev->lock held so there is
> no point in use RCU to protect this list.
>
> In order to test the LE auto connection mechanism please follow the
> instructions below.
>
> To add a new auto connection address we write on le_auto_conn file following
> the format <address> <address type> <auto_connect>.
>
> The <address type> values are:
> * 0 for public address
> * 1 for random address
>
> The <auto_connect> values are (for more details see struct hci_conn_params):
> * 0 for disabled
> * 1 for always
> * 2 for link loss
>
> So for instance, if you want the kernel autonomously establishes connections
> with device AA:BB:CC:DD:EE:FF (public address) every time the device enters in
> connectable mode (starts advertising), you should run the command:
> $ echo "AA:BB:CC:DD:EE:FF 0 1" > /sys/kernel/debug/bluetooth/hci0/le_auto_conn
>
> To get the list of connection parameters configured in kernel, read the
> le_auto_conn file:
> $ cat /sys/kernel/debug/bluetooth/hci0/le_auto_conn
>
> Finally, to clear the connection parameters list, write an empty string:
> $ echo "" > /sys/kernel/debug/bluetooth/hci0/le_auto_conn
>
> Regards,
>
> Andre Guedes
>
>
> Andre Guedes (12):
> Bluetooth: Save connection interval parameters in hci_conn
> Bluetooth: Group list_head fields from strcut hci_dev together
> Bluetooth: Introduce connection parameters list
> Bluetooth: Use connection parameters if any
> Bluetooth: Stop scanning on LE connection
> Bluetooth: Introduce hdev->pend_le_conn list
> Bluetooth: Introduce LE auto connection infrastructure
> Bluetooth: Re-enable background scan in case of error
> Bluetooth: Temporarily stop background scanning on discovery
> Bluetooth: Auto connection and power on
> Bleutooth: Add support for auto connect options
> Bluetooth: Add le_auto_conn file on debugfs
>
> include/net/bluetooth/hci_core.h | 43 +++++-
> net/bluetooth/hci_conn.c | 39 ++++-
> net/bluetooth/hci_core.c | 318 +++++++++++++++++++++++++++++++++++++++
> net/bluetooth/hci_event.c | 60 ++++++++
> net/bluetooth/mgmt.c | 25 ++-
> 5 files changed, 473 insertions(+), 12 deletions(-)
>
> --
> 1.8.4
>
^ permalink raw reply
* [PATCH 31/31] Bluetooth: Add debugfs controls for LE CoC MPS and Credits
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds entries to debugfs to control the values used for the
MPS and Credits for LE Flow Control Mode.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ccfee0194a2b..abcd17220706 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -49,6 +49,9 @@ static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
+static u16 le_max_credits = L2CAP_LE_MAX_CREDITS;
+static u16 le_default_mps = L2CAP_LE_DEFAULT_MPS;
+
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
@@ -501,7 +504,7 @@ void l2cap_le_flowctl_init(struct l2cap_chan *chan)
chan->omtu = L2CAP_LE_MIN_MTU;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
chan->tx_credits = 0;
- chan->rx_credits = L2CAP_LE_MAX_CREDITS;
+ chan->rx_credits = le_max_credits;
if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
chan->mps = chan->imtu;
@@ -1214,7 +1217,7 @@ static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
chan->mps = chan->imtu;
else
- chan->mps = L2CAP_LE_DEFAULT_MPS;
+ chan->mps = le_default_mps;
skb_queue_head_init(&chan->tx_q);
@@ -6829,10 +6832,10 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
/* We return more credits to the sender only after the amount of
* credits falls below half of the initial amount.
*/
- if (chan->rx_credits >= L2CAP_LE_MAX_CREDITS / 2)
+ if (chan->rx_credits >= le_max_credits / 2)
return;
- return_credits = L2CAP_LE_MAX_CREDITS - chan->rx_credits;
+ return_credits = le_max_credits - chan->rx_credits;
BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
@@ -7446,6 +7449,11 @@ int __init l2cap_init(void)
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
NULL, &l2cap_debugfs_fops);
+ debugfs_create_u16("l2cap_le_max_credits", 0444, bt_debugfs,
+ &le_max_credits);
+ debugfs_create_u16("l2cap_le_default_mps", 0444, bt_debugfs,
+ &le_default_mps);
+
return 0;
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH 30/31] Bluetooth: Fix validating LE PSM values
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
LE PSM values have different ranges than those for BR/EDR. The valid
ranges for fixed, SIG assigned values is 0x0001-0x007f and for dynamic
PSM values 0x0080-0x00ff. We need to ensure that bind() and connect()
calls conform to these ranges when operating on LE CoC sockets.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 15 +++++++++++++--
net/bluetooth/l2cap_sock.c | 40 +++++++++++++++++++++++++++++++---------
2 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a1143127414a..ccfee0194a2b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1861,6 +1861,18 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
return c1;
}
+static bool is_valid_psm(u16 psm, u8 dst_type)
+{
+ if (!psm)
+ return false;
+
+ if (bdaddr_type_is_le(dst_type))
+ return (psm < 0x00ff);
+
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ return ((psm & 0x0101) == 0x0001);
+}
+
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type)
{
@@ -1881,8 +1893,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
l2cap_chan_lock(chan);
- /* PSM must be odd and lsb of upper byte must be 0 */
- if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
+ if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index c2424782c245..f4471fd6e99e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -53,6 +53,32 @@ bool l2cap_is_socket(struct socket *sock)
}
EXPORT_SYMBOL(l2cap_is_socket);
+static int l2cap_validate_bredr_psm(u16 psm)
+{
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ if ((psm & 0x0101) != 0x0001)
+ return -EINVAL;
+
+ /* Restrict usage of well-known PSMs */
+ if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE))
+ return -EACCES;
+
+ return 0;
+}
+
+static int l2cap_validate_le_psm(u16 psm)
+{
+ /* Valid LE_PSM ranges are defined only until 0x00ff */
+ if (psm > 0x00ff)
+ return -EINVAL;
+
+ /* Restrict fixed, SIG assigned PSM values to CAP_NET_BIND_SERVICE */
+ if (psm <= 0x007f && !capable(CAP_NET_BIND_SERVICE))
+ return -EACCES;
+
+ return 0;
+}
+
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
struct sock *sk = sock->sk;
@@ -94,17 +120,13 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (la.l2_psm) {
__u16 psm = __le16_to_cpu(la.l2_psm);
- /* PSM must be odd and lsb of upper byte must be 0 */
- if ((psm & 0x0101) != 0x0001) {
- err = -EINVAL;
- goto done;
- }
+ if (la.l2_bdaddr_type == BDADDR_BREDR)
+ err = l2cap_validate_bredr_psm(psm);
+ else
+ err = l2cap_validate_le_psm(psm);
- /* Restrict usage of well-known PSMs */
- if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {
- err = -EACCES;
+ if (err)
goto done;
- }
}
if (la.l2_cid)
--
1.8.4.2
^ permalink raw reply related
* [PATCH 29/31] Bluetooth: Fix CID ranges for LE CoC CID allocations
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
LE CoC used differend CIC ranges than BR/EDR L2CAP. The start of the
range is the same (0x0040) but the range ends at 0x007f (unlike BR/EDR
where it goes all the way to 0xffff).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 9 +++++++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index a4616eeeb8b5..ef6ca5fa8f5e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -252,6 +252,7 @@ struct l2cap_conn_rsp {
#define L2CAP_CID_SMP 0x0006
#define L2CAP_CID_DYN_START 0x0040
#define L2CAP_CID_DYN_END 0xffff
+#define L2CAP_CID_LE_DYN_END 0x007f
/* connect/create channel results */
#define L2CAP_CR_SUCCESS 0x0000
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8a74b36273b9..a1143127414a 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -213,9 +213,14 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
{
- u16 cid = L2CAP_CID_DYN_START;
+ u16 cid, dyn_end;
- for (; cid < L2CAP_CID_DYN_END; cid++) {
+ if (conn->hcon->type == LE_LINK)
+ dyn_end = L2CAP_CID_LE_DYN_END;
+ else
+ dyn_end = L2CAP_CID_DYN_END;
+
+ for (cid = L2CAP_CID_DYN_START; cid < dyn_end; cid++) {
if (!__l2cap_get_chan_by_scid(conn, cid))
return cid;
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH 28/31] Bluetooth: Fix clearing of chan->omtu for LE CoC channels
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
The outgoing MTU should only be set upon channel creation to the initial
minimum value (23) or from a remote connect req/rsp PDU.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 58fbb78a703f..8a74b36273b9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -516,12 +516,12 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
if (conn->hcon->type == LE_LINK) {
- /* LE connection */
- chan->omtu = L2CAP_DEFAULT_MTU;
- if (chan->dcid == L2CAP_CID_ATT)
+ if (chan->dcid == L2CAP_CID_ATT) {
+ chan->omtu = L2CAP_DEFAULT_MTU;
chan->scid = L2CAP_CID_ATT;
- else
+ } else {
chan->scid = l2cap_alloc_cid(conn);
+ }
} else {
/* Alloc CID for connection-oriented socket */
chan->scid = l2cap_alloc_cid(conn);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 27/31] Bluetooth: Limit LE MPS to the MTU value
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
It doesn't make sense to have an MPS value greater than the configured
MTU value since we will then not be able to fill up the packets to their
full possible size. We need to set the MPS both in flowctl_init()
as well as flowctl_start() since the imtu may change after init() but
start() is only called after we've sent the LE Connection Request PDU
which depends on having a valid MPS value.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 20b8dcf05edd..58fbb78a703f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -497,6 +497,11 @@ void l2cap_le_flowctl_init(struct l2cap_chan *chan)
chan->mode = L2CAP_MODE_LE_FLOWCTL;
chan->tx_credits = 0;
chan->rx_credits = L2CAP_LE_MAX_CREDITS;
+
+ if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
+ chan->mps = chan->imtu;
+ else
+ chan->mps = L2CAP_LE_DEFAULT_MPS;
}
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
@@ -645,7 +650,7 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
- rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = cpu_to_le16(result);
@@ -1201,6 +1206,11 @@ static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
+ if (chan->imtu < L2CAP_LE_DEFAULT_MPS)
+ chan->mps = chan->imtu;
+ else
+ chan->mps = L2CAP_LE_DEFAULT_MPS;
+
skb_queue_head_init(&chan->tx_q);
if (!chan->tx_credits)
@@ -1232,7 +1242,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
req.psm = chan->psm;
req.scid = cpu_to_le16(chan->scid);
req.mtu = cpu_to_le16(chan->imtu);
- req.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ req.mps = cpu_to_le16(chan->mps);
req.credits = cpu_to_le16(chan->rx_credits);
chan->ident = l2cap_get_ident(conn);
@@ -3826,7 +3836,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
- rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
@@ -5669,7 +5679,7 @@ response_unlock:
response:
if (chan) {
rsp.mtu = cpu_to_le16(chan->imtu);
- rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ rsp.mps = cpu_to_le16(chan->mps);
} else {
rsp.mtu = 0;
rsp.mps = 0;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 26/31] Bluetooth: Fix suspending the L2CAP socket if we start with 0 credits
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
If the peer gives us zero credits in its connection request or response
we must call the suspend channel callback so the BT_SK_SUSPEND flag gets
set and user space is blocked from sending data to the socket.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0e3a6eac289c..20b8dcf05edd 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1202,6 +1202,9 @@ static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
chan->sdu_len = 0;
skb_queue_head_init(&chan->tx_q);
+
+ if (!chan->tx_credits)
+ chan->ops->suspend(chan);
}
static void l2cap_chan_ready(struct l2cap_chan *chan)
--
1.8.4.2
^ permalink raw reply related
* [PATCH 25/31] Bluetooth: Fix LE L2CAP Connect Request handling together with SMP
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
Unlike BR/EDR, for LE when we're in the BT_CONNECT state we may or may
not have already have sent the Connect Request. This means that we need
some extra tracking of the request. This patch adds an extra channel
flag to prevent the request from being sent a second time.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 3 +++
2 files changed, 4 insertions(+)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c6c86d0e39c5..a4616eeeb8b5 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -695,6 +695,7 @@ enum {
FLAG_EXT_CTRL,
FLAG_EFS_ENABLE,
FLAG_DEFER_SETUP,
+ FLAG_LE_CONN_REQ_SENT,
};
enum {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index eb6edbcc3ce3..0e3a6eac289c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1223,6 +1223,9 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
struct l2cap_conn *conn = chan->conn;
struct l2cap_le_conn_req req;
+ if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
+ return;
+
req.psm = chan->psm;
req.scid = cpu_to_le16(chan->scid);
req.mtu = cpu_to_le16(chan->imtu);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 24/31] Bluetooth: Implement LE L2CAP reassembly
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
When receiving fragments over an LE Connection oriented Channel they
need to be collected up and eventually merged into a single SDU. This
patch adds the necessary code for collecting up the fragment skbs to the
channel context and passing them to the recv() callback when the entire
SDU has been received.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 79 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 76 insertions(+), 3 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 2c591afccb51..eb6edbcc3ce3 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6816,18 +6816,91 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
- if (!chan->rx_credits)
+ int err;
+
+ if (!chan->rx_credits) {
+ BT_ERR("No credits to receive LE L2CAP data");
return -ENOBUFS;
+ }
- if (chan->imtu < skb->len)
+ if (chan->imtu < skb->len) {
+ BT_ERR("Too big LE L2CAP PDU");
return -ENOBUFS;
+ }
chan->rx_credits--;
BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
l2cap_chan_le_send_credits(chan);
- return chan->ops->recv(chan, skb);
+ err = 0;
+
+ if (!chan->sdu) {
+ u16 sdu_len;
+
+ sdu_len = get_unaligned_le16(skb->data);
+ skb_pull(skb, L2CAP_SDULEN_SIZE);
+
+ BT_DBG("Start of new SDU. sdu_len %u skb->len %u imtu %u",
+ sdu_len, skb->len, chan->imtu);
+
+ if (sdu_len > chan->imtu) {
+ BT_ERR("Too big LE L2CAP SDU length received");
+ err = -EMSGSIZE;
+ goto failed;
+ }
+
+ if (skb->len > sdu_len) {
+ BT_ERR("Too much LE L2CAP data received");
+ err = -EINVAL;
+ goto failed;
+ }
+
+ if (skb->len == sdu_len)
+ return chan->ops->recv(chan, skb);
+
+ chan->sdu = skb;
+ chan->sdu_len = sdu_len;
+ chan->sdu_last_frag = skb;
+
+ return 0;
+ }
+
+ BT_DBG("SDU fragment. chan->sdu->len %u skb->len %u chan->sdu_len %u",
+ chan->sdu->len, skb->len, chan->sdu_len);
+
+ if (chan->sdu->len + skb->len > chan->sdu_len) {
+ BT_ERR("Too much LE L2CAP data received");
+ err = -EINVAL;
+ goto failed;
+ }
+
+ append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag);
+ skb = NULL;
+
+ if (chan->sdu->len == chan->sdu_len) {
+ err = chan->ops->recv(chan, chan->sdu);
+ if (!err) {
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ }
+ }
+
+failed:
+ if (err) {
+ kfree_skb(skb);
+ kfree_skb(chan->sdu);
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ }
+
+ /* We can't return an error here since we took care of the skb
+ * freeing internally. An error return would cause the caller to
+ * do a double-free of the skb.
+ */
+ return 0;
}
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
--
1.8.4.2
^ permalink raw reply related
* [PATCH 23/31] Bluetooth: Add LE L2CAP segmentation support for outgoing data
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds segmentation support for outgoing data packets. Packets
are segmented based on the MTU and MPS values. The l2cap_chan struct
already contains many helpful variables from BR/EDR Enhanced L2CAP which
can be used for this.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 127 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 126 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b0287f57d534..2c591afccb51 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -607,6 +607,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
break;
case L2CAP_MODE_LE_FLOWCTL:
+ skb_queue_purge(&chan->tx_q);
break;
case L2CAP_MODE_ERTM:
@@ -1194,12 +1195,24 @@ static void l2cap_move_done(struct l2cap_chan *chan)
}
}
+static void l2cap_le_flowctl_start(struct l2cap_chan *chan)
+{
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+
+ skb_queue_head_init(&chan->tx_q);
+}
+
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
chan->conf_state = 0;
__clear_chan_timer(chan);
+ if (chan->mode == L2CAP_MODE_LE_FLOWCTL)
+ l2cap_le_flowctl_start(chan);
+
chan->state = BT_CONNECTED;
chan->ops->ready(chan);
@@ -2521,6 +2534,89 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
return 0;
}
+static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
+ struct msghdr *msg,
+ size_t len, u16 sdulen)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct sk_buff *skb;
+ int err, count, hlen;
+ struct l2cap_hdr *lh;
+
+ BT_DBG("chan %p len %zu", chan, len);
+
+ if (!conn)
+ return ERR_PTR(-ENOTCONN);
+
+ hlen = L2CAP_HDR_SIZE;
+
+ if (sdulen)
+ hlen += L2CAP_SDULEN_SIZE;
+
+ count = min_t(unsigned int, (conn->mtu - hlen), len);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT);
+ if (IS_ERR(skb))
+ return skb;
+
+ /* Create L2CAP header */
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(chan->dcid);
+ lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+
+ if (sdulen)
+ put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
+
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
+static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
+ struct sk_buff_head *seg_queue,
+ struct msghdr *msg, size_t len)
+{
+ struct sk_buff *skb;
+ size_t pdu_len;
+ u16 sdu_len;
+
+ BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
+
+ pdu_len = chan->conn->mtu - L2CAP_HDR_SIZE;
+
+ pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
+
+ sdu_len = len;
+ pdu_len -= L2CAP_SDULEN_SIZE;
+
+ while (len > 0) {
+ if (len <= pdu_len)
+ pdu_len = len;
+
+ skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
+ if (IS_ERR(skb)) {
+ __skb_queue_purge(seg_queue);
+ return PTR_ERR(skb);
+ }
+
+ __skb_queue_tail(seg_queue, skb);
+
+ len -= pdu_len;
+
+ if (sdu_len) {
+ sdu_len = 0;
+ pdu_len += L2CAP_SDULEN_SIZE;
+ }
+ }
+
+ return 0;
+}
+
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority)
{
@@ -2543,10 +2639,39 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
+ /* Check outgoing MTU */
+ if (len > chan->omtu)
+ return -EMSGSIZE;
+
if (!chan->tx_credits)
return -EAGAIN;
- /* fall through */
+ __skb_queue_head_init(&seg_queue);
+
+ err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
+
+ if (chan->state != BT_CONNECTED) {
+ __skb_queue_purge(&seg_queue);
+ err = -ENOTCONN;
+ }
+
+ if (err)
+ return err;
+
+ skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
+
+ while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
+ l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
+ chan->tx_credits--;
+ }
+
+ if (!chan->tx_credits)
+ chan->ops->suspend(chan);
+
+ err = len;
+
+ break;
+
case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
if (len > chan->omtu)
--
1.8.4.2
^ permalink raw reply related
* [PATCH 22/31] Bluetooth: Introduce L2CAP channel callback for suspending
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
Setting the BT_SK_SUSPEND socket flag from the L2CAP core is causing a
dependency on the socket. So instead of doing that, use a channel
callback into the socket handling to suspend.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_sock.c | 9 +++++++++
2 files changed, 10 insertions(+)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 8c59ed17ee90..c6c86d0e39c5 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -593,6 +593,7 @@ struct l2cap_ops {
void (*ready) (struct l2cap_chan *chan);
void (*defer) (struct l2cap_chan *chan);
void (*resume) (struct l2cap_chan *chan);
+ void (*suspend) (struct l2cap_chan *chan);
void (*set_shutdown) (struct l2cap_chan *chan);
long (*get_sndtimeo) (struct l2cap_chan *chan);
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 9007def8c619..c2424782c245 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1321,6 +1321,14 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
return sk->sk_sndtimeo;
}
+static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+ sk->sk_state_change(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -1331,6 +1339,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
.resume = l2cap_sock_resume_cb,
+ .suspend = l2cap_sock_suspend_cb,
.set_shutdown = l2cap_sock_set_shutdown_cb,
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
--
1.8.4.2
^ permalink raw reply related
* [PATCH 21/31] Bluetooth: Reject LE CoC commands when the feature is not enabled
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
Since LE CoC support needs to be enabled through a module option for now
we need to reject any related signaling PDUs in addition to rejecting
the creation of LE CoC sockets (which we already do).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fd3804dc8ca6..b0287f57d534 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5595,6 +5595,17 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
{
int err = 0;
+ if (!enable_lecoc) {
+ switch (cmd->code) {
+ case L2CAP_LE_CONN_REQ:
+ case L2CAP_LE_CONN_RSP:
+ case L2CAP_LE_CREDITS:
+ case L2CAP_DISCONN_REQ:
+ case L2CAP_DISCONN_RSP:
+ return -EINVAL;
+ }
+ }
+
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
break;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 20/31] Bluetooth: Add LE flow control discipline
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds the necessary discipline for reacting to LE L2CAP
Credits packets, sending those packets, and modifying the known credits
accordingly.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 68 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 64 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b99bdc53c57b..fd3804dc8ca6 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2543,7 +2543,9 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
- chan->tx_credits--;
+ if (!chan->tx_credits)
+ return -EAGAIN;
+
/* fall through */
case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
@@ -5551,6 +5553,42 @@ response:
return 0;
}
+static inline int l2cap_le_credits(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_credits *pkt;
+ struct l2cap_chan *chan;
+ u16 cid, credits;
+
+ if (cmd_len != sizeof(*pkt))
+ return -EPROTO;
+
+ pkt = (struct l2cap_le_credits *) data;
+ cid = __le16_to_cpu(pkt->cid);
+ credits = __le16_to_cpu(pkt->credits);
+
+ BT_DBG("cid 0x%4.4x credits 0x%4.4x", cid, credits);
+
+ chan = l2cap_get_chan_by_dcid(conn, cid);
+ if (!chan)
+ return -EBADSLT;
+
+ chan->tx_credits += credits;
+
+ while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
+ l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
+ chan->tx_credits--;
+ }
+
+ if (chan->tx_credits)
+ chan->ops->resume(chan);
+
+ l2cap_chan_unlock(chan);
+
+ return 0;
+}
+
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -5576,6 +5614,10 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
break;
+ case L2CAP_LE_CREDITS:
+ err = l2cap_le_credits(conn, cmd, cmd_len, data);
+ break;
+
case L2CAP_DISCONN_REQ:
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
break;
@@ -6636,6 +6678,22 @@ 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_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ if (!chan->rx_credits)
+ return -ENOBUFS;
+
+ if (chan->imtu < skb->len)
+ return -ENOBUFS;
+
+ chan->rx_credits--;
+ BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
+
+ l2cap_chan_le_send_credits(chan);
+
+ return chan->ops->recv(chan, skb);
+}
+
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
struct sk_buff *skb)
{
@@ -6666,9 +6724,11 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
- chan->rx_credits--;
- l2cap_chan_le_send_credits(chan);
- /* fall through */
+ if (l2cap_le_data_rcv(chan, skb) < 0)
+ goto drop;
+
+ goto done;
+
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
--
1.8.4.2
^ permalink raw reply related
* [PATCH 19/31] Bluetooth: Implement returning of LE L2CAP credits
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
We should return credits to the remote side whenever they fall below a
certain level (in our case under half of the initially given amount).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8a1c528908fb..b99bdc53c57b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -2542,8 +2542,10 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
}
switch (chan->mode) {
- case L2CAP_MODE_BASIC:
case L2CAP_MODE_LE_FLOWCTL:
+ chan->tx_credits--;
+ /* fall through */
+ case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
if (len > chan->omtu)
return -EMSGSIZE;
@@ -6608,6 +6610,32 @@ drop:
return 0;
}
+static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_credits pkt;
+ u16 return_credits;
+
+ /* We return more credits to the sender only after the amount of
+ * credits falls below half of the initial amount.
+ */
+ if (chan->rx_credits >= L2CAP_LE_MAX_CREDITS / 2)
+ return;
+
+ return_credits = L2CAP_LE_MAX_CREDITS - chan->rx_credits;
+
+ BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
+
+ chan->rx_credits += return_credits;
+
+ pkt.cid = cpu_to_le16(chan->scid);
+ pkt.credits = cpu_to_le16(return_credits);
+
+ chan->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
+}
+
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
struct sk_buff *skb)
{
@@ -6638,6 +6666,9 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
+ chan->rx_credits--;
+ l2cap_chan_le_send_credits(chan);
+ /* fall through */
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
--
1.8.4.2
^ permalink raw reply related
* [PATCH 18/31] Bluetooth: Add new BT_SNDMTU and BT_RCVMTU socket options
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds new socket options for LE sockets since the existing
L2CAP_OPTIONS socket option is not usable for LE. For now, the new
socket options also require LE CoC support to be explicitly enabled to
leave some playroom in case something needs to be changed in a backwards
incompatible way.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/bluetooth.h | 3 +++
net/bluetooth/l2cap_sock.c | 52 +++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 2a628b28249f..f4f9ee466791 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -115,6 +115,9 @@ struct bt_voice {
#define BT_VOICE_TRANSPARENT 0x0003
#define BT_VOICE_CVSD_16BIT 0x0060
+#define BT_SNDMTU 12
+#define BT_RCVMTU 13
+
__printf(1, 2)
int bt_info(const char *fmt, ...);
__printf(1, 2)
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index a20fcc3ddcd9..9007def8c619 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -534,6 +534,31 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;
+ case BT_SNDMTU:
+ if (sk->sk_state != BT_CONNECTED) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ if (!enable_lecoc || !bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(chan->omtu, (u16 __user *) optval))
+ err = -EFAULT;
+ break;
+
+ case BT_RCVMTU:
+ if (!enable_lecoc || !bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(chan->imtu, (u16 __user *) optval))
+ err = -EFAULT;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -834,6 +859,33 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
+ case BT_SNDMTU:
+ /* Setting is not supported as it's the remote side that
+ * decides this.
+ */
+ err = -EINVAL;
+ break;
+
+ case BT_RCVMTU:
+ if (sk->sk_state == BT_CONNECTED) {
+ err = -EISCONN;
+ break;
+ }
+
+ if (!enable_lecoc || !bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ chan->imtu = opt;
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 17/31] Bluetooth: Limit L2CAP_OPTIONS socket option usage with LE
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
Most of the values in L2CAP_OPTIONS are not applicable for LE and those
that are have different semantics. It makes therefore sense to
completely block this socket option for LE and add (in a separate patch)
a new socket option for tweaking the values that do make sense (mainly
the send and receive MTU). Legacy user space ATT code still depends on
getsockopt for L2CAP_OPTIONS though so we need to plug a hole for that
for backwards compatibility.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_sock.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 61e25bafdf43..a20fcc3ddcd9 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -370,6 +370,16 @@ 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
+ * legacy ATT code depends on getsockopt for
+ * L2CAP_OPTIONS we need to let this pass.
+ */
+ if (bdaddr_type_is_le(chan->src_type) &&
+ chan->scid != L2CAP_CID_ATT) {
+ err = -EINVAL;
+ break;
+ }
+
memset(&opts, 0, sizeof(opts));
opts.imtu = chan->imtu;
opts.omtu = chan->omtu;
@@ -564,6 +574,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
switch (optname) {
case L2CAP_OPTIONS:
+ if (bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
if (sk->sk_state == BT_CONNECTED) {
err = -EINVAL;
break;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 16/31] Bluetooth: Track LE L2CAP credits in l2cap_chan
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds tracking of L2CAP connection oriented channel local and
remote credits to struct l2cap_chan and ensures that connect requests
and responses contain the right values.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 3 +++
net/bluetooth/l2cap_core.c | 16 +++++++++++-----
net/bluetooth/l2cap_sock.c | 2 ++
3 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c60498907180..8c59ed17ee90 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -514,6 +514,9 @@ struct l2cap_chan {
__u16 monitor_timeout;
__u16 mps;
+ __u16 tx_credits;
+ __u16 rx_credits;
+
__u8 tx_state;
__u8 rx_state;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4ca6fbc777f0..8a1c528908fb 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -495,6 +495,8 @@ void l2cap_le_flowctl_init(struct l2cap_chan *chan)
chan->imtu = L2CAP_DEFAULT_MTU;
chan->omtu = L2CAP_LE_MIN_MTU;
chan->mode = L2CAP_MODE_LE_FLOWCTL;
+ chan->tx_credits = 0;
+ chan->rx_credits = L2CAP_LE_MAX_CREDITS;
}
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
@@ -643,7 +645,7 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
- rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
@@ -1212,7 +1214,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
req.scid = cpu_to_le16(chan->scid);
req.mtu = cpu_to_le16(chan->imtu);
req.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
- req.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ req.credits = cpu_to_le16(chan->rx_credits);
chan->ident = l2cap_get_ident(conn);
@@ -3690,7 +3692,7 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
- rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
@@ -5342,6 +5344,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
chan->dcid = dcid;
chan->omtu = mtu;
chan->remote_mps = mps;
+ chan->tx_credits = credits;
l2cap_chan_ready(chan);
break;
@@ -5445,7 +5448,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data;
struct l2cap_le_conn_rsp rsp;
struct l2cap_chan *chan, *pchan;
- u16 dcid, scid, mtu, mps;
+ u16 dcid, scid, credits, mtu, mps;
__le16 psm;
u8 result;
@@ -5457,6 +5460,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
mps = __le16_to_cpu(req->mps);
psm = req->psm;
dcid = 0;
+ credits = 0;
if (mtu < 23 || mps < 23)
return -EPROTO;
@@ -5501,9 +5505,11 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
chan->dcid = scid;
chan->omtu = mtu;
chan->remote_mps = mps;
+ chan->tx_credits = __le16_to_cpu(req->credits);
__l2cap_chan_add(conn, chan);
dcid = chan->scid;
+ credits = chan->rx_credits;
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
@@ -5535,7 +5541,7 @@ response:
}
rsp.dcid = cpu_to_le16(dcid);
- rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ rsp.credits = cpu_to_le16(credits);
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 485ca349fed5..61e25bafdf43 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1321,6 +1321,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->tx_win_max = pchan->tx_win_max;
chan->sec_level = pchan->sec_level;
chan->flags = pchan->flags;
+ chan->tx_credits = pchan->tx_credits;
+ chan->rx_credits = pchan->rx_credits;
security_sk_clone(parent, sk);
} else {
--
1.8.4.2
^ permalink raw reply related
* [PATCH 15/31] Bluetooth: Add LE L2CAP flow control mode
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
The LE connection oriented channels have their own mode with its own
data transfer rules. In order to implement this properly we need to
distinguish L2CAP channels operating in this mode from other modes.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 7 +++++++
net/bluetooth/l2cap_core.c | 13 +++++++++++++
net/bluetooth/l2cap_sock.c | 21 ++++++++++++++++++---
3 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b622ccc0ab64..c60498907180 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -328,6 +328,12 @@ struct l2cap_conf_rfc {
#define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAMING 0x04
+/* Unlike the above this one doesn't actually map to anything that would
+ * ever be sent over the air. Therefore, use a value that's unlikely to
+ * ever be used in the BR/EDR configuration phase.
+ */
+#define L2CAP_MODE_LE_FLOWCTL 0x80
+
struct l2cap_conf_efs {
__u8 id;
__u8 stype;
@@ -861,6 +867,7 @@ 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);
void l2cap_chan_set_defaults(struct l2cap_chan *chan);
+void l2cap_le_flowctl_init(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);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index cc7421568c68..4ca6fbc777f0 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -490,6 +490,13 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
+void l2cap_le_flowctl_init(struct l2cap_chan *chan)
+{
+ chan->imtu = L2CAP_DEFAULT_MTU;
+ chan->omtu = L2CAP_LE_MIN_MTU;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+}
+
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,
@@ -597,6 +604,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
case L2CAP_MODE_BASIC:
break;
+ case L2CAP_MODE_LE_FLOWCTL:
+ break;
+
case L2CAP_MODE_ERTM:
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -1849,6 +1859,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
switch (chan->mode) {
case L2CAP_MODE_BASIC:
+ case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
@@ -2530,6 +2541,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
switch (chan->mode) {
case L2CAP_MODE_BASIC:
+ case L2CAP_MODE_LE_FLOWCTL:
/* Check outgoing MTU */
if (len > chan->omtu)
return -EMSGSIZE;
@@ -6619,6 +6631,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
goto drop;
switch (chan->mode) {
+ case L2CAP_MODE_LE_FLOWCTL:
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 5a1d0cb0b8d5..485ca349fed5 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -130,6 +130,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
bacpy(&chan->src, &la.l2_bdaddr);
chan->src_type = la.l2_bdaddr_type;
+ if (chan->psm && bdaddr_type_is_le(chan->src_type))
+ l2cap_le_flowctl_init(chan);
+
chan->state = BT_BOUND;
sk->sk_state = BT_BOUND;
@@ -200,6 +203,9 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
return -EINVAL;
}
+ if (chan->psm && bdaddr_type_is_le(chan->src_type))
+ l2cap_le_flowctl_init(chan);
+
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type);
if (err)
@@ -237,6 +243,7 @@ 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_ERTM:
case L2CAP_MODE_STREAMING:
@@ -588,6 +595,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
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;
@@ -862,10 +871,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP,
&bt_sk(sk)->flags)) {
- sk->sk_state = BT_CONFIG;
- pi->chan->state = BT_CONFIG;
+ 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);
+ } else {
+ sk->sk_state = BT_CONFIG;
+ pi->chan->state = BT_CONFIG;
+ __l2cap_connect_rsp_defer(pi->chan);
+ }
- __l2cap_connect_rsp_defer(pi->chan);
err = 0;
goto done;
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH 14/31] Bluetooth: Make l2cap_le_sig_cmd logic consistent
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch makes the error handling and return logic of l2cap_le_sig_cmd
consistent with its BR/EDR counterpart.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d6921a4222e9..cc7421568c68 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5535,34 +5535,42 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
{
+ int err = 0;
+
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
- return 0;
+ break;
case L2CAP_CONN_PARAM_UPDATE_REQ:
- return l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
+ err = l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
+ break;
case L2CAP_CONN_PARAM_UPDATE_RSP:
- return 0;
+ break;
case L2CAP_LE_CONN_RSP:
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
- return 0;
+ break;
case L2CAP_LE_CONN_REQ:
- return l2cap_le_connect_req(conn, cmd, cmd_len, data);
+ err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
+ break;
case L2CAP_DISCONN_REQ:
- return l2cap_disconnect_req(conn, cmd, cmd_len, data);
+ err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
+ break;
case L2CAP_DISCONN_RSP:
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
- return 0;
+ break;
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
- return -EINVAL;
+ err = -EINVAL;
+ break;
}
+
+ return err;
}
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
--
1.8.4.2
^ permalink raw reply related
* [PATCH 13/31] Bluetooth: Add L2CAP Disconnect suppport for LE
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
The normal L2CAP Disconnect request and response are also used for LE
connection oriented channels. Therefore, we can simply use the existing
handler functions for terminating LE based L2CAP channels.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 333031fde67e..d6921a4222e9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5552,6 +5552,13 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
case L2CAP_LE_CONN_REQ:
return l2cap_le_connect_req(conn, cmd, cmd_len, data);
+ case L2CAP_DISCONN_REQ:
+ return l2cap_disconnect_req(conn, cmd, cmd_len, data);
+
+ case L2CAP_DISCONN_RSP:
+ l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
+ return 0;
+
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
return -EINVAL;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 12/31] Bluetooth: Fix L2CAP channel closing for LE connections
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
Sending of the L2CAP Disconnect request should also be performed for LE
based channels. The proper thing to do is therefore to look at whether
there's a PSM specified for the channel instead of looking at the link
type.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 891995f845f6..333031fde67e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -674,8 +674,10 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONNECTED:
case BT_CONFIG:
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
+ /* ATT uses L2CAP_CHAN_CONN_ORIENTED so we must also
+ * check for chan->psm.
+ */
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && chan->psm) {
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
--
1.8.4.2
^ permalink raw reply related
* [PATCH 11/31] Bluetooth: Add basic LE L2CAP connect request receiving support
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds the necessary boiler plate code to handle receiving
L2CAP connect requests over LE and respond to them with a proper connect
response.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 150 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 151 insertions(+)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b9e3240edf02..b622ccc0ab64 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -846,6 +846,7 @@ int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void);
bool l2cap_is_socket(struct socket *sock);
+void __l2cap_le_connect_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);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5f9287fd86df..891995f845f6 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -617,6 +617,29 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
+static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_conn_rsp rsp;
+ u16 result;
+
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ result = L2CAP_CR_AUTHORIZATION;
+ else
+ result = L2CAP_CR_BAD_PSM;
+
+ l2cap_state_change(chan, BT_DISCONN);
+
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ 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;
@@ -663,6 +686,8 @@ 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);
}
l2cap_chan_del(chan, reason);
@@ -3641,6 +3666,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
return ptr - data;
}
+void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
+{
+ struct l2cap_le_conn_rsp rsp;
+ struct l2cap_conn *conn = chan->conn;
+
+ BT_DBG("chan %p", chan);
+
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
+ &rsp);
+}
+
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
{
struct l2cap_conn_rsp rsp;
@@ -5382,6 +5424,111 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
return err;
}
+static int l2cap_le_connect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data;
+ struct l2cap_le_conn_rsp rsp;
+ struct l2cap_chan *chan, *pchan;
+ u16 dcid, scid, mtu, mps;
+ __le16 psm;
+ u8 result;
+
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
+ scid = __le16_to_cpu(req->scid);
+ mtu = __le16_to_cpu(req->mtu);
+ mps = __le16_to_cpu(req->mps);
+ psm = req->psm;
+ dcid = 0;
+
+ if (mtu < 23 || mps < 23)
+ return -EPROTO;
+
+ BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm),
+ scid, mtu, mps);
+
+ /* 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_BAD_PSM;
+ chan = NULL;
+ goto response;
+ }
+
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(pchan);
+
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+ result = L2CAP_CR_AUTHENTICATION;
+ goto response_unlock;
+ }
+
+ /* Check if we already have channel with that dcid */
+ if (__l2cap_get_chan_by_dcid(conn, scid)) {
+ result = L2CAP_CR_NO_MEM;
+ goto response_unlock;
+ }
+
+ chan = pchan->ops->new_connection(pchan);
+ if (!chan) {
+ result = L2CAP_CR_NO_MEM;
+ goto response_unlock;
+ }
+
+ bacpy(&chan->src, &conn->hcon->src);
+ bacpy(&chan->dst, &conn->hcon->dst);
+ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
+ chan->psm = psm;
+ chan->dcid = scid;
+ chan->omtu = mtu;
+ chan->remote_mps = mps;
+
+ __l2cap_chan_add(conn, chan);
+ dcid = 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);
+ result = L2CAP_CR_PEND;
+ chan->ops->defer(chan);
+ } else {
+ l2cap_chan_ready(chan);
+ result = L2CAP_CR_SUCCESS;
+ }
+
+response_unlock:
+ l2cap_chan_unlock(pchan);
+ mutex_unlock(&conn->chan_lock);
+
+ if (result == L2CAP_CR_PEND)
+ return 0;
+
+response:
+ if (chan) {
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ } else {
+ rsp.mtu = 0;
+ rsp.mps = 0;
+ }
+
+ rsp.dcid = cpu_to_le16(dcid);
+ rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+ rsp.result = cpu_to_le16(result);
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp);
+
+ return 0;
+}
+
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -5400,6 +5547,9 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
return 0;
+ case L2CAP_LE_CONN_REQ:
+ return l2cap_le_connect_req(conn, cmd, cmd_len, data);
+
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
return -EINVAL;
--
1.8.4.2
^ permalink raw reply related
* [PATCH 10/31] Bluetooth: Refactor L2CAP connect rejection to its own function
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
We'll need to have a separate code patch for LE based connection
rejection so it's cleaner to move out the response construction code
into its own function (and later a second one for LE).
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 42 ++++++++++++++++++++++++------------------
1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 256662578f89..5f9287fd86df 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -617,6 +617,27 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
+static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_conn_rsp rsp;
+ u16 result;
+
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ result = L2CAP_CR_SEC_BLOCK;
+ else
+ result = L2CAP_CR_BAD_PSM;
+
+ l2cap_state_change(chan, BT_DISCONN);
+
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+}
+
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
{
struct l2cap_conn *conn = chan->conn;
@@ -639,24 +660,9 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
case BT_CONNECT2:
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
- struct l2cap_conn_rsp rsp;
- __u16 result;
-
- if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
- result = L2CAP_CR_SEC_BLOCK;
- else
- result = L2CAP_CR_BAD_PSM;
-
- l2cap_state_change(chan, BT_DISCONN);
-
- rsp.scid = cpu_to_le16(chan->dcid);
- rsp.dcid = cpu_to_le16(chan->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
- sizeof(rsp), &rsp);
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
+ if (conn->hcon->type == ACL_LINK)
+ l2cap_chan_connect_reject(chan);
}
l2cap_chan_del(chan, reason);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 09/31] Bluetooth: Add smp_sufficient_security helper function
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This function is needed both by the smp_conn_security function as well
as upcoming code to check for the security requirements when receiving
an L2CAP connect request over LE.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/smp.c | 16 ++++++++++++----
net/bluetooth/smp.h | 1 +
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e61e74a1aabb..45007362683b 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -750,6 +750,17 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+{
+ if (sec_level == BT_SECURITY_LOW)
+ return true;
+
+ if (hcon->sec_level >= sec_level)
+ return true;
+
+ return false;
+}
+
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -761,10 +772,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
return 1;
- if (sec_level == BT_SECURITY_LOW)
- return 1;
-
- if (hcon->sec_level >= sec_level)
+ if (smp_sufficient_security(hcon, sec_level))
return 1;
if (hcon->link_mode & HCI_LM_MASTER)
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index f8ba07f3e5fa..a700bcb490d7 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -136,6 +136,7 @@ struct smp_chan {
};
/* SMP Commands */
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 08/31] Bluetooth: Add initial code for LE L2CAP Connect Request
From: Johan Hedberg @ 2013-12-04 14:11 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1386166287-13693-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds the necessary code to send an LE L2CAP Connect Request
and handle its response when user space has provided us with an LE
socket with a PSM instead of a fixed CID.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 106 +++++++++++++++++++++++++++++++++++++++++----
1 file changed, 98 insertions(+), 8 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d250d8af7fd6..256662578f89 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1160,21 +1160,51 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
chan->ops->ready(chan);
}
+static void l2cap_le_connect(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_conn_req req;
+
+ req.psm = chan->psm;
+ req.scid = cpu_to_le16(chan->scid);
+ req.mtu = cpu_to_le16(chan->imtu);
+ req.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS);
+ req.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS);
+
+ chan->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_REQ,
+ sizeof(req), &req);
+}
+
+static void l2cap_le_start(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+
+ if (!smp_conn_security(conn->hcon, chan->sec_level))
+ return;
+
+ if (!chan->psm) {
+ l2cap_chan_ready(chan);
+ return;
+ }
+
+ if (chan->state == BT_CONNECT)
+ l2cap_le_connect(chan);
+}
+
static void l2cap_start_connection(struct l2cap_chan *chan)
{
if (__amp_capable(chan)) {
BT_DBG("chan %p AMP capable: discover AMPs", chan);
a2mp_discover_amp(chan);
+ } else if (chan->conn->hcon->type == LE_LINK) {
+ l2cap_le_start(chan);
} else {
l2cap_send_conn_req(chan);
}
}
-static void l2cap_le_start(struct l2cap_chan *chan)
-{
- l2cap_chan_ready(chan);
-}
-
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -1438,9 +1468,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
}
if (hcon->type == LE_LINK) {
- if (smp_conn_security(hcon, chan->sec_level))
- l2cap_chan_ready(chan);
-
+ l2cap_le_start(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
l2cap_chan_ready(chan);
@@ -5210,6 +5238,64 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
return 0;
}
+static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+ u16 dcid, mtu, mps, credits, result;
+ struct l2cap_chan *chan;
+ int err;
+
+ if (cmd_len < sizeof(*rsp))
+ return -EPROTO;
+
+ dcid = __le16_to_cpu(rsp->dcid);
+ mtu = __le16_to_cpu(rsp->mtu);
+ mps = __le16_to_cpu(rsp->mps);
+ credits = __le16_to_cpu(rsp->credits);
+ result = __le16_to_cpu(rsp->result);
+
+ if (result == L2CAP_CR_SUCCESS && (mtu < 23 || mps < 23))
+ return -EPROTO;
+
+ BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
+ dcid, mtu, mps, credits, result);
+
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan) {
+ err = -EBADSLT;
+ goto unlock;
+ }
+
+ err = 0;
+
+ l2cap_chan_lock(chan);
+
+ switch (result) {
+ case L2CAP_CR_SUCCESS:
+ chan->ident = 0;
+ chan->dcid = dcid;
+ chan->omtu = mtu;
+ chan->remote_mps = mps;
+ l2cap_chan_ready(chan);
+ break;
+
+ default:
+ l2cap_chan_del(chan, ECONNREFUSED);
+ break;
+ }
+
+ l2cap_chan_unlock(chan);
+
+unlock:
+ mutex_unlock(&conn->chan_lock);
+
+ return err;
+}
+
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -5304,6 +5390,10 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
case L2CAP_CONN_PARAM_UPDATE_RSP:
return 0;
+ case L2CAP_LE_CONN_RSP:
+ l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
+ return 0;
+
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
return -EINVAL;
--
1.8.4.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox