Release 4.11 net/bluetooth/l2cap_sock.c
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
Copyright (C) 2010 Google Inc.
Copyright (C) 2011 ProFUSION Embedded Systems
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
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
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
/* Bluetooth L2CAP sockets. */
#include <linux/module.h>
#include <linux/export.h>
#include <linux/sched/signal.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "smp.h"
static struct bt_sock_list l2cap_sk_list = {
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
};
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
int proto, gfp_t prio, int kern);
bool l2cap_is_socket(struct socket *sock)
{
return sock && sock->ops == &l2cap_sock_ops;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David Herrmann | 20 | 100.00% | 1 | 100.00% |
Total | 20 | 100.00% | 1 | 100.00% |
EXPORT_SYMBOL(l2cap_is_socket);
static int l2cap_validate_bredr_psm(u16 psm)
{
/* PSM must be odd and lsb of upper byte must be 0 */
if ((psm & 0x0101) != 0x0001)
return -EINVAL;
/* Restrict usage of well-known PSMs */
if (psm < L2CAP_PSM_DYN_START && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 44 | 100.00% | 2 | 100.00% |
Total | 44 | 100.00% | 2 | 100.00% |
static int l2cap_validate_le_psm(u16 psm)
{
/* Valid LE_PSM ranges are defined only until 0x00ff */
if (psm > L2CAP_PSM_LE_DYN_END)
return -EINVAL;
/* Restrict fixed, SIG assigned PSM values to CAP_NET_BIND_SERVICE */
if (psm < L2CAP_PSM_LE_DYN_START && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 40 | 100.00% | 2 | 100.00% |
Total | 40 | 100.00% | 2 | 100.00% |
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct sockaddr_l2 la;
int len, err = 0;
BT_DBG("sk %p", sk);
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
memset(&la, 0, sizeof(la));
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
if (la.l2_cid && la.l2_psm)
return -EINVAL;
if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
return -EINVAL;
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
/* We only allow ATT user space socket */
if (la.l2_cid &&
la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
lock_sock(sk);
if (sk->sk_state != BT_OPEN) {
err = -EBADFD;
goto done;
}
if (la.l2_psm) {
__u16 psm = __le16_to_cpu(la.l2_psm);
if (la.l2_bdaddr_type == BDADDR_BREDR)
err = l2cap_validate_bredr_psm(psm);
else
err = l2cap_validate_le_psm(psm);
if (err)
goto done;
}
bacpy(&chan->src, &la.l2_bdaddr);
chan->src_type = la.l2_bdaddr_type;
if (la.l2_cid)
err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid));
else
err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm);
if (err < 0)
goto done;
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_LESS:
if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_3DSP)
chan->sec_level = BT_SECURITY_SDP;
break;
case L2CAP_CHAN_CONN_ORIENTED:
if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP ||
__le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
chan->sec_level = BT_SECURITY_SDP;
break;
case L2CAP_CHAN_RAW:
chan->sec_level = BT_SECURITY_SDP;
break;
case L2CAP_CHAN_FIXED:
/* Fixed channels default to the L2CAP core not holding a
* hci_conn reference for them. For fixed channels mapping to
* L2CAP sockets we do want to hold a reference so set the
* appropriate flag to request it.
*/
set_bit(FLAG_HOLD_HCI_CONN, &chan->flags);
break;
}
if (chan->psm && bdaddr_type_is_le(chan->src_type))
chan->mode = L2CAP_MODE_LE_FLOWCTL;
chan->state = BT_BOUND;
sk->sk_state = BT_BOUND;
done:
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 258 | 60.14% | 4 | 19.05% |
Johan Hedberg | 124 | 28.90% | 9 | 42.86% |
Marcel Holtmann | 37 | 8.62% | 4 | 19.05% |
Ville Tervo | 4 | 0.93% | 1 | 4.76% |
Santosh Nayak | 3 | 0.70% | 1 | 4.76% |
Andrei Emeltchenko | 2 | 0.47% | 1 | 4.76% |
Joe Perches | 1 | 0.23% | 1 | 4.76% |
Total | 429 | 100.00% | 21 | 100.00% |
static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
int alen, int flags)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct sockaddr_l2 la;
int len, err = 0;
BT_DBG("sk %p", sk);
if (!addr || alen < sizeof(addr->sa_family) ||
addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
memset(&la, 0, sizeof(la));
len = min_t(unsigned int, sizeof(la), alen);
memcpy(&la, addr, len);
if (la.l2_cid && la.l2_psm)
return -EINVAL;
if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
return -EINVAL;
/* Check that the socket wasn't bound to something that
* conflicts with the address given to connect(). If chan->src
* is BDADDR_ANY it means bind() was never used, in which case
* chan->src_type and la.l2_bdaddr_type do not need to match.
*/
if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) &&
bdaddr_type_is_le(la.l2_bdaddr_type)) {
/* Old user space versions will try to incorrectly bind
* the ATT socket using BDADDR_BREDR. We need to accept
* this and fix up the source address type only when
* both the source CID and destination CID indicate
* ATT. Anything else is an invalid combination.
*/
if (chan->scid != L2CAP_CID_ATT ||
la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
/* We don't have the hdev available here to make a
* better decision on random vs public, but since all
* user space versions that exhibit this issue anyway do
* not support random local addresses assuming public
* here is good enough.
*/
chan->src_type = BDADDR_LE_PUBLIC;
}
if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR)
return -EINVAL;
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
/* We only allow ATT user space socket */
if (la.l2_cid &&
la.l2_cid != cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
if (chan->psm && bdaddr_type_is_le(chan->src_type))
chan->mode = L2CAP_MODE_LE_FLOWCTL;
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type);
if (err)
return err;
lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 176 | 52.23% | 3 | 17.65% |
Johan Hedberg | 135 | 40.06% | 7 | 41.18% |
Andrei Emeltchenko | 8 | 2.37% | 2 | 11.76% |
Ville Tervo | 7 | 2.08% | 1 | 5.88% |
Andre Guedes | 4 | 1.19% | 1 | 5.88% |
Santosh Nayak | 3 | 0.89% | 1 | 5.88% |
Marcel Holtmann | 2 | 0.59% | 1 | 5.88% |
Joe Perches | 2 | 0.59% | 1 | 5.88% |
Total | 337 | 100.00% | 17 | 100.00% |
static int l2cap_sock_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
int err = 0;
BT_DBG("sk %p backlog %d", sk, backlog);
lock_sock(sk);
if (sk->sk_state != BT_BOUND) {
err = -EBADFD;
goto done;
}
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) {
err = -EINVAL;
goto done;
}
switch (chan->mode) {
case L2CAP_MODE_BASIC:
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
break;
/* fall through */
default:
err = -EOPNOTSUPP;
goto done;
}
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
/* Listening channels need to use nested locking in order not to
* cause lockdep warnings when the created child channels end up
* being locked in the same thread as the parent channel.
*/
atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
chan->state = BT_LISTEN;
sk->sk_state = BT_LISTEN;
done:
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 140 | 78.65% | 3 | 42.86% |
Marcel Holtmann | 23 | 12.92% | 1 | 14.29% |
Johan Hedberg | 15 | 8.43% | 3 | 42.86% |
Total | 178 | 100.00% | 7 | 100.00% |
static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
int flags, bool kern)
{
DEFINE_WAIT_FUNC(wait, woken_wake_function);
struct sock *sk = sock->sk, *nsk;
long timeo;
int err = 0;
lock_sock_nested(sk, L2CAP_NESTING_PARENT);
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
BT_DBG("sk %p timeo %ld", sk, timeo);
/* Wait for an incoming connection. (wake-one). */
add_wait_queue_exclusive(sk_sleep(sk), &wait);
while (1) {
if (sk->sk_state != BT_LISTEN) {
err = -EBADFD;
break;
}
nsk = bt_accept_dequeue(sk, newsock);
if (nsk)
break;
if (!timeo) {
err = -EAGAIN;
break;
}
if (signal_pending(current)) {
err = sock_intr_errno(timeo);
break;
}
release_sock(sk);
timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
lock_sock_nested(sk, L2CAP_NESTING_PARENT);
}
remove_wait_queue(sk_sleep(sk), &wait);
if (err)
goto done;
newsock->state = SS_CONNECTED;
BT_DBG("new socket %p", nsk);
done:
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 171 | 78.08% | 1 | 20.00% |
Peter Hurley | 43 | 19.63% | 2 | 40.00% |
David Howells | 3 | 1.37% | 1 | 20.00% |
Johan Hedberg | 2 | 0.91% | 1 | 20.00% |
Total | 219 | 100.00% | 5 | 100.00% |
static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
int *len, int peer)
{
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
BT_DBG("sock %p, sk %p", sock, sk);
if (peer && sk->sk_state != BT_CONNECTED &&
sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2 &&
sk->sk_state != BT_CONFIG)
return -ENOTCONN;
memset(la, 0, sizeof(struct sockaddr_l2));
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
la->l2_psm = chan->psm;
if (peer) {
bacpy(&la->l2_bdaddr, &chan->dst);
la->l2_cid = cpu_to_le16(chan->dcid);
la->l2_bdaddr_type = chan->dst_type;
} else {
bacpy(&la->l2_bdaddr, &chan->src);
la->l2_cid = cpu_to_le16(chan->scid);
la->l2_bdaddr_type = chan->src_type;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 141 | 67.46% | 3 | 30.00% |
Johan Hedberg | 37 | 17.70% | 4 | 40.00% |
Marcel Holtmann | 18 | 8.61% | 2 | 20.00% |
Mathias Krause | 13 | 6.22% | 1 | 10.00% |
Total | 209 | 100.00% | 10 | 100.00% |
static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct l2cap_options opts;
struct l2cap_conninfo cinfo;
int len, err = 0;
u32 opt;
BT_DBG("sk %p", sk);
if (get_user(len, optlen))
return -EFAULT;
lock_sock(sk);
switch (optname) {
case L2CAP_OPTIONS:
/* LE sockets should use BT_SNDMTU/BT_RCVMTU, but since
* legacy ATT code depends on getsockopt for
* L2CAP_OPTIONS we need to let this pass.
*/
if (bdaddr_type_is_le(chan->src_type) &&
chan->scid != L2CAP_CID_ATT) {
err = -EINVAL;
break;
}
memset(&opts, 0, sizeof(opts));
opts.imtu = chan->imtu;
opts.omtu = chan->omtu;
opts.flush_to = chan->flush_to;
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len))
err = -EFAULT;
break;
case L2CAP_LM:
switch (chan->sec_level) {
case BT_SECURITY_LOW:
opt = L2CAP_LM_AUTH;
break;
case BT_SECURITY_MEDIUM:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
break;
case BT_SECURITY_HIGH:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
L2CAP_LM_SECURE;
break;
case BT_SECURITY_FIPS:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
L2CAP_LM_SECURE | L2CAP_LM_FIPS;
break;
default:
opt = 0;
break;
}
if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))
opt |= L2CAP_LM_MASTER;
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
opt |= L2CAP_LM_RELIABLE;
if (put_user(opt, (u32 __user *) optval))
err = -EFAULT;
break;
case L2CAP_CONNINFO:
if (sk->sk_state != BT_CONNECTED &&
!(sk->sk_state == BT_CONNECT2 &&
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
err = -ENOTCONN;
break;
}
memset(&cinfo, 0, sizeof(cinfo));
cinfo.hci_handle = chan->conn->hcon->handle;
memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3);
len = min_t(unsigned int, len, sizeof(cinfo));
if (copy_to_user(optval, (char *) &cinfo, len))
err = -EFAULT;
break;
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 389 | 83.30% | 6 | 50.00% |
Johan Hedberg | 24 | 5.14% | 1 | 8.33% |
Marcel Holtmann | 14 | 3.00% | 1 | 8.33% |
Andrei Emeltchenko | 14 | 3.00% | 2 | 16.67% |
Filip Palian | 13 | 2.78% | 1 | 8.33% |
Vasiliy Kulikov | 13 | 2.78% | 1 | 8.33% |
Total | 467 | 100.00% | 12 | 100.00% |
static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct bt_security sec;
struct bt_power pwr;
int len, err = 0;
BT_DBG("sk %p", sk);
if (level == SOL_L2CAP)
return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
if (level != SOL_BLUETOOTH)
return -ENOPROTOOPT;
if (get_user(len, optlen))
return -EFAULT;
lock_sock(sk);
switch (optname) {
case BT_SECURITY:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
chan->chan_type != L2CAP_CHAN_FIXED &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
}
memset(&sec, 0, sizeof(sec));
if (chan->conn) {
sec.level = chan->conn->hcon->sec_level;
if (sk->sk_state == BT_CONNECTED)
sec.key_size = chan->conn->hcon->enc_key_size;
} else {
sec.level = chan->sec_level;
}
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
err = -EFAULT;
break;
case BT_DEFER_SETUP:
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
err = -EINVAL;
break;
}
if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
(u32 __user *) optval))
err = -EFAULT;
break;
case BT_FLUSHABLE:
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
(u32 __user *) optval))
err = -EFAULT;
break;
case BT_POWER:
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
&& sk->sk_type != SOCK_RAW) {
err = -EINVAL;
break;
}
pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
len = min_t(unsigned int, len, sizeof(pwr));
if (copy_to_user(optval, (char *) &pwr, len))
err = -EFAULT;
break;
case BT_CHANNEL_POLICY:
if (put_user(chan->chan_policy, (u32 __user *) optval))
err = -EFAULT;
break;
case BT_SNDMTU:
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
}
if (sk->sk_state != BT_CONNECTED) {
err = -ENOTCONN;
break;
}
if (put_user(chan->omtu, (u16 __user *) optval))
err = -EFAULT;
break;
case BT_RCVMTU:
if (!bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
}
if (put_user(chan->imtu, (u16 __user *) optval))
err = -EFAULT;
break;
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 289 | 51.89% | 5 | 38.46% |
Johan Hedberg | 108 | 19.39% | 2 | 15.38% |
Jaikumar Ganesh | 75 | 13.46% | 1 | 7.69% |
Vinicius Costa Gomes | 33 | 5.92% | 1 | 7.69% |
Andrei Emeltchenko | 27 | 4.85% | 3 | 23.08% |
Mat Martineau | 25 | 4.49% | 1 | 7.69% |
Total | 557 | 100.00% | 13 | 100.00% |
static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
{
switch (chan->scid) {
case L2CAP_CID_ATT:
if (mtu < L2CAP_LE_MIN_MTU)
return false;
break;
default:
if (mtu < L2CAP_DEFAULT_MIN_MTU)
return false;
}
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andre Guedes | 47 | 97.92% | 2 | 66.67% |
Johan Hedberg | 1 | 2.08% | 1 | 33.33% |
Total | 48 | 100.00% | 3 | 100.00% |
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
struct l2cap_options opts;
int len, err = 0;
u32 opt;
BT_DBG("sk %p", sk);
lock_sock(sk);
switch (optname) {
case L2CAP_OPTIONS:
if (bdaddr_type_is_le(chan->src_type)) {
err = -EINVAL;
break;
}
if (sk->sk_state == BT_CONNECTED) {
err = -EINVAL;
break;
}
opts.imtu = chan->imtu;
opts.omtu = chan->omtu;
opts.flush_to = chan->flush_to;
opts.mode = chan->mode;
opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx;
opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) {
err = -EFAULT;
break;
}
if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {
err = -EINVAL;
break;
}
if (!l2cap_valid_mtu(chan, opts.imtu)) {
err = -EINVAL;
break;
}
chan->mode = opts.mode;
switch (chan->mode) {
case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_BASIC:
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
break;
case L2CAP_MODE_ERTM:
case