2007-02-09 22:24:33 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
BlueZ - Bluetooth protocol stack for Linux
|
|
|
|
Copyright (C) 2000-2001 Qualcomm Incorporated
|
2010-07-13 22:57:11 +08:00
|
|
|
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
|
2010-07-17 03:18:39 +08:00
|
|
|
Copyright (C) 2010 Google Inc.
|
2011-12-18 23:39:33 +08:00
|
|
|
Copyright (C) 2011 ProFUSION Embedded Systems
|
2012-04-28 07:50:55 +08:00
|
|
|
Copyright (c) 2012 Code Aurora Forum. All rights reserved.
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License version 2 as
|
|
|
|
published by the Free Software Foundation;
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
2007-02-09 22:24:33 +08:00
|
|
|
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
2005-04-17 06:20:36 +08:00
|
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
2007-02-09 22:24:33 +08:00
|
|
|
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
|
|
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
2005-04-17 06:20:36 +08:00
|
|
|
SOFTWARE IS DISCLAIMED.
|
|
|
|
*/
|
|
|
|
|
2011-02-04 06:50:35 +08:00
|
|
|
/* Bluetooth L2CAP core. */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
2010-03-21 12:27:45 +08:00
|
|
|
#include <linux/debugfs.h>
|
2009-08-21 09:26:02 +08:00
|
|
|
#include <linux/crc16.h>
|
Bluetooth: split sk_filter in l2cap_sock_recv_cb
During an audit for sk_filter(), we found that rx_busy_skb handling
in l2cap_sock_recv_cb() and l2cap_sock_recvmsg() looks not quite as
intended.
The assumption from commit e328140fdacb ("Bluetooth: Use event-driven
approach for handling ERTM receive buffer") is that errors returned
from sock_queue_rcv_skb() are due to receive buffer shortage. However,
nothing should prevent doing a setsockopt() with SO_ATTACH_FILTER on
the socket, that could drop some of the incoming skbs when handled in
sock_queue_rcv_skb().
In that case sock_queue_rcv_skb() will return with -EPERM, propagated
from sk_filter() and if in L2CAP_MODE_ERTM mode, wrong assumption was
that we failed due to receive buffer being full. From that point onwards,
due to the to-be-dropped skb being held in rx_busy_skb, we cannot make
any forward progress as rx_busy_skb is never cleared from l2cap_sock_recvmsg(),
due to the filter drop verdict over and over coming from sk_filter().
Meanwhile, in l2cap_sock_recv_cb() all new incoming skbs are being
dropped due to rx_busy_skb being occupied.
Instead, just use __sock_queue_rcv_skb() where an error really tells that
there's a receive buffer issue. Split the sk_filter() and enable it for
non-segmented modes at queuing time since at this point in time the skb has
already been through the ERTM state machine and it has been acked, so dropping
is not allowed. Instead, for ERTM and streaming mode, call sk_filter() in
l2cap_data_rcv() so the packet can be dropped before the state machine sees it.
Fixes: e328140fdacb ("Bluetooth: Use event-driven approach for handling ERTM receive buffer")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2016-07-28 02:40:14 +08:00
|
|
|
#include <linux/filter.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <net/bluetooth/bluetooth.h>
|
|
|
|
#include <net/bluetooth/hci_core.h>
|
|
|
|
#include <net/bluetooth/l2cap.h>
|
2013-10-11 05:54:14 +08:00
|
|
|
|
2013-10-11 05:54:16 +08:00
|
|
|
#include "smp.h"
|
2013-10-11 05:54:15 +08:00
|
|
|
#include "a2mp.h"
|
2013-10-11 05:54:14 +08:00
|
|
|
#include "amp.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-01-28 07:11:35 +08:00
|
|
|
#define LE_FLOWCTL_MAX_CREDITS 65535
|
|
|
|
|
2012-05-18 11:53:55 +08:00
|
|
|
bool disable_ertm;
|
2007-10-20 19:38:51 +08:00
|
|
|
|
2013-10-12 23:18:19 +08:00
|
|
|
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-01 14:54:45 +08:00
|
|
|
static LIST_HEAD(chan_list);
|
|
|
|
static DEFINE_RWLOCK(chan_list_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
|
2012-10-06 17:07:01 +08:00
|
|
|
u8 code, u8 ident, u16 dlen, void *data);
|
2011-04-29 04:55:53 +08:00
|
|
|
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
|
2012-10-06 17:07:01 +08:00
|
|
|
void *data);
|
2017-09-10 05:15:59 +08:00
|
|
|
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size);
|
2012-11-28 23:59:39 +08:00
|
|
|
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
2012-10-06 17:07:01 +08:00
|
|
|
struct sk_buff_head *skbs, u8 event);
|
2012-05-18 11:53:32 +08:00
|
|
|
|
2015-01-15 19:06:44 +08:00
|
|
|
static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
|
2013-10-13 23:50:41 +08:00
|
|
|
{
|
2015-01-15 19:06:44 +08:00
|
|
|
if (link_type == LE_LINK) {
|
|
|
|
if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
|
2013-10-13 23:50:41 +08:00
|
|
|
return BDADDR_LE_PUBLIC;
|
|
|
|
else
|
|
|
|
return BDADDR_LE_RANDOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BDADDR_BREDR;
|
|
|
|
}
|
|
|
|
|
2015-01-15 19:06:44 +08:00
|
|
|
static inline u8 bdaddr_src_type(struct hci_conn *hcon)
|
|
|
|
{
|
|
|
|
return bdaddr_type(hcon->type, hcon->src_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline u8 bdaddr_dst_type(struct hci_conn *hcon)
|
|
|
|
{
|
|
|
|
return bdaddr_type(hcon->type, hcon->dst_type);
|
|
|
|
}
|
|
|
|
|
2006-07-03 16:02:46 +08:00
|
|
|
/* ---- L2CAP channels ---- */
|
2011-05-18 01:34:52 +08:00
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
|
|
|
|
u16 cid)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2012-02-21 18:54:55 +08:00
|
|
|
struct l2cap_chan *c;
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(c, &conn->chan_l, list) {
|
|
|
|
if (c->dcid == cid)
|
|
|
|
return c;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
2012-02-21 18:54:55 +08:00
|
|
|
return NULL;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
|
|
|
|
u16 cid)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2012-02-21 18:54:55 +08:00
|
|
|
struct l2cap_chan *c;
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(c, &conn->chan_l, list) {
|
|
|
|
if (c->scid == cid)
|
|
|
|
return c;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
2012-02-21 18:54:55 +08:00
|
|
|
return NULL;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find channel with given SCID.
|
2012-05-03 00:42:00 +08:00
|
|
|
* Returns locked channel. */
|
2012-10-06 17:07:01 +08:00
|
|
|
static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
|
|
|
|
u16 cid)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *c;
|
2011-04-01 03:17:41 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2011-04-01 03:17:41 +08:00
|
|
|
c = __l2cap_get_chan_by_scid(conn, cid);
|
2012-05-03 00:42:00 +08:00
|
|
|
if (c)
|
|
|
|
l2cap_chan_lock(c);
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
2011-03-25 11:22:30 +08:00
|
|
|
return c;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:09 +08:00
|
|
|
/* Find channel with given DCID.
|
|
|
|
* Returns locked channel.
|
|
|
|
*/
|
|
|
|
static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
|
|
|
|
u16 cid)
|
|
|
|
{
|
|
|
|
struct l2cap_chan *c;
|
|
|
|
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
c = __l2cap_get_chan_by_dcid(conn, cid);
|
|
|
|
if (c)
|
|
|
|
l2cap_chan_lock(c);
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
|
|
|
|
u8 ident)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2012-02-21 18:54:55 +08:00
|
|
|
struct l2cap_chan *c;
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(c, &conn->chan_l, list) {
|
|
|
|
if (c->ident == ident)
|
|
|
|
return c;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
2012-02-21 18:54:55 +08:00
|
|
|
return NULL;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
|
|
|
|
u8 ident)
|
|
|
|
{
|
|
|
|
struct l2cap_chan *c;
|
|
|
|
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
c = __l2cap_get_chan_by_ident(conn, ident);
|
|
|
|
if (c)
|
|
|
|
l2cap_chan_lock(c);
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
|
2011-04-19 05:38:43 +08:00
|
|
|
{
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *c;
|
2011-04-19 05:38:43 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
list_for_each_entry(c, &chan_list, global_l) {
|
2013-10-13 23:12:47 +08:00
|
|
|
if (c->sport == psm && !bacmp(&c->src, src))
|
2011-11-16 16:32:22 +08:00
|
|
|
return c;
|
2011-04-19 05:38:43 +08:00
|
|
|
}
|
2011-11-16 16:32:22 +08:00
|
|
|
return NULL;
|
2011-04-19 05:38:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
|
|
|
|
{
|
2011-04-19 06:36:44 +08:00
|
|
|
int err;
|
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
write_lock(&chan_list_lock);
|
2011-04-19 05:38:43 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
if (psm && __l2cap_global_chan_by_addr(psm, src)) {
|
2011-04-19 06:36:44 +08:00
|
|
|
err = -EADDRINUSE;
|
|
|
|
goto done;
|
2011-04-19 05:38:43 +08:00
|
|
|
}
|
|
|
|
|
2011-04-19 06:36:44 +08:00
|
|
|
if (psm) {
|
|
|
|
chan->psm = psm;
|
|
|
|
chan->sport = psm;
|
|
|
|
err = 0;
|
|
|
|
} else {
|
2016-01-27 06:19:10 +08:00
|
|
|
u16 p, start, end, incr;
|
|
|
|
|
|
|
|
if (chan->src_type == BDADDR_BREDR) {
|
|
|
|
start = L2CAP_PSM_DYN_START;
|
|
|
|
end = L2CAP_PSM_AUTO_END;
|
|
|
|
incr = 2;
|
|
|
|
} else {
|
|
|
|
start = L2CAP_PSM_LE_DYN_START;
|
|
|
|
end = L2CAP_PSM_LE_DYN_END;
|
|
|
|
incr = 1;
|
|
|
|
}
|
2011-04-19 06:36:44 +08:00
|
|
|
|
|
|
|
err = -EINVAL;
|
2016-01-27 06:19:10 +08:00
|
|
|
for (p = start; p <= end; p += incr)
|
2011-04-28 05:26:32 +08:00
|
|
|
if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) {
|
2011-04-19 06:36:44 +08:00
|
|
|
chan->psm = cpu_to_le16(p);
|
|
|
|
chan->sport = cpu_to_le16(p);
|
|
|
|
err = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-04-19 05:38:43 +08:00
|
|
|
|
2011-04-19 06:36:44 +08:00
|
|
|
done:
|
2011-12-23 01:14:39 +08:00
|
|
|
write_unlock(&chan_list_lock);
|
2011-04-19 06:36:44 +08:00
|
|
|
return err;
|
2011-04-19 05:38:43 +08:00
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_add_psm);
|
2011-04-19 05:38:43 +08:00
|
|
|
|
|
|
|
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
|
|
|
|
{
|
2011-12-23 01:14:39 +08:00
|
|
|
write_lock(&chan_list_lock);
|
2011-04-19 05:38:43 +08:00
|
|
|
|
2014-08-08 03:56:50 +08:00
|
|
|
/* Override the defaults (which are for conn-oriented) */
|
|
|
|
chan->omtu = L2CAP_DEFAULT_MTU;
|
|
|
|
chan->chan_type = L2CAP_CHAN_FIXED;
|
|
|
|
|
2011-04-19 05:38:43 +08:00
|
|
|
chan->scid = scid;
|
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
write_unlock(&chan_list_lock);
|
2011-04-19 05:38:43 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-04-01 03:17:41 +08:00
|
|
|
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2013-10-08 16:31:00 +08:00
|
|
|
u16 cid, dyn_end;
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2013-10-08 16:31:00 +08:00
|
|
|
if (conn->hcon->type == LE_LINK)
|
|
|
|
dyn_end = L2CAP_CID_LE_DYN_END;
|
|
|
|
else
|
|
|
|
dyn_end = L2CAP_CID_DYN_END;
|
|
|
|
|
2015-11-02 20:39:16 +08:00
|
|
|
for (cid = L2CAP_CID_DYN_START; cid <= dyn_end; cid++) {
|
2011-04-01 03:17:41 +08:00
|
|
|
if (!__l2cap_get_chan_by_scid(conn, cid))
|
2006-07-03 16:02:46 +08:00
|
|
|
return cid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-22 00:21:40 +08:00
|
|
|
static void l2cap_state_change(struct l2cap_chan *chan, int state)
|
2011-06-03 11:19:47 +08:00
|
|
|
{
|
2012-02-17 17:40:57 +08:00
|
|
|
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
|
2012-10-06 17:07:01 +08:00
|
|
|
state_to_string(state));
|
2011-11-24 06:11:46 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
chan->state = state;
|
2013-10-16 06:24:45 +08:00
|
|
|
chan->ops->state_change(chan, state, 0);
|
2011-06-03 11:19:47 +08:00
|
|
|
}
|
|
|
|
|
2013-10-16 06:24:46 +08:00
|
|
|
static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
|
|
|
|
int state, int err)
|
2012-02-21 18:54:58 +08:00
|
|
|
{
|
2013-10-16 06:24:46 +08:00
|
|
|
chan->state = state;
|
2013-10-16 06:24:45 +08:00
|
|
|
chan->ops->state_change(chan, chan->state, err);
|
2012-02-21 18:54:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
|
|
|
|
{
|
2013-10-16 06:24:46 +08:00
|
|
|
chan->ops->state_change(chan, chan->state, err);
|
2012-02-21 18:54:58 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:49 +08:00
|
|
|
static void __set_retrans_timer(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
if (!delayed_work_pending(&chan->monitor_timer) &&
|
|
|
|
chan->retrans_timeout) {
|
|
|
|
l2cap_set_timer(chan, &chan->retrans_timer,
|
|
|
|
msecs_to_jiffies(chan->retrans_timeout));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __set_monitor_timer(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
if (chan->monitor_timeout) {
|
|
|
|
l2cap_set_timer(chan, &chan->monitor_timer,
|
|
|
|
msecs_to_jiffies(chan->monitor_timeout));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:32 +08:00
|
|
|
static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
|
|
|
|
u16 seq)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
skb_queue_walk(head, skb) {
|
2015-03-31 04:21:01 +08:00
|
|
|
if (bt_cb(skb)->l2cap.txseq == seq)
|
2012-05-18 11:53:32 +08:00
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-12 01:48:42 +08:00
|
|
|
/* ---- L2CAP sequence number lists ---- */
|
|
|
|
|
|
|
|
/* For ERTM, ordered lists of sequence numbers must be tracked for
|
|
|
|
* SREJ requests that are received and for frames that are to be
|
|
|
|
* retransmitted. These seq_list functions implement a singly-linked
|
|
|
|
* list in an array, where membership in the list can also be checked
|
|
|
|
* in constant time. Items can also be added to the tail of the list
|
|
|
|
* and removed from the head in constant time, without further memory
|
|
|
|
* allocs or frees.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int l2cap_seq_list_init(struct l2cap_seq_list *seq_list, u16 size)
|
|
|
|
{
|
|
|
|
size_t alloc_size, i;
|
|
|
|
|
|
|
|
/* Allocated size is a power of 2 to map sequence numbers
|
|
|
|
* (which may be up to 14 bits) in to a smaller array that is
|
|
|
|
* sized for the negotiated ERTM transmit windows.
|
|
|
|
*/
|
|
|
|
alloc_size = roundup_pow_of_two(size);
|
|
|
|
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:55:00 +08:00
|
|
|
seq_list->list = kmalloc_array(alloc_size, sizeof(u16), GFP_KERNEL);
|
2012-04-12 01:48:42 +08:00
|
|
|
if (!seq_list->list)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
seq_list->mask = alloc_size - 1;
|
|
|
|
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
for (i = 0; i < alloc_size; i++)
|
|
|
|
seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list)
|
|
|
|
{
|
|
|
|
kfree(seq_list->list);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list,
|
|
|
|
u16 seq)
|
|
|
|
{
|
|
|
|
/* Constant-time check for list membership */
|
|
|
|
return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
}
|
|
|
|
|
2014-01-19 03:32:59 +08:00
|
|
|
static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
|
2012-04-12 01:48:42 +08:00
|
|
|
{
|
2014-01-19 03:32:59 +08:00
|
|
|
u16 seq = seq_list->head;
|
2012-04-12 01:48:42 +08:00
|
|
|
u16 mask = seq_list->mask;
|
|
|
|
|
2014-01-19 03:32:59 +08:00
|
|
|
seq_list->head = seq_list->list[seq & mask];
|
|
|
|
seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
|
|
|
|
if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
|
|
|
|
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
|
2012-04-12 01:48:42 +08:00
|
|
|
}
|
|
|
|
|
2014-01-19 03:32:59 +08:00
|
|
|
return seq;
|
2012-04-12 01:48:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
|
|
|
|
{
|
2012-05-10 05:28:00 +08:00
|
|
|
u16 i;
|
2012-04-12 01:48:42 +08:00
|
|
|
|
2012-05-10 05:28:00 +08:00
|
|
|
if (seq_list->head == L2CAP_SEQ_LIST_CLEAR)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i <= seq_list->mask; i++)
|
|
|
|
seq_list->list[i] = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
|
|
|
|
seq_list->head = L2CAP_SEQ_LIST_CLEAR;
|
|
|
|
seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
|
2012-04-12 01:48:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq)
|
|
|
|
{
|
|
|
|
u16 mask = seq_list->mask;
|
|
|
|
|
|
|
|
/* All appends happen in constant time */
|
|
|
|
|
2012-05-10 05:28:00 +08:00
|
|
|
if (seq_list->list[seq & mask] != L2CAP_SEQ_LIST_CLEAR)
|
|
|
|
return;
|
2012-04-12 01:48:42 +08:00
|
|
|
|
2012-05-10 05:28:00 +08:00
|
|
|
if (seq_list->tail == L2CAP_SEQ_LIST_CLEAR)
|
|
|
|
seq_list->head = seq;
|
|
|
|
else
|
|
|
|
seq_list->list[seq_list->tail & mask] = seq;
|
|
|
|
|
|
|
|
seq_list->tail = seq;
|
|
|
|
seq_list->list[seq & mask] = L2CAP_SEQ_LIST_TAIL;
|
2012-04-12 01:48:42 +08:00
|
|
|
}
|
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
static void l2cap_chan_timeout(struct work_struct *work)
|
2011-05-03 05:25:01 +08:00
|
|
|
{
|
2011-06-24 06:29:58 +08:00
|
|
|
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
|
2012-10-06 17:07:01 +08:00
|
|
|
chan_timer.work);
|
2012-02-21 18:54:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2011-05-03 05:25:01 +08:00
|
|
|
int reason;
|
|
|
|
|
2012-02-17 17:40:56 +08:00
|
|
|
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
2011-05-03 05:25:01 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2011-05-03 05:25:01 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
|
2011-05-03 05:25:01 +08:00
|
|
|
reason = ECONNREFUSED;
|
2011-06-03 11:19:47 +08:00
|
|
|
else if (chan->state == BT_CONNECT &&
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->sec_level != BT_SECURITY_SDP)
|
2011-05-03 05:25:01 +08:00
|
|
|
reason = ECONNREFUSED;
|
|
|
|
else
|
|
|
|
reason = ETIMEDOUT;
|
|
|
|
|
2011-05-05 06:42:50 +08:00
|
|
|
l2cap_chan_close(chan, reason);
|
2011-05-03 05:25:01 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2011-05-03 05:25:01 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
chan->ops->close(chan);
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
2011-12-22 06:02:36 +08:00
|
|
|
l2cap_chan_put(chan);
|
2011-05-03 05:25:01 +08:00
|
|
|
}
|
|
|
|
|
2012-03-26 00:59:16 +08:00
|
|
|
struct l2cap_chan *l2cap_chan_create(void)
|
2011-03-25 11:22:30 +08:00
|
|
|
{
|
|
|
|
struct l2cap_chan *chan;
|
|
|
|
|
|
|
|
chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
|
|
|
|
if (!chan)
|
|
|
|
return NULL;
|
|
|
|
|
2012-02-21 18:54:56 +08:00
|
|
|
mutex_init(&chan->lock);
|
|
|
|
|
2014-11-13 15:46:04 +08:00
|
|
|
/* Set default lock nesting level */
|
|
|
|
atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
|
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
write_lock(&chan_list_lock);
|
2011-04-28 05:26:32 +08:00
|
|
|
list_add(&chan->global_l, &chan_list);
|
2011-12-23 01:14:39 +08:00
|
|
|
write_unlock(&chan_list_lock);
|
2011-04-28 05:26:32 +08:00
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout);
|
2011-05-03 05:25:01 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
chan->state = BT_OPEN;
|
|
|
|
|
2012-07-28 02:21:21 +08:00
|
|
|
kref_init(&chan->kref);
|
2011-05-18 01:34:52 +08:00
|
|
|
|
2012-05-18 12:14:09 +08:00
|
|
|
/* This flag is cleared in l2cap_chan_ready() */
|
|
|
|
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
|
|
|
|
|
2012-03-26 00:59:16 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2011-11-03 23:05:44 +08:00
|
|
|
|
2011-03-25 11:22:30 +08:00
|
|
|
return chan;
|
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_create);
|
2011-03-25 11:22:30 +08:00
|
|
|
|
2012-07-28 02:21:21 +08:00
|
|
|
static void l2cap_chan_destroy(struct kref *kref)
|
2011-04-26 02:10:41 +08:00
|
|
|
{
|
2012-07-28 02:21:21 +08:00
|
|
|
struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
|
|
|
|
|
2012-07-13 20:47:55 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
write_lock(&chan_list_lock);
|
2011-04-28 05:26:32 +08:00
|
|
|
list_del(&chan->global_l);
|
2011-12-23 01:14:39 +08:00
|
|
|
write_unlock(&chan_list_lock);
|
2011-04-28 05:26:32 +08:00
|
|
|
|
2012-07-13 20:47:55 +08:00
|
|
|
kfree(chan);
|
2011-04-26 02:10:41 +08:00
|
|
|
}
|
|
|
|
|
2012-07-13 20:47:54 +08:00
|
|
|
void l2cap_chan_hold(struct l2cap_chan *c)
|
|
|
|
{
|
2016-11-15 00:29:48 +08:00
|
|
|
BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
|
2012-07-13 20:47:54 +08:00
|
|
|
|
2012-07-28 02:21:21 +08:00
|
|
|
kref_get(&c->kref);
|
2012-07-13 20:47:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void l2cap_chan_put(struct l2cap_chan *c)
|
|
|
|
{
|
2016-11-15 00:29:48 +08:00
|
|
|
BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
|
2012-07-13 20:47:54 +08:00
|
|
|
|
2012-07-28 02:21:21 +08:00
|
|
|
kref_put(&c->kref, l2cap_chan_destroy);
|
2012-07-13 20:47:54 +08:00
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_put);
|
2012-07-13 20:47:54 +08:00
|
|
|
|
2012-03-28 21:31:25 +08:00
|
|
|
void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
chan->fcs = L2CAP_FCS_CRC16;
|
|
|
|
chan->max_tx = L2CAP_DEFAULT_MAX_TX;
|
|
|
|
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
|
|
|
|
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
|
2014-05-28 19:43:04 +08:00
|
|
|
chan->remote_max_tx = chan->max_tx;
|
|
|
|
chan->remote_tx_win = chan->tx_win;
|
2012-07-10 20:47:07 +08:00
|
|
|
chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
|
2012-03-28 21:31:25 +08:00
|
|
|
chan->sec_level = BT_SECURITY_LOW;
|
2014-05-28 19:43:04 +08:00
|
|
|
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
|
|
|
chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
|
|
|
|
chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
|
|
|
|
chan->conf_state = 0;
|
2012-03-28 21:31:25 +08:00
|
|
|
|
|
|
|
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
|
2012-03-28 21:31:25 +08:00
|
|
|
|
2013-12-05 20:55:33 +08:00
|
|
|
static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
|
2013-05-17 17:49:23 +08:00
|
|
|
{
|
2013-12-05 20:55:33 +08:00
|
|
|
chan->sdu = NULL;
|
|
|
|
chan->sdu_last_frag = NULL;
|
|
|
|
chan->sdu_len = 0;
|
2013-05-17 18:09:05 +08:00
|
|
|
chan->tx_credits = 0;
|
2018-09-04 18:39:20 +08:00
|
|
|
/* Derive MPS from connection MTU to stop HCI fragmentation */
|
|
|
|
chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE);
|
2018-09-04 18:39:21 +08:00
|
|
|
/* Give enough credits for a full packet */
|
|
|
|
chan->rx_credits = (chan->imtu / chan->mps) + 1;
|
2013-12-05 20:55:33 +08:00
|
|
|
|
|
|
|
skb_queue_head_init(&chan->tx_q);
|
2013-05-17 17:49:23 +08:00
|
|
|
}
|
|
|
|
|
2012-09-27 22:26:16 +08:00
|
|
|
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2009-04-20 12:31:08 +08:00
|
|
|
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
|
2012-03-09 20:16:17 +08:00
|
|
|
__le16_to_cpu(chan->psm), chan->dcid);
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2011-11-07 20:20:25 +08:00
|
|
|
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
|
2009-02-12 21:02:50 +08:00
|
|
|
|
2011-04-14 07:23:55 +08:00
|
|
|
chan->conn = conn;
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2012-02-06 21:04:00 +08:00
|
|
|
switch (chan->chan_type) {
|
|
|
|
case L2CAP_CHAN_CONN_ORIENTED:
|
2014-01-24 16:35:41 +08:00
|
|
|
/* Alloc CID for connection-oriented socket */
|
|
|
|
chan->scid = l2cap_alloc_cid(conn);
|
|
|
|
if (conn->hcon->type == ACL_LINK)
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->omtu = L2CAP_DEFAULT_MTU;
|
2012-02-06 21:04:00 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CHAN_CONN_LESS:
|
2006-07-03 16:02:46 +08:00
|
|
|
/* Connectionless socket */
|
2011-04-14 06:50:45 +08:00
|
|
|
chan->scid = L2CAP_CID_CONN_LESS;
|
|
|
|
chan->dcid = L2CAP_CID_CONN_LESS;
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->omtu = L2CAP_DEFAULT_MTU;
|
2012-02-06 21:04:00 +08:00
|
|
|
break;
|
|
|
|
|
2014-01-24 16:35:40 +08:00
|
|
|
case L2CAP_CHAN_FIXED:
|
|
|
|
/* Caller will set CID and CID specific MTU values */
|
2012-05-29 18:59:16 +08:00
|
|
|
break;
|
|
|
|
|
2012-02-06 21:04:00 +08:00
|
|
|
default:
|
2006-07-03 16:02:46 +08:00
|
|
|
/* Raw socket can send/recv signalling messages only */
|
2011-04-14 06:50:45 +08:00
|
|
|
chan->scid = L2CAP_CID_SIGNALING;
|
|
|
|
chan->dcid = L2CAP_CID_SIGNALING;
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->omtu = L2CAP_DEFAULT_MTU;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2011-10-13 21:18:54 +08:00
|
|
|
chan->local_id = L2CAP_BESTEFFORT_ID;
|
|
|
|
chan->local_stype = L2CAP_SERV_BESTEFFORT;
|
|
|
|
chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE;
|
|
|
|
chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME;
|
|
|
|
chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT;
|
2012-10-08 16:14:41 +08:00
|
|
|
chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO;
|
2011-10-13 21:18:54 +08:00
|
|
|
|
2011-12-22 06:02:36 +08:00
|
|
|
l2cap_chan_hold(chan);
|
2011-04-01 03:17:41 +08:00
|
|
|
|
2014-08-16 02:17:06 +08:00
|
|
|
/* Only keep a reference for fixed channels if they requested it */
|
|
|
|
if (chan->chan_type != L2CAP_CHAN_FIXED ||
|
|
|
|
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
|
|
|
|
hci_conn_hold(conn->hcon);
|
2013-04-30 00:35:43 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_add(&chan->list, &conn->chan_l);
|
2012-02-22 23:11:55 +08:00
|
|
|
}
|
|
|
|
|
2012-05-29 18:59:01 +08:00
|
|
|
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
2012-02-22 23:11:55 +08:00
|
|
|
{
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
__l2cap_chan_add(conn, chan);
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2012-05-29 18:59:01 +08:00
|
|
|
void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
2006-07-03 16:02:46 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2011-05-18 01:59:01 +08:00
|
|
|
__clear_chan_timer(chan);
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2014-11-13 20:37:50 +08:00
|
|
|
BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
|
|
|
|
state_to_string(chan->state));
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2014-08-08 14:28:03 +08:00
|
|
|
chan->ops->teardown(chan, err);
|
|
|
|
|
2007-02-09 22:24:33 +08:00
|
|
|
if (conn) {
|
2012-10-15 16:58:44 +08:00
|
|
|
struct amp_mgr *mgr = conn->hcon->amp_mgr;
|
2011-04-01 03:17:41 +08:00
|
|
|
/* Delete from channel list */
|
2012-02-21 18:54:55 +08:00
|
|
|
list_del(&chan->list);
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2011-12-22 06:02:36 +08:00
|
|
|
l2cap_chan_put(chan);
|
2011-04-01 03:17:41 +08:00
|
|
|
|
2011-04-14 07:23:55 +08:00
|
|
|
chan->conn = NULL;
|
2012-05-31 16:01:37 +08:00
|
|
|
|
2014-08-16 02:17:06 +08:00
|
|
|
/* Reference was only held for non-fixed channels or
|
|
|
|
* fixed channels that explicitly requested it using the
|
|
|
|
* FLAG_HOLD_HCI_CONN flag.
|
|
|
|
*/
|
|
|
|
if (chan->chan_type != L2CAP_CHAN_FIXED ||
|
|
|
|
test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
|
2013-04-07 02:28:37 +08:00
|
|
|
hci_conn_drop(conn->hcon);
|
2012-10-15 16:58:44 +08:00
|
|
|
|
|
|
|
if (mgr && mgr->bredr_chan == chan)
|
|
|
|
mgr->bredr_chan = NULL;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
|
|
|
|
2012-10-31 21:46:34 +08:00
|
|
|
if (chan->hs_hchan) {
|
|
|
|
struct hci_chan *hs_hchan = chan->hs_hchan;
|
|
|
|
|
|
|
|
BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan);
|
|
|
|
amp_disconnect_logical_link(hs_hchan);
|
|
|
|
}
|
|
|
|
|
2012-05-18 12:14:09 +08:00
|
|
|
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
|
2011-04-26 02:10:41 +08:00
|
|
|
return;
|
2011-04-02 02:13:36 +08:00
|
|
|
|
2012-05-19 07:22:38 +08:00
|
|
|
switch(chan->mode) {
|
|
|
|
case L2CAP_MODE_BASIC:
|
|
|
|
break;
|
2010-05-14 07:50:12 +08:00
|
|
|
|
2013-05-17 17:49:23 +08:00
|
|
|
case L2CAP_MODE_LE_FLOWCTL:
|
2013-05-31 22:54:51 +08:00
|
|
|
skb_queue_purge(&chan->tx_q);
|
2013-05-17 17:49:23 +08:00
|
|
|
break;
|
|
|
|
|
2012-05-19 07:22:38 +08:00
|
|
|
case L2CAP_MODE_ERTM:
|
2011-05-18 02:13:19 +08:00
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
__clear_monitor_timer(chan);
|
|
|
|
__clear_ack_timer(chan);
|
2010-05-14 07:50:12 +08:00
|
|
|
|
2011-03-26 07:36:10 +08:00
|
|
|
skb_queue_purge(&chan->srej_q);
|
2010-05-14 07:50:12 +08:00
|
|
|
|
2012-04-12 01:48:42 +08:00
|
|
|
l2cap_seq_list_free(&chan->srej_list);
|
|
|
|
l2cap_seq_list_free(&chan->retrans_list);
|
2012-05-19 07:22:38 +08:00
|
|
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
skb_queue_purge(&chan->tx_q);
|
|
|
|
break;
|
2010-05-14 07:50:12 +08:00
|
|
|
}
|
2012-05-19 07:22:38 +08:00
|
|
|
|
|
|
|
return;
|
2006-07-03 16:02:46 +08:00
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_del);
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2014-09-06 03:19:50 +08:00
|
|
|
static void l2cap_conn_update_id_addr(struct work_struct *work)
|
2014-02-19 03:41:33 +08:00
|
|
|
{
|
2014-09-06 03:19:50 +08:00
|
|
|
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
|
|
|
|
id_addr_update_work);
|
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2014-02-19 03:41:33 +08:00
|
|
|
struct l2cap_chan *chan;
|
|
|
|
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
|
|
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
bacpy(&chan->dst, &hcon->dst);
|
2015-01-15 19:06:44 +08:00
|
|
|
chan->dst_type = bdaddr_dst_type(hcon);
|
2014-02-19 03:41:33 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
}
|
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
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))
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_AUTHORIZATION;
|
2013-05-14 18:27:21 +08:00
|
|
|
else
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_BAD_PSM;
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
l2cap_state_change(chan, BT_DISCONN);
|
|
|
|
|
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
|
|
|
rsp.mtu = cpu_to_le16(chan->imtu);
|
2013-10-07 21:35:26 +08:00
|
|
|
rsp.mps = cpu_to_le16(chan->mps);
|
2013-05-17 18:09:05 +08:00
|
|
|
rsp.credits = cpu_to_le16(chan->rx_credits);
|
2013-05-14 18:27:21 +08:00
|
|
|
rsp.result = cpu_to_le16(result);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
|
|
|
|
&rsp);
|
|
|
|
}
|
|
|
|
|
2013-05-15 03:24:44 +08:00
|
|
|
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);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2013-05-15 03:24:44 +08:00
|
|
|
|
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
|
|
|
}
|
|
|
|
|
2011-05-05 06:42:50 +08:00
|
|
|
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
|
2011-04-29 04:55:53 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = chan->conn;
|
|
|
|
|
2013-10-13 23:12:47 +08:00
|
|
|
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
2011-04-29 04:55:53 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
switch (chan->state) {
|
2011-04-29 04:55:53 +08:00
|
|
|
case BT_LISTEN:
|
2012-10-06 18:51:54 +08:00
|
|
|
chan->ops->teardown(chan, 0);
|
2011-04-29 04:55:53 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BT_CONNECTED:
|
|
|
|
case BT_CONFIG:
|
2014-01-29 07:28:04 +08:00
|
|
|
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
|
2013-10-16 06:24:47 +08:00
|
|
|
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, reason);
|
2011-04-29 04:55:53 +08:00
|
|
|
} else
|
|
|
|
l2cap_chan_del(chan, reason);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BT_CONNECT2:
|
2013-05-15 03:24:44 +08:00
|
|
|
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
|
|
|
|
if (conn->hcon->type == ACL_LINK)
|
|
|
|
l2cap_chan_connect_reject(chan);
|
2013-05-14 18:27:21 +08:00
|
|
|
else if (conn->hcon->type == LE_LINK)
|
|
|
|
l2cap_chan_le_connect_reject(chan);
|
2011-04-29 04:55:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_del(chan, reason);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BT_CONNECT:
|
|
|
|
case BT_DISCONN:
|
|
|
|
l2cap_chan_del(chan, reason);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2012-10-06 18:51:54 +08:00
|
|
|
chan->ops->teardown(chan, 0);
|
2011-04-29 04:55:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL(l2cap_chan_close);
|
2011-04-29 04:55:53 +08:00
|
|
|
|
2011-04-13 05:31:57 +08:00
|
|
|
static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
|
2008-07-15 02:13:44 +08:00
|
|
|
{
|
2013-10-12 22:19:31 +08:00
|
|
|
switch (chan->chan_type) {
|
|
|
|
case L2CAP_CHAN_RAW:
|
2011-04-13 05:31:57 +08:00
|
|
|
switch (chan->sec_level) {
|
2011-01-19 14:36:50 +08:00
|
|
|
case BT_SECURITY_HIGH:
|
2014-01-16 14:37:40 +08:00
|
|
|
case BT_SECURITY_FIPS:
|
2011-01-19 14:36:50 +08:00
|
|
|
return HCI_AT_DEDICATED_BONDING_MITM;
|
|
|
|
case BT_SECURITY_MEDIUM:
|
|
|
|
return HCI_AT_DEDICATED_BONDING;
|
|
|
|
default:
|
|
|
|
return HCI_AT_NO_BONDING;
|
|
|
|
}
|
2013-10-12 22:19:31 +08:00
|
|
|
break;
|
2013-10-12 22:19:32 +08:00
|
|
|
case L2CAP_CHAN_CONN_LESS:
|
2014-03-13 01:52:35 +08:00
|
|
|
if (chan->psm == cpu_to_le16(L2CAP_PSM_3DSP)) {
|
2013-10-12 22:19:32 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_LOW)
|
|
|
|
chan->sec_level = BT_SECURITY_SDP;
|
|
|
|
}
|
2014-01-16 14:37:40 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_HIGH ||
|
|
|
|
chan->sec_level == BT_SECURITY_FIPS)
|
2013-10-12 22:19:32 +08:00
|
|
|
return HCI_AT_NO_BONDING_MITM;
|
|
|
|
else
|
|
|
|
return HCI_AT_NO_BONDING;
|
|
|
|
break;
|
2013-10-12 22:19:31 +08:00
|
|
|
case L2CAP_CHAN_CONN_ORIENTED:
|
2014-03-13 01:52:35 +08:00
|
|
|
if (chan->psm == cpu_to_le16(L2CAP_PSM_SDP)) {
|
2013-10-12 22:19:31 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_LOW)
|
|
|
|
chan->sec_level = BT_SECURITY_SDP;
|
2009-02-09 09:48:38 +08:00
|
|
|
|
2014-01-16 14:37:40 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_HIGH ||
|
|
|
|
chan->sec_level == BT_SECURITY_FIPS)
|
2013-10-12 22:19:31 +08:00
|
|
|
return HCI_AT_NO_BONDING_MITM;
|
|
|
|
else
|
|
|
|
return HCI_AT_NO_BONDING;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
default:
|
2011-04-13 05:31:57 +08:00
|
|
|
switch (chan->sec_level) {
|
2009-02-12 23:19:45 +08:00
|
|
|
case BT_SECURITY_HIGH:
|
2014-01-16 14:37:40 +08:00
|
|
|
case BT_SECURITY_FIPS:
|
2011-01-19 14:36:50 +08:00
|
|
|
return HCI_AT_GENERAL_BONDING_MITM;
|
2009-02-12 23:19:45 +08:00
|
|
|
case BT_SECURITY_MEDIUM:
|
2011-01-19 14:36:50 +08:00
|
|
|
return HCI_AT_GENERAL_BONDING;
|
2009-02-12 23:19:45 +08:00
|
|
|
default:
|
2011-01-19 14:36:50 +08:00
|
|
|
return HCI_AT_NO_BONDING;
|
2009-02-12 23:19:45 +08:00
|
|
|
}
|
2013-10-12 22:19:31 +08:00
|
|
|
break;
|
2009-02-09 09:48:38 +08:00
|
|
|
}
|
2011-01-19 14:36:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Service level security */
|
2014-07-17 20:35:38 +08:00
|
|
|
int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
|
2011-01-19 14:36:50 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2011-01-19 14:36:50 +08:00
|
|
|
__u8 auth_type;
|
|
|
|
|
2013-05-14 18:25:37 +08:00
|
|
|
if (conn->hcon->type == LE_LINK)
|
|
|
|
return smp_conn_security(conn->hcon, chan->sec_level);
|
|
|
|
|
2011-04-13 05:31:57 +08:00
|
|
|
auth_type = l2cap_get_auth_type(chan);
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2014-07-17 20:35:38 +08:00
|
|
|
return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
|
|
|
|
initiator);
|
2008-07-15 02:13:44 +08:00
|
|
|
}
|
|
|
|
|
2011-06-01 14:54:45 +08:00
|
|
|
static u8 l2cap_get_ident(struct l2cap_conn *conn)
|
2007-10-20 19:37:56 +08:00
|
|
|
{
|
|
|
|
u8 id;
|
|
|
|
|
|
|
|
/* Get next available identificator.
|
|
|
|
* 1 - 128 are used by kernel.
|
|
|
|
* 129 - 199 are reserved.
|
|
|
|
* 200 - 254 are used by utilities like l2ping, etc.
|
|
|
|
*/
|
|
|
|
|
2014-07-14 02:50:15 +08:00
|
|
|
mutex_lock(&conn->ident_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
|
|
|
if (++conn->tx_ident > 128)
|
|
|
|
conn->tx_ident = 1;
|
|
|
|
|
|
|
|
id = conn->tx_ident;
|
|
|
|
|
2014-07-14 02:50:15 +08:00
|
|
|
mutex_unlock(&conn->ident_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
|
|
|
|
void *data)
|
2007-10-20 19:37:56 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
|
2011-01-03 17:14:36 +08:00
|
|
|
u8 flags;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
|
|
|
BT_DBG("code 0x%2.2x", code);
|
|
|
|
|
|
|
|
if (!skb)
|
2010-05-02 03:15:43 +08:00
|
|
|
return;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2014-11-19 22:41:17 +08:00
|
|
|
/* Use NO_FLUSH if supported or we have an LE link (which does
|
|
|
|
* not support auto-flushing packets) */
|
|
|
|
if (lmp_no_flush_capable(conn->hcon->hdev) ||
|
|
|
|
conn->hcon->type == LE_LINK)
|
2011-01-03 17:14:36 +08:00
|
|
|
flags = ACL_START_NO_FLUSH;
|
|
|
|
else
|
|
|
|
flags = ACL_START;
|
|
|
|
|
2011-05-24 09:06:04 +08:00
|
|
|
bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
2011-11-01 16:58:57 +08:00
|
|
|
skb->priority = HCI_PRIO_MAX;
|
2011-05-24 09:06:04 +08:00
|
|
|
|
2011-11-02 21:52:01 +08:00
|
|
|
hci_send_acl(conn->hchan, skb, flags);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:10 +08:00
|
|
|
static bool __chan_is_moving(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
return chan->move_state != L2CAP_MOVE_STABLE &&
|
|
|
|
chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
|
|
|
|
}
|
|
|
|
|
2011-11-02 21:52:01 +08:00
|
|
|
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct hci_conn *hcon = chan->conn->hcon;
|
|
|
|
u16 flags;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
|
2012-10-06 17:07:01 +08:00
|
|
|
skb->priority);
|
2011-11-02 21:52:01 +08:00
|
|
|
|
2012-10-24 06:24:18 +08:00
|
|
|
if (chan->hs_hcon && !__chan_is_moving(chan)) {
|
|
|
|
if (chan->hs_hchan)
|
|
|
|
hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
|
|
|
|
else
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-19 22:41:17 +08:00
|
|
|
/* Use NO_FLUSH for LE links (where this is the only option) or
|
|
|
|
* if the BR/EDR link supports it and flushing has not been
|
|
|
|
* explicitly requested (through FLAG_FLUSHABLE).
|
|
|
|
*/
|
|
|
|
if (hcon->type == LE_LINK ||
|
|
|
|
(!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
|
|
|
|
lmp_no_flush_capable(hcon->hdev)))
|
2011-11-02 21:52:01 +08:00
|
|
|
flags = ACL_START_NO_FLUSH;
|
|
|
|
else
|
|
|
|
flags = ACL_START;
|
2011-05-24 09:06:04 +08:00
|
|
|
|
2011-11-02 21:52:01 +08:00
|
|
|
bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
|
|
|
hci_send_acl(chan->conn->hchan, skb, flags);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2012-04-12 01:48:43 +08:00
|
|
|
static void __unpack_enhanced_control(u16 enh, struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
control->reqseq = (enh & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
|
|
|
|
control->final = (enh & L2CAP_CTRL_FINAL) >> L2CAP_CTRL_FINAL_SHIFT;
|
|
|
|
|
|
|
|
if (enh & L2CAP_CTRL_FRAME_TYPE) {
|
|
|
|
/* S-Frame */
|
|
|
|
control->sframe = 1;
|
|
|
|
control->poll = (enh & L2CAP_CTRL_POLL) >> L2CAP_CTRL_POLL_SHIFT;
|
|
|
|
control->super = (enh & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
|
|
|
|
|
|
|
|
control->sar = 0;
|
|
|
|
control->txseq = 0;
|
|
|
|
} else {
|
|
|
|
/* I-Frame */
|
|
|
|
control->sframe = 0;
|
|
|
|
control->sar = (enh & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
|
|
|
|
control->txseq = (enh & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
|
|
|
|
|
|
|
|
control->poll = 0;
|
|
|
|
control->super = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __unpack_extended_control(u32 ext, struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
control->reqseq = (ext & L2CAP_EXT_CTRL_REQSEQ) >> L2CAP_EXT_CTRL_REQSEQ_SHIFT;
|
|
|
|
control->final = (ext & L2CAP_EXT_CTRL_FINAL) >> L2CAP_EXT_CTRL_FINAL_SHIFT;
|
|
|
|
|
|
|
|
if (ext & L2CAP_EXT_CTRL_FRAME_TYPE) {
|
|
|
|
/* S-Frame */
|
|
|
|
control->sframe = 1;
|
|
|
|
control->poll = (ext & L2CAP_EXT_CTRL_POLL) >> L2CAP_EXT_CTRL_POLL_SHIFT;
|
|
|
|
control->super = (ext & L2CAP_EXT_CTRL_SUPERVISE) >> L2CAP_EXT_CTRL_SUPER_SHIFT;
|
|
|
|
|
|
|
|
control->sar = 0;
|
|
|
|
control->txseq = 0;
|
|
|
|
} else {
|
|
|
|
/* I-Frame */
|
|
|
|
control->sframe = 0;
|
|
|
|
control->sar = (ext & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
|
|
|
|
control->txseq = (ext & L2CAP_EXT_CTRL_TXSEQ) >> L2CAP_EXT_CTRL_TXSEQ_SHIFT;
|
|
|
|
|
|
|
|
control->poll = 0;
|
|
|
|
control->super = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void __unpack_control(struct l2cap_chan *chan,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
|
|
|
|
__unpack_extended_control(get_unaligned_le32(skb->data),
|
2015-03-31 04:21:01 +08:00
|
|
|
&bt_cb(skb)->l2cap);
|
2012-05-18 11:53:36 +08:00
|
|
|
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
|
2012-04-12 01:48:43 +08:00
|
|
|
} else {
|
|
|
|
__unpack_enhanced_control(get_unaligned_le16(skb->data),
|
2015-03-31 04:21:01 +08:00
|
|
|
&bt_cb(skb)->l2cap);
|
2012-05-18 11:53:36 +08:00
|
|
|
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
|
2012-04-12 01:48:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-26 07:36:15 +08:00
|
|
|
static u32 __pack_extended_control(struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
u32 packed;
|
|
|
|
|
|
|
|
packed = control->reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT;
|
|
|
|
packed |= control->final << L2CAP_EXT_CTRL_FINAL_SHIFT;
|
|
|
|
|
|
|
|
if (control->sframe) {
|
|
|
|
packed |= control->poll << L2CAP_EXT_CTRL_POLL_SHIFT;
|
|
|
|
packed |= control->super << L2CAP_EXT_CTRL_SUPER_SHIFT;
|
|
|
|
packed |= L2CAP_EXT_CTRL_FRAME_TYPE;
|
|
|
|
} else {
|
|
|
|
packed |= control->sar << L2CAP_EXT_CTRL_SAR_SHIFT;
|
|
|
|
packed |= control->txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return packed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16 __pack_enhanced_control(struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
u16 packed;
|
|
|
|
|
|
|
|
packed = control->reqseq << L2CAP_CTRL_REQSEQ_SHIFT;
|
|
|
|
packed |= control->final << L2CAP_CTRL_FINAL_SHIFT;
|
|
|
|
|
|
|
|
if (control->sframe) {
|
|
|
|
packed |= control->poll << L2CAP_CTRL_POLL_SHIFT;
|
|
|
|
packed |= control->super << L2CAP_CTRL_SUPER_SHIFT;
|
|
|
|
packed |= L2CAP_CTRL_FRAME_TYPE;
|
|
|
|
} else {
|
|
|
|
packed |= control->sar << L2CAP_CTRL_SAR_SHIFT;
|
|
|
|
packed |= control->txseq << L2CAP_CTRL_TXSEQ_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return packed;
|
|
|
|
}
|
|
|
|
|
2012-04-12 01:48:43 +08:00
|
|
|
static inline void __pack_control(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
|
|
|
|
put_unaligned_le32(__pack_extended_control(control),
|
|
|
|
skb->data + L2CAP_HDR_SIZE);
|
|
|
|
} else {
|
|
|
|
put_unaligned_le16(__pack_enhanced_control(control),
|
|
|
|
skb->data + L2CAP_HDR_SIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-30 00:29:16 +08:00
|
|
|
static inline unsigned int __ertm_hdr_size(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
return L2CAP_EXT_HDR_SIZE;
|
|
|
|
else
|
|
|
|
return L2CAP_ENH_HDR_SIZE;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:35 +08:00
|
|
|
static struct sk_buff *l2cap_create_sframe_pdu(struct l2cap_chan *chan,
|
|
|
|
u32 control)
|
2012-04-26 07:36:15 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct l2cap_hdr *lh;
|
2012-05-30 00:29:16 +08:00
|
|
|
int hlen = __ertm_hdr_size(chan);
|
2012-04-26 07:36:15 +08:00
|
|
|
|
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16)
|
|
|
|
hlen += L2CAP_FCS_SIZE;
|
|
|
|
|
2012-05-18 11:53:35 +08:00
|
|
|
skb = bt_skb_alloc(hlen, GFP_KERNEL);
|
2012-04-26 07:36:15 +08:00
|
|
|
|
|
|
|
if (!skb)
|
2012-05-18 11:53:35 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2012-04-26 07:36:15 +08:00
|
|
|
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2012-04-26 07:36:15 +08:00
|
|
|
lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
|
|
|
|
lh->cid = cpu_to_le16(chan->dcid);
|
|
|
|
|
2012-05-18 11:53:35 +08:00
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
put_unaligned_le32(control, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
|
|
|
|
else
|
|
|
|
put_unaligned_le16(control, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
|
2012-04-26 07:36:15 +08:00
|
|
|
|
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16) {
|
2012-05-18 11:53:35 +08:00
|
|
|
u16 fcs = crc16(0, (u8 *)skb->data, skb->len);
|
2012-04-26 07:36:15 +08:00
|
|
|
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
|
|
|
|
}
|
|
|
|
|
|
|
|
skb->priority = HCI_PRIO_MAX;
|
2012-05-18 11:53:35 +08:00
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_send_sframe(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u32 control_field;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
|
|
|
|
|
|
|
if (!control->sframe)
|
|
|
|
return;
|
|
|
|
|
2012-10-24 06:24:19 +08:00
|
|
|
if (__chan_is_moving(chan))
|
|
|
|
return;
|
|
|
|
|
2012-05-18 11:53:35 +08:00
|
|
|
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
|
|
|
|
!control->poll)
|
|
|
|
control->final = 1;
|
|
|
|
|
|
|
|
if (control->super == L2CAP_SUPER_RR)
|
|
|
|
clear_bit(CONN_RNR_SENT, &chan->conn_state);
|
|
|
|
else if (control->super == L2CAP_SUPER_RNR)
|
|
|
|
set_bit(CONN_RNR_SENT, &chan->conn_state);
|
|
|
|
|
|
|
|
if (control->super != L2CAP_SUPER_SREJ) {
|
|
|
|
chan->last_acked_seq = control->reqseq;
|
|
|
|
__clear_ack_timer(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("reqseq %d, final %d, poll %d, super %d", control->reqseq,
|
|
|
|
control->final, control->poll, control->super);
|
|
|
|
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
control_field = __pack_extended_control(control);
|
|
|
|
else
|
|
|
|
control_field = __pack_enhanced_control(control);
|
|
|
|
|
|
|
|
skb = l2cap_create_sframe_pdu(chan, control_field);
|
|
|
|
if (!IS_ERR(skb))
|
|
|
|
l2cap_do_send(chan, skb);
|
2012-04-26 07:36:15 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:48 +08:00
|
|
|
static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll)
|
2012-04-26 07:36:15 +08:00
|
|
|
{
|
2012-05-18 11:53:48 +08:00
|
|
|
struct l2cap_ctrl control;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, poll %d", chan, poll);
|
|
|
|
|
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
|
|
|
control.poll = poll;
|
|
|
|
|
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
|
|
|
|
control.super = L2CAP_SUPER_RNR;
|
|
|
|
else
|
|
|
|
control.super = L2CAP_SUPER_RR;
|
2012-04-26 07:36:15 +08:00
|
|
|
|
2012-05-18 11:53:48 +08:00
|
|
|
control.reqseq = chan->buffer_seq;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
2012-04-26 07:36:15 +08:00
|
|
|
}
|
|
|
|
|
2011-04-13 05:15:09 +08:00
|
|
|
static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
|
2010-07-08 17:14:41 +08:00
|
|
|
{
|
2014-08-08 03:56:43 +08:00
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
|
|
|
|
return true;
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
|
2010-07-08 17:14:41 +08:00
|
|
|
}
|
|
|
|
|
2013-10-06 02:47:49 +08:00
|
|
|
static bool __amp_capable(struct l2cap_chan *chan)
|
2013-10-06 02:47:48 +08:00
|
|
|
{
|
2013-10-06 02:47:49 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2013-10-06 02:47:48 +08:00
|
|
|
struct hci_dev *hdev;
|
2013-10-06 02:47:49 +08:00
|
|
|
bool amp_available = false;
|
|
|
|
|
2014-12-02 16:09:26 +08:00
|
|
|
if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
|
2013-10-06 02:47:49 +08:00
|
|
|
return false;
|
|
|
|
|
2014-12-02 16:09:26 +08:00
|
|
|
if (!(conn->remote_fixed_chan & L2CAP_FC_A2MP))
|
2013-10-06 02:47:49 +08:00
|
|
|
return false;
|
2013-10-06 02:47:48 +08:00
|
|
|
|
|
|
|
read_lock(&hci_dev_list_lock);
|
2013-10-06 02:47:49 +08:00
|
|
|
list_for_each_entry(hdev, &hci_dev_list, list) {
|
2013-10-06 02:47:48 +08:00
|
|
|
if (hdev->amp_type != AMP_TYPE_BREDR &&
|
2013-10-06 02:47:49 +08:00
|
|
|
test_bit(HCI_UP, &hdev->flags)) {
|
|
|
|
amp_available = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-10-06 02:47:48 +08:00
|
|
|
read_unlock(&hci_dev_list_lock);
|
|
|
|
|
2013-10-06 02:47:49 +08:00
|
|
|
if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED)
|
|
|
|
return amp_available;
|
2013-10-02 13:59:22 +08:00
|
|
|
|
|
|
|
return false;
|
2012-09-27 22:26:16 +08:00
|
|
|
}
|
|
|
|
|
2012-10-31 21:46:30 +08:00
|
|
|
static bool l2cap_check_efs(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
/* Check EFS parameters */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-27 22:26:21 +08:00
|
|
|
void l2cap_send_conn_req(struct l2cap_chan *chan)
|
2012-02-24 22:00:00 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = chan->conn;
|
|
|
|
struct l2cap_conn_req req;
|
|
|
|
|
|
|
|
req.scid = cpu_to_le16(chan->scid);
|
|
|
|
req.psm = chan->psm;
|
|
|
|
|
|
|
|
chan->ident = l2cap_get_ident(conn);
|
|
|
|
|
|
|
|
set_bit(CONF_CONNECT_PEND, &chan->conf_state);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:17 +08:00
|
|
|
static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
|
|
|
|
{
|
|
|
|
struct l2cap_create_chan_req req;
|
|
|
|
req.scid = cpu_to_le16(chan->scid);
|
|
|
|
req.psm = chan->psm;
|
|
|
|
req.amp_id = amp_id;
|
|
|
|
|
|
|
|
chan->ident = l2cap_get_ident(chan->conn);
|
|
|
|
|
|
|
|
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
|
|
|
|
sizeof(req), &req);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:10 +08:00
|
|
|
static void l2cap_move_setup(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
if (chan->mode != L2CAP_MODE_ERTM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
__clear_monitor_timer(chan);
|
|
|
|
__clear_ack_timer(chan);
|
|
|
|
|
|
|
|
chan->retry_count = 0;
|
|
|
|
skb_queue_walk(&chan->tx_q, skb) {
|
2015-03-31 04:21:01 +08:00
|
|
|
if (bt_cb(skb)->l2cap.retries)
|
|
|
|
bt_cb(skb)->l2cap.retries = 1;
|
2012-10-24 06:24:10 +08:00
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->expected_tx_seq = chan->buffer_seq;
|
|
|
|
|
|
|
|
clear_bit(CONN_REJ_ACT, &chan->conn_state);
|
|
|
|
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
|
|
|
|
l2cap_seq_list_clear(&chan->retrans_list);
|
|
|
|
l2cap_seq_list_clear(&chan->srej_list);
|
|
|
|
skb_queue_purge(&chan->srej_q);
|
|
|
|
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_XMIT;
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_MOVE;
|
|
|
|
|
|
|
|
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:12 +08:00
|
|
|
static void l2cap_move_done(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
u8 move_role = chan->move_role;
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
chan->move_state = L2CAP_MOVE_STABLE;
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_NONE;
|
|
|
|
|
|
|
|
if (chan->mode != L2CAP_MODE_ERTM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (move_role) {
|
|
|
|
case L2CAP_MOVE_ROLE_INITIATOR:
|
|
|
|
l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_WAIT_F;
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_ROLE_RESPONDER:
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_WAIT_P;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-21 02:46:08 +08:00
|
|
|
static void l2cap_chan_ready(struct l2cap_chan *chan)
|
|
|
|
{
|
2015-02-16 17:42:11 +08:00
|
|
|
/* The channel may have already been flagged as connected in
|
|
|
|
* case of receiving data before the L2CAP info req/rsp
|
|
|
|
* procedure is complete.
|
|
|
|
*/
|
|
|
|
if (chan->state == BT_CONNECTED)
|
|
|
|
return;
|
|
|
|
|
2012-05-18 12:14:09 +08:00
|
|
|
/* This clears all conf flags, including CONF_NOT_COMPLETE */
|
2012-04-21 02:46:08 +08:00
|
|
|
chan->conf_state = 0;
|
|
|
|
__clear_chan_timer(chan);
|
|
|
|
|
2013-12-05 20:55:33 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits)
|
|
|
|
chan->ops->suspend(chan);
|
2013-05-31 22:54:51 +08:00
|
|
|
|
2012-05-28 09:27:53 +08:00
|
|
|
chan->state = BT_CONNECTED;
|
2012-04-21 02:46:08 +08:00
|
|
|
|
2012-05-30 14:55:32 +08:00
|
|
|
chan->ops->ready(chan);
|
2012-04-21 02:46:08 +08:00
|
|
|
}
|
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
static void l2cap_le_connect(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = chan->conn;
|
|
|
|
struct l2cap_le_conn_req req;
|
|
|
|
|
2013-12-03 04:12:22 +08:00
|
|
|
if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
|
|
|
|
return;
|
|
|
|
|
2018-09-04 18:39:20 +08:00
|
|
|
l2cap_le_flowctl_init(chan);
|
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
req.psm = chan->psm;
|
|
|
|
req.scid = cpu_to_le16(chan->scid);
|
|
|
|
req.mtu = cpu_to_le16(chan->imtu);
|
2013-10-07 21:35:26 +08:00
|
|
|
req.mps = cpu_to_le16(chan->mps);
|
2013-05-17 18:09:05 +08:00
|
|
|
req.credits = cpu_to_le16(chan->rx_credits);
|
2013-05-13 19:15:56 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-09-27 22:26:16 +08:00
|
|
|
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);
|
2013-05-13 19:15:56 +08:00
|
|
|
} else if (chan->conn->hcon->type == LE_LINK) {
|
|
|
|
l2cap_le_start(chan);
|
2012-09-27 22:26:16 +08:00
|
|
|
} else {
|
|
|
|
l2cap_send_conn_req(chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-11 08:37:46 +08:00
|
|
|
static void l2cap_request_info(struct l2cap_conn *conn)
|
|
|
|
{
|
|
|
|
struct l2cap_info_req req;
|
|
|
|
|
|
|
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
|
|
|
|
|
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
|
|
|
|
conn->info_ident = l2cap_get_ident(conn);
|
|
|
|
|
|
|
|
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
|
|
|
|
sizeof(req), &req);
|
|
|
|
}
|
|
|
|
|
2011-03-26 00:59:37 +08:00
|
|
|
static void l2cap_do_start(struct l2cap_chan *chan)
|
2008-07-15 02:13:44 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2012-04-21 02:46:08 +08:00
|
|
|
if (conn->hcon->type == LE_LINK) {
|
2013-05-13 16:15:07 +08:00
|
|
|
l2cap_le_start(chan);
|
2012-04-21 02:46:08 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-11 08:37:46 +08:00
|
|
|
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) {
|
|
|
|
l2cap_request_info(conn);
|
|
|
|
return;
|
|
|
|
}
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2014-09-11 08:37:46 +08:00
|
|
|
if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
|
|
|
|
return;
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2014-09-11 08:37:46 +08:00
|
|
|
if (l2cap_chan_check_security(chan, true) &&
|
|
|
|
__l2cap_no_conn_pending(chan))
|
|
|
|
l2cap_start_connection(chan);
|
2008-07-15 02:13:44 +08:00
|
|
|
}
|
|
|
|
|
2010-06-08 07:54:45 +08:00
|
|
|
static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
|
|
|
|
{
|
|
|
|
u32 local_feat_mask = l2cap_feat_mask;
|
2010-07-19 03:25:54 +08:00
|
|
|
if (!disable_ertm)
|
2010-06-08 07:54:45 +08:00
|
|
|
local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING;
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask;
|
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask;
|
|
|
|
default:
|
|
|
|
return 0x00;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-28 23:59:39 +08:00
|
|
|
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
|
2009-07-23 21:27:23 +08:00
|
|
|
{
|
2012-11-28 23:59:39 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2009-07-23 21:27:23 +08:00
|
|
|
struct l2cap_disconn_req req;
|
|
|
|
|
2010-05-14 07:50:12 +08:00
|
|
|
if (!conn)
|
|
|
|
return;
|
|
|
|
|
2012-09-06 20:05:42 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_ERTM && chan->state == BT_CONNECTED) {
|
2011-05-18 02:13:19 +08:00
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
__clear_monitor_timer(chan);
|
|
|
|
__clear_ack_timer(chan);
|
2010-05-14 07:50:12 +08:00
|
|
|
}
|
|
|
|
|
2014-01-24 16:35:40 +08:00
|
|
|
if (chan->scid == L2CAP_CID_A2MP) {
|
2012-10-06 18:47:38 +08:00
|
|
|
l2cap_state_change(chan, BT_DISCONN);
|
2012-05-29 18:59:16 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
req.dcid = cpu_to_le16(chan->dcid);
|
|
|
|
req.scid = cpu_to_le16(chan->scid);
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
|
|
|
|
sizeof(req), &req);
|
2010-05-14 07:50:12 +08:00
|
|
|
|
2013-10-16 06:24:46 +08:00
|
|
|
l2cap_state_change_and_error(chan, BT_DISCONN, err);
|
2009-07-23 21:27:23 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* ---- L2CAP connections ---- */
|
2007-10-20 19:37:56 +08:00
|
|
|
static void l2cap_conn_start(struct l2cap_conn *conn)
|
|
|
|
{
|
2012-02-21 18:54:55 +08:00
|
|
|
struct l2cap_chan *chan, *tmp;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
|
|
|
BT_DBG("conn %p", conn);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2011-05-03 04:13:55 +08:00
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
2014-09-11 08:37:46 +08:00
|
|
|
l2cap_chan_ready(chan);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2008-07-15 02:13:44 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state == BT_CONNECT) {
|
2014-07-17 20:35:38 +08:00
|
|
|
if (!l2cap_chan_check_security(chan, true) ||
|
2012-10-06 17:07:01 +08:00
|
|
|
!__l2cap_no_conn_pending(chan)) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2010-07-10 03:38:35 +08:00
|
|
|
continue;
|
|
|
|
}
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
|
2012-10-06 17:07:01 +08:00
|
|
|
&& test_bit(CONF_STATE2_DEVICE,
|
2011-06-11 04:02:12 +08:00
|
|
|
&chan->conf_state)) {
|
2011-06-03 11:19:47 +08:00
|
|
|
l2cap_chan_close(chan, ECONNRESET);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2010-07-10 03:38:35 +08:00
|
|
|
continue;
|
2008-07-15 02:13:54 +08:00
|
|
|
}
|
2010-07-10 03:38:35 +08:00
|
|
|
|
2012-09-27 22:26:16 +08:00
|
|
|
l2cap_start_connection(chan);
|
2010-07-10 03:38:35 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
} else if (chan->state == BT_CONNECT2) {
|
2008-07-15 02:13:44 +08:00
|
|
|
struct l2cap_conn_rsp rsp;
|
2010-07-09 07:08:18 +08:00
|
|
|
char buf[128];
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp.scid = cpu_to_le16(chan->dcid);
|
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2014-07-17 20:35:38 +08:00
|
|
|
if (l2cap_chan_check_security(chan, false)) {
|
2013-10-14 17:45:34 +08:00
|
|
|
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
|
2012-10-12 19:35:24 +08:00
|
|
|
chan->ops->defer(chan);
|
2009-01-16 04:57:00 +08:00
|
|
|
|
|
|
|
} else {
|
2013-10-22 00:21:39 +08:00
|
|
|
l2cap_state_change(chan, BT_CONFIG);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2009-01-16 04:57:00 +08:00
|
|
|
}
|
2008-07-15 02:13:44 +08:00
|
|
|
} else {
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
|
2008-07-15 02:13:44 +08:00
|
|
|
}
|
|
|
|
|
2011-03-26 00:59:37 +08:00
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
|
2012-10-06 17:07:01 +08:00
|
|
|
sizeof(rsp), &rsp);
|
2010-07-09 07:08:18 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
|
2012-10-06 17:07:01 +08:00
|
|
|
rsp.result != L2CAP_CR_SUCCESS) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2010-07-09 07:08:18 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_REQ_SENT, &chan->conf_state);
|
2010-07-09 07:08:18 +08:00
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_req++;
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2011-02-11 09:38:50 +08:00
|
|
|
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|
|
|
{
|
2013-10-18 18:43:01 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2014-07-09 17:59:13 +08:00
|
|
|
struct hci_dev *hdev = hcon->hdev;
|
2011-02-11 09:38:50 +08:00
|
|
|
|
2014-08-08 03:56:47 +08:00
|
|
|
BT_DBG("%s conn %p", hdev->name, conn);
|
2011-02-11 09:38:50 +08:00
|
|
|
|
2014-08-08 03:56:47 +08:00
|
|
|
/* For outgoing pairing which doesn't necessarily have an
|
|
|
|
* associated socket (e.g. mgmt_pair_device).
|
|
|
|
*/
|
|
|
|
if (hcon->out)
|
|
|
|
smp_conn_security(hcon, hcon->pending_sec_level);
|
2013-10-18 18:43:01 +08:00
|
|
|
|
2014-06-23 18:18:51 +08:00
|
|
|
/* For LE slave connections, make sure the connection interval
|
|
|
|
* is in the range of the minium and maximum interval that has
|
|
|
|
* been configured for this connection. If not, then trigger
|
|
|
|
* the connection update procedure.
|
|
|
|
*/
|
2014-07-16 16:42:27 +08:00
|
|
|
if (hcon->role == HCI_ROLE_SLAVE &&
|
2014-06-23 18:18:51 +08:00
|
|
|
(hcon->le_conn_interval < hcon->le_conn_min_interval ||
|
|
|
|
hcon->le_conn_interval > hcon->le_conn_max_interval)) {
|
|
|
|
struct l2cap_conn_param_update_req req;
|
|
|
|
|
|
|
|
req.min = cpu_to_le16(hcon->le_conn_min_interval);
|
|
|
|
req.max = cpu_to_le16(hcon->le_conn_max_interval);
|
|
|
|
req.latency = cpu_to_le16(hcon->le_conn_latency);
|
|
|
|
req.to_multiplier = cpu_to_le16(hcon->le_supv_timeout);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn),
|
|
|
|
L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req);
|
|
|
|
}
|
2011-02-11 09:38:50 +08:00
|
|
|
}
|
|
|
|
|
2007-10-20 19:37:56 +08:00
|
|
|
static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|
|
|
{
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2012-08-24 08:32:43 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2008-07-15 02:13:44 +08:00
|
|
|
BT_DBG("conn %p", conn);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2014-09-11 08:37:46 +08:00
|
|
|
if (hcon->type == ACL_LINK)
|
|
|
|
l2cap_request_info(conn);
|
|
|
|
|
2014-08-08 03:56:47 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
2011-04-01 03:17:41 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2014-01-24 16:35:40 +08:00
|
|
|
if (chan->scid == L2CAP_CID_A2MP) {
|
2012-05-29 18:59:16 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-08-24 08:32:43 +08:00
|
|
|
if (hcon->type == LE_LINK) {
|
2013-05-13 19:15:56 +08:00
|
|
|
l2cap_le_start(chan);
|
2011-06-18 09:46:26 +08:00
|
|
|
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
2014-09-11 08:37:46 +08:00
|
|
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
|
|
|
|
l2cap_chan_ready(chan);
|
2012-12-07 13:29:10 +08:00
|
|
|
} else if (chan->state == BT_CONNECT) {
|
2011-03-26 00:59:37 +08:00
|
|
|
l2cap_do_start(chan);
|
2012-12-07 13:29:10 +08:00
|
|
|
}
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2014-01-18 02:45:11 +08:00
|
|
|
|
2014-08-08 14:28:04 +08:00
|
|
|
if (hcon->type == LE_LINK)
|
|
|
|
l2cap_le_conn_ready(conn);
|
|
|
|
|
2014-01-18 02:45:11 +08:00
|
|
|
queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Notify sockets that we cannot guaranty reliability anymore */
|
|
|
|
static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
|
|
|
|
{
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
|
|
|
BT_DBG("conn %p", conn);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
2011-10-11 19:04:32 +08:00
|
|
|
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
|
2012-10-06 18:34:52 +08:00
|
|
|
l2cap_chan_set_err(chan, err);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2007-10-20 19:37:56 +08:00
|
|
|
}
|
|
|
|
|
2011-12-15 11:16:14 +08:00
|
|
|
static void l2cap_info_timeout(struct work_struct *work)
|
2007-10-20 19:37:56 +08:00
|
|
|
{
|
2011-12-15 11:16:14 +08:00
|
|
|
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
|
2012-10-06 17:07:01 +08:00
|
|
|
info_timer.work);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2009-02-07 06:35:19 +08:00
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
2009-02-09 16:18:02 +08:00
|
|
|
conn->info_ident = 0;
|
2009-02-07 06:35:19 +08:00
|
|
|
|
2007-10-20 19:37:56 +08:00
|
|
|
l2cap_conn_start(conn);
|
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:45 +08:00
|
|
|
/*
|
|
|
|
* l2cap_user
|
|
|
|
* External modules can register l2cap_user objects on l2cap_conn. The ->probe
|
|
|
|
* callback is called during registration. The ->remove callback is called
|
|
|
|
* during unregistration.
|
|
|
|
* An l2cap_user object can either be explicitly unregistered or when the
|
|
|
|
* underlying l2cap_conn object is deleted. This guarantees that l2cap->hcon,
|
|
|
|
* l2cap->hchan, .. are valid as long as the remove callback hasn't been called.
|
|
|
|
* External modules must own a reference to the l2cap_conn object if they intend
|
|
|
|
* to call l2cap_unregister_user(). The l2cap_conn object might get destroyed at
|
|
|
|
* any time if they don't.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
|
|
|
{
|
|
|
|
struct hci_dev *hdev = conn->hcon->hdev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* We need to check whether l2cap_conn is registered. If it is not, we
|
|
|
|
* must not register the l2cap_user. l2cap_conn_del() is unregisters
|
|
|
|
* l2cap_conn objects, but doesn't provide its own locking. Instead, it
|
|
|
|
* relies on the parent hci_conn object to be locked. This itself relies
|
|
|
|
* on the hci_dev object to be locked. So we must lock the hci device
|
|
|
|
* here, too. */
|
|
|
|
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
|
2015-06-11 01:28:33 +08:00
|
|
|
if (!list_empty(&user->list)) {
|
2013-04-07 02:28:45 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* conn->hchan is NULL after l2cap_conn_del() was called */
|
|
|
|
if (!conn->hchan) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = user->probe(conn, user);
|
|
|
|
if (ret)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
list_add(&user->list, &conn->users);
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(l2cap_register_user);
|
|
|
|
|
|
|
|
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
|
|
|
{
|
|
|
|
struct hci_dev *hdev = conn->hcon->hdev;
|
|
|
|
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
|
2015-06-11 01:28:33 +08:00
|
|
|
if (list_empty(&user->list))
|
2013-04-07 02:28:45 +08:00
|
|
|
goto out_unlock;
|
|
|
|
|
2015-07-01 02:43:40 +08:00
|
|
|
list_del_init(&user->list);
|
2013-04-07 02:28:45 +08:00
|
|
|
user->remove(conn, user);
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(l2cap_unregister_user);
|
|
|
|
|
|
|
|
static void l2cap_unregister_all_users(struct l2cap_conn *conn)
|
|
|
|
{
|
|
|
|
struct l2cap_user *user;
|
|
|
|
|
|
|
|
while (!list_empty(&conn->users)) {
|
|
|
|
user = list_first_entry(&conn->users, struct l2cap_user, list);
|
2015-07-01 02:43:40 +08:00
|
|
|
list_del_init(&user->list);
|
2013-04-07 02:28:45 +08:00
|
|
|
user->remove(conn, user);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 00:37:41 +08:00
|
|
|
static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
|
|
|
struct l2cap_chan *chan, *l;
|
|
|
|
|
|
|
|
if (!conn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
|
|
|
|
|
|
|
|
kfree_skb(conn->rx_skb);
|
|
|
|
|
2014-01-18 02:45:11 +08:00
|
|
|
skb_queue_purge(&conn->pending_rx);
|
2014-06-12 18:15:13 +08:00
|
|
|
|
|
|
|
/* We can not call flush_work(&conn->pending_rx_work) here since we
|
|
|
|
* might block if we are running on a worker from the same workqueue
|
|
|
|
* pending_rx_work is waiting on.
|
|
|
|
*/
|
|
|
|
if (work_pending(&conn->pending_rx_work))
|
|
|
|
cancel_work_sync(&conn->pending_rx_work);
|
2014-01-18 02:45:11 +08:00
|
|
|
|
2014-09-06 03:19:50 +08:00
|
|
|
if (work_pending(&conn->id_addr_update_work))
|
|
|
|
cancel_work_sync(&conn->id_addr_update_work);
|
|
|
|
|
2013-04-07 02:28:45 +08:00
|
|
|
l2cap_unregister_all_users(conn);
|
|
|
|
|
2014-08-19 01:33:28 +08:00
|
|
|
/* Force the connection to be immediately dropped */
|
|
|
|
hcon->disc_timeout = 0;
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
2011-06-15 00:37:41 +08:00
|
|
|
/* Kill channels */
|
|
|
|
list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_hold(chan);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2011-06-15 00:37:41 +08:00
|
|
|
l2cap_chan_del(chan, err);
|
2012-02-22 23:11:56 +08:00
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
chan->ops->close(chan);
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_put(chan);
|
2011-06-15 00:37:41 +08:00
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
2011-11-02 21:52:01 +08:00
|
|
|
hci_chan_del(conn->hchan);
|
|
|
|
|
2011-06-15 00:37:41 +08:00
|
|
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
2012-01-31 04:26:29 +08:00
|
|
|
cancel_delayed_work_sync(&conn->info_timer);
|
2011-06-15 00:37:41 +08:00
|
|
|
|
|
|
|
hcon->l2cap_data = NULL;
|
2013-04-07 02:28:44 +08:00
|
|
|
conn->hchan = NULL;
|
|
|
|
l2cap_conn_put(conn);
|
2011-06-15 00:37:41 +08:00
|
|
|
}
|
|
|
|
|
2013-04-07 02:28:44 +08:00
|
|
|
static void l2cap_conn_free(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
|
|
|
|
|
|
|
|
hci_conn_put(conn->hcon);
|
|
|
|
kfree(conn);
|
|
|
|
}
|
|
|
|
|
2014-08-16 02:06:57 +08:00
|
|
|
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn)
|
2013-04-07 02:28:44 +08:00
|
|
|
{
|
|
|
|
kref_get(&conn->ref);
|
2014-08-16 02:06:57 +08:00
|
|
|
return conn;
|
2013-04-07 02:28:44 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(l2cap_conn_get);
|
|
|
|
|
|
|
|
void l2cap_conn_put(struct l2cap_conn *conn)
|
|
|
|
{
|
|
|
|
kref_put(&conn->ref, l2cap_conn_free);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(l2cap_conn_put);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* ---- Socket interface ---- */
|
|
|
|
|
2012-04-21 02:46:07 +08:00
|
|
|
/* Find socket with psm and source / destination bdaddr.
|
2005-04-17 06:20:36 +08:00
|
|
|
* Returns closest match.
|
|
|
|
*/
|
2012-04-21 02:46:07 +08:00
|
|
|
static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
|
|
|
|
bdaddr_t *src,
|
2013-05-14 18:23:13 +08:00
|
|
|
bdaddr_t *dst,
|
|
|
|
u8 link_type)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *c, *c1 = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
read_lock(&chan_list_lock);
|
2010-11-02 02:43:53 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
list_for_each_entry(c, &chan_list, global_l) {
|
2011-06-03 11:19:47 +08:00
|
|
|
if (state && c->state != state)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
2013-05-14 18:23:13 +08:00
|
|
|
if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
|
|
|
|
continue;
|
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
if (c->psm == psm) {
|
2012-04-21 02:46:07 +08:00
|
|
|
int src_match, dst_match;
|
|
|
|
int src_any, dst_any;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Exact match. */
|
2013-10-13 23:12:47 +08:00
|
|
|
src_match = !bacmp(&c->src, src);
|
|
|
|
dst_match = !bacmp(&c->dst, dst);
|
2012-04-21 02:46:07 +08:00
|
|
|
if (src_match && dst_match) {
|
2014-08-08 03:56:42 +08:00
|
|
|
l2cap_chan_hold(c);
|
2011-06-01 14:29:54 +08:00
|
|
|
read_unlock(&chan_list_lock);
|
2011-04-28 05:26:32 +08:00
|
|
|
return c;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Closest match */
|
2013-10-13 23:12:47 +08:00
|
|
|
src_any = !bacmp(&c->src, BDADDR_ANY);
|
|
|
|
dst_any = !bacmp(&c->dst, BDADDR_ANY);
|
2012-04-21 02:46:07 +08:00
|
|
|
if ((src_match && dst_any) || (src_any && dst_match) ||
|
|
|
|
(src_any && dst_any))
|
2011-04-28 05:26:32 +08:00
|
|
|
c1 = c;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-08 03:56:42 +08:00
|
|
|
if (c1)
|
|
|
|
l2cap_chan_hold(c1);
|
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
read_unlock(&chan_list_lock);
|
2010-11-02 02:43:53 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
return c1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
static void l2cap_monitor_timeout(struct work_struct *work)
|
2009-08-21 09:26:00 +08:00
|
|
|
{
|
2011-06-24 06:29:58 +08:00
|
|
|
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
|
2012-05-18 11:53:49 +08:00
|
|
|
monitor_timer.work);
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2011-03-26 06:43:39 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2010-04-20 01:45:38 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2012-05-18 11:53:50 +08:00
|
|
|
if (!chan->conn) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-03-23 15:42:15 +08:00
|
|
|
l2cap_chan_put(chan);
|
2009-08-21 09:26:00 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-21 20:47:46 +08:00
|
|
|
l2cap_tx(chan, NULL, NULL, L2CAP_EV_MONITOR_TO);
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-03-23 15:42:15 +08:00
|
|
|
l2cap_chan_put(chan);
|
2009-08-21 09:26:00 +08:00
|
|
|
}
|
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
static void l2cap_retrans_timeout(struct work_struct *work)
|
2009-08-21 09:26:00 +08:00
|
|
|
{
|
2011-06-24 06:29:58 +08:00
|
|
|
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
|
2012-05-18 11:53:49 +08:00
|
|
|
retrans_timer.work);
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2011-04-05 02:59:54 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2010-04-20 01:45:38 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2012-05-18 11:53:50 +08:00
|
|
|
if (!chan->conn) {
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
l2cap_chan_put(chan);
|
|
|
|
return;
|
|
|
|
}
|
2012-02-22 23:11:56 +08:00
|
|
|
|
2012-05-21 20:47:46 +08:00
|
|
|
l2cap_tx(chan, NULL, NULL, L2CAP_EV_RETRANS_TO);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-03-23 15:42:15 +08:00
|
|
|
l2cap_chan_put(chan);
|
2009-08-21 09:26:00 +08:00
|
|
|
}
|
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
static void l2cap_streaming_send(struct l2cap_chan *chan,
|
|
|
|
struct sk_buff_head *skbs)
|
2009-08-21 09:26:01 +08:00
|
|
|
{
|
2010-08-31 05:44:44 +08:00
|
|
|
struct sk_buff *skb;
|
2012-05-18 11:53:33 +08:00
|
|
|
struct l2cap_ctrl *control;
|
2009-08-21 09:26:01 +08:00
|
|
|
|
2012-05-18 11:53:33 +08:00
|
|
|
BT_DBG("chan %p, skbs %p", chan, skbs);
|
|
|
|
|
2012-10-24 06:24:19 +08:00
|
|
|
if (__chan_is_moving(chan))
|
|
|
|
return;
|
|
|
|
|
2012-05-18 11:53:33 +08:00
|
|
|
skb_queue_splice_tail_init(skbs, &chan->tx_q);
|
|
|
|
|
|
|
|
while (!skb_queue_empty(&chan->tx_q)) {
|
|
|
|
|
|
|
|
skb = skb_dequeue(&chan->tx_q);
|
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.retries = 1;
|
|
|
|
control = &bt_cb(skb)->l2cap;
|
2012-05-18 11:53:33 +08:00
|
|
|
|
|
|
|
control->reqseq = 0;
|
|
|
|
control->txseq = chan->next_tx_seq;
|
|
|
|
|
|
|
|
__pack_control(chan, control, skb);
|
2009-08-21 09:26:01 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16) {
|
2012-05-18 11:53:33 +08:00
|
|
|
u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
|
|
|
|
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
|
2009-08-21 09:26:02 +08:00
|
|
|
}
|
|
|
|
|
2011-04-13 05:31:57 +08:00
|
|
|
l2cap_do_send(chan, skb);
|
2009-08-21 09:26:01 +08:00
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("Sent txseq %u", control->txseq);
|
2012-05-18 11:53:33 +08:00
|
|
|
|
2011-10-17 17:19:57 +08:00
|
|
|
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
|
2012-05-18 11:53:33 +08:00
|
|
|
chan->frames_sent++;
|
2009-08-21 09:26:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-28 22:24:33 +08:00
|
|
|
static int l2cap_ertm_send(struct l2cap_chan *chan)
|
2009-08-21 09:25:57 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb, *tx_skb;
|
2012-05-18 11:53:34 +08:00
|
|
|
struct l2cap_ctrl *control;
|
|
|
|
int sent = 0;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state != BT_CONNECTED)
|
2010-05-14 07:50:12 +08:00
|
|
|
return -ENOTCONN;
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
|
|
|
|
return 0;
|
|
|
|
|
2012-10-24 06:24:19 +08:00
|
|
|
if (__chan_is_moving(chan))
|
|
|
|
return 0;
|
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
while (chan->tx_send_head &&
|
|
|
|
chan->unacked_frames < chan->remote_tx_win &&
|
|
|
|
chan->tx_state == L2CAP_TX_STATE_XMIT) {
|
2009-12-23 19:07:14 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
skb = chan->tx_send_head;
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.retries = 1;
|
|
|
|
control = &bt_cb(skb)->l2cap;
|
2010-06-19 07:37:33 +08:00
|
|
|
|
2011-06-11 08:28:49 +08:00
|
|
|
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
|
2012-05-18 11:53:34 +08:00
|
|
|
control->final = 1;
|
2011-06-11 08:28:49 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
control->reqseq = chan->buffer_seq;
|
|
|
|
chan->last_acked_seq = chan->buffer_seq;
|
|
|
|
control->txseq = chan->next_tx_seq;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
__pack_control(chan, control, skb);
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16) {
|
2012-05-18 11:53:34 +08:00
|
|
|
u16 fcs = crc16(0, (u8 *) skb->data, skb->len);
|
|
|
|
put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE));
|
2009-08-21 09:26:02 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
/* Clone after data has been modified. Data is assumed to be
|
|
|
|
read-only (for locking purposes) on cloned sk_buffs.
|
|
|
|
*/
|
|
|
|
tx_skb = skb_clone(skb, GFP_KERNEL);
|
2010-05-02 03:15:43 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
if (!tx_skb)
|
|
|
|
break;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
__set_retrans_timer(chan);
|
2011-10-17 17:19:57 +08:00
|
|
|
|
|
|
|
chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq);
|
2012-05-18 11:53:34 +08:00
|
|
|
chan->unacked_frames++;
|
2011-04-01 11:38:50 +08:00
|
|
|
chan->frames_sent++;
|
2012-05-18 11:53:34 +08:00
|
|
|
sent++;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2011-04-05 03:16:44 +08:00
|
|
|
if (skb_queue_is_last(&chan->tx_q, skb))
|
|
|
|
chan->tx_send_head = NULL;
|
2009-08-21 09:25:57 +08:00
|
|
|
else
|
2011-04-05 03:16:44 +08:00
|
|
|
chan->tx_send_head = skb_queue_next(&chan->tx_q, skb);
|
2012-05-18 11:53:34 +08:00
|
|
|
|
|
|
|
l2cap_do_send(chan, tx_skb);
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("Sent txseq %u", control->txseq);
|
2009-08-21 09:25:57 +08:00
|
|
|
}
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("Sent %d, %u unacked, %u in ERTM queue", sent,
|
|
|
|
chan->unacked_frames, skb_queue_len(&chan->tx_q));
|
2012-05-18 11:53:34 +08:00
|
|
|
|
|
|
|
return sent;
|
2010-05-02 03:15:37 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:43 +08:00
|
|
|
static void l2cap_ertm_resend(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
struct l2cap_ctrl control;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct sk_buff *tx_skb;
|
|
|
|
u16 seq;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
|
|
|
|
return;
|
|
|
|
|
2012-10-24 06:24:19 +08:00
|
|
|
if (__chan_is_moving(chan))
|
|
|
|
return;
|
|
|
|
|
2012-05-18 11:53:43 +08:00
|
|
|
while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
|
|
|
|
seq = l2cap_seq_list_pop(&chan->retrans_list);
|
|
|
|
|
|
|
|
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq);
|
|
|
|
if (!skb) {
|
|
|
|
BT_DBG("Error: Can't retransmit seq %d, frame missing",
|
2012-10-06 17:07:01 +08:00
|
|
|
seq);
|
2012-05-18 11:53:43 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.retries++;
|
|
|
|
control = bt_cb(skb)->l2cap;
|
2012-05-18 11:53:43 +08:00
|
|
|
|
|
|
|
if (chan->max_tx != 0 &&
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.retries > chan->max_tx) {
|
2012-05-18 11:53:43 +08:00
|
|
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:43 +08:00
|
|
|
l2cap_seq_list_clear(&chan->retrans_list);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
control.reqseq = chan->buffer_seq;
|
|
|
|
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
|
|
|
|
control.final = 1;
|
|
|
|
else
|
|
|
|
control.final = 0;
|
|
|
|
|
|
|
|
if (skb_cloned(skb)) {
|
|
|
|
/* Cloned sk_buffs are read-only, so we need a
|
|
|
|
* writeable copy
|
|
|
|
*/
|
2012-05-29 06:18:14 +08:00
|
|
|
tx_skb = skb_copy(skb, GFP_KERNEL);
|
2012-05-18 11:53:43 +08:00
|
|
|
} else {
|
2012-05-29 06:18:14 +08:00
|
|
|
tx_skb = skb_clone(skb, GFP_KERNEL);
|
2012-05-18 11:53:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!tx_skb) {
|
|
|
|
l2cap_seq_list_clear(&chan->retrans_list);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update skb contents */
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
|
|
|
|
put_unaligned_le32(__pack_extended_control(&control),
|
|
|
|
tx_skb->data + L2CAP_HDR_SIZE);
|
|
|
|
} else {
|
|
|
|
put_unaligned_le16(__pack_enhanced_control(&control),
|
|
|
|
tx_skb->data + L2CAP_HDR_SIZE);
|
|
|
|
}
|
|
|
|
|
2014-08-14 15:35:34 +08:00
|
|
|
/* Update FCS */
|
2012-05-18 11:53:43 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16) {
|
2014-08-14 15:35:34 +08:00
|
|
|
u16 fcs = crc16(0, (u8 *) tx_skb->data,
|
|
|
|
tx_skb->len - L2CAP_FCS_SIZE);
|
|
|
|
put_unaligned_le16(fcs, skb_tail_pointer(tx_skb) -
|
|
|
|
L2CAP_FCS_SIZE);
|
2012-05-18 11:53:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_do_send(chan, tx_skb);
|
|
|
|
|
|
|
|
BT_DBG("Resent txseq %d", control.txseq);
|
|
|
|
|
|
|
|
chan->last_acked_seq = chan->buffer_seq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:46 +08:00
|
|
|
static void l2cap_retransmit(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
|
|
|
|
|
|
|
l2cap_seq_list_append(&chan->retrans_list, control->reqseq);
|
|
|
|
l2cap_ertm_resend(chan);
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:42 +08:00
|
|
|
static void l2cap_retransmit_all(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
2012-05-18 11:53:43 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
|
|
|
|
|
|
|
if (control->poll)
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
|
|
|
|
|
|
|
l2cap_seq_list_clear(&chan->retrans_list);
|
|
|
|
|
|
|
|
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (chan->unacked_frames) {
|
|
|
|
skb_queue_walk(&chan->tx_q, skb) {
|
2015-03-31 04:21:01 +08:00
|
|
|
if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
|
2012-10-06 17:07:01 +08:00
|
|
|
skb == chan->tx_send_head)
|
2012-05-18 11:53:43 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_queue_walk_from(&chan->tx_q, skb) {
|
|
|
|
if (skb == chan->tx_send_head)
|
|
|
|
break;
|
|
|
|
|
|
|
|
l2cap_seq_list_append(&chan->retrans_list,
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.txseq);
|
2012-05-18 11:53:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_ertm_resend(chan);
|
|
|
|
}
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:39 +08:00
|
|
|
static void l2cap_send_ack(struct l2cap_chan *chan)
|
2010-05-02 03:15:37 +08:00
|
|
|
{
|
2012-05-18 11:53:39 +08:00
|
|
|
struct l2cap_ctrl control;
|
|
|
|
u16 frames_to_ack = __seq_offset(chan, chan->buffer_seq,
|
|
|
|
chan->last_acked_seq);
|
|
|
|
int threshold;
|
2010-05-02 03:15:37 +08:00
|
|
|
|
2012-05-18 11:53:39 +08:00
|
|
|
BT_DBG("chan %p last_acked_seq %d buffer_seq %d",
|
|
|
|
chan, chan->last_acked_seq, chan->buffer_seq);
|
2010-05-02 03:15:37 +08:00
|
|
|
|
2012-05-18 11:53:39 +08:00
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
2010-05-02 03:15:45 +08:00
|
|
|
|
2012-05-18 11:53:39 +08:00
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
|
|
|
|
chan->rx_state == L2CAP_RX_STATE_RECV) {
|
|
|
|
__clear_ack_timer(chan);
|
|
|
|
control.super = L2CAP_SUPER_RNR;
|
|
|
|
control.reqseq = chan->buffer_seq;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
|
|
|
} else {
|
|
|
|
if (!test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) {
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
/* If any i-frames were sent, they included an ack */
|
|
|
|
if (chan->buffer_seq == chan->last_acked_seq)
|
|
|
|
frames_to_ack = 0;
|
|
|
|
}
|
2010-05-02 03:15:45 +08:00
|
|
|
|
2012-07-10 20:47:07 +08:00
|
|
|
/* Ack now if the window is 3/4ths full.
|
2012-05-18 11:53:39 +08:00
|
|
|
* Calculate without mul or div
|
|
|
|
*/
|
2012-07-10 20:47:07 +08:00
|
|
|
threshold = chan->ack_win;
|
2012-05-18 11:53:39 +08:00
|
|
|
threshold += threshold << 1;
|
|
|
|
threshold >>= 2;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("frames_to_ack %u, threshold %d", frames_to_ack,
|
2012-05-18 11:53:39 +08:00
|
|
|
threshold);
|
|
|
|
|
|
|
|
if (frames_to_ack >= threshold) {
|
|
|
|
__clear_ack_timer(chan);
|
|
|
|
control.super = L2CAP_SUPER_RR;
|
|
|
|
control.reqseq = chan->buffer_seq;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
|
|
|
frames_to_ack = 0;
|
|
|
|
}
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-05-18 11:53:39 +08:00
|
|
|
if (frames_to_ack)
|
|
|
|
__set_ack_timer(chan);
|
|
|
|
}
|
2012-01-11 17:59:47 +08:00
|
|
|
}
|
|
|
|
|
2012-03-08 12:25:00 +08:00
|
|
|
static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
|
|
|
|
struct msghdr *msg, int len,
|
|
|
|
int count, struct sk_buff *skb)
|
2009-08-21 09:25:57 +08:00
|
|
|
{
|
2012-01-13 23:21:43 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct sk_buff **frag;
|
2012-04-07 07:15:47 +08:00
|
|
|
int sent = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-11-02 10:09:04 +08:00
|
|
|
if (!copy_from_iter_full(skb_put(skb, count), count, &msg->msg_iter))
|
2009-08-21 09:25:57 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sent += count;
|
|
|
|
len -= count;
|
|
|
|
|
|
|
|
/* Continuation fragments (no L2CAP header) */
|
|
|
|
frag = &skb_shinfo(skb)->frag_list;
|
|
|
|
while (len) {
|
2012-05-16 00:22:55 +08:00
|
|
|
struct sk_buff *tmp;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
count = min_t(unsigned int, conn->mtu, len);
|
|
|
|
|
2014-06-08 17:22:28 +08:00
|
|
|
tmp = chan->ops->alloc_skb(chan, 0, count,
|
2012-05-16 00:22:55 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
if (IS_ERR(tmp))
|
|
|
|
return PTR_ERR(tmp);
|
|
|
|
|
|
|
|
*frag = tmp;
|
2012-01-20 20:08:03 +08:00
|
|
|
|
2016-11-02 10:09:04 +08:00
|
|
|
if (!copy_from_iter_full(skb_put(*frag, count), count,
|
|
|
|
&msg->msg_iter))
|
2009-08-21 09:25:57 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sent += count;
|
|
|
|
len -= count;
|
|
|
|
|
2012-05-12 00:16:12 +08:00
|
|
|
skb->len += (*frag)->len;
|
|
|
|
skb->data_len += (*frag)->len;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
frag = &(*frag)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sent;
|
2009-08-21 09:25:57 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-01 16:58:57 +08:00
|
|
|
static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
|
2014-06-05 21:22:51 +08:00
|
|
|
struct msghdr *msg, size_t len)
|
2009-08-21 09:25:57 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct sk_buff *skb;
|
2011-10-17 17:19:58 +08:00
|
|
|
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct l2cap_hdr *lh;
|
|
|
|
|
2014-06-05 21:22:51 +08:00
|
|
|
BT_DBG("chan %p psm 0x%2.2x len %zu", chan,
|
|
|
|
__le16_to_cpu(chan->psm), len);
|
2009-08-21 09:25:57 +08:00
|
|
|
|
|
|
|
count = min_t(unsigned int, (conn->mtu - hlen), len);
|
2012-01-20 20:08:03 +08:00
|
|
|
|
2014-06-08 17:22:28 +08:00
|
|
|
skb = chan->ops->alloc_skb(chan, hlen, count,
|
2012-04-07 07:15:47 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
if (IS_ERR(skb))
|
|
|
|
return skb;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
|
|
|
/* Create L2CAP header */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2011-04-14 06:50:45 +08:00
|
|
|
lh->cid = cpu_to_le16(chan->dcid);
|
2012-05-03 15:55:52 +08:00
|
|
|
lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
|
2013-10-12 21:01:26 +08:00
|
|
|
put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-01-13 23:21:43 +08:00
|
|
|
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
|
2009-08-21 09:25:57 +08:00
|
|
|
if (unlikely(err < 0)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2011-11-01 16:58:57 +08:00
|
|
|
static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
|
2014-06-05 21:22:51 +08:00
|
|
|
struct msghdr *msg, size_t len)
|
2009-08-21 09:25:57 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct sk_buff *skb;
|
2012-05-03 15:54:21 +08:00
|
|
|
int err, count;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct l2cap_hdr *lh;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p len %zu", chan, len);
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-05-03 15:54:21 +08:00
|
|
|
count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
|
2012-01-20 20:08:03 +08:00
|
|
|
|
2014-06-08 17:22:28 +08:00
|
|
|
skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count,
|
2012-04-07 07:15:47 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
if (IS_ERR(skb))
|
|
|
|
return skb;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
|
|
|
/* Create L2CAP header */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2011-04-14 06:50:45 +08:00
|
|
|
lh->cid = cpu_to_le16(chan->dcid);
|
2012-05-02 22:56:17 +08:00
|
|
|
lh->len = cpu_to_le16(len);
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-01-13 23:21:43 +08:00
|
|
|
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
|
2009-08-21 09:25:57 +08:00
|
|
|
if (unlikely(err < 0)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2011-09-13 01:00:50 +08:00
|
|
|
static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
|
2012-07-10 20:27:49 +08:00
|
|
|
struct msghdr *msg, size_t len,
|
|
|
|
u16 sdulen)
|
2009-08-21 09:25:57 +08:00
|
|
|
{
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct sk_buff *skb;
|
2011-10-11 18:37:52 +08:00
|
|
|
int err, count, hlen;
|
2009-08-21 09:25:57 +08:00
|
|
|
struct l2cap_hdr *lh;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p len %zu", chan, len);
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2010-05-02 03:15:41 +08:00
|
|
|
if (!conn)
|
|
|
|
return ERR_PTR(-ENOTCONN);
|
|
|
|
|
2012-05-30 00:29:16 +08:00
|
|
|
hlen = __ertm_hdr_size(chan);
|
2011-10-11 18:37:52 +08:00
|
|
|
|
2009-08-21 09:25:58 +08:00
|
|
|
if (sdulen)
|
2011-10-17 17:19:58 +08:00
|
|
|
hlen += L2CAP_SDULEN_SIZE;
|
2009-08-21 09:25:58 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16)
|
2011-10-17 17:19:58 +08:00
|
|
|
hlen += L2CAP_FCS_SIZE;
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2009-08-21 09:25:57 +08:00
|
|
|
count = min_t(unsigned int, (conn->mtu - hlen), len);
|
2012-01-20 20:08:03 +08:00
|
|
|
|
2014-06-08 17:22:28 +08:00
|
|
|
skb = chan->ops->alloc_skb(chan, hlen, count,
|
2012-04-07 07:15:47 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
if (IS_ERR(skb))
|
|
|
|
return skb;
|
2009-08-21 09:25:57 +08:00
|
|
|
|
|
|
|
/* Create L2CAP header */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2011-04-14 06:50:45 +08:00
|
|
|
lh->cid = cpu_to_le16(chan->dcid);
|
2009-08-21 09:25:57 +08:00
|
|
|
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
|
2011-10-17 17:19:56 +08:00
|
|
|
|
2012-05-18 11:53:34 +08:00
|
|
|
/* Control header is populated later */
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
put_unaligned_le32(0, skb_put(skb, L2CAP_EXT_CTRL_SIZE));
|
|
|
|
else
|
|
|
|
put_unaligned_le16(0, skb_put(skb, L2CAP_ENH_CTRL_SIZE));
|
2011-10-17 17:19:56 +08:00
|
|
|
|
2009-08-21 09:25:58 +08:00
|
|
|
if (sdulen)
|
2011-10-17 17:19:58 +08:00
|
|
|
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
|
2009-08-21 09:25:57 +08:00
|
|
|
|
2012-01-13 23:21:43 +08:00
|
|
|
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
|
2009-08-21 09:25:57 +08:00
|
|
|
if (unlikely(err < 0)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
2009-08-21 09:26:00 +08:00
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.fcs = chan->fcs;
|
|
|
|
bt_cb(skb)->l2cap.retries = 0;
|
2009-08-21 09:25:57 +08:00
|
|
|
return skb;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
static int l2cap_segment_sdu(struct l2cap_chan *chan,
|
|
|
|
struct sk_buff_head *seg_queue,
|
|
|
|
struct msghdr *msg, size_t len)
|
2009-08-21 09:25:58 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2012-05-03 00:42:02 +08:00
|
|
|
u16 sdu_len;
|
|
|
|
size_t pdu_len;
|
|
|
|
u8 sar;
|
2009-08-21 09:25:58 +08:00
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
|
2009-08-21 09:25:58 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
/* It is critical that ERTM PDUs fit in a single HCI fragment,
|
|
|
|
* so fragmented skbs are not used. The HCI layer's handling
|
|
|
|
* of fragmented skbs is not compatible with ERTM's queueing.
|
|
|
|
*/
|
2009-08-21 09:25:58 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
/* PDU size is derived from the HCI MTU */
|
|
|
|
pdu_len = chan->conn->mtu;
|
2009-08-21 09:25:58 +08:00
|
|
|
|
2012-10-24 06:24:21 +08:00
|
|
|
/* Constrain PDU size for BR/EDR connections */
|
|
|
|
if (!chan->hs_hcon)
|
|
|
|
pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
|
2012-05-03 00:42:02 +08:00
|
|
|
|
|
|
|
/* Adjust for largest possible L2CAP overhead. */
|
2012-05-26 05:57:05 +08:00
|
|
|
if (chan->fcs)
|
|
|
|
pdu_len -= L2CAP_FCS_SIZE;
|
|
|
|
|
2012-05-30 00:29:16 +08:00
|
|
|
pdu_len -= __ertm_hdr_size(chan);
|
2012-05-03 00:42:02 +08:00
|
|
|
|
|
|
|
/* Remote device may have requested smaller PDUs */
|
|
|
|
pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
|
|
|
|
|
|
|
|
if (len <= pdu_len) {
|
|
|
|
sar = L2CAP_SAR_UNSEGMENTED;
|
|
|
|
sdu_len = 0;
|
|
|
|
pdu_len = len;
|
|
|
|
} else {
|
|
|
|
sar = L2CAP_SAR_START;
|
|
|
|
sdu_len = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (len > 0) {
|
|
|
|
skb = l2cap_create_iframe_pdu(chan, msg, pdu_len, sdu_len);
|
2009-08-21 09:25:58 +08:00
|
|
|
|
|
|
|
if (IS_ERR(skb)) {
|
2012-05-03 00:42:02 +08:00
|
|
|
__skb_queue_purge(seg_queue);
|
2009-08-21 09:25:58 +08:00
|
|
|
return PTR_ERR(skb);
|
|
|
|
}
|
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.sar = sar;
|
2012-05-03 00:42:02 +08:00
|
|
|
__skb_queue_tail(seg_queue, skb);
|
|
|
|
|
|
|
|
len -= pdu_len;
|
2014-08-13 22:01:41 +08:00
|
|
|
if (sdu_len)
|
2012-05-03 00:42:02 +08:00
|
|
|
sdu_len = 0;
|
|
|
|
|
|
|
|
if (len <= pdu_len) {
|
|
|
|
sar = L2CAP_SAR_END;
|
|
|
|
pdu_len = len;
|
|
|
|
} else {
|
|
|
|
sar = L2CAP_SAR_CONTINUE;
|
|
|
|
}
|
2009-08-21 09:25:58 +08:00
|
|
|
}
|
|
|
|
|
2012-05-30 00:29:17 +08:00
|
|
|
return 0;
|
2009-08-21 09:25:58 +08:00
|
|
|
}
|
|
|
|
|
2013-05-31 22:54:51 +08:00
|
|
|
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);
|
|
|
|
|
2014-06-08 17:22:28 +08:00
|
|
|
skb = chan->ops->alloc_skb(chan, hlen, count,
|
2013-05-31 22:54:51 +08:00
|
|
|
msg->msg_flags & MSG_DONTWAIT);
|
|
|
|
if (IS_ERR(skb))
|
|
|
|
return skb;
|
|
|
|
|
|
|
|
/* Create L2CAP header */
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2013-05-31 22:54:51 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
sdu_len = len;
|
2014-08-16 02:06:51 +08:00
|
|
|
pdu_len = chan->remote_mps - L2CAP_SDULEN_SIZE;
|
2013-05-31 22:54:51 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-04-12 03:21:01 +08:00
|
|
|
static void l2cap_le_flowctl_send(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
int sent = 0;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
|
|
|
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
|
|
|
chan->tx_credits--;
|
|
|
|
sent++;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Sent %d credits %u queued %u", sent, chan->tx_credits,
|
|
|
|
skb_queue_len(&chan->tx_q));
|
|
|
|
}
|
|
|
|
|
2014-06-05 21:22:51 +08:00
|
|
|
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
|
2011-04-29 05:50:17 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
int err;
|
2012-05-03 00:42:02 +08:00
|
|
|
struct sk_buff_head seg_queue;
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2013-11-05 17:46:33 +08:00
|
|
|
if (!chan->conn)
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2011-04-29 05:50:17 +08:00
|
|
|
/* Connectionless channel */
|
2011-05-03 04:13:55 +08:00
|
|
|
if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
|
2014-06-05 21:22:51 +08:00
|
|
|
skb = l2cap_create_connless_pdu(chan, msg, len);
|
2011-04-29 05:50:17 +08:00
|
|
|
if (IS_ERR(skb))
|
|
|
|
return PTR_ERR(skb);
|
|
|
|
|
2014-02-26 00:16:22 +08:00
|
|
|
/* Channel lock is released before requesting new skb and then
|
|
|
|
* reacquired thus we need to recheck channel state.
|
|
|
|
*/
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2011-04-29 05:50:17 +08:00
|
|
|
l2cap_do_send(chan, skb);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (chan->mode) {
|
2013-05-17 17:49:23 +08:00
|
|
|
case L2CAP_MODE_LE_FLOWCTL:
|
2013-05-31 22:54:51 +08:00
|
|
|
/* Check outgoing MTU */
|
|
|
|
if (len > chan->omtu)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
__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);
|
|
|
|
|
2017-04-12 03:21:01 +08:00
|
|
|
l2cap_le_flowctl_send(chan);
|
2013-05-31 22:54:51 +08:00
|
|
|
|
|
|
|
if (!chan->tx_credits)
|
|
|
|
chan->ops->suspend(chan);
|
|
|
|
|
|
|
|
err = len;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2013-12-05 15:45:01 +08:00
|
|
|
case L2CAP_MODE_BASIC:
|
2011-04-29 05:50:17 +08:00
|
|
|
/* Check outgoing MTU */
|
|
|
|
if (len > chan->omtu)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
/* Create a basic PDU */
|
2014-06-05 21:22:51 +08:00
|
|
|
skb = l2cap_create_basic_pdu(chan, msg, len);
|
2011-04-29 05:50:17 +08:00
|
|
|
if (IS_ERR(skb))
|
|
|
|
return PTR_ERR(skb);
|
|
|
|
|
2014-02-26 00:16:22 +08:00
|
|
|
/* Channel lock is released before requesting new skb and then
|
|
|
|
* reacquired thus we need to recheck channel state.
|
|
|
|
*/
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2011-04-29 05:50:17 +08:00
|
|
|
l2cap_do_send(chan, skb);
|
|
|
|
err = len;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
case L2CAP_MODE_STREAMING:
|
2012-05-03 00:42:02 +08:00
|
|
|
/* Check outgoing MTU */
|
|
|
|
if (len > chan->omtu) {
|
|
|
|
err = -EMSGSIZE;
|
|
|
|
break;
|
|
|
|
}
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
__skb_queue_head_init(&seg_queue);
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
/* Do segmentation before calling in to the state machine,
|
|
|
|
* since it's possible to block while waiting for memory
|
|
|
|
* allocation.
|
|
|
|
*/
|
|
|
|
err = l2cap_segment_sdu(chan, &seg_queue, msg, len);
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
/* The channel could have been closed while segmenting,
|
|
|
|
* check that it is still connected.
|
|
|
|
*/
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
__skb_queue_purge(&seg_queue);
|
|
|
|
err = -ENOTCONN;
|
2011-04-29 05:50:17 +08:00
|
|
|
}
|
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
if (err)
|
2011-04-29 05:50:17 +08:00
|
|
|
break;
|
|
|
|
|
2012-05-18 11:53:33 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_ERTM)
|
2012-05-22 00:58:22 +08:00
|
|
|
l2cap_tx(chan, NULL, &seg_queue, L2CAP_EV_DATA_REQUEST);
|
2012-05-18 11:53:33 +08:00
|
|
|
else
|
2012-05-22 00:58:22 +08:00
|
|
|
l2cap_streaming_send(chan, &seg_queue);
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
err = len;
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-03 00:42:02 +08:00
|
|
|
/* If the skbs were not queued for sending, they'll still be in
|
|
|
|
* seg_queue and need to be purged.
|
|
|
|
*/
|
|
|
|
__skb_queue_purge(&seg_queue);
|
2011-04-29 05:50:17 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BT_DBG("bad state %1.1x", chan->mode);
|
|
|
|
err = -EBADFD;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_send);
|
2011-04-29 05:50:17 +08:00
|
|
|
|
2012-05-18 11:53:42 +08:00
|
|
|
static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
|
|
|
|
{
|
2012-05-18 11:53:44 +08:00
|
|
|
struct l2cap_ctrl control;
|
|
|
|
u16 seq;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p, txseq %u", chan, txseq);
|
2012-05-18 11:53:44 +08:00
|
|
|
|
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
|
|
|
control.super = L2CAP_SUPER_SREJ;
|
|
|
|
|
|
|
|
for (seq = chan->expected_tx_seq; seq != txseq;
|
|
|
|
seq = __next_seq(chan, seq)) {
|
|
|
|
if (!l2cap_ertm_seq_in_queue(&chan->srej_q, seq)) {
|
|
|
|
control.reqseq = seq;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
|
|
|
l2cap_seq_list_append(&chan->srej_list, seq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->expected_tx_seq = __next_seq(chan, txseq);
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_send_srej_tail(struct l2cap_chan *chan)
|
|
|
|
{
|
2012-05-18 11:53:44 +08:00
|
|
|
struct l2cap_ctrl control;
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
if (chan->srej_list.tail == L2CAP_SEQ_LIST_CLEAR)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
|
|
|
control.super = L2CAP_SUPER_SREJ;
|
|
|
|
control.reqseq = chan->srej_list.tail;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_send_srej_list(struct l2cap_chan *chan, u16 txseq)
|
|
|
|
{
|
2012-05-18 11:53:44 +08:00
|
|
|
struct l2cap_ctrl control;
|
|
|
|
u16 initial_head;
|
|
|
|
u16 seq;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p, txseq %u", chan, txseq);
|
2012-05-18 11:53:44 +08:00
|
|
|
|
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
|
|
|
control.super = L2CAP_SUPER_SREJ;
|
|
|
|
|
|
|
|
/* Capture initial list head to allow only one pass through the list. */
|
|
|
|
initial_head = chan->srej_list.head;
|
|
|
|
|
|
|
|
do {
|
|
|
|
seq = l2cap_seq_list_pop(&chan->srej_list);
|
|
|
|
if (seq == txseq || seq == L2CAP_SEQ_LIST_CLEAR)
|
|
|
|
break;
|
|
|
|
|
|
|
|
control.reqseq = seq;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
|
|
|
l2cap_seq_list_append(&chan->srej_list, seq);
|
|
|
|
} while (chan->srej_list.head != initial_head);
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:32 +08:00
|
|
|
static void l2cap_process_reqseq(struct l2cap_chan *chan, u16 reqseq)
|
|
|
|
{
|
|
|
|
struct sk_buff *acked_skb;
|
|
|
|
u16 ackseq;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("chan %p, reqseq %u", chan, reqseq);
|
2012-05-18 11:53:32 +08:00
|
|
|
|
|
|
|
if (chan->unacked_frames == 0 || reqseq == chan->expected_ack_seq)
|
|
|
|
return;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("expected_ack_seq %u, unacked_frames %u",
|
2012-05-18 11:53:32 +08:00
|
|
|
chan->expected_ack_seq, chan->unacked_frames);
|
|
|
|
|
|
|
|
for (ackseq = chan->expected_ack_seq; ackseq != reqseq;
|
|
|
|
ackseq = __next_seq(chan, ackseq)) {
|
|
|
|
|
|
|
|
acked_skb = l2cap_ertm_seq_in_queue(&chan->tx_q, ackseq);
|
|
|
|
if (acked_skb) {
|
|
|
|
skb_unlink(acked_skb, &chan->tx_q);
|
|
|
|
kfree_skb(acked_skb);
|
|
|
|
chan->unacked_frames--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->expected_ack_seq = reqseq;
|
|
|
|
|
|
|
|
if (chan->unacked_frames == 0)
|
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("unacked_frames %u", chan->unacked_frames);
|
2012-05-18 11:53:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_abort_rx_srej_sent(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
chan->expected_tx_seq = chan->buffer_seq;
|
|
|
|
l2cap_seq_list_clear(&chan->srej_list);
|
|
|
|
skb_queue_purge(&chan->srej_q);
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_RECV;
|
|
|
|
}
|
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
static void l2cap_tx_state_xmit(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff_head *skbs, u8 event)
|
2012-05-18 11:53:32 +08:00
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
|
|
|
|
event);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case L2CAP_EV_DATA_REQUEST:
|
|
|
|
if (chan->tx_send_head == NULL)
|
|
|
|
chan->tx_send_head = skb_peek(skbs);
|
|
|
|
|
|
|
|
skb_queue_splice_tail_init(skbs, &chan->tx_q);
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_LOCAL_BUSY_DETECTED:
|
|
|
|
BT_DBG("Enter LOCAL_BUSY");
|
|
|
|
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
|
|
|
|
/* The SREJ_SENT state must be aborted if we are to
|
|
|
|
* enter the LOCAL_BUSY state.
|
|
|
|
*/
|
|
|
|
l2cap_abort_rx_srej_sent(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_send_ack(chan);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_LOCAL_BUSY_CLEAR:
|
|
|
|
BT_DBG("Exit LOCAL_BUSY");
|
|
|
|
clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
|
|
|
|
struct l2cap_ctrl local_control;
|
|
|
|
|
|
|
|
memset(&local_control, 0, sizeof(local_control));
|
|
|
|
local_control.sframe = 1;
|
|
|
|
local_control.super = L2CAP_SUPER_RR;
|
|
|
|
local_control.poll = 1;
|
|
|
|
local_control.reqseq = chan->buffer_seq;
|
2012-05-18 11:53:35 +08:00
|
|
|
l2cap_send_sframe(chan, &local_control);
|
2012-05-18 11:53:32 +08:00
|
|
|
|
|
|
|
chan->retry_count = 1;
|
|
|
|
__set_monitor_timer(chan);
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
|
|
|
|
l2cap_process_reqseq(chan, control->reqseq);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_EXPLICIT_POLL:
|
|
|
|
l2cap_send_rr_or_rnr(chan, 1);
|
|
|
|
chan->retry_count = 1;
|
|
|
|
__set_monitor_timer(chan);
|
|
|
|
__clear_ack_timer(chan);
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RETRANS_TO:
|
|
|
|
l2cap_send_rr_or_rnr(chan, 1);
|
|
|
|
chan->retry_count = 1;
|
|
|
|
__set_monitor_timer(chan);
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_FBIT:
|
|
|
|
/* Nothing to process */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
static void l2cap_tx_state_wait_f(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff_head *skbs, u8 event)
|
2012-05-18 11:53:32 +08:00
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p, skbs %p, event %d", chan, control, skbs,
|
|
|
|
event);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case L2CAP_EV_DATA_REQUEST:
|
|
|
|
if (chan->tx_send_head == NULL)
|
|
|
|
chan->tx_send_head = skb_peek(skbs);
|
|
|
|
/* Queue data, but don't send. */
|
|
|
|
skb_queue_splice_tail_init(skbs, &chan->tx_q);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_LOCAL_BUSY_DETECTED:
|
|
|
|
BT_DBG("Enter LOCAL_BUSY");
|
|
|
|
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
|
|
|
|
/* The SREJ_SENT state must be aborted if we are to
|
|
|
|
* enter the LOCAL_BUSY state.
|
|
|
|
*/
|
|
|
|
l2cap_abort_rx_srej_sent(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_send_ack(chan);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_LOCAL_BUSY_CLEAR:
|
|
|
|
BT_DBG("Exit LOCAL_BUSY");
|
|
|
|
clear_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (test_bit(CONN_RNR_SENT, &chan->conn_state)) {
|
|
|
|
struct l2cap_ctrl local_control;
|
|
|
|
memset(&local_control, 0, sizeof(local_control));
|
|
|
|
local_control.sframe = 1;
|
|
|
|
local_control.super = L2CAP_SUPER_RR;
|
|
|
|
local_control.poll = 1;
|
|
|
|
local_control.reqseq = chan->buffer_seq;
|
2012-05-18 11:53:35 +08:00
|
|
|
l2cap_send_sframe(chan, &local_control);
|
2012-05-18 11:53:32 +08:00
|
|
|
|
|
|
|
chan->retry_count = 1;
|
|
|
|
__set_monitor_timer(chan);
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_WAIT_F;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_REQSEQ_AND_FBIT:
|
|
|
|
l2cap_process_reqseq(chan, control->reqseq);
|
|
|
|
|
|
|
|
/* Fall through */
|
|
|
|
|
|
|
|
case L2CAP_EV_RECV_FBIT:
|
|
|
|
if (control && control->final) {
|
|
|
|
__clear_monitor_timer(chan);
|
|
|
|
if (chan->unacked_frames > 0)
|
|
|
|
__set_retrans_timer(chan);
|
|
|
|
chan->retry_count = 0;
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_XMIT;
|
|
|
|
BT_DBG("recv fbit tx_state 0x2.2%x", chan->tx_state);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_EXPLICIT_POLL:
|
|
|
|
/* Ignore */
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_MONITOR_TO:
|
|
|
|
if (chan->max_tx == 0 || chan->retry_count < chan->max_tx) {
|
|
|
|
l2cap_send_rr_or_rnr(chan, 1);
|
|
|
|
__set_monitor_timer(chan);
|
|
|
|
chan->retry_count++;
|
|
|
|
} else {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNABORTED);
|
2012-05-18 11:53:32 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-22 00:58:22 +08:00
|
|
|
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff_head *skbs, u8 event)
|
2012-05-18 11:53:32 +08:00
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p, skbs %p, event %d, state %d",
|
|
|
|
chan, control, skbs, event, chan->tx_state);
|
|
|
|
|
|
|
|
switch (chan->tx_state) {
|
|
|
|
case L2CAP_TX_STATE_XMIT:
|
2012-05-22 00:58:22 +08:00
|
|
|
l2cap_tx_state_xmit(chan, control, skbs, event);
|
2012-05-18 11:53:32 +08:00
|
|
|
break;
|
|
|
|
case L2CAP_TX_STATE_WAIT_F:
|
2012-05-22 00:58:22 +08:00
|
|
|
l2cap_tx_state_wait_f(chan, control, skbs, event);
|
2012-05-18 11:53:32 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Ignore event */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:37 +08:00
|
|
|
static void l2cap_pass_to_tx(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
2012-05-21 20:47:46 +08:00
|
|
|
l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_REQSEQ_AND_FBIT);
|
2012-05-18 11:53:37 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:46 +08:00
|
|
|
static void l2cap_pass_to_tx_fbit(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
2012-05-21 20:47:46 +08:00
|
|
|
l2cap_tx(chan, control, NULL, L2CAP_EV_RECV_FBIT);
|
2012-05-18 11:53:46 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Copy frame to all raw sockets on that connection */
|
|
|
|
static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
BT_DBG("conn %p", conn);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
2011-05-03 04:13:55 +08:00
|
|
|
if (chan->chan_type != L2CAP_CHAN_RAW)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
2013-10-22 04:22:25 +08:00
|
|
|
/* Don't send frame to the channel it came from */
|
2015-03-31 04:21:01 +08:00
|
|
|
if (bt_cb(skb)->l2cap.chan == chan)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2013-10-22 04:22:25 +08:00
|
|
|
|
2012-05-29 06:18:14 +08:00
|
|
|
nskb = skb_clone(skb, GFP_KERNEL);
|
2009-04-20 12:31:08 +08:00
|
|
|
if (!nskb)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2012-05-28 09:27:51 +08:00
|
|
|
if (chan->ops->recv(chan, nskb))
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(nskb);
|
|
|
|
}
|
2011-12-17 20:56:45 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---- L2CAP signalling commands ---- */
|
2012-07-10 20:27:49 +08:00
|
|
|
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
|
|
|
|
u8 ident, u16 dlen, void *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb, **frag;
|
|
|
|
struct l2cap_cmd_hdr *cmd;
|
|
|
|
struct l2cap_hdr *lh;
|
|
|
|
int len, count;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %u",
|
|
|
|
conn, code, ident, dlen);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-06-03 04:30:40 +08:00
|
|
|
if (conn->mtu < L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE)
|
|
|
|
return NULL;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
|
|
|
|
count = min_t(unsigned int, conn->mtu, len);
|
|
|
|
|
2012-05-29 06:18:14 +08:00
|
|
|
skb = bt_skb_alloc(count, GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!skb)
|
|
|
|
return NULL;
|
|
|
|
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
lh = skb_put(skb, L2CAP_HDR_SIZE);
|
2007-03-26 11:12:50 +08:00
|
|
|
lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
|
2011-02-12 05:28:54 +08:00
|
|
|
|
|
|
|
if (conn->hcon->type == LE_LINK)
|
2014-03-13 01:52:35 +08:00
|
|
|
lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING);
|
2011-02-12 05:28:54 +08:00
|
|
|
else
|
2014-03-13 01:52:35 +08:00
|
|
|
lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
networking: make skb_put & friends return void pointers
It seems like a historic accident that these return unsigned char *,
and in many places that means casts are required, more often than not.
Make these functions (skb_put, __skb_put and pskb_put) return void *
and remove all the casts across the tree, adding a (u8 *) cast only
where the unsigned char pointer was used directly, all done with the
following spatch:
@@
expression SKB, LEN;
typedef u8;
identifier fn = { skb_put, __skb_put };
@@
- *(fn(SKB, LEN))
+ *(u8 *)fn(SKB, LEN)
@@
expression E, SKB, LEN;
identifier fn = { skb_put, __skb_put };
type T;
@@
- E = ((T *)(fn(SKB, LEN)))
+ E = fn(SKB, LEN)
which actually doesn't cover pskb_put since there are only three
users overall.
A handful of stragglers were converted manually, notably a macro in
drivers/isdn/i4l/isdn_bsdcomp.c and, oddly enough, one of the many
instances in net/bluetooth/hci_sock.c. In the former file, I also
had to fix one whitespace problem spatch introduced.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:21 +08:00
|
|
|
cmd = skb_put(skb, L2CAP_CMD_HDR_SIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
cmd->code = code;
|
|
|
|
cmd->ident = ident;
|
2007-03-26 11:12:50 +08:00
|
|
|
cmd->len = cpu_to_le16(dlen);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dlen) {
|
|
|
|
count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(skb, data, count);
|
2005-04-17 06:20:36 +08:00
|
|
|
data += count;
|
|
|
|
}
|
|
|
|
|
|
|
|
len -= skb->len;
|
|
|
|
|
|
|
|
/* Continuation fragments (no L2CAP header) */
|
|
|
|
frag = &skb_shinfo(skb)->frag_list;
|
|
|
|
while (len) {
|
|
|
|
count = min_t(unsigned int, conn->mtu, len);
|
|
|
|
|
2012-05-29 06:18:14 +08:00
|
|
|
*frag = bt_skb_alloc(count, GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!*frag)
|
|
|
|
goto fail;
|
|
|
|
|
networking: introduce and use skb_put_data()
A common pattern with skb_put() is to just want to memcpy()
some data into the new space, introduce skb_put_data() for
this.
An spatch similar to the one for skb_put_zero() converts many
of the places using it:
@@
identifier p, p2;
expression len, skb, data;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_data(skb, data, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_data(skb, data, len);
)
(
p2 = (t2)p;
-memcpy(p2, data, len);
|
-memcpy(p, data, len);
)
@@
type t, t2;
identifier p, p2;
expression skb, data;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_data(skb, data, sizeof(t));
)
(
p2 = (t2)p;
-memcpy(p2, data, sizeof(*p));
|
-memcpy(p, data, sizeof(*p));
)
@@
expression skb, len, data;
@@
-memcpy(skb_put(skb, len), data, len);
+skb_put_data(skb, data, len);
(again, manually post-processed to retain some comments)
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:20 +08:00
|
|
|
skb_put_data(*frag, data, count);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
len -= count;
|
|
|
|
data += count;
|
|
|
|
|
|
|
|
frag = &(*frag)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return skb;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
|
|
|
|
unsigned long *val)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_opt *opt = *ptr;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = L2CAP_CONF_OPT_SIZE + opt->len;
|
|
|
|
*ptr += len;
|
|
|
|
|
|
|
|
*type = opt->type;
|
|
|
|
*olen = opt->len;
|
|
|
|
|
|
|
|
switch (opt->len) {
|
|
|
|
case 1:
|
|
|
|
*val = *((u8 *) opt->val);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2010-10-17 06:29:47 +08:00
|
|
|
*val = get_unaligned_le16(opt->val);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2010-10-17 06:29:47 +08:00
|
|
|
*val = get_unaligned_le32(opt->val);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*val = (unsigned long) opt->val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("type 0x%2.2x len %u val 0x%lx", *type, opt->len, *val);
|
2005-04-17 06:20:36 +08:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2017-09-10 05:15:59 +08:00
|
|
|
static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val, size_t size)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_opt *opt = *ptr;
|
|
|
|
|
2012-07-10 20:27:49 +08:00
|
|
|
BT_DBG("type 0x%2.2x len %u val 0x%lx", type, len, val);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-09-10 05:15:59 +08:00
|
|
|
if (size < L2CAP_CONF_OPT_SIZE + len)
|
|
|
|
return;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
opt->type = type;
|
|
|
|
opt->len = len;
|
|
|
|
|
|
|
|
switch (len) {
|
|
|
|
case 1:
|
|
|
|
*((u8 *) opt->val) = val;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
2010-10-19 00:25:53 +08:00
|
|
|
put_unaligned_le16(val, opt->val);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
2010-10-19 00:25:53 +08:00
|
|
|
put_unaligned_le32(val, opt->val);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
memcpy(opt->val, (void *) val, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptr += L2CAP_CONF_OPT_SIZE + len;
|
|
|
|
}
|
|
|
|
|
2017-09-10 05:15:59 +08:00
|
|
|
static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan, size_t size)
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_efs efs;
|
|
|
|
|
2011-11-16 16:32:21 +08:00
|
|
|
switch (chan->mode) {
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
efs.id = chan->local_id;
|
|
|
|
efs.stype = chan->local_stype;
|
|
|
|
efs.msdu = cpu_to_le16(chan->local_msdu);
|
|
|
|
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
|
2014-03-13 01:52:35 +08:00
|
|
|
efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
|
|
|
|
efs.flush_to = cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO);
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
efs.id = 1;
|
|
|
|
efs.stype = L2CAP_SERV_BESTEFFORT;
|
|
|
|
efs.msdu = cpu_to_le16(chan->local_msdu);
|
|
|
|
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
|
|
|
|
efs.acc_lat = 0;
|
|
|
|
efs.flush_to = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &efs, size);
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
}
|
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
static void l2cap_ack_timeout(struct work_struct *work)
|
2010-05-02 03:15:39 +08:00
|
|
|
{
|
2011-06-24 06:29:58 +08:00
|
|
|
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
|
2012-05-18 11:53:51 +08:00
|
|
|
ack_timer.work);
|
|
|
|
u16 frames_to_ack;
|
2010-05-02 03:15:39 +08:00
|
|
|
|
2011-12-23 02:56:05 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2012-05-18 11:53:51 +08:00
|
|
|
frames_to_ack = __seq_offset(chan, chan->buffer_seq,
|
|
|
|
chan->last_acked_seq);
|
2012-02-22 23:11:56 +08:00
|
|
|
|
2012-05-18 11:53:51 +08:00
|
|
|
if (frames_to_ack)
|
|
|
|
l2cap_send_rr_or_rnr(chan, 0);
|
2012-01-11 17:59:49 +08:00
|
|
|
|
2012-05-18 11:53:51 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-01-11 17:59:49 +08:00
|
|
|
l2cap_chan_put(chan);
|
2010-05-02 03:15:39 +08:00
|
|
|
}
|
|
|
|
|
2012-05-29 18:59:01 +08:00
|
|
|
int l2cap_ertm_init(struct l2cap_chan *chan)
|
2009-10-03 13:34:36 +08:00
|
|
|
{
|
2012-04-12 01:48:42 +08:00
|
|
|
int err;
|
|
|
|
|
2012-04-28 07:50:48 +08:00
|
|
|
chan->next_tx_seq = 0;
|
|
|
|
chan->expected_tx_seq = 0;
|
2011-03-26 06:58:34 +08:00
|
|
|
chan->expected_ack_seq = 0;
|
2011-04-01 11:38:50 +08:00
|
|
|
chan->unacked_frames = 0;
|
2011-03-26 06:58:34 +08:00
|
|
|
chan->buffer_seq = 0;
|
2011-04-01 11:38:50 +08:00
|
|
|
chan->frames_sent = 0;
|
2012-04-28 07:50:48 +08:00
|
|
|
chan->last_acked_seq = 0;
|
|
|
|
chan->sdu = NULL;
|
|
|
|
chan->sdu_last_frag = NULL;
|
|
|
|
chan->sdu_len = 0;
|
|
|
|
|
2012-05-15 05:49:27 +08:00
|
|
|
skb_queue_head_init(&chan->tx_q);
|
|
|
|
|
2013-10-06 02:47:44 +08:00
|
|
|
chan->local_amp_id = AMP_ID_BREDR;
|
|
|
|
chan->move_id = AMP_ID_BREDR;
|
2012-10-24 06:24:06 +08:00
|
|
|
chan->move_state = L2CAP_MOVE_STABLE;
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_NONE;
|
|
|
|
|
2012-04-28 07:50:48 +08:00
|
|
|
if (chan->mode != L2CAP_MODE_ERTM)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_RECV;
|
|
|
|
chan->tx_state = L2CAP_TX_STATE_XMIT;
|
2009-10-03 13:34:36 +08:00
|
|
|
|
2011-06-24 06:29:58 +08:00
|
|
|
INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
|
|
|
|
INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
|
|
|
|
INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout);
|
2009-10-03 13:34:36 +08:00
|
|
|
|
2011-03-26 07:36:10 +08:00
|
|
|
skb_queue_head_init(&chan->srej_q);
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2012-04-12 01:48:42 +08:00
|
|
|
err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2012-05-18 07:20:14 +08:00
|
|
|
err = l2cap_seq_list_init(&chan->retrans_list, chan->remote_tx_win);
|
|
|
|
if (err < 0)
|
|
|
|
l2cap_seq_list_free(&chan->srej_list);
|
|
|
|
|
|
|
|
return err;
|
2009-10-03 13:34:36 +08:00
|
|
|
}
|
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
|
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
if (l2cap_mode_supported(mode, remote_feat_mask))
|
|
|
|
return mode;
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
|
|
return L2CAP_MODE_BASIC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-02 13:59:22 +08:00
|
|
|
static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
|
2011-10-11 18:37:42 +08:00
|
|
|
{
|
2014-12-02 16:09:26 +08:00
|
|
|
return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
|
|
|
|
(conn->feat_mask & L2CAP_FEAT_EXT_WINDOW));
|
2011-10-11 18:37:42 +08:00
|
|
|
}
|
|
|
|
|
2013-10-02 13:59:22 +08:00
|
|
|
static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
{
|
2014-12-02 16:09:26 +08:00
|
|
|
return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
|
|
|
|
(conn->feat_mask & L2CAP_FEAT_EXT_FLOW));
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:20 +08:00
|
|
|
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_conf_rfc *rfc)
|
|
|
|
{
|
2013-10-06 02:47:44 +08:00
|
|
|
if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) {
|
2012-10-24 06:24:20 +08:00
|
|
|
u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
|
|
|
|
|
|
|
|
/* Class 1 devices have must have ERTM timeouts
|
|
|
|
* exceeding the Link Supervision Timeout. The
|
|
|
|
* default Link Supervision Timeout for AMP
|
|
|
|
* controllers is 10 seconds.
|
|
|
|
*
|
|
|
|
* Class 1 devices use 0xffffffff for their
|
|
|
|
* best-effort flush timeout, so the clamping logic
|
|
|
|
* will result in a timeout that meets the above
|
|
|
|
* requirement. ERTM timeouts are 16-bit values, so
|
|
|
|
* the maximum timeout is 65.535 seconds.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Convert timeout to milliseconds and round */
|
|
|
|
ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000);
|
|
|
|
|
|
|
|
/* This is the recommended formula for class 2 devices
|
|
|
|
* that start ERTM timers when packets are sent to the
|
|
|
|
* controller.
|
|
|
|
*/
|
|
|
|
ertm_to = 3 * ertm_to + 500;
|
|
|
|
|
|
|
|
if (ertm_to > 0xffff)
|
|
|
|
ertm_to = 0xffff;
|
|
|
|
|
|
|
|
rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
|
|
|
|
rfc->monitor_timeout = rfc->retrans_timeout;
|
|
|
|
} else {
|
2014-03-13 01:52:35 +08:00
|
|
|
rfc->retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
|
|
|
|
rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
|
2012-10-24 06:24:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-11 18:37:42 +08:00
|
|
|
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
|
2013-10-02 13:59:22 +08:00
|
|
|
__l2cap_ews_supported(chan->conn)) {
|
2011-10-11 18:37:42 +08:00
|
|
|
/* use extended control field */
|
|
|
|
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
2011-10-17 17:19:57 +08:00
|
|
|
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
|
|
|
|
} else {
|
2011-10-11 18:37:42 +08:00
|
|
|
chan->tx_win = min_t(u16, chan->tx_win,
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_DEFAULT_TX_WINDOW);
|
2011-10-17 17:19:57 +08:00
|
|
|
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
|
|
|
|
}
|
2012-07-10 20:47:07 +08:00
|
|
|
chan->ack_win = chan->tx_win;
|
2011-10-11 18:37:42 +08:00
|
|
|
}
|
|
|
|
|
2017-09-10 05:15:59 +08:00
|
|
|
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_req *req = data;
|
2011-04-14 04:20:49 +08:00
|
|
|
struct l2cap_conf_rfc rfc = { .mode = chan->mode };
|
2005-04-17 06:20:36 +08:00
|
|
|
void *ptr = req->data;
|
2017-09-10 05:15:59 +08:00
|
|
|
void *endptr = data + data_size;
|
2011-10-17 17:19:59 +08:00
|
|
|
u16 size;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-05 02:59:54 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-26 01:16:54 +08:00
|
|
|
if (chan->num_conf_req || chan->num_conf_rsp)
|
2009-07-05 02:06:24 +08:00
|
|
|
goto done;
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
switch (chan->mode) {
|
2009-07-05 02:06:24 +08:00
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
case L2CAP_MODE_ERTM:
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
|
2010-06-04 05:43:28 +08:00
|
|
|
break;
|
|
|
|
|
2013-10-02 13:59:22 +08:00
|
|
|
if (__l2cap_efs_supported(chan->conn))
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
|
|
|
|
2010-06-10 03:39:05 +08:00
|
|
|
/* fall through */
|
2009-07-05 02:06:24 +08:00
|
|
|
default:
|
2011-04-14 07:23:55 +08:00
|
|
|
chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask);
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2011-04-14 04:20:49 +08:00
|
|
|
if (chan->imtu != L2CAP_DEFAULT_MTU)
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
|
2011-01-25 02:01:43 +08:00
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
switch (chan->mode) {
|
2009-05-03 14:07:53 +08:00
|
|
|
case L2CAP_MODE_BASIC:
|
2014-07-09 17:53:35 +08:00
|
|
|
if (disable_ertm)
|
|
|
|
break;
|
|
|
|
|
2011-04-14 07:23:55 +08:00
|
|
|
if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
|
2012-10-06 17:07:01 +08:00
|
|
|
!(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
|
2010-08-04 10:49:29 +08:00
|
|
|
break;
|
|
|
|
|
2010-06-09 07:05:31 +08:00
|
|
|
rfc.mode = L2CAP_MODE_BASIC;
|
|
|
|
rfc.txwin_size = 0;
|
|
|
|
rfc.max_transmit = 0;
|
|
|
|
rfc.retrans_timeout = 0;
|
|
|
|
rfc.monitor_timeout = 0;
|
|
|
|
rfc.max_pdu_size = 0;
|
|
|
|
|
2010-08-04 10:49:29 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &rfc, endptr - ptr);
|
2009-05-03 14:07:53 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
rfc.mode = L2CAP_MODE_ERTM;
|
2011-04-14 02:57:03 +08:00
|
|
|
rfc.max_transmit = chan->max_tx;
|
2012-10-24 06:24:20 +08:00
|
|
|
|
|
|
|
__l2cap_set_ertm_timeouts(chan, &rfc);
|
2011-10-17 17:19:59 +08:00
|
|
|
|
|
|
|
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
|
|
|
|
L2CAP_FCS_SIZE);
|
2011-10-17 17:19:59 +08:00
|
|
|
rfc.max_pdu_size = cpu_to_le16(size);
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2011-10-11 18:37:42 +08:00
|
|
|
l2cap_txwin_setup(chan);
|
|
|
|
|
|
|
|
rfc.txwin_size = min_t(u16, chan->tx_win,
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_DEFAULT_TX_WINDOW);
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2010-08-04 10:49:29 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &rfc, endptr - ptr);
|
2010-08-04 10:49:29 +08:00
|
|
|
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
|
2011-10-11 18:37:42 +08:00
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
|
2017-09-10 05:15:59 +08:00
|
|
|
chan->tx_win, endptr - ptr);
|
2012-11-29 23:46:06 +08:00
|
|
|
|
|
|
|
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
|
|
|
|
if (chan->fcs == L2CAP_FCS_NONE ||
|
2012-11-29 23:46:08 +08:00
|
|
|
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
|
2012-11-29 23:46:06 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
|
2017-09-10 05:15:59 +08:00
|
|
|
chan->fcs, endptr - ptr);
|
2012-11-29 23:46:06 +08:00
|
|
|
}
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_STREAMING:
|
2012-05-18 11:53:53 +08:00
|
|
|
l2cap_txwin_setup(chan);
|
2009-07-05 02:06:24 +08:00
|
|
|
rfc.mode = L2CAP_MODE_STREAMING;
|
|
|
|
rfc.txwin_size = 0;
|
|
|
|
rfc.max_transmit = 0;
|
|
|
|
rfc.retrans_timeout = 0;
|
|
|
|
rfc.monitor_timeout = 0;
|
2011-10-17 17:19:59 +08:00
|
|
|
|
|
|
|
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
|
|
|
|
L2CAP_FCS_SIZE);
|
2011-10-17 17:19:59 +08:00
|
|
|
rfc.max_pdu_size = cpu_to_le16(size);
|
2009-05-03 14:07:53 +08:00
|
|
|
|
2010-08-04 10:49:29 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &rfc, endptr - ptr);
|
2010-08-04 10:49:29 +08:00
|
|
|
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
|
Bluetooth: EFS: add efs option in L2CAP conf req
Add Extended Flow Specification option when building L2CAP
Configuration Request. EFS is added if both the local and
remote L2CAP entities have indicated support for the
Extended Flow Specification for BR/EDR.
...
< ACL data: handle 1 flags 0x00 dlen 10
L2CAP(s): Info req: type 2
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x01f8
Enhanced Retransmission mode
Streaming mode
FCS Option
Extended Flow Specification
Fixed Channels
Extended Window Size
...
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
...
Based upon haijun.liu <haijun.liu@atheros.com> series of patches
(sent Sun, 22 Aug 2010)
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-13 21:18:55 +08:00
|
|
|
|
2012-11-29 23:46:06 +08:00
|
|
|
if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
|
|
|
|
if (chan->fcs == L2CAP_FCS_NONE ||
|
2012-11-29 23:46:08 +08:00
|
|
|
test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
|
2012-11-29 23:46:06 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
|
2017-09-10 05:15:59 +08:00
|
|
|
chan->fcs, endptr - ptr);
|
2012-11-29 23:46:06 +08:00
|
|
|
}
|
2009-05-03 14:07:53 +08:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
req->dcid = cpu_to_le16(chan->dcid);
|
2014-03-13 01:52:35 +08:00
|
|
|
req->flags = cpu_to_le16(0);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ptr - data;
|
|
|
|
}
|
|
|
|
|
2017-09-10 05:15:59 +08:00
|
|
|
static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-05-24 20:27:19 +08:00
|
|
|
struct l2cap_conf_rsp *rsp = data;
|
|
|
|
void *ptr = rsp->data;
|
2017-09-10 05:15:59 +08:00
|
|
|
void *endptr = data + data_size;
|
2011-03-26 01:16:54 +08:00
|
|
|
void *req = chan->conf_req;
|
|
|
|
int len = chan->conf_len;
|
2007-05-24 20:27:19 +08:00
|
|
|
int type, hint, olen;
|
|
|
|
unsigned long val;
|
2007-10-20 19:39:51 +08:00
|
|
|
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
|
2011-10-17 19:35:30 +08:00
|
|
|
struct l2cap_conf_efs efs;
|
|
|
|
u8 remote_efs = 0;
|
2007-10-20 19:37:06 +08:00
|
|
|
u16 mtu = L2CAP_DEFAULT_MTU;
|
2007-05-24 20:27:19 +08:00
|
|
|
u16 result = L2CAP_CONF_SUCCESS;
|
2011-10-17 17:19:59 +08:00
|
|
|
u16 size;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-26 01:16:54 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2006-11-19 05:15:00 +08:00
|
|
|
|
2007-05-24 20:27:19 +08:00
|
|
|
while (len >= L2CAP_CONF_OPT_SIZE) {
|
|
|
|
len -= l2cap_get_conf_opt(&req, &type, &olen, &val);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-20 12:31:07 +08:00
|
|
|
hint = type & L2CAP_CONF_HINT;
|
2009-05-03 09:57:55 +08:00
|
|
|
type &= L2CAP_CONF_MASK;
|
2007-05-24 20:27:19 +08:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case L2CAP_CONF_MTU:
|
2007-10-20 19:37:06 +08:00
|
|
|
mtu = val;
|
2007-05-24 20:27:19 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_FLUSH_TO:
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->flush_to = val;
|
2007-05-24 20:27:19 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_QOS:
|
|
|
|
break;
|
|
|
|
|
2007-10-20 19:39:51 +08:00
|
|
|
case L2CAP_CONF_RFC:
|
|
|
|
if (olen == sizeof(rfc))
|
|
|
|
memcpy(&rfc, (void *) val, olen);
|
|
|
|
break;
|
|
|
|
|
2009-08-21 09:26:02 +08:00
|
|
|
case L2CAP_CONF_FCS:
|
|
|
|
if (val == L2CAP_FCS_NONE)
|
2012-11-29 23:46:08 +08:00
|
|
|
set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
|
2011-10-17 19:35:30 +08:00
|
|
|
break;
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2011-10-17 19:35:30 +08:00
|
|
|
case L2CAP_CONF_EFS:
|
Bluetooth: Prevent stack info leak from the EFS element.
In the function l2cap_parse_conf_rsp and in the function
l2cap_parse_conf_req the following variable is declared without
initialization:
struct l2cap_conf_efs efs;
In addition, when parsing input configuration parameters in both of
these functions, the switch case for handling EFS elements may skip the
memcpy call that will write to the efs variable:
...
case L2CAP_CONF_EFS:
if (olen == sizeof(efs))
memcpy(&efs, (void *)val, olen);
...
The olen in the above if is attacker controlled, and regardless of that
if, in both of these functions the efs variable would eventually be
added to the outgoing configuration request that is being built:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs);
So by sending a configuration request, or response, that contains an
L2CAP_CONF_EFS element, but with an element length that is not
sizeof(efs) - the memcpy to the uninitialized efs variable can be
avoided, and the uninitialized variable would be returned to the
attacker (16 bytes).
This issue has been assigned CVE-2017-1000410
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Ben Seri <ben@armis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-08 22:14:47 +08:00
|
|
|
if (olen == sizeof(efs)) {
|
|
|
|
remote_efs = 1;
|
2011-10-17 19:35:30 +08:00
|
|
|
memcpy(&efs, (void *) val, olen);
|
Bluetooth: Prevent stack info leak from the EFS element.
In the function l2cap_parse_conf_rsp and in the function
l2cap_parse_conf_req the following variable is declared without
initialization:
struct l2cap_conf_efs efs;
In addition, when parsing input configuration parameters in both of
these functions, the switch case for handling EFS elements may skip the
memcpy call that will write to the efs variable:
...
case L2CAP_CONF_EFS:
if (olen == sizeof(efs))
memcpy(&efs, (void *)val, olen);
...
The olen in the above if is attacker controlled, and regardless of that
if, in both of these functions the efs variable would eventually be
added to the outgoing configuration request that is being built:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs);
So by sending a configuration request, or response, that contains an
L2CAP_CONF_EFS element, but with an element length that is not
sizeof(efs) - the memcpy to the uninitialized efs variable can be
avoided, and the uninitialized variable would be returned to the
attacker (16 bytes).
This issue has been assigned CVE-2017-1000410
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Ben Seri <ben@armis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-08 22:14:47 +08:00
|
|
|
}
|
2009-08-21 09:26:02 +08:00
|
|
|
break;
|
|
|
|
|
2011-10-11 18:37:42 +08:00
|
|
|
case L2CAP_CONF_EWS:
|
2014-12-02 16:09:26 +08:00
|
|
|
if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
|
2011-10-11 18:37:42 +08:00
|
|
|
return -ECONNREFUSED;
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2011-10-11 18:37:42 +08:00
|
|
|
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
|
|
|
set_bit(CONF_EWS_RECV, &chan->conf_state);
|
2011-10-17 17:19:57 +08:00
|
|
|
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
|
2011-10-11 18:37:42 +08:00
|
|
|
chan->remote_tx_win = val;
|
2009-08-21 09:26:02 +08:00
|
|
|
break;
|
|
|
|
|
2007-05-24 20:27:19 +08:00
|
|
|
default:
|
|
|
|
if (hint)
|
|
|
|
break;
|
|
|
|
|
|
|
|
result = L2CAP_CONF_UNKNOWN;
|
|
|
|
*((u8 *) ptr++) = type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-26 01:16:54 +08:00
|
|
|
if (chan->num_conf_rsp || chan->num_conf_req > 1)
|
2009-07-05 02:06:24 +08:00
|
|
|
goto done;
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
switch (chan->mode) {
|
2009-07-05 02:06:24 +08:00
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
case L2CAP_MODE_ERTM:
|
2011-06-11 04:02:12 +08:00
|
|
|
if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->mode = l2cap_select_mode(rfc.mode,
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->conn->feat_mask);
|
2010-06-04 05:43:28 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-10-17 19:35:30 +08:00
|
|
|
if (remote_efs) {
|
2013-10-02 13:59:22 +08:00
|
|
|
if (__l2cap_efs_supported(chan->conn))
|
2011-10-17 19:35:30 +08:00
|
|
|
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
|
|
|
else
|
|
|
|
return -ECONNREFUSED;
|
|
|
|
}
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
if (chan->mode != rfc.mode)
|
2009-07-05 02:06:24 +08:00
|
|
|
return -ECONNREFUSED;
|
2010-06-09 06:09:48 +08:00
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2011-04-14 04:20:49 +08:00
|
|
|
if (chan->mode != rfc.mode) {
|
2009-07-05 02:06:24 +08:00
|
|
|
result = L2CAP_CONF_UNACCEPT;
|
2011-04-14 04:20:49 +08:00
|
|
|
rfc.mode = chan->mode;
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2011-03-26 01:16:54 +08:00
|
|
|
if (chan->num_conf_rsp == 1)
|
2009-07-05 02:06:24 +08:00
|
|
|
return -ECONNREFUSED;
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &rfc, endptr - ptr);
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
|
|
|
|
2007-05-24 20:27:19 +08:00
|
|
|
if (result == L2CAP_CONF_SUCCESS) {
|
|
|
|
/* Configure output options and let the other side know
|
|
|
|
* which ones we don't like. */
|
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
if (mtu < L2CAP_DEFAULT_MIN_MTU)
|
|
|
|
result = L2CAP_CONF_UNACCEPT;
|
|
|
|
else {
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->omtu = mtu;
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_MTU_DONE, &chan->conf_state);
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr);
|
2007-10-20 19:39:51 +08:00
|
|
|
|
2011-10-17 19:35:30 +08:00
|
|
|
if (remote_efs) {
|
|
|
|
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
|
2012-10-06 17:07:01 +08:00
|
|
|
efs.stype != L2CAP_SERV_NOTRAFIC &&
|
|
|
|
efs.stype != chan->local_stype) {
|
2011-10-17 19:35:30 +08:00
|
|
|
|
|
|
|
result = L2CAP_CONF_UNACCEPT;
|
|
|
|
|
|
|
|
if (chan->num_conf_req >= 1)
|
|
|
|
return -ECONNREFUSED;
|
|
|
|
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
|
2012-10-06 17:07:01 +08:00
|
|
|
sizeof(efs),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &efs, endptr - ptr);
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
} else {
|
2011-11-02 00:06:23 +08:00
|
|
|
/* Send PENDING Conf Rsp */
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
result = L2CAP_CONF_PENDING;
|
|
|
|
set_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
|
2011-10-17 19:35:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
switch (rfc.mode) {
|
|
|
|
case L2CAP_MODE_BASIC:
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_MODE_DONE, &chan->conf_state);
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_ERTM:
|
2011-10-11 18:37:42 +08:00
|
|
|
if (!test_bit(CONF_EWS_RECV, &chan->conf_state))
|
|
|
|
chan->remote_tx_win = rfc.txwin_size;
|
|
|
|
else
|
|
|
|
rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
|
2010-08-06 06:54:22 +08:00
|
|
|
|
2011-03-26 07:15:28 +08:00
|
|
|
chan->remote_max_tx = rfc.max_transmit;
|
2010-05-02 03:15:40 +08:00
|
|
|
|
2011-10-17 17:19:59 +08:00
|
|
|
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
|
|
|
|
L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
|
2011-10-17 17:19:59 +08:00
|
|
|
rfc.max_pdu_size = cpu_to_le16(size);
|
|
|
|
chan->remote_mps = size;
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2012-10-24 06:24:20 +08:00
|
|
|
__l2cap_set_ertm_timeouts(chan, &rfc);
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_MODE_DONE, &chan->conf_state);
|
2009-10-18 08:41:01 +08:00
|
|
|
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
2017-09-10 05:15:59 +08:00
|
|
|
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
|
2009-10-18 08:41:01 +08:00
|
|
|
|
2011-10-17 19:35:30 +08:00
|
|
|
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
|
|
|
|
chan->remote_id = efs.id;
|
|
|
|
chan->remote_stype = efs.stype;
|
|
|
|
chan->remote_msdu = le16_to_cpu(efs.msdu);
|
|
|
|
chan->remote_flush_to =
|
2012-10-06 17:07:01 +08:00
|
|
|
le32_to_cpu(efs.flush_to);
|
2011-10-17 19:35:30 +08:00
|
|
|
chan->remote_acc_lat =
|
2012-10-06 17:07:01 +08:00
|
|
|
le32_to_cpu(efs.acc_lat);
|
2011-10-17 19:35:30 +08:00
|
|
|
chan->remote_sdu_itime =
|
|
|
|
le32_to_cpu(efs.sdu_itime);
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
|
2012-10-06 17:07:01 +08:00
|
|
|
sizeof(efs),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &efs, endptr - ptr);
|
2011-10-17 19:35:30 +08:00
|
|
|
}
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_STREAMING:
|
2011-10-17 17:19:59 +08:00
|
|
|
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
|
|
|
|
L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
|
2011-10-17 17:19:59 +08:00
|
|
|
rfc.max_pdu_size = cpu_to_le16(size);
|
|
|
|
chan->remote_mps = size;
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_MODE_DONE, &chan->conf_state);
|
2009-10-18 08:41:01 +08:00
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
|
2017-09-10 05:15:59 +08:00
|
|
|
(unsigned long) &rfc, endptr - ptr);
|
2009-10-18 08:41:01 +08:00
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-05-24 20:27:19 +08:00
|
|
|
result = L2CAP_CONF_UNACCEPT;
|
|
|
|
|
2007-10-20 19:39:51 +08:00
|
|
|
memset(&rfc, 0, sizeof(rfc));
|
2011-04-14 04:20:49 +08:00
|
|
|
rfc.mode = chan->mode;
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
2007-10-20 19:39:51 +08:00
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
if (result == L2CAP_CONF_SUCCESS)
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp->scid = cpu_to_le16(chan->dcid);
|
2007-05-24 20:27:19 +08:00
|
|
|
rsp->result = cpu_to_le16(result);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp->flags = cpu_to_le16(0);
|
2007-05-24 20:27:19 +08:00
|
|
|
|
|
|
|
return ptr - data;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
|
2017-09-10 05:15:59 +08:00
|
|
|
void *data, size_t size, u16 *result)
|
2009-07-05 02:06:24 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_req *req = data;
|
|
|
|
void *ptr = req->data;
|
2017-09-10 05:15:59 +08:00
|
|
|
void *endptr = data + size;
|
2009-07-05 02:06:24 +08:00
|
|
|
int type, olen;
|
|
|
|
unsigned long val;
|
2011-12-09 09:23:21 +08:00
|
|
|
struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
|
2011-11-07 20:20:33 +08:00
|
|
|
struct l2cap_conf_efs efs;
|
2009-07-05 02:06:24 +08:00
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data);
|
2009-07-05 02:06:24 +08:00
|
|
|
|
|
|
|
while (len >= L2CAP_CONF_OPT_SIZE) {
|
|
|
|
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case L2CAP_CONF_MTU:
|
|
|
|
if (val < L2CAP_DEFAULT_MIN_MTU) {
|
|
|
|
*result = L2CAP_CONF_UNACCEPT;
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
|
2009-07-05 02:06:24 +08:00
|
|
|
} else
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->imtu = val;
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_FLUSH_TO:
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->flush_to = val;
|
2009-07-05 02:06:24 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
|
2017-09-10 05:15:59 +08:00
|
|
|
2, chan->flush_to, endptr - ptr);
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_RFC:
|
|
|
|
if (olen == sizeof(rfc))
|
|
|
|
memcpy(&rfc, (void *)val, olen);
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
|
2012-10-06 17:07:01 +08:00
|
|
|
rfc.mode != chan->mode)
|
2009-07-05 02:06:24 +08:00
|
|
|
return -ECONNREFUSED;
|
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->fcs = 0;
|
2009-07-05 02:06:24 +08:00
|
|
|
|
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
|
2017-09-10 05:15:59 +08:00
|
|
|
sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
2011-10-11 18:37:42 +08:00
|
|
|
|
|
|
|
case L2CAP_CONF_EWS:
|
2012-07-10 20:47:07 +08:00
|
|
|
chan->ack_win = min_t(u16, val, chan->ack_win);
|
2011-11-02 00:06:23 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
|
2017-09-10 05:15:59 +08:00
|
|
|
chan->tx_win, endptr - ptr);
|
2011-10-11 18:37:42 +08:00
|
|
|
break;
|
2011-11-07 20:20:33 +08:00
|
|
|
|
|
|
|
case L2CAP_CONF_EFS:
|
Bluetooth: Prevent stack info leak from the EFS element.
In the function l2cap_parse_conf_rsp and in the function
l2cap_parse_conf_req the following variable is declared without
initialization:
struct l2cap_conf_efs efs;
In addition, when parsing input configuration parameters in both of
these functions, the switch case for handling EFS elements may skip the
memcpy call that will write to the efs variable:
...
case L2CAP_CONF_EFS:
if (olen == sizeof(efs))
memcpy(&efs, (void *)val, olen);
...
The olen in the above if is attacker controlled, and regardless of that
if, in both of these functions the efs variable would eventually be
added to the outgoing configuration request that is being built:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs);
So by sending a configuration request, or response, that contains an
L2CAP_CONF_EFS element, but with an element length that is not
sizeof(efs) - the memcpy to the uninitialized efs variable can be
avoided, and the uninitialized variable would be returned to the
attacker (16 bytes).
This issue has been assigned CVE-2017-1000410
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Ben Seri <ben@armis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-08 22:14:47 +08:00
|
|
|
if (olen == sizeof(efs)) {
|
2011-11-07 20:20:33 +08:00
|
|
|
memcpy(&efs, (void *)val, olen);
|
|
|
|
|
Bluetooth: Prevent stack info leak from the EFS element.
In the function l2cap_parse_conf_rsp and in the function
l2cap_parse_conf_req the following variable is declared without
initialization:
struct l2cap_conf_efs efs;
In addition, when parsing input configuration parameters in both of
these functions, the switch case for handling EFS elements may skip the
memcpy call that will write to the efs variable:
...
case L2CAP_CONF_EFS:
if (olen == sizeof(efs))
memcpy(&efs, (void *)val, olen);
...
The olen in the above if is attacker controlled, and regardless of that
if, in both of these functions the efs variable would eventually be
added to the outgoing configuration request that is being built:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs);
So by sending a configuration request, or response, that contains an
L2CAP_CONF_EFS element, but with an element length that is not
sizeof(efs) - the memcpy to the uninitialized efs variable can be
avoided, and the uninitialized variable would be returned to the
attacker (16 bytes).
This issue has been assigned CVE-2017-1000410
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Ben Seri <ben@armis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-08 22:14:47 +08:00
|
|
|
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
|
|
|
|
efs.stype != L2CAP_SERV_NOTRAFIC &&
|
|
|
|
efs.stype != chan->local_stype)
|
|
|
|
return -ECONNREFUSED;
|
2011-11-07 20:20:33 +08:00
|
|
|
|
Bluetooth: Prevent stack info leak from the EFS element.
In the function l2cap_parse_conf_rsp and in the function
l2cap_parse_conf_req the following variable is declared without
initialization:
struct l2cap_conf_efs efs;
In addition, when parsing input configuration parameters in both of
these functions, the switch case for handling EFS elements may skip the
memcpy call that will write to the efs variable:
...
case L2CAP_CONF_EFS:
if (olen == sizeof(efs))
memcpy(&efs, (void *)val, olen);
...
The olen in the above if is attacker controlled, and regardless of that
if, in both of these functions the efs variable would eventually be
added to the outgoing configuration request that is being built:
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs);
So by sending a configuration request, or response, that contains an
L2CAP_CONF_EFS element, but with an element length that is not
sizeof(efs) - the memcpy to the uninitialized efs variable can be
avoided, and the uninitialized variable would be returned to the
attacker (16 bytes).
This issue has been assigned CVE-2017-1000410
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: Gustavo Padovan <gustavo@padovan.org>
Cc: Johan Hedberg <johan.hedberg@gmail.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Ben Seri <ben@armis.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-12-08 22:14:47 +08:00
|
|
|
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
|
|
|
|
(unsigned long) &efs, endptr - ptr);
|
|
|
|
}
|
2011-11-07 20:20:33 +08:00
|
|
|
break;
|
2012-11-29 23:46:07 +08:00
|
|
|
|
|
|
|
case L2CAP_CONF_FCS:
|
|
|
|
if (*result == L2CAP_CONF_PENDING)
|
|
|
|
if (val == L2CAP_FCS_NONE)
|
2012-11-29 23:46:08 +08:00
|
|
|
set_bit(CONF_RECV_NO_FCS,
|
2012-11-29 23:46:07 +08:00
|
|
|
&chan->conf_state);
|
|
|
|
break;
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode)
|
2010-06-09 07:08:49 +08:00
|
|
|
return -ECONNREFUSED;
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
chan->mode = rfc.mode;
|
2010-06-09 07:08:49 +08:00
|
|
|
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) {
|
2009-07-05 02:06:24 +08:00
|
|
|
switch (rfc.mode) {
|
|
|
|
case L2CAP_MODE_ERTM:
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
|
|
|
|
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
|
|
|
|
chan->mps = le16_to_cpu(rfc.max_pdu_size);
|
2012-07-10 20:47:07 +08:00
|
|
|
if (!test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
chan->ack_win = min_t(u16, chan->ack_win,
|
|
|
|
rfc.txwin_size);
|
2011-11-07 20:20:33 +08:00
|
|
|
|
|
|
|
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
|
|
|
|
chan->local_msdu = le16_to_cpu(efs.msdu);
|
|
|
|
chan->local_sdu_itime =
|
2012-10-06 17:07:01 +08:00
|
|
|
le32_to_cpu(efs.sdu_itime);
|
2011-11-07 20:20:33 +08:00
|
|
|
chan->local_acc_lat = le32_to_cpu(efs.acc_lat);
|
|
|
|
chan->local_flush_to =
|
2012-10-06 17:07:01 +08:00
|
|
|
le32_to_cpu(efs.flush_to);
|
2011-11-07 20:20:33 +08:00
|
|
|
}
|
2009-07-05 02:06:24 +08:00
|
|
|
break;
|
2011-11-07 20:20:33 +08:00
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
case L2CAP_MODE_STREAMING:
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->mps = le16_to_cpu(rfc.max_pdu_size);
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
req->dcid = cpu_to_le16(chan->dcid);
|
2014-03-13 01:52:35 +08:00
|
|
|
req->flags = cpu_to_le16(0);
|
2009-07-05 02:06:24 +08:00
|
|
|
|
|
|
|
return ptr - data;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
|
|
|
|
u16 result, u16 flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_rsp *rsp = data;
|
|
|
|
void *ptr = rsp->data;
|
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp->scid = cpu_to_le16(chan->dcid);
|
2007-05-24 20:27:19 +08:00
|
|
|
rsp->result = cpu_to_le16(result);
|
2007-03-26 11:12:50 +08:00
|
|
|
rsp->flags = cpu_to_le16(flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return ptr - data;
|
|
|
|
}
|
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
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);
|
2013-10-07 21:35:26 +08:00
|
|
|
rsp.mps = cpu_to_le16(chan->mps);
|
2013-05-17 18:09:05 +08:00
|
|
|
rsp.credits = cpu_to_le16(chan->rx_credits);
|
2018-10-05 17:18:12 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS);
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
|
|
|
|
&rsp);
|
|
|
|
}
|
|
|
|
|
2011-04-14 07:23:55 +08:00
|
|
|
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
|
2011-03-26 01:30:37 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn_rsp rsp;
|
2011-04-14 07:23:55 +08:00
|
|
|
struct l2cap_conn *conn = chan->conn;
|
2011-03-26 01:30:37 +08:00
|
|
|
u8 buf[128];
|
2012-10-31 21:46:28 +08:00
|
|
|
u8 rsp_code;
|
2011-03-26 01:30:37 +08:00
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp.scid = cpu_to_le16(chan->dcid);
|
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2012-10-31 21:46:28 +08:00
|
|
|
|
|
|
|
if (chan->hs_hcon)
|
|
|
|
rsp_code = L2CAP_CREATE_CHAN_RSP;
|
|
|
|
else
|
|
|
|
rsp_code = L2CAP_CONN_RSP;
|
|
|
|
|
|
|
|
BT_DBG("chan %p rsp_code %u", chan, rsp_code);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp);
|
2011-03-26 01:30:37 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
|
2011-03-26 01:30:37 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
|
2011-03-26 01:30:37 +08:00
|
|
|
chan->num_conf_req++;
|
|
|
|
}
|
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
|
2010-05-02 03:15:39 +08:00
|
|
|
{
|
|
|
|
int type, olen;
|
|
|
|
unsigned long val;
|
2012-07-10 20:47:07 +08:00
|
|
|
/* Use sane default values in case a misbehaving remote device
|
|
|
|
* did not send an RFC or extended window size option.
|
|
|
|
*/
|
|
|
|
u16 txwin_ext = chan->ack_win;
|
|
|
|
struct l2cap_conf_rfc rfc = {
|
|
|
|
.mode = chan->mode,
|
2014-03-13 01:52:35 +08:00
|
|
|
.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO),
|
|
|
|
.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO),
|
2012-07-10 20:47:07 +08:00
|
|
|
.max_pdu_size = cpu_to_le16(chan->imtu),
|
|
|
|
.txwin_size = min_t(u16, chan->ack_win, L2CAP_DEFAULT_TX_WINDOW),
|
|
|
|
};
|
2010-05-02 03:15:39 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len);
|
2010-05-02 03:15:39 +08:00
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING))
|
2010-05-02 03:15:39 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
while (len >= L2CAP_CONF_OPT_SIZE) {
|
|
|
|
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
|
|
|
|
|
2012-07-10 20:47:07 +08:00
|
|
|
switch (type) {
|
|
|
|
case L2CAP_CONF_RFC:
|
|
|
|
if (olen == sizeof(rfc))
|
|
|
|
memcpy(&rfc, (void *)val, olen);
|
2012-06-08 17:33:33 +08:00
|
|
|
break;
|
2012-07-10 20:47:07 +08:00
|
|
|
case L2CAP_CONF_EWS:
|
|
|
|
txwin_ext = val;
|
|
|
|
break;
|
|
|
|
}
|
2010-05-02 03:15:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (rfc.mode) {
|
|
|
|
case L2CAP_MODE_ERTM:
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
|
|
|
|
chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
|
2012-07-10 20:47:07 +08:00
|
|
|
chan->mps = le16_to_cpu(rfc.max_pdu_size);
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
chan->ack_win = min_t(u16, chan->ack_win, txwin_ext);
|
|
|
|
else
|
|
|
|
chan->ack_win = min_t(u16, chan->ack_win,
|
|
|
|
rfc.txwin_size);
|
2010-05-02 03:15:39 +08:00
|
|
|
break;
|
|
|
|
case L2CAP_MODE_STREAMING:
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->mps = le16_to_cpu(rfc.max_pdu_size);
|
2010-05-02 03:15:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_command_rej(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2007-10-20 19:37:56 +08:00
|
|
|
{
|
2011-07-10 13:47:44 +08:00
|
|
|
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len < sizeof(*rej))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2011-07-10 13:47:44 +08:00
|
|
|
if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
|
2007-10-20 19:37:56 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
|
2012-10-06 17:07:01 +08:00
|
|
|
cmd->ident == conn->info_ident) {
|
2012-01-31 04:26:28 +08:00
|
|
|
cancel_delayed_work(&conn->info_timer);
|
2009-02-07 06:35:19 +08:00
|
|
|
|
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
2009-02-09 16:18:02 +08:00
|
|
|
conn->info_ident = 0;
|
2009-02-07 06:35:19 +08:00
|
|
|
|
2007-10-20 19:37:56 +08:00
|
|
|
l2cap_conn_start(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:07 +08:00
|
|
|
static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u8 *data, u8 rsp_code, u8 amp_id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
|
|
|
|
struct l2cap_conn_rsp rsp;
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *chan = NULL, *pchan;
|
2008-09-09 13:19:20 +08:00
|
|
|
int result, status = L2CAP_CS_NO_INFO;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
|
2008-09-09 13:19:20 +08:00
|
|
|
__le16 psm = req->psm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-03-09 20:16:17 +08:00
|
|
|
BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Check if we have socket listening on psm */
|
2013-10-13 20:24:01 +08:00
|
|
|
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
|
2013-05-14 18:23:13 +08:00
|
|
|
&conn->hcon->dst, ACL_LINK);
|
2011-04-28 05:26:32 +08:00
|
|
|
if (!pchan) {
|
2005-04-17 06:20:36 +08:00
|
|
|
result = L2CAP_CR_BAD_PSM;
|
|
|
|
goto sendresp;
|
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2013-10-22 00:21:41 +08:00
|
|
|
l2cap_chan_lock(pchan);
|
2010-11-02 02:43:53 +08:00
|
|
|
|
2008-09-09 13:19:20 +08:00
|
|
|
/* Check if the ACL is secure enough (if not SDP) */
|
2014-03-13 01:52:35 +08:00
|
|
|
if (psm != cpu_to_le16(L2CAP_PSM_SDP) &&
|
2012-10-06 17:07:01 +08:00
|
|
|
!hci_conn_check_link_mode(conn->hcon)) {
|
2011-11-07 20:20:25 +08:00
|
|
|
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
|
2008-09-09 13:19:20 +08:00
|
|
|
result = L2CAP_CR_SEC_BLOCK;
|
|
|
|
goto response;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
result = L2CAP_CR_NO_MEM;
|
|
|
|
|
Bluetooth: Errata Service Release 8, Erratum 3253
L2CAP: New result values
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
As per the ESR08_V1.0.0, 1.11.2 Erratum 3253, Page No. 54,
"Remote CID invalid Issue".
Applies to Core Specification versions: V5.0, V4.2, v4.1, v4.0, and v3.0 + HS
Vol 3, Part A, Section 4.2, 4.3, 4.14, 4.15.
Core Specification Version 5.0, Page No.1753, Table 4.6 and
Page No. 1767, Table 4.14
New result values are added to l2cap connect/create channel response as
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
Signed-off-by: Mallikarjun Phulari <mallikarjun.phulari@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2018-10-05 17:18:13 +08:00
|
|
|
/* Check for valid dynamic CID range (as per Erratum 3253) */
|
|
|
|
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_DYN_END) {
|
|
|
|
result = L2CAP_CR_INVALID_SCID;
|
|
|
|
goto response;
|
|
|
|
}
|
|
|
|
|
2012-05-28 09:27:58 +08:00
|
|
|
/* Check if we already have channel with that dcid */
|
Bluetooth: Errata Service Release 8, Erratum 3253
L2CAP: New result values
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
As per the ESR08_V1.0.0, 1.11.2 Erratum 3253, Page No. 54,
"Remote CID invalid Issue".
Applies to Core Specification versions: V5.0, V4.2, v4.1, v4.0, and v3.0 + HS
Vol 3, Part A, Section 4.2, 4.3, 4.14, 4.15.
Core Specification Version 5.0, Page No.1753, Table 4.6 and
Page No. 1767, Table 4.14
New result values are added to l2cap connect/create channel response as
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
Signed-off-by: Mallikarjun Phulari <mallikarjun.phulari@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2018-10-05 17:18:13 +08:00
|
|
|
if (__l2cap_get_chan_by_dcid(conn, scid)) {
|
|
|
|
result = L2CAP_CR_SCID_IN_USE;
|
2012-05-28 09:27:58 +08:00
|
|
|
goto response;
|
Bluetooth: Errata Service Release 8, Erratum 3253
L2CAP: New result values
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
As per the ESR08_V1.0.0, 1.11.2 Erratum 3253, Page No. 54,
"Remote CID invalid Issue".
Applies to Core Specification versions: V5.0, V4.2, v4.1, v4.0, and v3.0 + HS
Vol 3, Part A, Section 4.2, 4.3, 4.14, 4.15.
Core Specification Version 5.0, Page No.1753, Table 4.6 and
Page No. 1767, Table 4.14
New result values are added to l2cap connect/create channel response as
0x0006 - Connection refused – Invalid Source CID
0x0007 - Connection refused – Source CID already allocated
Signed-off-by: Mallikarjun Phulari <mallikarjun.phulari@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2018-10-05 17:18:13 +08:00
|
|
|
}
|
2012-05-28 09:27:58 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
chan = pchan->ops->new_connection(pchan);
|
2011-05-17 04:24:37 +08:00
|
|
|
if (!chan)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto response;
|
|
|
|
|
2013-08-06 00:59:12 +08:00
|
|
|
/* For certain devices (ex: HID mouse), support for authentication,
|
|
|
|
* pairing and bonding is optional. For such devices, inorder to avoid
|
|
|
|
* the ACL alive for too long after L2CAP disconnection, reset the ACL
|
|
|
|
* disc_timeout back to HCI_DISCONN_TIMEOUT during L2CAP connect.
|
|
|
|
*/
|
|
|
|
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
|
|
|
2013-10-13 23:12:47 +08:00
|
|
|
bacpy(&chan->src, &conn->hcon->src);
|
|
|
|
bacpy(&chan->dst, &conn->hcon->dst);
|
2015-01-15 19:06:44 +08:00
|
|
|
chan->src_type = bdaddr_src_type(conn->hcon);
|
|
|
|
chan->dst_type = bdaddr_dst_type(conn->hcon);
|
2011-04-14 06:50:45 +08:00
|
|
|
chan->psm = psm;
|
|
|
|
chan->dcid = scid;
|
2012-10-24 06:24:07 +08:00
|
|
|
chan->local_amp_id = amp_id;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
__l2cap_chan_add(conn, chan);
|
2011-03-25 11:22:30 +08:00
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
dcid = chan->scid;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-10-16 06:24:47 +08:00
|
|
|
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-26 00:59:37 +08:00
|
|
|
chan->ident = cmd->ident;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-07 06:35:19 +08:00
|
|
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
|
2014-07-17 20:35:38 +08:00
|
|
|
if (l2cap_chan_check_security(chan, false)) {
|
2013-10-14 17:45:34 +08:00
|
|
|
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONNECT2);
|
2009-01-16 04:57:00 +08:00
|
|
|
result = L2CAP_CR_PEND;
|
|
|
|
status = L2CAP_CS_AUTHOR_PEND;
|
2012-10-12 19:35:24 +08:00
|
|
|
chan->ops->defer(chan);
|
2009-01-16 04:57:00 +08:00
|
|
|
} else {
|
2012-10-24 06:24:07 +08:00
|
|
|
/* Force pending result for AMP controllers.
|
|
|
|
* The connection will succeed after the
|
|
|
|
* physical link is up.
|
|
|
|
*/
|
2013-10-06 02:47:44 +08:00
|
|
|
if (amp_id == AMP_ID_BREDR) {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONFIG);
|
2012-10-24 06:24:07 +08:00
|
|
|
result = L2CAP_CR_SUCCESS;
|
2013-10-06 02:47:44 +08:00
|
|
|
} else {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONNECT2);
|
2013-10-06 02:47:44 +08:00
|
|
|
result = L2CAP_CR_PEND;
|
2012-10-24 06:24:07 +08:00
|
|
|
}
|
2009-01-16 04:57:00 +08:00
|
|
|
status = L2CAP_CS_NO_INFO;
|
|
|
|
}
|
2008-07-15 02:13:44 +08:00
|
|
|
} else {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONNECT2);
|
2008-07-15 02:13:44 +08:00
|
|
|
result = L2CAP_CR_PEND;
|
|
|
|
status = L2CAP_CS_AUTHEN_PEND;
|
|
|
|
}
|
|
|
|
} else {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONNECT2);
|
2008-07-15 02:13:44 +08:00
|
|
|
result = L2CAP_CR_PEND;
|
|
|
|
status = L2CAP_CS_NO_INFO;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
response:
|
2013-10-22 00:21:41 +08:00
|
|
|
l2cap_chan_unlock(pchan);
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2014-08-08 03:56:42 +08:00
|
|
|
l2cap_chan_put(pchan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sendresp:
|
2007-03-26 11:12:50 +08:00
|
|
|
rsp.scid = cpu_to_le16(scid);
|
|
|
|
rsp.dcid = cpu_to_le16(dcid);
|
|
|
|
rsp.result = cpu_to_le16(result);
|
|
|
|
rsp.status = cpu_to_le16(status);
|
2012-10-11 22:48:22 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
|
2008-07-15 02:13:44 +08:00
|
|
|
|
|
|
|
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
|
|
|
|
struct l2cap_info_req info;
|
2014-03-13 01:52:35 +08:00
|
|
|
info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
|
2008-07-15 02:13:44 +08:00
|
|
|
|
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
|
|
|
|
conn->info_ident = l2cap_get_ident(conn);
|
|
|
|
|
2012-03-02 06:25:33 +08:00
|
|
|
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
|
2008-07-15 02:13:44 +08:00
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
|
|
|
|
sizeof(info), &info);
|
2008-07-15 02:13:44 +08:00
|
|
|
}
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
|
2012-10-06 17:07:01 +08:00
|
|
|
result == L2CAP_CR_SUCCESS) {
|
2010-07-09 07:08:18 +08:00
|
|
|
u8 buf[128];
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_REQ_SENT, &chan->conf_state);
|
2010-07-09 07:08:18 +08:00
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_req++;
|
2010-07-09 07:08:18 +08:00
|
|
|
}
|
2012-10-24 06:24:07 +08:00
|
|
|
|
|
|
|
return chan;
|
2012-10-11 22:48:22 +08:00
|
|
|
}
|
2010-07-09 07:08:18 +08:00
|
|
|
|
2012-10-11 22:48:22 +08:00
|
|
|
static int l2cap_connect_req(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
|
2012-10-11 22:48:22 +08:00
|
|
|
{
|
2013-01-10 12:58:35 +08:00
|
|
|
struct hci_dev *hdev = conn->hcon->hdev;
|
|
|
|
struct hci_conn *hcon = conn->hcon;
|
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len < sizeof(struct l2cap_conn_req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2013-01-10 12:58:35 +08:00
|
|
|
hci_dev_lock(hdev);
|
2015-03-13 17:11:00 +08:00
|
|
|
if (hci_dev_test_flag(hdev, HCI_MGMT) &&
|
2013-01-10 12:58:35 +08:00
|
|
|
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
|
2014-10-07 16:44:10 +08:00
|
|
|
mgmt_device_connected(hdev, hcon, 0, NULL, 0);
|
2013-01-10 12:58:35 +08:00
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
|
2012-10-12 19:40:40 +08:00
|
|
|
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:08 +08:00
|
|
|
static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
|
|
|
|
u16 scid, dcid, result, status;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 req[128];
|
2012-02-21 18:54:55 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len < sizeof(*rsp))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
scid = __le16_to_cpu(rsp->scid);
|
|
|
|
dcid = __le16_to_cpu(rsp->dcid);
|
|
|
|
result = __le16_to_cpu(rsp->result);
|
|
|
|
status = __le16_to_cpu(rsp->status);
|
|
|
|
|
2012-02-21 18:54:54 +08:00
|
|
|
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
|
2012-10-06 17:07:01 +08:00
|
|
|
dcid, scid, result, status);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (scid) {
|
2012-02-21 18:54:55 +08:00
|
|
|
chan = __l2cap_get_chan_by_scid(conn, scid);
|
|
|
|
if (!chan) {
|
2013-09-16 18:05:14 +08:00
|
|
|
err = -EBADSLT;
|
2012-02-21 18:54:55 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
2012-02-21 18:54:55 +08:00
|
|
|
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
|
|
|
if (!chan) {
|
2013-09-16 18:05:14 +08:00
|
|
|
err = -EBADSLT;
|
2012-02-21 18:54:55 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
err = 0;
|
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2011-03-25 11:22:30 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (result) {
|
|
|
|
case L2CAP_CR_SUCCESS:
|
2011-06-03 11:19:47 +08:00
|
|
|
l2cap_state_change(chan, BT_CONFIG);
|
2011-03-26 00:59:37 +08:00
|
|
|
chan->ident = 0;
|
2011-04-14 06:50:45 +08:00
|
|
|
chan->dcid = dcid;
|
2011-06-11 04:02:12 +08:00
|
|
|
clear_bit(CONF_CONNECT_PEND, &chan->conf_state);
|
2009-02-07 06:56:36 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
|
2010-07-09 07:08:18 +08:00
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, req, sizeof(req)), req);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_req++;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CR_PEND:
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_CONNECT_PEND, &chan->conf_state);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2011-03-25 11:22:30 +08:00
|
|
|
l2cap_chan_del(chan, ECONNREFUSED);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-02-21 18:54:55 +08:00
|
|
|
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
static inline void set_default_fcs(struct l2cap_chan *chan)
|
2010-08-25 06:35:42 +08:00
|
|
|
{
|
|
|
|
/* FCS is enabled only in ERTM or streaming mode, if one or both
|
|
|
|
* sides request it.
|
|
|
|
*/
|
2011-04-14 04:20:49 +08:00
|
|
|
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
2012-11-29 23:46:08 +08:00
|
|
|
else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state))
|
2011-04-14 02:57:03 +08:00
|
|
|
chan->fcs = L2CAP_FCS_CRC16;
|
2010-08-25 06:35:42 +08:00
|
|
|
}
|
|
|
|
|
2012-09-21 17:30:05 +08:00
|
|
|
static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
|
|
|
|
u8 ident, u16 flags)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = chan->conn;
|
|
|
|
|
|
|
|
BT_DBG("conn %p chan %p ident %d flags 0x%4.4x", conn, chan, ident,
|
|
|
|
flags);
|
|
|
|
|
|
|
|
clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
|
|
|
|
set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, ident, L2CAP_CONF_RSP,
|
|
|
|
l2cap_build_conf_rsp(chan, data,
|
|
|
|
L2CAP_CONF_SUCCESS, flags), data);
|
|
|
|
}
|
|
|
|
|
2013-10-16 16:20:47 +08:00
|
|
|
static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
|
|
|
|
u16 scid, u16 dcid)
|
|
|
|
{
|
|
|
|
struct l2cap_cmd_rej_cid rej;
|
|
|
|
|
2014-03-13 01:52:35 +08:00
|
|
|
rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID);
|
2013-10-16 16:20:47 +08:00
|
|
|
rej.scid = __cpu_to_le16(scid);
|
|
|
|
rej.dcid = __cpu_to_le16(dcid);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_config_req(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;
|
|
|
|
u16 dcid, flags;
|
|
|
|
u8 rsp[64];
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2012-04-12 01:48:42 +08:00
|
|
|
int len, err = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len < sizeof(*req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
dcid = __le16_to_cpu(req->dcid);
|
|
|
|
flags = __le16_to_cpu(req->flags);
|
|
|
|
|
|
|
|
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
|
|
|
|
|
2011-04-01 03:17:41 +08:00
|
|
|
chan = l2cap_get_chan_by_scid(conn, dcid);
|
2013-10-16 16:20:47 +08:00
|
|
|
if (!chan) {
|
|
|
|
cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-07-22 04:38:42 +08:00
|
|
|
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
|
2013-10-16 16:20:47 +08:00
|
|
|
cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
|
|
|
|
chan->dcid);
|
2006-11-19 05:15:20 +08:00
|
|
|
goto unlock;
|
2010-06-14 13:26:15 +08:00
|
|
|
}
|
2006-11-19 05:15:20 +08:00
|
|
|
|
2007-05-24 20:27:19 +08:00
|
|
|
/* Reject if config buffer is too small. */
|
2007-07-29 15:17:25 +08:00
|
|
|
len = cmd_len - sizeof(*req);
|
2013-05-28 18:46:30 +08:00
|
|
|
if (chan->conf_len + len > sizeof(chan->conf_req)) {
|
2007-05-24 20:27:19 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_build_conf_rsp(chan, rsp,
|
|
|
|
L2CAP_CONF_REJECT, flags), rsp);
|
2007-05-24 20:27:19 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store config. */
|
2011-03-26 01:16:54 +08:00
|
|
|
memcpy(chan->conf_req + chan->conf_len, req->data, len);
|
|
|
|
chan->conf_len += len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-23 20:44:06 +08:00
|
|
|
if (flags & L2CAP_CONF_FLAG_CONTINUATION) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Incomplete config. Send empty response. */
|
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_build_conf_rsp(chan, rsp,
|
|
|
|
L2CAP_CONF_SUCCESS, flags), rsp);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Complete config. */
|
2017-09-10 05:15:59 +08:00
|
|
|
len = l2cap_parse_conf_req(chan, rsp, sizeof(rsp));
|
2009-07-05 02:06:24 +08:00
|
|
|
if (len < 0) {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto unlock;
|
2009-07-05 02:06:24 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
chan->ident = cmd->ident;
|
2007-05-24 20:27:19 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_rsp++;
|
2007-05-24 20:27:19 +08:00
|
|
|
|
|
|
|
/* Reset config buffer. */
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->conf_len = 0;
|
2007-05-24 20:27:19 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state))
|
2007-10-20 19:35:42 +08:00
|
|
|
goto unlock;
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
|
2011-04-14 02:57:03 +08:00
|
|
|
set_default_fcs(chan);
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2012-04-28 07:50:48 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_ERTM ||
|
|
|
|
chan->mode == L2CAP_MODE_STREAMING)
|
2012-04-12 01:48:42 +08:00
|
|
|
err = l2cap_ertm_init(chan);
|
|
|
|
|
|
|
|
if (err < 0)
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, -err);
|
2012-04-12 01:48:42 +08:00
|
|
|
else
|
|
|
|
l2cap_chan_ready(chan);
|
2009-10-03 13:34:36 +08:00
|
|
|
|
2007-10-20 19:35:42 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
|
2008-07-15 02:13:44 +08:00
|
|
|
u8 buf[64];
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_req++;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-10-28 12:12:20 +08:00
|
|
|
/* Got Conf Rsp PENDING from remote side and assume we sent
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
Conf Rsp PENDING in the code above */
|
|
|
|
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
|
2012-09-21 17:30:05 +08:00
|
|
|
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
|
|
|
|
/* check compatibility */
|
|
|
|
|
2012-10-15 16:58:42 +08:00
|
|
|
/* Send rsp for BR/EDR channel */
|
2012-10-31 21:46:35 +08:00
|
|
|
if (!chan->hs_hcon)
|
2012-10-15 16:58:42 +08:00
|
|
|
l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags);
|
|
|
|
else
|
|
|
|
chan->ident = cmd->ident;
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
unlock:
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-04-12 01:48:42 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_config_rsp(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
|
|
|
|
u16 scid, flags, result;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2013-05-28 18:46:30 +08:00
|
|
|
int len = cmd_len - sizeof(*rsp);
|
2012-04-12 01:48:42 +08:00
|
|
|
int err = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len < sizeof(*rsp))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
scid = __le16_to_cpu(rsp->scid);
|
|
|
|
flags = __le16_to_cpu(rsp->flags);
|
|
|
|
result = __le16_to_cpu(rsp->result);
|
|
|
|
|
2012-03-12 18:13:07 +08:00
|
|
|
BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x len %d", scid, flags,
|
|
|
|
result, len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-01 03:17:41 +08:00
|
|
|
chan = l2cap_get_chan_by_scid(conn, scid);
|
2011-03-25 11:22:30 +08:00
|
|
|
if (!chan)
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (result) {
|
|
|
|
case L2CAP_CONF_SUCCESS:
|
2011-04-14 02:57:03 +08:00
|
|
|
l2cap_conf_rfc_get(chan, rsp->data, len);
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
clear_bit(CONF_REM_CONF_PEND, &chan->conf_state);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
case L2CAP_CONF_PENDING:
|
|
|
|
set_bit(CONF_REM_CONF_PEND, &chan->conf_state);
|
|
|
|
|
|
|
|
if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
|
2017-09-10 05:15:59 +08:00
|
|
|
buf, sizeof(buf), &result);
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
if (len < 0) {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-10-31 21:46:35 +08:00
|
|
|
if (!chan->hs_hcon) {
|
2012-10-15 16:58:42 +08:00
|
|
|
l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
|
|
|
|
0);
|
2012-10-31 21:46:30 +08:00
|
|
|
} else {
|
|
|
|
if (l2cap_check_efs(chan)) {
|
|
|
|
amp_create_logical_link(chan);
|
|
|
|
chan->ident = cmd->ident;
|
|
|
|
}
|
|
|
|
}
|
Bluetooth: EFS: implement L2CAP config pending state
Add L2CAP Config Pending state for EFS. Currently after receiving
Config Response Pending respond with Config Response Success.
...
> ACL data: handle 1 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
> ACL data: handle 1 flags 0x02 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 45
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
< ACL data: handle 1 flags 0x00 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 47
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33
Pending
MTU 672
RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498)
EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff,
AccLat 0xffffffff, FlushTO 0x0000ffff)
> ACL data: handle 1 flags 0x02 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 14
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0
Success
< ACL data: handle 1 flags 0x00 dlen 510
L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113]
I-frame: Start (len 672) TxSeq 0 ReqSeq 0
...
Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
2011-10-17 19:35:32 +08:00
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
case L2CAP_CONF_UNACCEPT:
|
2011-03-26 01:16:54 +08:00
|
|
|
if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
|
2009-07-05 02:06:24 +08:00
|
|
|
char req[64];
|
|
|
|
|
2010-03-19 16:26:28 +08:00
|
|
|
if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2010-03-19 16:26:28 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2009-07-05 02:06:24 +08:00
|
|
|
/* throw out any old stored conf requests */
|
|
|
|
result = L2CAP_CONF_SUCCESS;
|
2011-04-13 05:15:09 +08:00
|
|
|
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
|
2017-09-10 05:15:59 +08:00
|
|
|
req, sizeof(req), &result);
|
2009-07-05 02:06:24 +08:00
|
|
|
if (len < 0) {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2009-07-05 02:06:24 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn),
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_CONF_REQ, len, req);
|
2011-03-26 01:16:54 +08:00
|
|
|
chan->num_conf_req++;
|
2009-07-05 02:06:24 +08:00
|
|
|
if (result != L2CAP_CONF_SUCCESS)
|
|
|
|
goto done;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-09 22:24:33 +08:00
|
|
|
default:
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_set_err(chan, ECONNRESET);
|
2012-02-21 18:54:58 +08:00
|
|
|
|
2012-03-02 06:25:33 +08:00
|
|
|
__set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-05-23 20:44:06 +08:00
|
|
|
if (flags & L2CAP_CONF_FLAG_CONTINUATION)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto done;
|
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
set_bit(CONF_INPUT_DONE, &chan->conf_state);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-11 04:02:12 +08:00
|
|
|
if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) {
|
2011-04-14 02:57:03 +08:00
|
|
|
set_default_fcs(chan);
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2012-04-28 07:50:48 +08:00
|
|
|
if (chan->mode == L2CAP_MODE_ERTM ||
|
|
|
|
chan->mode == L2CAP_MODE_STREAMING)
|
2012-04-12 01:48:42 +08:00
|
|
|
err = l2cap_ertm_init(chan);
|
2009-10-03 13:34:36 +08:00
|
|
|
|
2012-04-12 01:48:42 +08:00
|
|
|
if (err < 0)
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, -err);
|
2012-04-12 01:48:42 +08:00
|
|
|
else
|
|
|
|
l2cap_chan_ready(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2012-04-12 01:48:42 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
|
|
|
|
struct l2cap_disconn_rsp rsp;
|
|
|
|
u16 dcid, scid;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len != sizeof(*req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
scid = __le16_to_cpu(req->scid);
|
|
|
|
dcid = __le16_to_cpu(req->dcid);
|
|
|
|
|
|
|
|
BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
|
|
|
chan = __l2cap_get_chan_by_scid(conn, dcid);
|
|
|
|
if (!chan) {
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
2013-10-16 16:20:47 +08:00
|
|
|
cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
|
|
|
|
return 0;
|
2012-02-21 18:54:55 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
|
|
|
rsp.scid = cpu_to_le16(chan->dcid);
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
|
|
|
|
|
2013-10-16 06:24:48 +08:00
|
|
|
chan->ops->set_shutdown(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_hold(chan);
|
2011-03-25 11:22:30 +08:00
|
|
|
l2cap_chan_del(chan, ECONNRESET);
|
2012-02-22 23:11:56 +08:00
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
chan->ops->close(chan);
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_put(chan);
|
2012-02-21 18:54:55 +08:00
|
|
|
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
|
|
|
|
u16 dcid, scid;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len != sizeof(*rsp))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
scid = __le16_to_cpu(rsp->scid);
|
|
|
|
dcid = __le16_to_cpu(rsp->dcid);
|
|
|
|
|
|
|
|
BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
|
|
|
chan = __l2cap_get_chan_by_scid(conn, scid);
|
|
|
|
if (!chan) {
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2012-02-21 18:54:55 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2011-03-25 11:22:30 +08:00
|
|
|
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_hold(chan);
|
2011-03-25 11:22:30 +08:00
|
|
|
l2cap_chan_del(chan, 0);
|
2012-02-22 23:11:56 +08:00
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
chan->ops->close(chan);
|
2012-04-28 07:50:50 +08:00
|
|
|
l2cap_chan_put(chan);
|
2012-02-21 18:54:55 +08:00
|
|
|
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_information_req(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_info_req *req = (struct l2cap_info_req *) data;
|
|
|
|
u16 type;
|
|
|
|
|
2013-05-28 18:46:30 +08:00
|
|
|
if (cmd_len != sizeof(*req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
type = __le16_to_cpu(req->type);
|
|
|
|
|
|
|
|
BT_DBG("type 0x%4.4x", type);
|
|
|
|
|
2007-10-20 19:38:51 +08:00
|
|
|
if (type == L2CAP_IT_FEAT_MASK) {
|
|
|
|
u8 buf[8];
|
2009-05-03 10:09:01 +08:00
|
|
|
u32 feat_mask = l2cap_feat_mask;
|
2007-10-20 19:38:51 +08:00
|
|
|
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
|
|
|
|
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
|
2010-07-19 03:25:54 +08:00
|
|
|
if (!disable_ertm)
|
2009-08-21 09:26:02 +08:00
|
|
|
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
|
2012-10-06 17:07:01 +08:00
|
|
|
| L2CAP_FEAT_FCS;
|
2014-12-02 16:09:26 +08:00
|
|
|
if (conn->local_fixed_chan & L2CAP_FC_A2MP)
|
2011-10-11 18:37:42 +08:00
|
|
|
feat_mask |= L2CAP_FEAT_EXT_FLOW
|
2012-10-06 17:07:01 +08:00
|
|
|
| L2CAP_FEAT_EXT_WINDOW;
|
2011-09-16 21:26:32 +08:00
|
|
|
|
2009-08-24 11:45:20 +08:00
|
|
|
put_unaligned_le32(feat_mask, rsp->data);
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
|
|
|
|
buf);
|
2009-02-09 16:18:02 +08:00
|
|
|
} else if (type == L2CAP_IT_FIXED_CHAN) {
|
|
|
|
u8 buf[12];
|
|
|
|
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
|
2011-11-03 07:18:34 +08:00
|
|
|
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
|
|
|
|
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
|
2014-12-02 16:09:26 +08:00
|
|
|
rsp->data[0] = conn->local_fixed_chan;
|
|
|
|
memset(rsp->data + 1, 0, 7);
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
|
|
|
|
buf);
|
2007-10-20 19:38:51 +08:00
|
|
|
} else {
|
|
|
|
struct l2cap_info_rsp rsp;
|
|
|
|
rsp.type = cpu_to_le16(type);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP);
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp),
|
|
|
|
&rsp);
|
2007-10-20 19:38:51 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static inline int l2cap_information_rsp(struct l2cap_conn *conn,
|
2013-05-28 18:46:30 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
|
|
|
|
u16 type, result;
|
|
|
|
|
2013-06-21 22:25:11 +08:00
|
|
|
if (cmd_len < sizeof(*rsp))
|
2013-05-28 18:46:30 +08:00
|
|
|
return -EPROTO;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
type = __le16_to_cpu(rsp->type);
|
|
|
|
result = __le16_to_cpu(rsp->result);
|
|
|
|
|
|
|
|
BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
|
|
|
|
|
2011-03-25 17:31:41 +08:00
|
|
|
/* L2CAP Info req/rsp are unbound to channels, add extra checks */
|
|
|
|
if (cmd->ident != conn->info_ident ||
|
2012-10-06 17:07:01 +08:00
|
|
|
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
|
2011-03-25 17:31:41 +08:00
|
|
|
return 0;
|
|
|
|
|
2012-01-31 04:26:28 +08:00
|
|
|
cancel_delayed_work(&conn->info_timer);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2010-08-04 14:43:33 +08:00
|
|
|
if (result != L2CAP_IR_SUCCESS) {
|
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
|
|
|
conn->info_ident = 0;
|
|
|
|
|
|
|
|
l2cap_conn_start(conn);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-29 16:41:41 +08:00
|
|
|
switch (type) {
|
|
|
|
case L2CAP_IT_FEAT_MASK:
|
2008-05-03 07:25:46 +08:00
|
|
|
conn->feat_mask = get_unaligned_le32(rsp->data);
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2009-05-03 09:57:55 +08:00
|
|
|
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
|
2009-02-09 16:18:02 +08:00
|
|
|
struct l2cap_info_req req;
|
2014-03-13 01:52:35 +08:00
|
|
|
req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
|
2009-02-09 16:18:02 +08:00
|
|
|
|
|
|
|
conn->info_ident = l2cap_get_ident(conn);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, conn->info_ident,
|
2012-10-06 17:07:01 +08:00
|
|
|
L2CAP_INFO_REQ, sizeof(req), &req);
|
2009-02-09 16:18:02 +08:00
|
|
|
} else {
|
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
|
|
|
conn->info_ident = 0;
|
|
|
|
|
|
|
|
l2cap_conn_start(conn);
|
|
|
|
}
|
2012-02-29 16:41:41 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_IT_FIXED_CHAN:
|
2014-12-02 16:09:26 +08:00
|
|
|
conn->remote_fixed_chan = rsp->data[0];
|
2009-02-07 06:35:19 +08:00
|
|
|
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
2009-02-09 16:18:02 +08:00
|
|
|
conn->info_ident = 0;
|
2009-02-07 06:35:19 +08:00
|
|
|
|
|
|
|
l2cap_conn_start(conn);
|
2012-02-29 16:41:41 +08:00
|
|
|
break;
|
2009-02-07 06:35:19 +08:00
|
|
|
}
|
2007-10-20 19:37:56 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:07 +08:00
|
|
|
static int l2cap_create_channel_req(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u16 cmd_len, void *data)
|
2011-11-03 07:18:32 +08:00
|
|
|
{
|
|
|
|
struct l2cap_create_chan_req *req = data;
|
2012-11-01 21:37:02 +08:00
|
|
|
struct l2cap_create_chan_rsp rsp;
|
2012-10-24 06:24:07 +08:00
|
|
|
struct l2cap_chan *chan;
|
2012-11-01 21:37:02 +08:00
|
|
|
struct hci_dev *hdev;
|
2011-11-03 07:18:32 +08:00
|
|
|
u16 psm, scid;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(*req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
2014-12-02 16:09:26 +08:00
|
|
|
if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
|
2011-11-03 07:18:32 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
psm = le16_to_cpu(req->psm);
|
|
|
|
scid = le16_to_cpu(req->scid);
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
|
2011-11-03 07:18:32 +08:00
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
/* For controller id 0 make BR/EDR connection */
|
2013-10-06 02:47:44 +08:00
|
|
|
if (req->amp_id == AMP_ID_BREDR) {
|
2012-11-01 21:37:02 +08:00
|
|
|
l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
|
|
|
|
req->amp_id);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-24 06:24:07 +08:00
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
/* Validate AMP controller id */
|
|
|
|
hdev = hci_dev_get(req->amp_id);
|
|
|
|
if (!hdev)
|
|
|
|
goto error;
|
2012-10-24 06:24:07 +08:00
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) {
|
|
|
|
hci_dev_put(hdev);
|
|
|
|
goto error;
|
|
|
|
}
|
2012-10-24 06:24:07 +08:00
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
|
|
|
|
req->amp_id);
|
|
|
|
if (chan) {
|
|
|
|
struct amp_mgr *mgr = conn->hcon->amp_mgr;
|
|
|
|
struct hci_conn *hs_hcon;
|
2012-10-24 06:24:07 +08:00
|
|
|
|
2013-10-13 17:23:40 +08:00
|
|
|
hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
|
|
|
|
&conn->hcon->dst);
|
2012-11-01 21:37:02 +08:00
|
|
|
if (!hs_hcon) {
|
|
|
|
hci_dev_put(hdev);
|
2013-10-16 16:20:47 +08:00
|
|
|
cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
|
|
|
|
chan->dcid);
|
|
|
|
return 0;
|
2012-10-24 06:24:07 +08:00
|
|
|
}
|
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
|
|
|
|
|
|
|
|
mgr->bredr_chan = chan;
|
|
|
|
chan->hs_hcon = hs_hcon;
|
2012-11-20 23:16:22 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
2012-11-01 21:37:02 +08:00
|
|
|
conn->mtu = hdev->block_mtu;
|
2012-10-24 06:24:07 +08:00
|
|
|
}
|
2011-11-03 07:18:32 +08:00
|
|
|
|
2012-11-01 21:37:02 +08:00
|
|
|
hci_dev_put(hdev);
|
2011-11-03 07:18:32 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-11-01 21:37:02 +08:00
|
|
|
|
|
|
|
error:
|
|
|
|
rsp.dcid = 0;
|
|
|
|
rsp.scid = cpu_to_le16(scid);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_BAD_AMP);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2012-11-01 21:37:02 +08:00
|
|
|
|
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
|
|
|
|
sizeof(rsp), &rsp);
|
|
|
|
|
2013-09-16 18:05:13 +08:00
|
|
|
return 0;
|
2011-11-03 07:18:32 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:17 +08:00
|
|
|
static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
|
|
|
|
{
|
|
|
|
struct l2cap_move_chan_req req;
|
|
|
|
u8 ident;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
|
|
|
|
|
|
|
|
ident = l2cap_get_ident(chan->conn);
|
|
|
|
chan->ident = ident;
|
|
|
|
|
|
|
|
req.icid = cpu_to_le16(chan->scid);
|
|
|
|
req.dest_amp_id = dest_amp_id;
|
|
|
|
|
|
|
|
l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
|
|
|
|
&req);
|
|
|
|
|
|
|
|
__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_rsp rsp;
|
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
BT_DBG("chan %p, result 0x%4.4x", chan, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
rsp.icid = cpu_to_le16(chan->dcid);
|
2011-11-03 07:18:35 +08:00
|
|
|
rsp.result = cpu_to_le16(result);
|
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP,
|
|
|
|
sizeof(rsp), &rsp);
|
2011-11-03 07:18:35 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_cfm cfm;
|
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
BT_DBG("chan %p, result 0x%4.4x", chan, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
chan->ident = l2cap_get_ident(chan->conn);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
cfm.icid = cpu_to_le16(chan->scid);
|
2011-11-03 07:18:35 +08:00
|
|
|
cfm.result = cpu_to_le16(result);
|
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM,
|
|
|
|
sizeof(cfm), &cfm);
|
|
|
|
|
|
|
|
__set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
|
|
|
|
{
|
|
|
|
struct l2cap_move_chan_cfm cfm;
|
|
|
|
|
|
|
|
BT_DBG("conn %p, icid 0x%4.4x", conn, icid);
|
|
|
|
|
|
|
|
cfm.icid = cpu_to_le16(icid);
|
2014-03-13 01:52:35 +08:00
|
|
|
cfm.result = cpu_to_le16(L2CAP_MC_UNCONFIRMED);
|
2012-10-24 06:24:14 +08:00
|
|
|
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
|
|
|
|
sizeof(cfm), &cfm);
|
2011-11-03 07:18:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
|
2012-07-10 20:27:50 +08:00
|
|
|
u16 icid)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_cfm_rsp rsp;
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("icid 0x%4.4x", icid);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
|
|
|
rsp.icid = cpu_to_le16(icid);
|
|
|
|
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:12 +08:00
|
|
|
static void __release_logical_link(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
chan->hs_hchan = NULL;
|
|
|
|
chan->hs_hcon = NULL;
|
|
|
|
|
|
|
|
/* Placeholder - release the logical link */
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
static void l2cap_logical_fail(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
/* Logical link setup failed */
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
/* Create channel failure, disconnect */
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-10-24 06:24:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (chan->move_role) {
|
|
|
|
case L2CAP_MOVE_ROLE_RESPONDER:
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP);
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_ROLE_INITIATOR:
|
|
|
|
if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
|
|
|
|
chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
|
|
|
|
/* Remote has only sent pending or
|
|
|
|
* success responses, clean up
|
|
|
|
*/
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Other amp move states imply that the move
|
|
|
|
* has already aborted
|
|
|
|
*/
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_logical_finish_create(struct l2cap_chan *chan,
|
|
|
|
struct hci_chan *hchan)
|
|
|
|
{
|
|
|
|
struct l2cap_conf_rsp rsp;
|
|
|
|
|
2012-10-31 21:46:27 +08:00
|
|
|
chan->hs_hchan = hchan;
|
2012-10-24 06:24:15 +08:00
|
|
|
chan->hs_hcon->l2cap_data = chan->conn;
|
|
|
|
|
2012-10-25 20:20:43 +08:00
|
|
|
l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0);
|
2012-10-24 06:24:15 +08:00
|
|
|
|
|
|
|
if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
|
2012-10-25 20:20:42 +08:00
|
|
|
int err;
|
2012-10-24 06:24:15 +08:00
|
|
|
|
|
|
|
set_default_fcs(chan);
|
|
|
|
|
|
|
|
err = l2cap_ertm_init(chan);
|
|
|
|
if (err < 0)
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, -err);
|
2012-10-24 06:24:15 +08:00
|
|
|
else
|
|
|
|
l2cap_chan_ready(chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_logical_finish_move(struct l2cap_chan *chan,
|
|
|
|
struct hci_chan *hchan)
|
|
|
|
{
|
|
|
|
chan->hs_hcon = hchan->conn;
|
|
|
|
chan->hs_hcon->l2cap_data = chan->conn;
|
|
|
|
|
|
|
|
BT_DBG("move_state %d", chan->move_state);
|
|
|
|
|
|
|
|
switch (chan->move_state) {
|
|
|
|
case L2CAP_MOVE_WAIT_LOGICAL_COMP:
|
|
|
|
/* Move confirm will be sent after a success
|
|
|
|
* response is received
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_WAIT_LOGICAL_CFM:
|
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
|
|
|
|
} else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
|
|
|
|
} else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
|
|
|
|
l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Move was not in expected state, free the channel */
|
|
|
|
__release_logical_link(chan);
|
|
|
|
|
|
|
|
chan->move_state = L2CAP_MOVE_STABLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call with chan locked */
|
2012-10-25 20:20:45 +08:00
|
|
|
void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
|
|
|
|
u8 status)
|
2012-10-24 06:24:14 +08:00
|
|
|
{
|
2012-10-24 06:24:15 +08:00
|
|
|
BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
l2cap_logical_fail(chan);
|
|
|
|
__release_logical_link(chan);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
/* Ignore logical link if channel is on BR/EDR */
|
2013-10-06 02:47:44 +08:00
|
|
|
if (chan->local_amp_id != AMP_ID_BREDR)
|
2012-10-24 06:24:15 +08:00
|
|
|
l2cap_logical_finish_create(chan, hchan);
|
|
|
|
} else {
|
|
|
|
l2cap_logical_finish_move(chan, hchan);
|
|
|
|
}
|
2012-10-24 06:24:14 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:23 +08:00
|
|
|
void l2cap_move_start(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
2013-10-06 02:47:44 +08:00
|
|
|
if (chan->local_amp_id == AMP_ID_BREDR) {
|
2012-10-24 06:24:23 +08:00
|
|
|
if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
|
|
|
|
return;
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
|
|
|
|
/* Placeholder - start physical link setup */
|
|
|
|
} else {
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
|
|
|
|
chan->move_id = 0;
|
|
|
|
l2cap_move_setup(chan);
|
|
|
|
l2cap_send_move_chan_req(chan, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:17 +08:00
|
|
|
static void l2cap_do_create(struct l2cap_chan *chan, int result,
|
|
|
|
u8 local_amp_id, u8 remote_amp_id)
|
|
|
|
{
|
2012-11-20 23:16:19 +08:00
|
|
|
BT_DBG("chan %p state %s %u -> %u", chan, state_to_string(chan->state),
|
|
|
|
local_amp_id, remote_amp_id);
|
|
|
|
|
2012-11-14 23:39:31 +08:00
|
|
|
chan->fcs = L2CAP_FCS_NONE;
|
|
|
|
|
2012-11-20 23:16:19 +08:00
|
|
|
/* Outgoing channel on AMP */
|
|
|
|
if (chan->state == BT_CONNECT) {
|
|
|
|
if (result == L2CAP_CR_SUCCESS) {
|
|
|
|
chan->local_amp_id = local_amp_id;
|
|
|
|
l2cap_send_create_chan_req(chan, remote_amp_id);
|
|
|
|
} else {
|
|
|
|
/* Revert to BR/EDR connect */
|
|
|
|
l2cap_send_conn_req(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Incoming channel on AMP */
|
|
|
|
if (__l2cap_no_conn_pending(chan)) {
|
2012-10-24 06:24:17 +08:00
|
|
|
struct l2cap_conn_rsp rsp;
|
|
|
|
char buf[128];
|
|
|
|
rsp.scid = cpu_to_le16(chan->dcid);
|
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
|
|
|
|
|
|
|
if (result == L2CAP_CR_SUCCESS) {
|
|
|
|
/* Send successful response */
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2012-10-24 06:24:17 +08:00
|
|
|
} else {
|
|
|
|
/* Send negative response */
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM);
|
|
|
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
2012-10-24 06:24:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
|
|
|
|
sizeof(rsp), &rsp);
|
|
|
|
|
|
|
|
if (result == L2CAP_CR_SUCCESS) {
|
2013-10-22 00:21:40 +08:00
|
|
|
l2cap_state_change(chan, BT_CONFIG);
|
2012-10-24 06:24:17 +08:00
|
|
|
set_bit(CONF_REQ_SENT, &chan->conf_state);
|
|
|
|
l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
|
|
|
|
L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
|
2012-10-24 06:24:17 +08:00
|
|
|
chan->num_conf_req++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
|
|
|
|
u8 remote_amp_id)
|
|
|
|
{
|
|
|
|
l2cap_move_setup(chan);
|
|
|
|
chan->move_id = local_amp_id;
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_RSP;
|
|
|
|
|
|
|
|
l2cap_send_move_chan_req(chan, remote_amp_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
|
|
|
|
{
|
|
|
|
struct hci_chan *hchan = NULL;
|
|
|
|
|
|
|
|
/* Placeholder - get hci_chan for logical link */
|
|
|
|
|
|
|
|
if (hchan) {
|
|
|
|
if (hchan->state == BT_CONNECTED) {
|
|
|
|
/* Logical link is ready to go */
|
|
|
|
chan->hs_hcon = hchan->conn;
|
|
|
|
chan->hs_hcon->l2cap_data = chan->conn;
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
|
|
|
|
l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
|
|
|
|
|
|
|
|
l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
|
|
|
|
} else {
|
|
|
|
/* Wait for logical link to be ready */
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Logical link not available */
|
|
|
|
l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
|
|
|
|
{
|
|
|
|
if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
|
|
|
|
u8 rsp_result;
|
|
|
|
if (result == -EINVAL)
|
|
|
|
rsp_result = L2CAP_MR_BAD_ID;
|
|
|
|
else
|
|
|
|
rsp_result = L2CAP_MR_NOT_ALLOWED;
|
|
|
|
|
|
|
|
l2cap_send_move_chan_rsp(chan, rsp_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_NONE;
|
|
|
|
chan->move_state = L2CAP_MOVE_STABLE;
|
|
|
|
|
|
|
|
/* Restart data transmission */
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
}
|
|
|
|
|
2012-11-14 23:39:30 +08:00
|
|
|
/* Invoke with locked chan */
|
|
|
|
void __l2cap_physical_cfm(struct l2cap_chan *chan, int result)
|
2012-10-24 06:24:17 +08:00
|
|
|
{
|
2012-10-31 21:46:29 +08:00
|
|
|
u8 local_amp_id = chan->local_amp_id;
|
2012-11-01 21:37:03 +08:00
|
|
|
u8 remote_amp_id = chan->remote_amp_id;
|
2012-10-31 21:46:29 +08:00
|
|
|
|
2012-10-24 06:24:17 +08:00
|
|
|
BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
|
|
|
|
chan, result, local_amp_id, remote_amp_id);
|
|
|
|
|
|
|
|
if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->state != BT_CONNECTED) {
|
|
|
|
l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
|
|
|
|
} else if (result != L2CAP_MR_SUCCESS) {
|
|
|
|
l2cap_do_move_cancel(chan, result);
|
|
|
|
} else {
|
|
|
|
switch (chan->move_role) {
|
|
|
|
case L2CAP_MOVE_ROLE_INITIATOR:
|
|
|
|
l2cap_do_move_initiate(chan, local_amp_id,
|
|
|
|
remote_amp_id);
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_ROLE_RESPONDER:
|
|
|
|
l2cap_do_move_respond(chan, result);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
l2cap_do_move_cancel(chan, result);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
|
2012-07-10 20:27:50 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u16 cmd_len, void *data)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_req *req = data;
|
2012-10-24 06:24:15 +08:00
|
|
|
struct l2cap_move_chan_rsp rsp;
|
2012-10-24 06:24:10 +08:00
|
|
|
struct l2cap_chan *chan;
|
2011-11-03 07:18:35 +08:00
|
|
|
u16 icid = 0;
|
|
|
|
u16 result = L2CAP_MR_NOT_ALLOWED;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(*req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
icid = le16_to_cpu(req->icid);
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2014-12-02 16:09:26 +08:00
|
|
|
if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
|
2011-11-03 07:18:35 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-10-24 06:24:10 +08:00
|
|
|
chan = l2cap_get_chan_by_dcid(conn, icid);
|
|
|
|
if (!chan) {
|
2012-10-24 06:24:15 +08:00
|
|
|
rsp.icid = cpu_to_le16(icid);
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
|
2012-10-24 06:24:15 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP,
|
|
|
|
sizeof(rsp), &rsp);
|
2012-10-24 06:24:10 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:15 +08:00
|
|
|
chan->ident = cmd->ident;
|
|
|
|
|
2012-10-24 06:24:10 +08:00
|
|
|
if (chan->scid < L2CAP_CID_DYN_START ||
|
|
|
|
chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
|
|
|
|
(chan->mode != L2CAP_MODE_ERTM &&
|
|
|
|
chan->mode != L2CAP_MODE_STREAMING)) {
|
|
|
|
result = L2CAP_MR_NOT_ALLOWED;
|
|
|
|
goto send_move_response;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->local_amp_id == req->dest_amp_id) {
|
|
|
|
result = L2CAP_MR_SAME_ID;
|
|
|
|
goto send_move_response;
|
|
|
|
}
|
|
|
|
|
2013-10-06 02:47:44 +08:00
|
|
|
if (req->dest_amp_id != AMP_ID_BREDR) {
|
2012-10-24 06:24:10 +08:00
|
|
|
struct hci_dev *hdev;
|
|
|
|
hdev = hci_dev_get(req->dest_amp_id);
|
|
|
|
if (!hdev || hdev->dev_type != HCI_AMP ||
|
|
|
|
!test_bit(HCI_UP, &hdev->flags)) {
|
|
|
|
if (hdev)
|
|
|
|
hci_dev_put(hdev);
|
|
|
|
|
|
|
|
result = L2CAP_MR_BAD_ID;
|
|
|
|
goto send_move_response;
|
|
|
|
}
|
|
|
|
hci_dev_put(hdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Detect a move collision. Only send a collision response
|
|
|
|
* if this side has "lost", otherwise proceed with the move.
|
|
|
|
* The winner has the larger bd_addr.
|
|
|
|
*/
|
|
|
|
if ((__chan_is_moving(chan) ||
|
|
|
|
chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
|
2013-10-13 20:24:01 +08:00
|
|
|
bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) {
|
2012-10-24 06:24:10 +08:00
|
|
|
result = L2CAP_MR_COLLISION;
|
|
|
|
goto send_move_response;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
|
|
|
|
l2cap_move_setup(chan);
|
|
|
|
chan->move_id = req->dest_amp_id;
|
|
|
|
icid = chan->dcid;
|
|
|
|
|
2013-10-06 02:47:44 +08:00
|
|
|
if (req->dest_amp_id == AMP_ID_BREDR) {
|
2012-10-24 06:24:10 +08:00
|
|
|
/* Moving to BR/EDR */
|
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
|
|
|
|
result = L2CAP_MR_PEND;
|
|
|
|
} else {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
|
|
|
|
result = L2CAP_MR_SUCCESS;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
|
|
|
|
/* Placeholder - uncomment when amp functions are available */
|
|
|
|
/*amp_accept_physical(chan, req->dest_amp_id);*/
|
|
|
|
result = L2CAP_MR_PEND;
|
|
|
|
}
|
|
|
|
|
|
|
|
send_move_response:
|
2012-10-24 06:24:15 +08:00
|
|
|
l2cap_send_move_chan_rsp(chan, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:10 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
|
|
|
|
{
|
|
|
|
struct l2cap_chan *chan;
|
|
|
|
struct hci_chan *hchan = NULL;
|
|
|
|
|
|
|
|
chan = l2cap_get_chan_by_scid(conn, icid);
|
|
|
|
if (!chan) {
|
|
|
|
l2cap_send_move_chan_cfm_icid(conn, icid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
__clear_chan_timer(chan);
|
|
|
|
if (result == L2CAP_MR_PEND)
|
|
|
|
__set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
|
|
|
|
|
|
|
|
switch (chan->move_state) {
|
|
|
|
case L2CAP_MOVE_WAIT_LOGICAL_COMP:
|
|
|
|
/* Move confirm will be sent when logical link
|
|
|
|
* is complete.
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_WAIT_RSP_SUCCESS:
|
|
|
|
if (result == L2CAP_MR_PEND) {
|
|
|
|
break;
|
|
|
|
} else if (test_bit(CONN_LOCAL_BUSY,
|
|
|
|
&chan->conn_state)) {
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
|
|
|
|
} else {
|
|
|
|
/* Logical link is up or moving to BR/EDR,
|
|
|
|
* proceed with move
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_MOVE_WAIT_RSP:
|
|
|
|
/* Moving to AMP */
|
|
|
|
if (result == L2CAP_MR_SUCCESS) {
|
|
|
|
/* Remote is ready, send confirm immediately
|
|
|
|
* after logical link is ready
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
|
|
|
|
} else {
|
|
|
|
/* Both logical link and move success
|
|
|
|
* are required to confirm
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Placeholder - get hci_chan for logical link */
|
|
|
|
if (!hchan) {
|
|
|
|
/* Logical link not available */
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the logical link is not yet connected, do not
|
|
|
|
* send confirmation.
|
|
|
|
*/
|
|
|
|
if (hchan->state != BT_CONNECTED)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Logical link is already ready to go */
|
|
|
|
|
|
|
|
chan->hs_hcon = hchan->conn;
|
|
|
|
chan->hs_hcon->l2cap_data = chan->conn;
|
|
|
|
|
|
|
|
if (result == L2CAP_MR_SUCCESS) {
|
|
|
|
/* Can confirm now */
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
|
|
|
|
} else {
|
|
|
|
/* Now only need move success
|
|
|
|
* to confirm
|
|
|
|
*/
|
|
|
|
chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Any other amp move state means the move failed. */
|
|
|
|
chan->move_id = chan->local_amp_id;
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
|
|
|
|
u16 result)
|
|
|
|
{
|
|
|
|
struct l2cap_chan *chan;
|
|
|
|
|
|
|
|
chan = l2cap_get_chan_by_ident(conn, ident);
|
|
|
|
if (!chan) {
|
|
|
|
/* Could not locate channel, icid is best guess */
|
|
|
|
l2cap_send_move_chan_cfm_icid(conn, icid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
__clear_chan_timer(chan);
|
|
|
|
|
|
|
|
if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
|
|
|
|
if (result == L2CAP_MR_COLLISION) {
|
|
|
|
chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
|
|
|
|
} else {
|
|
|
|
/* Cleanup - cancel move */
|
|
|
|
chan->move_id = chan->local_amp_id;
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
|
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u16 cmd_len, void *data)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_rsp *rsp = data;
|
|
|
|
u16 icid, result;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(*rsp))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
icid = le16_to_cpu(rsp->icid);
|
|
|
|
result = le16_to_cpu(rsp->result);
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:14 +08:00
|
|
|
if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
|
|
|
|
l2cap_move_continue(conn, icid, result);
|
|
|
|
else
|
|
|
|
l2cap_move_fail(conn, cmd->ident, icid, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:12 +08:00
|
|
|
static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u16 cmd_len, void *data)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_cfm *cfm = data;
|
2012-10-24 06:24:12 +08:00
|
|
|
struct l2cap_chan *chan;
|
2011-11-03 07:18:35 +08:00
|
|
|
u16 icid, result;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(*cfm))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
icid = le16_to_cpu(cfm->icid);
|
|
|
|
result = le16_to_cpu(cfm->result);
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:12 +08:00
|
|
|
chan = l2cap_get_chan_by_dcid(conn, icid);
|
|
|
|
if (!chan) {
|
|
|
|
/* Spec requires a response even if the icid was not found */
|
|
|
|
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
|
|
|
|
if (result == L2CAP_MC_CONFIRMED) {
|
|
|
|
chan->local_amp_id = chan->move_id;
|
2013-10-06 02:47:44 +08:00
|
|
|
if (chan->local_amp_id == AMP_ID_BREDR)
|
2012-10-24 06:24:12 +08:00
|
|
|
__release_logical_link(chan);
|
|
|
|
} else {
|
|
|
|
chan->move_id = chan->local_amp_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
}
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
|
|
|
|
|
2012-10-24 06:24:12 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
|
2012-07-10 20:27:50 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd,
|
|
|
|
u16 cmd_len, void *data)
|
2011-11-03 07:18:35 +08:00
|
|
|
{
|
|
|
|
struct l2cap_move_chan_cfm_rsp *rsp = data;
|
2012-10-24 06:24:16 +08:00
|
|
|
struct l2cap_chan *chan;
|
2011-11-03 07:18:35 +08:00
|
|
|
u16 icid;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(*rsp))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
icid = le16_to_cpu(rsp->icid);
|
|
|
|
|
2012-07-10 20:27:50 +08:00
|
|
|
BT_DBG("icid 0x%4.4x", icid);
|
2011-11-03 07:18:35 +08:00
|
|
|
|
2012-10-24 06:24:16 +08:00
|
|
|
chan = l2cap_get_chan_by_scid(conn, icid);
|
|
|
|
if (!chan)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
__clear_chan_timer(chan);
|
|
|
|
|
|
|
|
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
|
|
|
|
chan->local_amp_id = chan->move_id;
|
|
|
|
|
2013-10-06 02:47:44 +08:00
|
|
|
if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan)
|
2012-10-24 06:24:16 +08:00
|
|
|
__release_logical_link(chan);
|
|
|
|
|
|
|
|
l2cap_move_done(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-12 05:28:55 +08:00
|
|
|
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
2012-10-06 17:07:01 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd,
|
2013-05-15 15:07:15 +08:00
|
|
|
u16 cmd_len, u8 *data)
|
2011-02-12 05:28:55 +08:00
|
|
|
{
|
|
|
|
struct hci_conn *hcon = conn->hcon;
|
|
|
|
struct l2cap_conn_param_update_req *req;
|
|
|
|
struct l2cap_conn_param_update_rsp rsp;
|
2013-05-15 15:07:15 +08:00
|
|
|
u16 min, max, latency, to_multiplier;
|
2011-02-17 06:44:53 +08:00
|
|
|
int err;
|
2011-02-12 05:28:55 +08:00
|
|
|
|
2014-07-16 16:42:27 +08:00
|
|
|
if (hcon->role != HCI_ROLE_MASTER)
|
2011-02-12 05:28:55 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
req = (struct l2cap_conn_param_update_req *) data;
|
2011-02-18 06:16:55 +08:00
|
|
|
min = __le16_to_cpu(req->min);
|
|
|
|
max = __le16_to_cpu(req->max);
|
2011-02-12 05:28:55 +08:00
|
|
|
latency = __le16_to_cpu(req->latency);
|
|
|
|
to_multiplier = __le16_to_cpu(req->to_multiplier);
|
|
|
|
|
|
|
|
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x",
|
2012-10-06 17:07:01 +08:00
|
|
|
min, max, latency, to_multiplier);
|
2011-02-12 05:28:55 +08:00
|
|
|
|
|
|
|
memset(&rsp, 0, sizeof(rsp));
|
2011-02-17 06:44:53 +08:00
|
|
|
|
2014-06-26 08:52:52 +08:00
|
|
|
err = hci_check_conn_params(min, max, latency, to_multiplier);
|
2011-02-17 06:44:53 +08:00
|
|
|
if (err)
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
|
2011-02-12 05:28:55 +08:00
|
|
|
else
|
2014-03-13 01:52:35 +08:00
|
|
|
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
|
2011-02-12 05:28:55 +08:00
|
|
|
|
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
|
2012-10-06 17:07:01 +08:00
|
|
|
sizeof(rsp), &rsp);
|
2011-02-12 05:28:55 +08:00
|
|
|
|
2014-07-02 05:10:11 +08:00
|
|
|
if (!err) {
|
2014-07-02 22:37:32 +08:00
|
|
|
u8 store_hint;
|
|
|
|
|
|
|
|
store_hint = hci_le_conn_update(hcon, min, max, latency,
|
|
|
|
to_multiplier);
|
2014-07-02 05:10:11 +08:00
|
|
|
mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
|
2014-07-02 22:37:32 +08:00
|
|
|
store_hint, min, max, latency,
|
|
|
|
to_multiplier);
|
2014-07-02 05:10:11 +08:00
|
|
|
|
|
|
|
}
|
2011-02-17 06:44:53 +08:00
|
|
|
|
2011-02-12 05:28:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
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;
|
2014-11-13 16:55:19 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2013-05-13 19:15:56 +08:00
|
|
|
u16 dcid, mtu, mps, credits, result;
|
|
|
|
struct l2cap_chan *chan;
|
2014-11-13 16:55:19 +08:00
|
|
|
int err, sec_level;
|
2013-05-13 19:15:56 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2018-10-05 17:18:12 +08:00
|
|
|
if (result == L2CAP_CR_LE_SUCCESS && (mtu < 23 || mps < 23 ||
|
2015-11-02 20:39:17 +08:00
|
|
|
dcid < L2CAP_CID_DYN_START ||
|
|
|
|
dcid > L2CAP_CID_LE_DYN_END))
|
2013-05-13 19:15:56 +08:00
|
|
|
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) {
|
2018-10-05 17:18:12 +08:00
|
|
|
case L2CAP_CR_LE_SUCCESS:
|
2015-11-02 20:39:17 +08:00
|
|
|
if (__l2cap_get_chan_by_dcid(conn, dcid)) {
|
|
|
|
err = -EBADSLT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
chan->ident = 0;
|
|
|
|
chan->dcid = dcid;
|
|
|
|
chan->omtu = mtu;
|
|
|
|
chan->remote_mps = mps;
|
2013-05-17 18:09:05 +08:00
|
|
|
chan->tx_credits = credits;
|
2013-05-13 19:15:56 +08:00
|
|
|
l2cap_chan_ready(chan);
|
|
|
|
break;
|
|
|
|
|
2018-10-05 17:18:12 +08:00
|
|
|
case L2CAP_CR_LE_AUTHENTICATION:
|
|
|
|
case L2CAP_CR_LE_ENCRYPTION:
|
2014-11-13 16:55:19 +08:00
|
|
|
/* 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_LE_CONN_REQ_SENT, &chan->flags);
|
|
|
|
|
|
|
|
smp_conn_security(hcon, chan->sec_level);
|
|
|
|
break;
|
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
default:
|
|
|
|
l2cap_chan_del(chan, ECONNREFUSED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
2012-10-06 17:07:01 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2011-02-12 05:28:54 +08:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
switch (cmd->code) {
|
|
|
|
case L2CAP_COMMAND_REJ:
|
2013-05-28 18:46:30 +08:00
|
|
|
l2cap_command_rej(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONN_REQ:
|
2013-05-28 18:46:30 +08:00
|
|
|
err = l2cap_connect_req(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONN_RSP:
|
2012-10-11 22:48:21 +08:00
|
|
|
case L2CAP_CREATE_CHAN_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_REQ:
|
|
|
|
err = l2cap_config_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_CONF_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_config_rsp(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_DISCONN_REQ:
|
2013-05-28 18:46:30 +08:00
|
|
|
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_DISCONN_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_ECHO_REQ:
|
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_ECHO_RSP:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_INFO_REQ:
|
2013-05-28 18:46:30 +08:00
|
|
|
err = l2cap_information_req(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_INFO_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_information_rsp(conn, cmd, cmd_len, data);
|
2011-02-12 05:28:54 +08:00
|
|
|
break;
|
|
|
|
|
2011-11-03 07:18:32 +08:00
|
|
|
case L2CAP_CREATE_CHAN_REQ:
|
|
|
|
err = l2cap_create_channel_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
2011-11-03 07:18:35 +08:00
|
|
|
case L2CAP_MOVE_CHAN_REQ:
|
|
|
|
err = l2cap_move_channel_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MOVE_CHAN_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
|
2011-11-03 07:18:35 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MOVE_CHAN_CFM:
|
|
|
|
err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MOVE_CHAN_CFM_RSP:
|
2013-09-16 18:05:17 +08:00
|
|
|
l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
|
2011-11-03 07:18:35 +08:00
|
|
|
break;
|
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
default:
|
|
|
|
BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
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;
|
2013-05-17 18:09:05 +08:00
|
|
|
u16 dcid, scid, credits, mtu, mps;
|
2013-05-14 18:27:21 +08:00
|
|
|
__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;
|
2013-05-17 18:09:05 +08:00
|
|
|
credits = 0;
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
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) {
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_BAD_PSM;
|
2013-05-14 18:27:21 +08:00
|
|
|
chan = NULL;
|
|
|
|
goto response;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
l2cap_chan_lock(pchan);
|
|
|
|
|
2014-11-13 16:55:18 +08:00
|
|
|
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
|
|
|
|
SMP_ALLOW_STK)) {
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_AUTHENTICATION;
|
2013-05-14 18:27:21 +08:00
|
|
|
chan = NULL;
|
|
|
|
goto response_unlock;
|
|
|
|
}
|
|
|
|
|
2015-11-02 20:39:15 +08:00
|
|
|
/* Check for valid dynamic CID range */
|
|
|
|
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_INVALID_SCID;
|
2015-11-02 20:39:15 +08:00
|
|
|
chan = NULL;
|
|
|
|
goto response_unlock;
|
|
|
|
}
|
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
/* Check if we already have channel with that dcid */
|
|
|
|
if (__l2cap_get_chan_by_dcid(conn, scid)) {
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_SCID_IN_USE;
|
2013-05-14 18:27:21 +08:00
|
|
|
chan = NULL;
|
|
|
|
goto response_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan = pchan->ops->new_connection(pchan);
|
|
|
|
if (!chan) {
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_NO_MEM;
|
2013-05-14 18:27:21 +08:00
|
|
|
goto response_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
bacpy(&chan->src, &conn->hcon->src);
|
|
|
|
bacpy(&chan->dst, &conn->hcon->dst);
|
2015-01-15 19:06:44 +08:00
|
|
|
chan->src_type = bdaddr_src_type(conn->hcon);
|
|
|
|
chan->dst_type = bdaddr_dst_type(conn->hcon);
|
2013-05-14 18:27:21 +08:00
|
|
|
chan->psm = psm;
|
|
|
|
chan->dcid = scid;
|
|
|
|
chan->omtu = mtu;
|
|
|
|
chan->remote_mps = mps;
|
2013-05-17 18:09:05 +08:00
|
|
|
chan->tx_credits = __le16_to_cpu(req->credits);
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
__l2cap_chan_add(conn, chan);
|
2018-09-04 18:39:20 +08:00
|
|
|
|
|
|
|
l2cap_le_flowctl_init(chan);
|
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
dcid = chan->scid;
|
2013-05-17 18:09:05 +08:00
|
|
|
credits = chan->rx_credits;
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
__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);
|
2014-09-01 14:45:03 +08:00
|
|
|
/* The following result value is actually not defined
|
|
|
|
* for LE CoC but we use it to let the function know
|
|
|
|
* that it should bail out after doing its cleanup
|
|
|
|
* instead of sending a response.
|
|
|
|
*/
|
2013-05-14 18:27:21 +08:00
|
|
|
result = L2CAP_CR_PEND;
|
|
|
|
chan->ops->defer(chan);
|
|
|
|
} else {
|
|
|
|
l2cap_chan_ready(chan);
|
2018-10-05 17:18:12 +08:00
|
|
|
result = L2CAP_CR_LE_SUCCESS;
|
2013-05-14 18:27:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
response_unlock:
|
|
|
|
l2cap_chan_unlock(pchan);
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
2014-08-08 03:56:42 +08:00
|
|
|
l2cap_chan_put(pchan);
|
2013-05-14 18:27:21 +08:00
|
|
|
|
|
|
|
if (result == L2CAP_CR_PEND)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
response:
|
|
|
|
if (chan) {
|
|
|
|
rsp.mtu = cpu_to_le16(chan->imtu);
|
2013-10-07 21:35:26 +08:00
|
|
|
rsp.mps = cpu_to_le16(chan->mps);
|
2013-05-14 18:27:21 +08:00
|
|
|
} else {
|
|
|
|
rsp.mtu = 0;
|
|
|
|
rsp.mps = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rsp.dcid = cpu_to_le16(dcid);
|
2013-05-17 18:09:05 +08:00
|
|
|
rsp.credits = cpu_to_le16(credits);
|
2013-05-14 18:27:21 +08:00
|
|
|
rsp.result = cpu_to_le16(result);
|
|
|
|
|
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:45:01 +08:00
|
|
|
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;
|
2014-01-28 07:11:35 +08:00
|
|
|
u16 cid, credits, max_credits;
|
2013-12-05 15:45:01 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-01-28 07:11:35 +08:00
|
|
|
max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
|
|
|
|
if (credits > max_credits) {
|
|
|
|
BT_ERR("LE credits overflow");
|
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2014-10-14 02:24:45 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2014-01-28 07:11:35 +08:00
|
|
|
|
|
|
|
/* Return 0 so that we don't trigger an unnecessary
|
|
|
|
* command reject packet.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:45:01 +08:00
|
|
|
chan->tx_credits += credits;
|
|
|
|
|
2017-04-12 03:21:01 +08:00
|
|
|
/* Resume sending */
|
|
|
|
l2cap_le_flowctl_send(chan);
|
2013-12-05 15:45:01 +08:00
|
|
|
|
|
|
|
if (chan->tx_credits)
|
|
|
|
chan->ops->resume(chan);
|
|
|
|
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-10 16:52:48 +08:00
|
|
|
static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
|
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
|
|
|
{
|
|
|
|
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
|
|
|
|
struct l2cap_chan *chan;
|
|
|
|
|
|
|
|
if (cmd_len < sizeof(*rej))
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
|
|
|
|
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
|
|
|
if (!chan)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
l2cap_chan_del(chan, ECONNREFUSED);
|
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
|
|
|
|
done:
|
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
2013-05-15 15:07:15 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
|
|
|
u8 *data)
|
2011-02-12 05:28:54 +08:00
|
|
|
{
|
2013-12-02 18:21:29 +08:00
|
|
|
int err = 0;
|
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
switch (cmd->code) {
|
|
|
|
case L2CAP_COMMAND_REJ:
|
2013-12-10 16:52:48 +08:00
|
|
|
l2cap_le_command_rej(conn, cmd, cmd_len, data);
|
2013-12-02 18:21:29 +08:00
|
|
|
break;
|
2011-02-12 05:28:54 +08:00
|
|
|
|
|
|
|
case L2CAP_CONN_PARAM_UPDATE_REQ:
|
2013-12-02 18:21:29 +08:00
|
|
|
err = l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
2011-02-12 05:28:54 +08:00
|
|
|
|
|
|
|
case L2CAP_CONN_PARAM_UPDATE_RSP:
|
2013-12-02 18:21:29 +08:00
|
|
|
break;
|
2011-02-12 05:28:54 +08:00
|
|
|
|
2013-05-13 19:15:56 +08:00
|
|
|
case L2CAP_LE_CONN_RSP:
|
|
|
|
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
|
2013-12-02 18:21:29 +08:00
|
|
|
break;
|
2013-05-13 19:15:56 +08:00
|
|
|
|
2013-05-14 18:27:21 +08:00
|
|
|
case L2CAP_LE_CONN_REQ:
|
2013-12-02 18:21:29 +08:00
|
|
|
err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
2013-05-14 18:27:21 +08:00
|
|
|
|
2013-12-05 15:45:01 +08:00
|
|
|
case L2CAP_LE_CREDITS:
|
|
|
|
err = l2cap_le_credits(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
|
|
|
|
2013-05-15 15:16:06 +08:00
|
|
|
case L2CAP_DISCONN_REQ:
|
2013-12-02 18:21:29 +08:00
|
|
|
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
|
|
|
|
break;
|
2013-05-15 15:16:06 +08:00
|
|
|
|
|
|
|
case L2CAP_DISCONN_RSP:
|
|
|
|
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
|
2013-12-02 18:21:29 +08:00
|
|
|
break;
|
2013-05-15 15:16:06 +08:00
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
default:
|
|
|
|
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
|
2013-12-02 18:21:29 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
2011-02-12 05:28:54 +08:00
|
|
|
}
|
2013-12-02 18:21:29 +08:00
|
|
|
|
|
|
|
return err;
|
2011-02-12 05:28:54 +08:00
|
|
|
}
|
|
|
|
|
2013-04-30 00:35:33 +08:00
|
|
|
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
2013-09-16 18:05:18 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2013-10-03 16:26:37 +08:00
|
|
|
struct l2cap_cmd_hdr *cmd;
|
|
|
|
u16 len;
|
2013-04-30 00:35:33 +08:00
|
|
|
int err;
|
|
|
|
|
2013-09-16 18:05:18 +08:00
|
|
|
if (hcon->type != LE_LINK)
|
2013-10-02 23:28:21 +08:00
|
|
|
goto drop;
|
2013-09-16 18:05:18 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
if (skb->len < L2CAP_CMD_HDR_SIZE)
|
|
|
|
goto drop;
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
cmd = (void *) skb->data;
|
|
|
|
skb_pull(skb, L2CAP_CMD_HDR_SIZE);
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
len = le16_to_cpu(cmd->len);
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
if (len != skb->len || !cmd->ident) {
|
|
|
|
BT_DBG("corrupted command");
|
|
|
|
goto drop;
|
|
|
|
}
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-05-15 15:07:15 +08:00
|
|
|
err = l2cap_le_sig_cmd(conn, cmd, len, skb->data);
|
2013-10-03 16:26:37 +08:00
|
|
|
if (err) {
|
|
|
|
struct l2cap_cmd_rej_unk rej;
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2013-10-03 16:26:37 +08:00
|
|
|
BT_ERR("Wrong link type (%d)", err);
|
2013-04-30 00:35:33 +08:00
|
|
|
|
2014-03-13 01:52:35 +08:00
|
|
|
rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
2013-10-03 16:26:37 +08:00
|
|
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
|
|
|
|
sizeof(rej), &rej);
|
2013-04-30 00:35:33 +08:00
|
|
|
}
|
|
|
|
|
2013-10-02 23:28:21 +08:00
|
|
|
drop:
|
2013-04-30 00:35:33 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2011-02-12 05:28:54 +08:00
|
|
|
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
2012-10-06 17:07:01 +08:00
|
|
|
struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-09-16 18:05:18 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 *data = skb->data;
|
|
|
|
int len = skb->len;
|
|
|
|
struct l2cap_cmd_hdr cmd;
|
2011-02-12 05:28:54 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
l2cap_raw_recv(conn, skb);
|
|
|
|
|
2013-09-16 18:05:18 +08:00
|
|
|
if (hcon->type != ACL_LINK)
|
2013-10-02 23:28:21 +08:00
|
|
|
goto drop;
|
2013-09-16 18:05:18 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
while (len >= L2CAP_CMD_HDR_SIZE) {
|
2007-07-29 15:17:25 +08:00
|
|
|
u16 cmd_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
|
|
|
|
data += L2CAP_CMD_HDR_SIZE;
|
|
|
|
len -= L2CAP_CMD_HDR_SIZE;
|
|
|
|
|
2007-07-29 15:17:25 +08:00
|
|
|
cmd_len = le16_to_cpu(cmd.len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
|
|
|
|
cmd.ident);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-07-29 15:17:25 +08:00
|
|
|
if (cmd_len > len || !cmd.ident) {
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_DBG("corrupted command");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-30 00:35:33 +08:00
|
|
|
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err) {
|
2011-07-10 13:47:44 +08:00
|
|
|
struct l2cap_cmd_rej_unk rej;
|
2011-03-24 01:38:32 +08:00
|
|
|
|
|
|
|
BT_ERR("Wrong link type (%d)", err);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-03-13 01:52:35 +08:00
|
|
|
rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
2012-10-06 17:07:01 +08:00
|
|
|
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
|
|
|
|
sizeof(rej), &rej);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-07-29 15:17:25 +08:00
|
|
|
data += cmd_len;
|
|
|
|
len -= cmd_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-10-02 23:28:21 +08:00
|
|
|
drop:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb)
|
2009-08-21 09:26:02 +08:00
|
|
|
{
|
|
|
|
u16 our_fcs, rcv_fcs;
|
2011-10-11 18:37:52 +08:00
|
|
|
int hdr_size;
|
|
|
|
|
|
|
|
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
|
|
|
|
hdr_size = L2CAP_EXT_HDR_SIZE;
|
|
|
|
else
|
|
|
|
hdr_size = L2CAP_ENH_HDR_SIZE;
|
2009-08-21 09:26:02 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16) {
|
2011-10-17 17:19:58 +08:00
|
|
|
skb_trim(skb, skb->len - L2CAP_FCS_SIZE);
|
2009-08-21 09:26:02 +08:00
|
|
|
rcv_fcs = get_unaligned_le16(skb->data + skb->len);
|
|
|
|
our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
|
|
|
|
|
|
|
|
if (our_fcs != rcv_fcs)
|
2010-06-23 00:56:27 +08:00
|
|
|
return -EBADMSG;
|
2009-08-21 09:26:02 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:52 +08:00
|
|
|
static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
|
2010-05-02 03:15:36 +08:00
|
|
|
{
|
2012-05-18 11:53:41 +08:00
|
|
|
struct l2cap_ctrl control;
|
2010-05-02 03:15:36 +08:00
|
|
|
|
2012-05-18 11:53:41 +08:00
|
|
|
BT_DBG("chan %p", chan);
|
2010-05-02 03:15:36 +08:00
|
|
|
|
2012-05-18 11:53:41 +08:00
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
control.sframe = 1;
|
|
|
|
control.final = 1;
|
|
|
|
control.reqseq = chan->buffer_seq;
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
2010-05-02 03:15:36 +08:00
|
|
|
|
2011-06-11 08:28:49 +08:00
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
2012-05-18 11:53:41 +08:00
|
|
|
control.super = L2CAP_SUPER_RNR;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
2010-05-02 03:15:36 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:41 +08:00
|
|
|
if (test_and_clear_bit(CONN_REMOTE_BUSY, &chan->conn_state) &&
|
|
|
|
chan->unacked_frames > 0)
|
|
|
|
__set_retrans_timer(chan);
|
2010-05-02 03:15:36 +08:00
|
|
|
|
2012-05-18 11:53:41 +08:00
|
|
|
/* Send pending iframes */
|
2011-03-26 06:43:39 +08:00
|
|
|
l2cap_ertm_send(chan);
|
2010-05-02 03:15:36 +08:00
|
|
|
|
2011-06-11 08:28:49 +08:00
|
|
|
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) &&
|
2012-05-18 11:53:41 +08:00
|
|
|
test_bit(CONN_SEND_FBIT, &chan->conn_state)) {
|
|
|
|
/* F-bit wasn't sent in an s-frame or i-frame yet, so
|
|
|
|
* send it now.
|
|
|
|
*/
|
|
|
|
control.super = L2CAP_SUPER_RR;
|
|
|
|
l2cap_send_sframe(chan, &control);
|
2010-05-02 03:15:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-06 17:07:01 +08:00
|
|
|
static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag,
|
|
|
|
struct sk_buff **last_frag)
|
2010-05-02 03:15:44 +08:00
|
|
|
{
|
2011-07-23 05:54:00 +08:00
|
|
|
/* skb->len reflects data in skb as well as all fragments
|
|
|
|
* skb->data_len reflects only data in fragments
|
|
|
|
*/
|
|
|
|
if (!skb_has_frag_list(skb))
|
|
|
|
skb_shinfo(skb)->frag_list = new_frag;
|
|
|
|
|
|
|
|
new_frag->next = NULL;
|
|
|
|
|
|
|
|
(*last_frag)->next = new_frag;
|
|
|
|
*last_frag = new_frag;
|
|
|
|
|
|
|
|
skb->len += new_frag->len;
|
|
|
|
skb->data_len += new_frag->len;
|
|
|
|
skb->truesize += new_frag->truesize;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:37 +08:00
|
|
|
static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
|
|
|
|
struct l2cap_ctrl *control)
|
2011-07-23 05:54:00 +08:00
|
|
|
{
|
|
|
|
int err = -EINVAL;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2012-05-18 11:53:37 +08:00
|
|
|
switch (control->sar) {
|
2011-10-11 18:37:45 +08:00
|
|
|
case L2CAP_SAR_UNSEGMENTED:
|
2011-07-23 05:54:00 +08:00
|
|
|
if (chan->sdu)
|
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
err = chan->ops->recv(chan, skb);
|
2011-07-23 05:54:00 +08:00
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-10-11 18:37:45 +08:00
|
|
|
case L2CAP_SAR_START:
|
2011-07-23 05:54:00 +08:00
|
|
|
if (chan->sdu)
|
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
Bluetooth: split sk_filter in l2cap_sock_recv_cb
During an audit for sk_filter(), we found that rx_busy_skb handling
in l2cap_sock_recv_cb() and l2cap_sock_recvmsg() looks not quite as
intended.
The assumption from commit e328140fdacb ("Bluetooth: Use event-driven
approach for handling ERTM receive buffer") is that errors returned
from sock_queue_rcv_skb() are due to receive buffer shortage. However,
nothing should prevent doing a setsockopt() with SO_ATTACH_FILTER on
the socket, that could drop some of the incoming skbs when handled in
sock_queue_rcv_skb().
In that case sock_queue_rcv_skb() will return with -EPERM, propagated
from sk_filter() and if in L2CAP_MODE_ERTM mode, wrong assumption was
that we failed due to receive buffer being full. From that point onwards,
due to the to-be-dropped skb being held in rx_busy_skb, we cannot make
any forward progress as rx_busy_skb is never cleared from l2cap_sock_recvmsg(),
due to the filter drop verdict over and over coming from sk_filter().
Meanwhile, in l2cap_sock_recv_cb() all new incoming skbs are being
dropped due to rx_busy_skb being occupied.
Instead, just use __sock_queue_rcv_skb() where an error really tells that
there's a receive buffer issue. Split the sk_filter() and enable it for
non-segmented modes at queuing time since at this point in time the skb has
already been through the ERTM state machine and it has been acked, so dropping
is not allowed. Instead, for ERTM and streaming mode, call sk_filter() in
l2cap_data_rcv() so the packet can be dropped before the state machine sees it.
Fixes: e328140fdacb ("Bluetooth: Use event-driven approach for handling ERTM receive buffer")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2016-07-28 02:40:14 +08:00
|
|
|
if (!pskb_may_pull(skb, L2CAP_SDULEN_SIZE))
|
|
|
|
break;
|
|
|
|
|
2011-03-26 07:09:37 +08:00
|
|
|
chan->sdu_len = get_unaligned_le16(skb->data);
|
2011-10-17 17:19:58 +08:00
|
|
|
skb_pull(skb, L2CAP_SDULEN_SIZE);
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (chan->sdu_len > chan->imtu) {
|
|
|
|
err = -EMSGSIZE;
|
|
|
|
break;
|
|
|
|
}
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (skb->len >= chan->sdu_len)
|
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
chan->sdu = skb;
|
|
|
|
chan->sdu_last_frag = skb;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
skb = NULL;
|
|
|
|
err = 0;
|
2010-05-02 03:15:44 +08:00
|
|
|
break;
|
|
|
|
|
2011-10-11 18:37:45 +08:00
|
|
|
case L2CAP_SAR_CONTINUE:
|
2011-03-26 07:09:37 +08:00
|
|
|
if (!chan->sdu)
|
2011-07-23 05:54:00 +08:00
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
append_skb_frag(chan->sdu, skb,
|
|
|
|
&chan->sdu_last_frag);
|
|
|
|
skb = NULL;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (chan->sdu->len >= chan->sdu_len)
|
|
|
|
break;
|
2010-05-02 03:15:45 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
err = 0;
|
2010-05-02 03:15:44 +08:00
|
|
|
break;
|
|
|
|
|
2011-10-11 18:37:45 +08:00
|
|
|
case L2CAP_SAR_END:
|
2011-03-26 07:09:37 +08:00
|
|
|
if (!chan->sdu)
|
2011-07-23 05:54:00 +08:00
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
append_skb_frag(chan->sdu, skb,
|
|
|
|
&chan->sdu_last_frag);
|
|
|
|
skb = NULL;
|
2010-05-02 03:15:45 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (chan->sdu->len != chan->sdu_len)
|
|
|
|
break;
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
err = chan->ops->recv(chan, chan->sdu);
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (!err) {
|
|
|
|
/* Reassembly complete */
|
|
|
|
chan->sdu = NULL;
|
|
|
|
chan->sdu_last_frag = NULL;
|
|
|
|
chan->sdu_len = 0;
|
2010-05-02 03:15:44 +08:00
|
|
|
}
|
2010-05-02 03:15:44 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
if (err) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
kfree_skb(chan->sdu);
|
|
|
|
chan->sdu = NULL;
|
|
|
|
chan->sdu_last_frag = NULL;
|
|
|
|
chan->sdu_len = 0;
|
|
|
|
}
|
2010-05-02 03:15:44 +08:00
|
|
|
|
2011-07-23 05:54:00 +08:00
|
|
|
return err;
|
2010-05-02 03:15:44 +08:00
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:11 +08:00
|
|
|
static int l2cap_resegment(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
/* Placeholder */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:40 +08:00
|
|
|
void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
|
2011-07-08 00:39:01 +08:00
|
|
|
{
|
2012-05-18 11:53:40 +08:00
|
|
|
u8 event;
|
2010-06-22 06:39:50 +08:00
|
|
|
|
2012-05-18 11:53:40 +08:00
|
|
|
if (chan->mode != L2CAP_MODE_ERTM)
|
|
|
|
return;
|
2010-06-22 06:39:50 +08:00
|
|
|
|
2012-05-18 11:53:40 +08:00
|
|
|
event = busy ? L2CAP_EV_LOCAL_BUSY_DETECTED : L2CAP_EV_LOCAL_BUSY_CLEAR;
|
2012-05-21 20:47:46 +08:00
|
|
|
l2cap_tx(chan, NULL, NULL, event);
|
2010-05-02 03:15:44 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:42 +08:00
|
|
|
static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
|
|
|
|
{
|
2012-05-18 11:53:45 +08:00
|
|
|
int err = 0;
|
|
|
|
/* Pass sequential frames to l2cap_reassemble_sdu()
|
|
|
|
* until a gap is encountered.
|
|
|
|
*/
|
|
|
|
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
while (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
|
|
|
struct sk_buff *skb;
|
|
|
|
BT_DBG("Searching for skb with txseq %d (queue len %d)",
|
|
|
|
chan->buffer_seq, skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
skb = l2cap_ertm_seq_in_queue(&chan->srej_q, chan->buffer_seq);
|
|
|
|
|
|
|
|
if (!skb)
|
|
|
|
break;
|
|
|
|
|
|
|
|
skb_unlink(skb, &chan->srej_q);
|
|
|
|
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
|
2015-03-31 04:21:01 +08:00
|
|
|
err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
|
2012-05-18 11:53:45 +08:00
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb_queue_empty(&chan->srej_q)) {
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_RECV;
|
|
|
|
l2cap_send_ack(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_handle_srej(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
2012-05-18 11:53:46 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
|
|
|
|
|
|
|
if (control->reqseq == chan->next_tx_seq) {
|
|
|
|
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
|
|
|
|
|
|
|
|
if (skb == NULL) {
|
|
|
|
BT_DBG("Seq %d not available for retransmission",
|
|
|
|
control->reqseq);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-31 04:21:01 +08:00
|
|
|
if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
|
2012-05-18 11:53:46 +08:00
|
|
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (control->poll) {
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
|
|
|
l2cap_retransmit(chan, control);
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
|
|
|
|
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
|
|
|
|
set_bit(CONN_SREJ_ACT, &chan->conn_state);
|
|
|
|
chan->srej_save_reqseq = control->reqseq;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
l2cap_pass_to_tx_fbit(chan, control);
|
|
|
|
|
|
|
|
if (control->final) {
|
|
|
|
if (chan->srej_save_reqseq != control->reqseq ||
|
|
|
|
!test_and_clear_bit(CONN_SREJ_ACT,
|
|
|
|
&chan->conn_state))
|
|
|
|
l2cap_retransmit(chan, control);
|
|
|
|
} else {
|
|
|
|
l2cap_retransmit(chan, control);
|
|
|
|
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F) {
|
|
|
|
set_bit(CONN_SREJ_ACT, &chan->conn_state);
|
|
|
|
chan->srej_save_reqseq = control->reqseq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_handle_rej(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control)
|
|
|
|
{
|
2012-05-18 11:53:47 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p", chan, control);
|
|
|
|
|
|
|
|
if (control->reqseq == chan->next_tx_seq) {
|
|
|
|
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
|
|
|
|
|
|
|
|
if (chan->max_tx && skb &&
|
2015-03-31 04:21:01 +08:00
|
|
|
bt_cb(skb)->l2cap.retries >= chan->max_tx) {
|
2012-05-18 11:53:47 +08:00
|
|
|
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
|
|
|
|
if (control->final) {
|
|
|
|
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
|
|
|
|
l2cap_retransmit_all(chan, control);
|
|
|
|
} else {
|
|
|
|
l2cap_retransmit_all(chan, control);
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
if (chan->tx_state == L2CAP_TX_STATE_WAIT_F)
|
|
|
|
set_bit(CONN_REJ_ACT, &chan->conn_state);
|
|
|
|
}
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:37 +08:00
|
|
|
static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p, txseq %d", chan, txseq);
|
|
|
|
|
|
|
|
BT_DBG("last_acked_seq %d, expected_tx_seq %d", chan->last_acked_seq,
|
|
|
|
chan->expected_tx_seq);
|
|
|
|
|
|
|
|
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
|
|
|
|
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->tx_win) {
|
2012-05-18 11:53:37 +08:00
|
|
|
/* See notes below regarding "double poll" and
|
|
|
|
* invalid packets.
|
|
|
|
*/
|
|
|
|
if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
|
|
|
|
BT_DBG("Invalid/Ignore - after SREJ");
|
|
|
|
return L2CAP_TXSEQ_INVALID_IGNORE;
|
|
|
|
} else {
|
|
|
|
BT_DBG("Invalid - in window after SREJ sent");
|
|
|
|
return L2CAP_TXSEQ_INVALID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->srej_list.head == txseq) {
|
|
|
|
BT_DBG("Expected SREJ");
|
|
|
|
return L2CAP_TXSEQ_EXPECTED_SREJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (l2cap_ertm_seq_in_queue(&chan->srej_q, txseq)) {
|
|
|
|
BT_DBG("Duplicate SREJ - txseq already stored");
|
|
|
|
return L2CAP_TXSEQ_DUPLICATE_SREJ;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (l2cap_seq_list_contains(&chan->srej_list, txseq)) {
|
|
|
|
BT_DBG("Unexpected SREJ - not requested");
|
|
|
|
return L2CAP_TXSEQ_UNEXPECTED_SREJ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->expected_tx_seq == txseq) {
|
|
|
|
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
|
|
|
|
chan->tx_win) {
|
|
|
|
BT_DBG("Invalid - txseq outside tx window");
|
|
|
|
return L2CAP_TXSEQ_INVALID;
|
|
|
|
} else {
|
|
|
|
BT_DBG("Expected");
|
|
|
|
return L2CAP_TXSEQ_EXPECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__seq_offset(chan, txseq, chan->last_acked_seq) <
|
2012-10-06 17:07:01 +08:00
|
|
|
__seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)) {
|
2012-05-18 11:53:37 +08:00
|
|
|
BT_DBG("Duplicate - expected_tx_seq later than txseq");
|
|
|
|
return L2CAP_TXSEQ_DUPLICATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__seq_offset(chan, txseq, chan->last_acked_seq) >= chan->tx_win) {
|
|
|
|
/* A source of invalid packets is a "double poll" condition,
|
|
|
|
* where delays cause us to send multiple poll packets. If
|
|
|
|
* the remote stack receives and processes both polls,
|
|
|
|
* sequence numbers can wrap around in such a way that a
|
|
|
|
* resent frame has a sequence number that looks like new data
|
|
|
|
* with a sequence gap. This would trigger an erroneous SREJ
|
|
|
|
* request.
|
|
|
|
*
|
|
|
|
* Fortunately, this is impossible with a tx window that's
|
|
|
|
* less than half of the maximum sequence number, which allows
|
|
|
|
* invalid frames to be safely ignored.
|
|
|
|
*
|
|
|
|
* With tx window sizes greater than half of the tx window
|
|
|
|
* maximum, the frame is invalid and cannot be ignored. This
|
|
|
|
* causes a disconnect.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (chan->tx_win <= ((chan->tx_win_max + 1) >> 1)) {
|
|
|
|
BT_DBG("Invalid/Ignore - txseq outside tx window");
|
|
|
|
return L2CAP_TXSEQ_INVALID_IGNORE;
|
|
|
|
} else {
|
|
|
|
BT_DBG("Invalid - txseq outside tx window");
|
|
|
|
return L2CAP_TXSEQ_INVALID;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BT_DBG("Unexpected - txseq indicates missing frames");
|
|
|
|
return L2CAP_TXSEQ_UNEXPECTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:42 +08:00
|
|
|
static int l2cap_rx_state_recv(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb, u8 event)
|
|
|
|
{
|
|
|
|
int err = 0;
|
2013-09-23 02:44:10 +08:00
|
|
|
bool skb_in_use = false;
|
2012-05-18 11:53:42 +08:00
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
|
|
|
event);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case L2CAP_EV_RECV_IFRAME:
|
|
|
|
switch (l2cap_classify_txseq(chan, control->txseq)) {
|
|
|
|
case L2CAP_TXSEQ_EXPECTED:
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
|
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
|
|
|
BT_DBG("Busy, discarding expected seq %d",
|
|
|
|
control->txseq);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->expected_tx_seq = __next_seq(chan,
|
|
|
|
control->txseq);
|
|
|
|
|
|
|
|
chan->buffer_seq = chan->expected_tx_seq;
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
|
|
|
|
err = l2cap_reassemble_sdu(chan, skb, control);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (control->final) {
|
|
|
|
if (!test_and_clear_bit(CONN_REJ_ACT,
|
|
|
|
&chan->conn_state)) {
|
|
|
|
control->final = 0;
|
|
|
|
l2cap_retransmit_all(chan, control);
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
|
|
|
|
l2cap_send_ack(chan);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_UNEXPECTED:
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
|
|
|
|
/* Can't issue SREJ frames in the local busy state.
|
|
|
|
* Drop this frame, it will be seen as missing
|
|
|
|
* when local busy is exited.
|
|
|
|
*/
|
|
|
|
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
|
|
|
BT_DBG("Busy, discarding unexpected seq %d",
|
|
|
|
control->txseq);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There was a gap in the sequence, so an SREJ
|
|
|
|
* must be sent for each missing frame. The
|
|
|
|
* current frame is stored for later use.
|
|
|
|
*/
|
|
|
|
skb_queue_tail(&chan->srej_q, skb);
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
BT_DBG("Queued %p (queue len %d)", skb,
|
|
|
|
skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
clear_bit(CONN_SREJ_ACT, &chan->conn_state);
|
|
|
|
l2cap_seq_list_clear(&chan->srej_list);
|
|
|
|
l2cap_send_srej(chan, control->txseq);
|
|
|
|
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_SREJ_SENT;
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_DUPLICATE:
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_INVALID_IGNORE:
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_INVALID:
|
|
|
|
default:
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:42 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_RR:
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
if (control->final) {
|
|
|
|
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
|
2012-10-24 06:24:22 +08:00
|
|
|
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
|
|
|
|
!__chan_is_moving(chan)) {
|
2012-05-18 11:53:42 +08:00
|
|
|
control->final = 0;
|
|
|
|
l2cap_retransmit_all(chan, control);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
} else if (control->poll) {
|
|
|
|
l2cap_send_i_or_rr_or_rnr(chan);
|
|
|
|
} else {
|
|
|
|
if (test_and_clear_bit(CONN_REMOTE_BUSY,
|
|
|
|
&chan->conn_state) &&
|
|
|
|
chan->unacked_frames)
|
|
|
|
__set_retrans_timer(chan);
|
|
|
|
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_RNR:
|
|
|
|
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
if (control && control->poll) {
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
|
|
|
l2cap_send_rr_or_rnr(chan, 0);
|
|
|
|
}
|
|
|
|
__clear_retrans_timer(chan);
|
|
|
|
l2cap_seq_list_clear(&chan->retrans_list);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_REJ:
|
|
|
|
l2cap_handle_rej(chan, control);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_SREJ:
|
|
|
|
l2cap_handle_srej(chan, control);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb && !skb_in_use) {
|
|
|
|
BT_DBG("Freeing %p", skb);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb, u8 event)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
u16 txseq = control->txseq;
|
2013-09-23 02:44:10 +08:00
|
|
|
bool skb_in_use = false;
|
2012-05-18 11:53:42 +08:00
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
|
|
|
event);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case L2CAP_EV_RECV_IFRAME:
|
|
|
|
switch (l2cap_classify_txseq(chan, txseq)) {
|
|
|
|
case L2CAP_TXSEQ_EXPECTED:
|
|
|
|
/* Keep frame for reassembly later */
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
skb_queue_tail(&chan->srej_q, skb);
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
BT_DBG("Queued %p (queue len %d)", skb,
|
|
|
|
skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
chan->expected_tx_seq = __next_seq(chan, txseq);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_EXPECTED_SREJ:
|
|
|
|
l2cap_seq_list_pop(&chan->srej_list);
|
|
|
|
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
skb_queue_tail(&chan->srej_q, skb);
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
BT_DBG("Queued %p (queue len %d)", skb,
|
|
|
|
skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
err = l2cap_rx_queued_iframes(chan);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_UNEXPECTED:
|
|
|
|
/* Got a frame that can't be reassembled yet.
|
|
|
|
* Save it for later, and send SREJs to cover
|
|
|
|
* the missing frames.
|
|
|
|
*/
|
|
|
|
skb_queue_tail(&chan->srej_q, skb);
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
BT_DBG("Queued %p (queue len %d)", skb,
|
|
|
|
skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
l2cap_send_srej(chan, control->txseq);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_UNEXPECTED_SREJ:
|
|
|
|
/* This frame was requested with an SREJ, but
|
|
|
|
* some expected retransmitted frames are
|
|
|
|
* missing. Request retransmission of missing
|
|
|
|
* SREJ'd frames.
|
|
|
|
*/
|
|
|
|
skb_queue_tail(&chan->srej_q, skb);
|
2013-09-23 02:44:10 +08:00
|
|
|
skb_in_use = true;
|
2012-05-18 11:53:42 +08:00
|
|
|
BT_DBG("Queued %p (queue len %d)", skb,
|
|
|
|
skb_queue_len(&chan->srej_q));
|
|
|
|
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
l2cap_send_srej_list(chan, control->txseq);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_DUPLICATE_SREJ:
|
|
|
|
/* We've already queued this frame. Drop this copy. */
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_DUPLICATE:
|
|
|
|
/* Expecting a later sequence number, so this frame
|
|
|
|
* was already received. Ignore it completely.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_INVALID_IGNORE:
|
|
|
|
break;
|
|
|
|
case L2CAP_TXSEQ_INVALID:
|
|
|
|
default:
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:42 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_RR:
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
if (control->final) {
|
|
|
|
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
if (!test_and_clear_bit(CONN_REJ_ACT,
|
|
|
|
&chan->conn_state)) {
|
|
|
|
control->final = 0;
|
|
|
|
l2cap_retransmit_all(chan, control);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_ertm_send(chan);
|
|
|
|
} else if (control->poll) {
|
|
|
|
if (test_and_clear_bit(CONN_REMOTE_BUSY,
|
|
|
|
&chan->conn_state) &&
|
|
|
|
chan->unacked_frames) {
|
|
|
|
__set_retrans_timer(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
|
|
|
l2cap_send_srej_tail(chan);
|
|
|
|
} else {
|
|
|
|
if (test_and_clear_bit(CONN_REMOTE_BUSY,
|
|
|
|
&chan->conn_state) &&
|
|
|
|
chan->unacked_frames)
|
|
|
|
__set_retrans_timer(chan);
|
|
|
|
|
|
|
|
l2cap_send_ack(chan);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_RNR:
|
|
|
|
set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
if (control->poll) {
|
|
|
|
l2cap_send_srej_tail(chan);
|
|
|
|
} else {
|
|
|
|
struct l2cap_ctrl rr_control;
|
|
|
|
memset(&rr_control, 0, sizeof(rr_control));
|
|
|
|
rr_control.sframe = 1;
|
|
|
|
rr_control.super = L2CAP_SUPER_RR;
|
|
|
|
rr_control.reqseq = chan->buffer_seq;
|
|
|
|
l2cap_send_sframe(chan, &rr_control);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_REJ:
|
|
|
|
l2cap_handle_rej(chan, control);
|
|
|
|
break;
|
|
|
|
case L2CAP_EV_RECV_SREJ:
|
|
|
|
l2cap_handle_srej(chan, control);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb && !skb_in_use) {
|
|
|
|
BT_DBG("Freeing %p", skb);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-10-24 06:24:11 +08:00
|
|
|
static int l2cap_finish_move(struct l2cap_chan *chan)
|
|
|
|
{
|
|
|
|
BT_DBG("chan %p", chan);
|
|
|
|
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_RECV;
|
|
|
|
|
|
|
|
if (chan->hs_hcon)
|
|
|
|
chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
|
|
|
|
else
|
|
|
|
chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
|
|
|
|
|
|
|
|
return l2cap_resegment(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb, u8 event)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
|
|
|
event);
|
|
|
|
|
|
|
|
if (!control->poll)
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
l2cap_process_reqseq(chan, control->reqseq);
|
|
|
|
|
|
|
|
if (!skb_queue_empty(&chan->tx_q))
|
|
|
|
chan->tx_send_head = skb_peek(&chan->tx_q);
|
|
|
|
else
|
|
|
|
chan->tx_send_head = NULL;
|
|
|
|
|
|
|
|
/* Rewind next_tx_seq to the point expected
|
|
|
|
* by the receiver.
|
|
|
|
*/
|
|
|
|
chan->next_tx_seq = control->reqseq;
|
|
|
|
chan->unacked_frames = 0;
|
|
|
|
|
|
|
|
err = l2cap_finish_move(chan);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
set_bit(CONN_SEND_FBIT, &chan->conn_state);
|
|
|
|
l2cap_send_i_or_rr_or_rnr(chan);
|
|
|
|
|
|
|
|
if (event == L2CAP_EV_RECV_IFRAME)
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
return l2cap_rx_state_recv(chan, control, NULL, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
|
|
|
|
struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb, u8 event)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!control->final)
|
|
|
|
return -EPROTO;
|
|
|
|
|
|
|
|
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
|
|
|
|
|
|
|
|
chan->rx_state = L2CAP_RX_STATE_RECV;
|
|
|
|
l2cap_process_reqseq(chan, control->reqseq);
|
|
|
|
|
|
|
|
if (!skb_queue_empty(&chan->tx_q))
|
|
|
|
chan->tx_send_head = skb_peek(&chan->tx_q);
|
|
|
|
else
|
|
|
|
chan->tx_send_head = NULL;
|
|
|
|
|
|
|
|
/* Rewind next_tx_seq to the point expected
|
|
|
|
* by the receiver.
|
|
|
|
*/
|
|
|
|
chan->next_tx_seq = control->reqseq;
|
|
|
|
chan->unacked_frames = 0;
|
|
|
|
|
|
|
|
if (chan->hs_hcon)
|
|
|
|
chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
|
|
|
|
else
|
|
|
|
chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
|
|
|
|
|
|
|
|
err = l2cap_resegment(chan);
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
err = l2cap_rx_state_recv(chan, control, skb, event);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:42 +08:00
|
|
|
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
|
|
|
|
{
|
|
|
|
/* Make sure reqseq is for a packet that has been sent but not acked */
|
|
|
|
u16 unacked;
|
|
|
|
|
|
|
|
unacked = __seq_offset(chan, chan->next_tx_seq, chan->expected_ack_seq);
|
|
|
|
return __seq_offset(chan, chan->next_tx_seq, reqseq) <= unacked;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb, u8 event)
|
2010-06-22 05:53:22 +08:00
|
|
|
{
|
2012-05-18 11:53:42 +08:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
BT_DBG("chan %p, control %p, skb %p, event %d, state %d", chan,
|
|
|
|
control, skb, event, chan->rx_state);
|
|
|
|
|
|
|
|
if (__valid_reqseq(chan, control->reqseq)) {
|
|
|
|
switch (chan->rx_state) {
|
|
|
|
case L2CAP_RX_STATE_RECV:
|
|
|
|
err = l2cap_rx_state_recv(chan, control, skb, event);
|
|
|
|
break;
|
|
|
|
case L2CAP_RX_STATE_SREJ_SENT:
|
|
|
|
err = l2cap_rx_state_srej_sent(chan, control, skb,
|
|
|
|
event);
|
|
|
|
break;
|
2012-10-24 06:24:11 +08:00
|
|
|
case L2CAP_RX_STATE_WAIT_P:
|
|
|
|
err = l2cap_rx_state_wait_p(chan, control, skb, event);
|
|
|
|
break;
|
|
|
|
case L2CAP_RX_STATE_WAIT_F:
|
|
|
|
err = l2cap_rx_state_wait_f(chan, control, skb, event);
|
|
|
|
break;
|
2012-05-18 11:53:42 +08:00
|
|
|
default:
|
|
|
|
/* shut it down */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d",
|
|
|
|
control->reqseq, chan->next_tx_seq,
|
|
|
|
chan->expected_ack_seq);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2012-05-18 11:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
2012-05-18 11:53:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
2012-05-18 11:53:37 +08:00
|
|
|
BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
|
|
|
|
chan->rx_state);
|
|
|
|
|
|
|
|
if (l2cap_classify_txseq(chan, control->txseq) ==
|
|
|
|
L2CAP_TXSEQ_EXPECTED) {
|
|
|
|
l2cap_pass_to_tx(chan, control);
|
|
|
|
|
|
|
|
BT_DBG("buffer_seq %d->%d", chan->buffer_seq,
|
|
|
|
__next_seq(chan, chan->buffer_seq));
|
|
|
|
|
|
|
|
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
|
|
|
|
|
|
|
|
l2cap_reassemble_sdu(chan, skb, control);
|
|
|
|
} else {
|
|
|
|
if (chan->sdu) {
|
|
|
|
kfree_skb(chan->sdu);
|
|
|
|
chan->sdu = NULL;
|
|
|
|
}
|
|
|
|
chan->sdu_last_frag = NULL;
|
|
|
|
chan->sdu_len = 0;
|
|
|
|
|
|
|
|
if (skb) {
|
|
|
|
BT_DBG("Freeing %p", skb);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->last_acked_seq = control->txseq;
|
|
|
|
chan->expected_tx_seq = __next_seq(chan, control->txseq);
|
|
|
|
|
2015-11-19 20:05:35 +08:00
|
|
|
return 0;
|
2012-05-18 11:53:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
|
|
|
{
|
2015-03-31 04:21:01 +08:00
|
|
|
struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
|
2012-05-18 11:53:36 +08:00
|
|
|
u16 len;
|
|
|
|
u8 event;
|
2010-06-22 05:53:22 +08:00
|
|
|
|
2012-04-12 01:48:43 +08:00
|
|
|
__unpack_control(chan, skb);
|
|
|
|
|
2010-06-22 05:53:22 +08:00
|
|
|
len = skb->len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can just drop the corrupted I-frame here.
|
|
|
|
* Receiver will miss it and start proper recovery
|
2012-05-18 11:53:36 +08:00
|
|
|
* procedures and ask for retransmission.
|
2010-06-22 05:53:22 +08:00
|
|
|
*/
|
2011-04-14 02:57:03 +08:00
|
|
|
if (l2cap_check_fcs(chan, skb))
|
2010-06-22 05:53:22 +08:00
|
|
|
goto drop;
|
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
if (!control->sframe && control->sar == L2CAP_SAR_START)
|
2011-10-17 17:19:58 +08:00
|
|
|
len -= L2CAP_SDULEN_SIZE;
|
2010-06-22 05:53:22 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (chan->fcs == L2CAP_FCS_CRC16)
|
2011-10-17 17:19:58 +08:00
|
|
|
len -= L2CAP_FCS_SIZE;
|
2010-06-22 05:53:22 +08:00
|
|
|
|
2011-04-14 02:57:03 +08:00
|
|
|
if (len > chan->mps) {
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2010-06-22 05:53:22 +08:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
Bluetooth: split sk_filter in l2cap_sock_recv_cb
During an audit for sk_filter(), we found that rx_busy_skb handling
in l2cap_sock_recv_cb() and l2cap_sock_recvmsg() looks not quite as
intended.
The assumption from commit e328140fdacb ("Bluetooth: Use event-driven
approach for handling ERTM receive buffer") is that errors returned
from sock_queue_rcv_skb() are due to receive buffer shortage. However,
nothing should prevent doing a setsockopt() with SO_ATTACH_FILTER on
the socket, that could drop some of the incoming skbs when handled in
sock_queue_rcv_skb().
In that case sock_queue_rcv_skb() will return with -EPERM, propagated
from sk_filter() and if in L2CAP_MODE_ERTM mode, wrong assumption was
that we failed due to receive buffer being full. From that point onwards,
due to the to-be-dropped skb being held in rx_busy_skb, we cannot make
any forward progress as rx_busy_skb is never cleared from l2cap_sock_recvmsg(),
due to the filter drop verdict over and over coming from sk_filter().
Meanwhile, in l2cap_sock_recv_cb() all new incoming skbs are being
dropped due to rx_busy_skb being occupied.
Instead, just use __sock_queue_rcv_skb() where an error really tells that
there's a receive buffer issue. Split the sk_filter() and enable it for
non-segmented modes at queuing time since at this point in time the skb has
already been through the ERTM state machine and it has been acked, so dropping
is not allowed. Instead, for ERTM and streaming mode, call sk_filter() in
l2cap_data_rcv() so the packet can be dropped before the state machine sees it.
Fixes: e328140fdacb ("Bluetooth: Use event-driven approach for handling ERTM receive buffer")
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
2016-07-28 02:40:14 +08:00
|
|
|
if ((chan->mode == L2CAP_MODE_ERTM ||
|
|
|
|
chan->mode == L2CAP_MODE_STREAMING) && sk_filter(chan->data, skb))
|
|
|
|
goto drop;
|
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
if (!control->sframe) {
|
|
|
|
int err;
|
2010-06-22 05:53:22 +08:00
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
BT_DBG("iframe sar %d, reqseq %d, final %d, txseq %d",
|
|
|
|
control->sar, control->reqseq, control->final,
|
|
|
|
control->txseq);
|
2010-06-22 05:53:22 +08:00
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
/* Validate F-bit - F=0 always valid, F=1 only
|
|
|
|
* valid in TX WAIT_F
|
|
|
|
*/
|
|
|
|
if (control->final && chan->tx_state != L2CAP_TX_STATE_WAIT_F)
|
2010-06-22 05:53:22 +08:00
|
|
|
goto drop;
|
2012-05-18 11:53:36 +08:00
|
|
|
|
|
|
|
if (chan->mode != L2CAP_MODE_STREAMING) {
|
|
|
|
event = L2CAP_EV_RECV_IFRAME;
|
|
|
|
err = l2cap_rx(chan, control, skb, event);
|
|
|
|
} else {
|
|
|
|
err = l2cap_stream_rx(chan, control, skb);
|
2010-06-22 05:53:22 +08:00
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
if (err)
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2010-06-22 05:53:22 +08:00
|
|
|
} else {
|
2012-05-18 11:53:36 +08:00
|
|
|
const u8 rx_func_to_event[4] = {
|
|
|
|
L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ,
|
|
|
|
L2CAP_EV_RECV_RNR, L2CAP_EV_RECV_SREJ
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Only I-frames are expected in streaming mode */
|
|
|
|
if (chan->mode == L2CAP_MODE_STREAMING)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
BT_DBG("sframe reqseq %d, final %d, poll %d, super %d",
|
|
|
|
control->reqseq, control->final, control->poll,
|
|
|
|
control->super);
|
|
|
|
|
2010-06-22 05:53:22 +08:00
|
|
|
if (len != 0) {
|
2012-11-20 23:16:21 +08:00
|
|
|
BT_ERR("Trailing bytes: %d in sframe", len);
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2010-06-22 05:53:22 +08:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2012-05-18 11:53:36 +08:00
|
|
|
/* Validate F and P bits */
|
|
|
|
if (control->final && (control->poll ||
|
|
|
|
chan->tx_state != L2CAP_TX_STATE_WAIT_F))
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
event = rx_func_to_event[control->super];
|
|
|
|
if (l2cap_rx(chan, control, skb, event))
|
2012-11-28 23:59:39 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2010-06-22 05:53:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:43:34 +08:00
|
|
|
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;
|
|
|
|
|
2018-09-04 18:39:21 +08:00
|
|
|
return_credits = ((chan->imtu / chan->mps) + 1) - chan->rx_credits;
|
2013-12-05 15:43:34 +08:00
|
|
|
|
2018-09-04 18:39:21 +08:00
|
|
|
if (!return_credits)
|
|
|
|
return;
|
2013-12-05 15:43:34 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-04 18:39:21 +08:00
|
|
|
static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
BT_DBG("SDU reassemble complete: chan %p skb->len %u", chan, skb->len);
|
|
|
|
|
|
|
|
/* Wait recv to confirm reception before updating the credits */
|
|
|
|
err = chan->ops->recv(chan, skb);
|
|
|
|
|
|
|
|
/* Update credits whenever an SDU is received */
|
|
|
|
l2cap_chan_le_send_credits(chan);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:45:01 +08:00
|
|
|
static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
|
|
|
{
|
2013-06-01 15:14:57 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!chan->rx_credits) {
|
|
|
|
BT_ERR("No credits to receive LE L2CAP data");
|
2014-01-28 07:11:34 +08:00
|
|
|
l2cap_send_disconn_req(chan, ECONNRESET);
|
2013-12-05 15:45:01 +08:00
|
|
|
return -ENOBUFS;
|
2013-06-01 15:14:57 +08:00
|
|
|
}
|
2013-12-05 15:45:01 +08:00
|
|
|
|
2013-06-01 15:14:57 +08:00
|
|
|
if (chan->imtu < skb->len) {
|
|
|
|
BT_ERR("Too big LE L2CAP PDU");
|
2013-12-05 15:45:01 +08:00
|
|
|
return -ENOBUFS;
|
2013-06-01 15:14:57 +08:00
|
|
|
}
|
2013-12-05 15:45:01 +08:00
|
|
|
|
|
|
|
chan->rx_credits--;
|
|
|
|
BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
|
|
|
|
|
2018-09-04 18:39:21 +08:00
|
|
|
/* Update if remote had run out of credits, this should only happens
|
|
|
|
* if the remote is not using the entire MPS.
|
|
|
|
*/
|
|
|
|
if (!chan->rx_credits)
|
|
|
|
l2cap_chan_le_send_credits(chan);
|
2013-12-05 15:45:01 +08:00
|
|
|
|
2013-06-01 15:14:57 +08:00
|
|
|
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)
|
2018-09-04 18:39:21 +08:00
|
|
|
return l2cap_le_recv(chan, skb);
|
2013-06-01 15:14:57 +08:00
|
|
|
|
|
|
|
chan->sdu = skb;
|
|
|
|
chan->sdu_len = sdu_len;
|
|
|
|
chan->sdu_last_frag = skb;
|
|
|
|
|
2018-09-04 18:39:22 +08:00
|
|
|
/* Detect if remote is not able to use the selected MPS */
|
|
|
|
if (skb->len + L2CAP_SDULEN_SIZE < chan->mps) {
|
|
|
|
u16 mps_len = skb->len + L2CAP_SDULEN_SIZE;
|
|
|
|
|
|
|
|
/* Adjust the number of credits */
|
|
|
|
BT_DBG("chan->mps %u -> %u", chan->mps, mps_len);
|
|
|
|
chan->mps = mps_len;
|
|
|
|
l2cap_chan_le_send_credits(chan);
|
|
|
|
}
|
|
|
|
|
2013-06-01 15:14:57 +08:00
|
|
|
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) {
|
2018-09-04 18:39:21 +08:00
|
|
|
err = l2cap_le_recv(chan, chan->sdu);
|
2013-06-01 15:14:57 +08:00
|
|
|
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;
|
2013-12-05 15:45:01 +08:00
|
|
|
}
|
|
|
|
|
2012-05-31 16:18:55 +08:00
|
|
|
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
|
|
|
|
struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-01 03:17:41 +08:00
|
|
|
chan = l2cap_get_chan_by_scid(conn, cid);
|
2011-03-25 11:22:30 +08:00
|
|
|
if (!chan) {
|
2012-05-29 18:59:17 +08:00
|
|
|
if (cid == L2CAP_CID_A2MP) {
|
|
|
|
chan = a2mp_channel_create(conn, skb);
|
|
|
|
if (!chan) {
|
|
|
|
kfree_skb(skb);
|
2012-05-31 16:18:55 +08:00
|
|
|
return;
|
2012-05-29 18:59:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
} else {
|
|
|
|
BT_DBG("unknown cid 0x%4.4x", cid);
|
|
|
|
/* Drop packet and return */
|
|
|
|
kfree_skb(skb);
|
2012-05-31 16:18:55 +08:00
|
|
|
return;
|
2012-05-29 18:59:17 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-05 02:59:54 +08:00
|
|
|
BT_DBG("chan %p, len %d", chan, skb->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-02-16 17:42:11 +08:00
|
|
|
/* If we receive data on a fixed channel before the info req/rsp
|
|
|
|
* procdure is done simply assume that the channel is supported
|
|
|
|
* and mark it as ready.
|
|
|
|
*/
|
|
|
|
if (chan->chan_type == L2CAP_CHAN_FIXED)
|
|
|
|
l2cap_chan_ready(chan);
|
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state != BT_CONNECTED)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
|
2011-04-14 04:20:49 +08:00
|
|
|
switch (chan->mode) {
|
2013-05-17 17:49:23 +08:00
|
|
|
case L2CAP_MODE_LE_FLOWCTL:
|
2013-12-05 15:45:01 +08:00
|
|
|
if (l2cap_le_data_rcv(chan, skb) < 0)
|
|
|
|
goto drop;
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
|
2009-08-21 09:25:57 +08:00
|
|
|
case L2CAP_MODE_BASIC:
|
|
|
|
/* If socket recv buffers overflows we drop data here
|
|
|
|
* which is *bad* because L2CAP has to be reliable.
|
|
|
|
* But we don't have any other choice. L2CAP doesn't
|
|
|
|
* provide flow control mechanism. */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-02-19 03:48:34 +08:00
|
|
|
if (chan->imtu < skb->len) {
|
|
|
|
BT_ERR("Dropping L2CAP data: receive buffer overflow");
|
2009-08-21 09:25:57 +08:00
|
|
|
goto drop;
|
2014-02-19 03:48:34 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-28 09:27:51 +08:00
|
|
|
if (!chan->ops->recv(chan, skb))
|
2009-08-21 09:25:57 +08:00
|
|
|
goto done;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case L2CAP_MODE_ERTM:
|
2009-08-21 09:26:01 +08:00
|
|
|
case L2CAP_MODE_STREAMING:
|
2012-05-18 11:53:36 +08:00
|
|
|
l2cap_data_rcv(chan, skb);
|
2009-08-21 09:26:01 +08:00
|
|
|
goto done;
|
|
|
|
|
2009-08-21 09:25:57 +08:00
|
|
|
default:
|
2011-04-14 04:20:49 +08:00
|
|
|
BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode);
|
2009-08-21 09:25:57 +08:00
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
done:
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-05-31 16:18:56 +08:00
|
|
|
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
|
|
|
struct sk_buff *skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-10-03 15:03:39 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-10-03 15:03:39 +08:00
|
|
|
if (hcon->type != ACL_LINK)
|
2014-08-08 03:56:42 +08:00
|
|
|
goto free_skb;
|
2013-10-03 15:03:39 +08:00
|
|
|
|
2013-05-14 18:23:13 +08:00
|
|
|
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
|
|
|
|
ACL_LINK);
|
2011-04-28 05:26:32 +08:00
|
|
|
if (!chan)
|
2014-08-08 03:56:42 +08:00
|
|
|
goto free_skb;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-24 22:35:32 +08:00
|
|
|
BT_DBG("chan %p, len %d", chan, skb->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
|
2011-06-18 09:46:27 +08:00
|
|
|
if (chan->imtu < skb->len)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
|
2013-10-14 03:55:29 +08:00
|
|
|
/* Store remote BD_ADDR and PSM for msg_name */
|
2015-03-31 04:21:01 +08:00
|
|
|
bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
|
|
|
|
bt_cb(skb)->l2cap.psm = psm;
|
2013-10-14 03:55:29 +08:00
|
|
|
|
2014-08-08 03:56:42 +08:00
|
|
|
if (!chan->ops->recv(chan, skb)) {
|
|
|
|
l2cap_chan_put(chan);
|
2012-05-31 16:18:56 +08:00
|
|
|
return;
|
2014-08-08 03:56:42 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
drop:
|
2014-08-08 03:56:42 +08:00
|
|
|
l2cap_chan_put(chan);
|
|
|
|
free_skb:
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct l2cap_hdr *lh = (void *) skb->data;
|
2014-01-18 02:45:11 +08:00
|
|
|
struct hci_conn *hcon = conn->hcon;
|
2007-07-29 15:16:36 +08:00
|
|
|
u16 cid, len;
|
|
|
|
__le16 psm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-01-18 02:45:11 +08:00
|
|
|
if (hcon->state != BT_CONNECTED) {
|
|
|
|
BT_DBG("queueing pending rx skb");
|
|
|
|
skb_queue_tail(&conn->pending_rx, skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
skb_pull(skb, L2CAP_HDR_SIZE);
|
|
|
|
cid = __le16_to_cpu(lh->cid);
|
|
|
|
len = __le16_to_cpu(lh->len);
|
|
|
|
|
2009-08-21 09:25:57 +08:00
|
|
|
if (len != skb->len) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-06 16:03:36 +08:00
|
|
|
/* Since we can't actively block incoming LE connections we must
|
|
|
|
* at least ensure that we ignore incoming data from them.
|
|
|
|
*/
|
|
|
|
if (hcon->type == LE_LINK &&
|
2014-07-09 17:59:13 +08:00
|
|
|
hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst,
|
2015-01-15 19:06:44 +08:00
|
|
|
bdaddr_dst_type(hcon))) {
|
2014-07-02 14:36:21 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_DBG("len %d, cid 0x%4.4x", len, cid);
|
|
|
|
|
|
|
|
switch (cid) {
|
2009-04-20 12:31:05 +08:00
|
|
|
case L2CAP_CID_SIGNALING:
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_sig_channel(conn, skb);
|
|
|
|
break;
|
|
|
|
|
2009-04-20 12:31:05 +08:00
|
|
|
case L2CAP_CID_CONN_LESS:
|
2012-03-09 20:16:17 +08:00
|
|
|
psm = get_unaligned((__le16 *) skb->data);
|
2012-05-29 15:04:05 +08:00
|
|
|
skb_pull(skb, L2CAP_PSMLEN_SIZE);
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_conless_channel(conn, psm, skb);
|
|
|
|
break;
|
|
|
|
|
2013-10-03 14:46:54 +08:00
|
|
|
case L2CAP_CID_LE_SIGNALING:
|
|
|
|
l2cap_le_sig_channel(conn, skb);
|
|
|
|
break;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
l2cap_data_channel(conn, cid, skb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-18 02:45:11 +08:00
|
|
|
static void process_pending_rx(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
|
|
|
|
pending_rx_work);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
BT_DBG("");
|
|
|
|
|
|
|
|
while ((skb = skb_dequeue(&conn->pending_rx)))
|
|
|
|
l2cap_recv_frame(conn, skb);
|
|
|
|
}
|
|
|
|
|
2014-01-18 02:45:10 +08:00
|
|
|
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
|
|
|
struct hci_chan *hchan;
|
|
|
|
|
|
|
|
if (conn)
|
|
|
|
return conn;
|
|
|
|
|
|
|
|
hchan = hci_chan_create(hcon);
|
|
|
|
if (!hchan)
|
|
|
|
return NULL;
|
|
|
|
|
2014-07-21 15:50:06 +08:00
|
|
|
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
|
2014-01-18 02:45:10 +08:00
|
|
|
if (!conn) {
|
|
|
|
hci_chan_del(hchan);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
kref_init(&conn->ref);
|
|
|
|
hcon->l2cap_data = conn;
|
2014-08-16 02:06:57 +08:00
|
|
|
conn->hcon = hci_conn_get(hcon);
|
2014-01-18 02:45:10 +08:00
|
|
|
conn->hchan = hchan;
|
|
|
|
|
|
|
|
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
|
|
|
|
|
|
|
|
switch (hcon->type) {
|
|
|
|
case LE_LINK:
|
|
|
|
if (hcon->hdev->le_mtu) {
|
|
|
|
conn->mtu = hcon->hdev->le_mtu;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
|
|
conn->mtu = hcon->hdev->acl_mtu;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->feat_mask = 0;
|
|
|
|
|
2014-12-02 16:09:26 +08:00
|
|
|
conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
|
|
|
|
|
|
|
|
if (hcon->type == ACL_LINK &&
|
2015-03-13 17:11:00 +08:00
|
|
|
hci_dev_test_flag(hcon->hdev, HCI_HS_ENABLED))
|
2014-12-02 16:09:26 +08:00
|
|
|
conn->local_fixed_chan |= L2CAP_FC_A2MP;
|
2014-01-18 02:45:10 +08:00
|
|
|
|
2015-03-13 17:11:00 +08:00
|
|
|
if (hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED) &&
|
2014-12-06 07:35:45 +08:00
|
|
|
(bredr_sc_enabled(hcon->hdev) ||
|
2015-03-14 01:20:35 +08:00
|
|
|
hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP)))
|
2014-08-14 17:34:26 +08:00
|
|
|
conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
|
|
|
|
|
2014-07-14 02:50:15 +08:00
|
|
|
mutex_init(&conn->ident_lock);
|
2014-01-18 02:45:10 +08:00
|
|
|
mutex_init(&conn->chan_lock);
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&conn->chan_l);
|
|
|
|
INIT_LIST_HEAD(&conn->users);
|
|
|
|
|
2014-08-12 03:06:41 +08:00
|
|
|
INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
|
2014-01-18 02:45:10 +08:00
|
|
|
|
2014-01-18 02:45:11 +08:00
|
|
|
skb_queue_head_init(&conn->pending_rx);
|
|
|
|
INIT_WORK(&conn->pending_rx_work, process_pending_rx);
|
2014-09-06 03:19:50 +08:00
|
|
|
INIT_WORK(&conn->id_addr_update_work, l2cap_conn_update_id_addr);
|
2014-01-18 02:45:11 +08:00
|
|
|
|
2014-01-18 02:45:10 +08:00
|
|
|
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct l2cap_conn *conn;
|
|
|
|
struct hci_conn *hcon;
|
|
|
|
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));
|
|
|
|
|
2016-11-12 23:03:07 +08:00
|
|
|
hdev = hci_get_route(dst, &chan->src, chan->src_type);
|
2014-01-18 02:45:10 +08:00
|
|
|
if (!hdev)
|
|
|
|
return -EHOSTUNREACH;
|
|
|
|
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
|
|
|
|
if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
|
|
|
|
chan->chan_type != L2CAP_CHAN_RAW) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2014-01-24 16:35:41 +08:00
|
|
|
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !psm) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->chan_type == L2CAP_CHAN_FIXED && !cid) {
|
2014-01-18 02:45:10 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (chan->mode) {
|
|
|
|
case L2CAP_MODE_BASIC:
|
|
|
|
break;
|
|
|
|
case L2CAP_MODE_LE_FLOWCTL:
|
|
|
|
break;
|
|
|
|
case L2CAP_MODE_ERTM:
|
|
|
|
case L2CAP_MODE_STREAMING:
|
|
|
|
if (!disable_ertm)
|
|
|
|
break;
|
|
|
|
/* fall through */
|
|
|
|
default:
|
2014-07-18 16:15:26 +08:00
|
|
|
err = -EOPNOTSUPP;
|
2014-01-18 02:45:10 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (chan->state) {
|
|
|
|
case BT_CONNECT:
|
|
|
|
case BT_CONNECT2:
|
|
|
|
case BT_CONFIG:
|
|
|
|
/* Already connecting */
|
|
|
|
err = 0;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
case BT_CONNECTED:
|
|
|
|
/* Already connected */
|
|
|
|
err = -EISCONN;
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
case BT_OPEN:
|
|
|
|
case BT_BOUND:
|
|
|
|
/* Can connect */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
err = -EBADFD;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set destination address and psm */
|
|
|
|
bacpy(&chan->dst, dst);
|
|
|
|
chan->dst_type = dst_type;
|
|
|
|
|
|
|
|
chan->psm = psm;
|
|
|
|
chan->dcid = cid;
|
|
|
|
|
2014-02-27 07:21:45 +08:00
|
|
|
if (bdaddr_type_is_le(dst_type)) {
|
|
|
|
/* Convert from L2CAP channel address type to HCI address type
|
|
|
|
*/
|
|
|
|
if (dst_type == BDADDR_LE_PUBLIC)
|
|
|
|
dst_type = ADDR_LE_DEV_PUBLIC;
|
|
|
|
else
|
|
|
|
dst_type = ADDR_LE_DEV_RANDOM;
|
|
|
|
|
2015-03-13 17:11:00 +08:00
|
|
|
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
2015-11-11 20:44:57 +08:00
|
|
|
hcon = hci_connect_le(hdev, dst, dst_type,
|
|
|
|
chan->sec_level,
|
|
|
|
HCI_LE_CONN_TIMEOUT,
|
2018-04-03 19:40:06 +08:00
|
|
|
HCI_ROLE_SLAVE, NULL);
|
2014-07-16 16:42:28 +08:00
|
|
|
else
|
2015-11-11 20:44:57 +08:00
|
|
|
hcon = hci_connect_le_scan(hdev, dst, dst_type,
|
|
|
|
chan->sec_level,
|
|
|
|
HCI_LE_CONN_TIMEOUT);
|
2014-07-07 20:02:28 +08:00
|
|
|
|
2014-02-27 07:21:45 +08:00
|
|
|
} else {
|
2014-07-07 20:02:27 +08:00
|
|
|
u8 auth_type = l2cap_get_auth_type(chan);
|
2014-02-27 07:21:44 +08:00
|
|
|
hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
|
2014-02-27 07:21:45 +08:00
|
|
|
}
|
2014-01-18 02:45:10 +08:00
|
|
|
|
|
|
|
if (IS_ERR(hcon)) {
|
|
|
|
err = PTR_ERR(hcon);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = l2cap_conn_add(hcon);
|
|
|
|
if (!conn) {
|
|
|
|
hci_conn_drop(hcon);
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2014-10-02 15:16:22 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
|
|
|
l2cap_chan_lock(chan);
|
|
|
|
|
2014-01-18 02:45:10 +08:00
|
|
|
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
|
|
|
|
hci_conn_drop(hcon);
|
|
|
|
err = -EBUSY;
|
2014-10-02 15:16:22 +08:00
|
|
|
goto chan_unlock;
|
2014-01-18 02:45:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update source addr of the socket */
|
|
|
|
bacpy(&chan->src, &hcon->src);
|
2015-01-15 19:06:44 +08:00
|
|
|
chan->src_type = bdaddr_src_type(hcon);
|
2014-01-18 02:45:10 +08:00
|
|
|
|
2014-10-02 15:16:22 +08:00
|
|
|
__l2cap_chan_add(conn, chan);
|
2014-01-18 02:45:10 +08:00
|
|
|
|
|
|
|
/* l2cap_chan_add takes its own ref so we can drop this one */
|
|
|
|
hci_conn_drop(hcon);
|
|
|
|
|
|
|
|
l2cap_state_change(chan, BT_CONNECT);
|
|
|
|
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
|
|
|
|
2014-01-29 07:16:48 +08:00
|
|
|
/* Release chan->sport so that it can be reused by other
|
|
|
|
* sockets (as it's only used for listening sockets).
|
|
|
|
*/
|
|
|
|
write_lock(&chan_list_lock);
|
|
|
|
chan->sport = 0;
|
|
|
|
write_unlock(&chan_list_lock);
|
|
|
|
|
2014-01-18 02:45:10 +08:00
|
|
|
if (hcon->state == BT_CONNECTED) {
|
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
|
|
|
__clear_chan_timer(chan);
|
2014-07-17 20:35:38 +08:00
|
|
|
if (l2cap_chan_check_security(chan, true))
|
2014-01-18 02:45:10 +08:00
|
|
|
l2cap_state_change(chan, BT_CONNECTED);
|
|
|
|
} else
|
|
|
|
l2cap_do_start(chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
2014-10-02 15:16:22 +08:00
|
|
|
chan_unlock:
|
2014-01-18 02:45:10 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2014-10-02 15:16:22 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
|
|
|
done:
|
2014-01-18 02:45:10 +08:00
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
hci_dev_put(hdev);
|
|
|
|
return err;
|
|
|
|
}
|
2014-06-18 21:37:08 +08:00
|
|
|
EXPORT_SYMBOL_GPL(l2cap_chan_connect);
|
2014-01-18 02:45:10 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* ---- L2CAP interface with lower layer (HCI) ---- */
|
|
|
|
|
2011-12-21 20:11:33 +08:00
|
|
|
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int exact = 0, lm1 = 0, lm2 = 0;
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *c;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-09-25 17:49:43 +08:00
|
|
|
BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Find listening sockets and check their link_mode */
|
2011-04-28 05:26:32 +08:00
|
|
|
read_lock(&chan_list_lock);
|
|
|
|
list_for_each_entry(c, &chan_list, global_l) {
|
2011-06-03 11:19:47 +08:00
|
|
|
if (c->state != BT_LISTEN)
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
2013-10-13 23:12:47 +08:00
|
|
|
if (!bacmp(&c->src, &hdev->bdaddr)) {
|
2009-01-16 04:58:38 +08:00
|
|
|
lm1 |= HCI_LM_ACCEPT;
|
2011-10-11 19:04:34 +08:00
|
|
|
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
|
2009-01-16 04:58:38 +08:00
|
|
|
lm1 |= HCI_LM_MASTER;
|
2005-04-17 06:20:36 +08:00
|
|
|
exact++;
|
2013-10-13 23:12:47 +08:00
|
|
|
} else if (!bacmp(&c->src, BDADDR_ANY)) {
|
2009-01-16 04:58:38 +08:00
|
|
|
lm2 |= HCI_LM_ACCEPT;
|
2011-10-11 19:04:34 +08:00
|
|
|
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
|
2009-01-16 04:58:38 +08:00
|
|
|
lm2 |= HCI_LM_MASTER;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-04-28 05:26:32 +08:00
|
|
|
read_unlock(&chan_list_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return exact ? lm1 : lm2;
|
|
|
|
}
|
|
|
|
|
2014-08-08 03:56:47 +08:00
|
|
|
/* Find the next fixed channel in BT_LISTEN state, continue iteration
|
|
|
|
* from an existing channel in the list or from the beginning of the
|
|
|
|
* global list (by passing NULL as first parameter).
|
|
|
|
*/
|
|
|
|
static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
|
2015-01-15 19:06:45 +08:00
|
|
|
struct hci_conn *hcon)
|
2014-08-08 03:56:47 +08:00
|
|
|
{
|
2015-01-15 19:06:45 +08:00
|
|
|
u8 src_type = bdaddr_src_type(hcon);
|
|
|
|
|
2014-08-08 03:56:47 +08:00
|
|
|
read_lock(&chan_list_lock);
|
|
|
|
|
|
|
|
if (c)
|
|
|
|
c = list_next_entry(c, global_l);
|
|
|
|
else
|
|
|
|
c = list_entry(chan_list.next, typeof(*c), global_l);
|
|
|
|
|
|
|
|
list_for_each_entry_from(c, &chan_list, global_l) {
|
|
|
|
if (c->chan_type != L2CAP_CHAN_FIXED)
|
|
|
|
continue;
|
|
|
|
if (c->state != BT_LISTEN)
|
|
|
|
continue;
|
2015-01-15 19:06:45 +08:00
|
|
|
if (bacmp(&c->src, &hcon->src) && bacmp(&c->src, BDADDR_ANY))
|
2014-08-08 03:56:48 +08:00
|
|
|
continue;
|
2015-01-15 19:06:45 +08:00
|
|
|
if (src_type != c->src_type)
|
2014-08-08 03:56:48 +08:00
|
|
|
continue;
|
2014-08-08 03:56:47 +08:00
|
|
|
|
|
|
|
l2cap_chan_hold(c);
|
|
|
|
read_unlock(&chan_list_lock);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
read_unlock(&chan_list_lock);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-18 20:53:57 +08:00
|
|
|
static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-08-08 03:56:47 +08:00
|
|
|
struct hci_dev *hdev = hcon->hdev;
|
2006-07-03 16:02:46 +08:00
|
|
|
struct l2cap_conn *conn;
|
2014-08-08 03:56:47 +08:00
|
|
|
struct l2cap_chan *pchan;
|
|
|
|
u8 dst_type;
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2015-02-18 20:53:57 +08:00
|
|
|
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
|
|
|
|
return;
|
|
|
|
|
2012-09-25 17:49:43 +08:00
|
|
|
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-08-08 03:56:46 +08:00
|
|
|
if (status) {
|
2011-06-30 09:18:29 +08:00
|
|
|
l2cap_conn_del(hcon, bt_to_errno(status));
|
2014-08-08 03:56:46 +08:00
|
|
|
return;
|
2012-10-31 21:46:26 +08:00
|
|
|
}
|
2014-08-08 03:56:46 +08:00
|
|
|
|
|
|
|
conn = l2cap_conn_add(hcon);
|
|
|
|
if (!conn)
|
|
|
|
return;
|
|
|
|
|
2015-01-15 19:06:44 +08:00
|
|
|
dst_type = bdaddr_dst_type(hcon);
|
2014-08-08 03:56:47 +08:00
|
|
|
|
|
|
|
/* If device is blocked, do not create channels for it */
|
|
|
|
if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Find fixed channels and notify them of the new connection. We
|
|
|
|
* use multiple individual lookups, continuing each time where
|
|
|
|
* we left off, because the list lock would prevent calling the
|
|
|
|
* potentially sleeping l2cap_chan_lock() function.
|
|
|
|
*/
|
2015-01-15 19:06:45 +08:00
|
|
|
pchan = l2cap_global_fixed_chan(NULL, hcon);
|
2014-08-08 03:56:47 +08:00
|
|
|
while (pchan) {
|
|
|
|
struct l2cap_chan *chan, *next;
|
|
|
|
|
|
|
|
/* Client fixed channels should override server ones */
|
|
|
|
if (__l2cap_get_chan_by_dcid(conn, pchan->scid))
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
l2cap_chan_lock(pchan);
|
|
|
|
chan = pchan->ops->new_connection(pchan);
|
|
|
|
if (chan) {
|
|
|
|
bacpy(&chan->src, &hcon->src);
|
|
|
|
bacpy(&chan->dst, &hcon->dst);
|
2015-01-15 19:06:44 +08:00
|
|
|
chan->src_type = bdaddr_src_type(hcon);
|
2014-08-08 03:56:47 +08:00
|
|
|
chan->dst_type = dst_type;
|
|
|
|
|
|
|
|
__l2cap_chan_add(conn, chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
l2cap_chan_unlock(pchan);
|
|
|
|
next:
|
2015-01-15 19:06:45 +08:00
|
|
|
next = l2cap_global_fixed_chan(pchan, hcon);
|
2014-08-08 03:56:47 +08:00
|
|
|
l2cap_chan_put(pchan);
|
|
|
|
pchan = next;
|
|
|
|
}
|
|
|
|
|
2014-08-08 03:56:46 +08:00
|
|
|
l2cap_conn_ready(conn);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-12-21 20:11:33 +08:00
|
|
|
int l2cap_disconn_ind(struct hci_conn *hcon)
|
2009-02-12 21:02:50 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
|
|
|
|
|
|
|
BT_DBG("hcon %p", hcon);
|
|
|
|
|
2011-12-21 20:11:33 +08:00
|
|
|
if (!conn)
|
2011-11-07 20:20:25 +08:00
|
|
|
return HCI_ERROR_REMOTE_USER_TERM;
|
2009-02-12 21:02:50 +08:00
|
|
|
return conn->disc_reason;
|
|
|
|
}
|
|
|
|
|
2015-02-18 20:53:58 +08:00
|
|
|
static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-02-18 20:53:58 +08:00
|
|
|
if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
|
|
|
|
return;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_DBG("hcon %p reason %d", hcon, reason);
|
|
|
|
|
2011-06-30 09:18:29 +08:00
|
|
|
l2cap_conn_del(hcon, bt_to_errno(reason));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-04-13 05:31:57 +08:00
|
|
|
static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
|
2009-01-16 04:58:44 +08:00
|
|
|
{
|
2011-05-03 04:13:55 +08:00
|
|
|
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
|
2009-02-05 04:07:19 +08:00
|
|
|
return;
|
|
|
|
|
2009-01-16 04:58:44 +08:00
|
|
|
if (encrypt == 0x00) {
|
2011-04-13 05:31:57 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_MEDIUM) {
|
2012-03-02 06:25:33 +08:00
|
|
|
__set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
|
2014-01-16 14:37:40 +08:00
|
|
|
} else if (chan->sec_level == BT_SECURITY_HIGH ||
|
|
|
|
chan->sec_level == BT_SECURITY_FIPS)
|
2011-05-05 06:42:50 +08:00
|
|
|
l2cap_chan_close(chan, ECONNREFUSED);
|
2009-01-16 04:58:44 +08:00
|
|
|
} else {
|
2011-04-13 05:31:57 +08:00
|
|
|
if (chan->sec_level == BT_SECURITY_MEDIUM)
|
2011-05-18 01:59:01 +08:00
|
|
|
__clear_chan_timer(chan);
|
2009-01-16 04:58:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-18 20:53:56 +08:00
|
|
|
static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-07-03 16:02:46 +08:00
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
2011-03-25 11:22:30 +08:00
|
|
|
struct l2cap_chan *chan;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-07-03 16:02:46 +08:00
|
|
|
if (!conn)
|
2015-02-18 20:53:56 +08:00
|
|
|
return;
|
2006-07-03 16:02:46 +08:00
|
|
|
|
2012-07-10 20:27:51 +08:00
|
|
|
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_lock(&conn->chan_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_lock(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-10 20:27:51 +08:00
|
|
|
BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
|
|
|
|
state_to_string(chan->state));
|
2011-01-27 08:42:57 +08:00
|
|
|
|
2014-01-24 16:35:40 +08:00
|
|
|
if (chan->scid == L2CAP_CID_A2MP) {
|
2012-07-19 22:03:47 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-08-08 03:56:45 +08:00
|
|
|
if (!status && encrypt)
|
|
|
|
chan->sec_level = hcon->sec_level;
|
2011-01-27 08:42:57 +08:00
|
|
|
|
2012-11-16 00:14:53 +08:00
|
|
|
if (!__l2cap_no_conn_pending(chan)) {
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2009-02-07 06:56:36 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (!status && (chan->state == BT_CONNECTED ||
|
2012-10-06 17:07:01 +08:00
|
|
|
chan->state == BT_CONFIG)) {
|
2013-10-14 17:53:54 +08:00
|
|
|
chan->ops->resume(chan);
|
2011-04-13 05:31:57 +08:00
|
|
|
l2cap_check_encryption(chan, encrypt);
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2008-07-15 02:13:45 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-06-03 11:19:47 +08:00
|
|
|
if (chan->state == BT_CONNECT) {
|
2013-12-03 04:13:24 +08:00
|
|
|
if (!status)
|
2012-09-27 22:26:16 +08:00
|
|
|
l2cap_start_connection(chan);
|
2013-12-03 04:13:24 +08:00
|
|
|
else
|
2012-03-02 06:25:33 +08:00
|
|
|
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
2014-11-13 16:55:17 +08:00
|
|
|
} else if (chan->state == BT_CONNECT2 &&
|
|
|
|
chan->mode != L2CAP_MODE_LE_FLOWCTL) {
|
2008-07-15 02:13:54 +08:00
|
|
|
struct l2cap_conn_rsp rsp;
|
2011-06-14 17:48:19 +08:00
|
|
|
__u16 res, stat;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-15 02:13:54 +08:00
|
|
|
if (!status) {
|
2013-10-14 17:45:34 +08:00
|
|
|
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
2011-06-14 17:48:19 +08:00
|
|
|
res = L2CAP_CR_PEND;
|
|
|
|
stat = L2CAP_CS_AUTHOR_PEND;
|
2012-10-12 19:35:24 +08:00
|
|
|
chan->ops->defer(chan);
|
2011-06-14 17:48:19 +08:00
|
|
|
} else {
|
2013-10-22 00:21:39 +08:00
|
|
|
l2cap_state_change(chan, BT_CONFIG);
|
2011-06-14 17:48:19 +08:00
|
|
|
res = L2CAP_CR_SUCCESS;
|
|
|
|
stat = L2CAP_CS_NO_INFO;
|
|
|
|
}
|
2008-07-15 02:13:54 +08:00
|
|
|
} else {
|
2013-10-22 00:21:39 +08:00
|
|
|
l2cap_state_change(chan, BT_DISCONN);
|
2012-03-02 06:25:33 +08:00
|
|
|
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
2011-06-14 17:48:19 +08:00
|
|
|
res = L2CAP_CR_SEC_BLOCK;
|
|
|
|
stat = L2CAP_CS_NO_INFO;
|
2008-07-15 02:13:54 +08:00
|
|
|
}
|
|
|
|
|
2011-04-14 06:50:45 +08:00
|
|
|
rsp.scid = cpu_to_le16(chan->dcid);
|
|
|
|
rsp.dcid = cpu_to_le16(chan->scid);
|
2011-06-14 17:48:19 +08:00
|
|
|
rsp.result = cpu_to_le16(res);
|
|
|
|
rsp.status = cpu_to_le16(stat);
|
2011-03-26 00:59:37 +08:00
|
|
|
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
|
2012-10-06 17:07:01 +08:00
|
|
|
sizeof(rsp), &rsp);
|
2012-05-24 05:59:30 +08:00
|
|
|
|
|
|
|
if (!test_bit(CONF_REQ_SENT, &chan->conf_state) &&
|
|
|
|
res == L2CAP_CR_SUCCESS) {
|
|
|
|
char buf[128];
|
|
|
|
set_bit(CONF_REQ_SENT, &chan->conf_state);
|
|
|
|
l2cap_send_cmd(conn, l2cap_get_ident(conn),
|
|
|
|
L2CAP_CONF_REQ,
|
2017-09-10 05:15:59 +08:00
|
|
|
l2cap_build_conf_req(chan, buf, sizeof(buf)),
|
2012-05-24 05:59:30 +08:00
|
|
|
buf);
|
|
|
|
chan->num_conf_req++;
|
|
|
|
}
|
2008-07-15 02:13:54 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-02-22 23:11:56 +08:00
|
|
|
l2cap_chan_unlock(chan);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-02-21 18:54:55 +08:00
|
|
|
mutex_unlock(&conn->chan_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-06-09 17:47:22 +08:00
|
|
|
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct l2cap_conn *conn = hcon->l2cap_data;
|
2012-10-15 16:58:40 +08:00
|
|
|
struct l2cap_hdr *hdr;
|
|
|
|
int len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-15 16:58:41 +08:00
|
|
|
/* For AMP controller do not create l2cap conn */
|
2016-07-05 20:30:14 +08:00
|
|
|
if (!conn && hcon->hdev->dev_type != HCI_PRIMARY)
|
2012-10-15 16:58:41 +08:00
|
|
|
goto drop;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-01-11 23:20:20 +08:00
|
|
|
if (!conn)
|
2013-04-12 00:55:50 +08:00
|
|
|
conn = l2cap_conn_add(hcon);
|
2011-01-11 23:20:20 +08:00
|
|
|
|
|
|
|
if (!conn)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
|
|
|
|
BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
|
|
|
|
|
2012-10-15 16:58:40 +08:00
|
|
|
switch (flags) {
|
|
|
|
case ACL_START:
|
|
|
|
case ACL_START_NO_FLUSH:
|
|
|
|
case ACL_COMPLETE:
|
2005-04-17 06:20:36 +08:00
|
|
|
if (conn->rx_len) {
|
|
|
|
BT_ERR("Unexpected start frame (len %d)", skb->len);
|
|
|
|
kfree_skb(conn->rx_skb);
|
|
|
|
conn->rx_skb = NULL;
|
|
|
|
conn->rx_len = 0;
|
|
|
|
l2cap_conn_unreliable(conn, ECOMM);
|
|
|
|
}
|
|
|
|
|
2010-09-15 19:28:43 +08:00
|
|
|
/* Start fragment always begin with Basic L2CAP header */
|
|
|
|
if (skb->len < L2CAP_HDR_SIZE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_ERR("Frame is too short (len %d)", skb->len);
|
|
|
|
l2cap_conn_unreliable(conn, ECOMM);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (struct l2cap_hdr *) skb->data;
|
|
|
|
len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
|
|
|
|
|
|
|
|
if (len == skb->len) {
|
|
|
|
/* Complete frame received */
|
|
|
|
l2cap_recv_frame(conn, skb);
|
2015-06-09 17:47:22 +08:00
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Start: total len %d, frag len %d", len, skb->len);
|
|
|
|
|
|
|
|
if (skb->len > len) {
|
|
|
|
BT_ERR("Frame is too long (len %d, expected len %d)",
|
2012-10-06 17:07:01 +08:00
|
|
|
skb->len, len);
|
2005-04-17 06:20:36 +08:00
|
|
|
l2cap_conn_unreliable(conn, ECOMM);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate skb for the complete frame (with header) */
|
2012-05-29 06:18:14 +08:00
|
|
|
conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
|
2009-04-20 12:31:08 +08:00
|
|
|
if (!conn->rx_skb)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto drop;
|
|
|
|
|
2007-03-28 05:55:52 +08:00
|
|
|
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
2012-10-06 17:07:01 +08:00
|
|
|
skb->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
conn->rx_len = len - skb->len;
|
2012-10-15 16:58:40 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ACL_CONT:
|
2005-04-17 06:20:36 +08:00
|
|
|
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
|
|
|
|
|
|
|
|
if (!conn->rx_len) {
|
|
|
|
BT_ERR("Unexpected continuation frame (len %d)", skb->len);
|
|
|
|
l2cap_conn_unreliable(conn, ECOMM);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skb->len > conn->rx_len) {
|
|
|
|
BT_ERR("Fragment is too long (len %d, expected %d)",
|
2012-10-06 17:07:01 +08:00
|
|
|
skb->len, conn->rx_len);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(conn->rx_skb);
|
|
|
|
conn->rx_skb = NULL;
|
|
|
|
conn->rx_len = 0;
|
|
|
|
l2cap_conn_unreliable(conn, ECOMM);
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2007-03-28 05:55:52 +08:00
|
|
|
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
|
2012-10-06 17:07:01 +08:00
|
|
|
skb->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
conn->rx_len -= skb->len;
|
|
|
|
|
|
|
|
if (!conn->rx_len) {
|
2013-10-10 19:33:37 +08:00
|
|
|
/* Complete frame received. l2cap_recv_frame
|
|
|
|
* takes ownership of the skb so set the global
|
|
|
|
* rx_skb pointer to NULL first.
|
|
|
|
*/
|
|
|
|
struct sk_buff *rx_skb = conn->rx_skb;
|
2005-04-17 06:20:36 +08:00
|
|
|
conn->rx_skb = NULL;
|
2013-10-10 19:33:37 +08:00
|
|
|
l2cap_recv_frame(conn, rx_skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-10-15 16:58:40 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2015-02-18 20:53:56 +08:00
|
|
|
static struct hci_cb l2cap_cb = {
|
|
|
|
.name = "L2CAP",
|
2015-02-18 20:53:57 +08:00
|
|
|
.connect_cfm = l2cap_connect_cfm,
|
2015-02-18 20:53:58 +08:00
|
|
|
.disconn_cfm = l2cap_disconn_cfm,
|
2015-02-18 20:53:56 +08:00
|
|
|
.security_cfm = l2cap_security_cfm,
|
|
|
|
};
|
|
|
|
|
2010-03-21 12:27:45 +08:00
|
|
|
static int l2cap_debugfs_show(struct seq_file *f, void *p)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-04-28 05:26:32 +08:00
|
|
|
struct l2cap_chan *c;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
read_lock(&chan_list_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-04-28 05:26:32 +08:00
|
|
|
list_for_each_entry(c, &chan_list, global_l) {
|
2015-01-15 05:44:21 +08:00
|
|
|
seq_printf(f, "%pMR (%u) %pMR (%u) %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
|
|
|
|
&c->src, c->src_type, &c->dst, c->dst_type,
|
2012-09-25 17:49:44 +08:00
|
|
|
c->state, __le16_to_cpu(c->psm),
|
|
|
|
c->scid, c->dcid, c->imtu, c->omtu,
|
|
|
|
c->sec_level, c->mode);
|
2012-01-19 17:19:50 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-12-23 01:14:39 +08:00
|
|
|
read_unlock(&chan_list_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-03-21 12:27:45 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2018-11-05 22:56:19 +08:00
|
|
|
DEFINE_SHOW_ATTRIBUTE(l2cap_debugfs);
|
2010-03-21 12:27:45 +08:00
|
|
|
|
|
|
|
static struct dentry *l2cap_debugfs;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-02-08 06:08:52 +08:00
|
|
|
int __init l2cap_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-09 01:57:38 +08:00
|
|
|
|
2011-02-04 06:50:35 +08:00
|
|
|
err = l2cap_init_sockets();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2015-02-18 20:53:56 +08:00
|
|
|
hci_register_cb(&l2cap_cb);
|
|
|
|
|
2013-10-18 08:24:16 +08:00
|
|
|
if (IS_ERR_OR_NULL(bt_debugfs))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
|
|
|
|
NULL, &l2cap_debugfs_fops);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-08 06:08:52 +08:00
|
|
|
void l2cap_exit(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-03-21 12:27:45 +08:00
|
|
|
debugfs_remove(l2cap_debugfs);
|
2015-02-18 20:53:56 +08:00
|
|
|
hci_unregister_cb(&l2cap_cb);
|
2011-02-04 06:50:35 +08:00
|
|
|
l2cap_cleanup_sockets();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-07-19 03:25:54 +08:00
|
|
|
module_param(disable_ertm, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
|