Release 4.11 net/bluetooth/l2cap_core.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
Copyright (c) 2012 Code Aurora Forum. All rights reserved.
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 core. */
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/crc16.h>
#include <linux/filter.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include "smp.h"
#include "a2mp.h"
#include "amp.h"
#define LE_FLOWCTL_MAX_CREDITS 65535
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
static u16 le_max_credits = L2CAP_LE_MAX_CREDITS;
static u16 le_default_mps = L2CAP_LE_DEFAULT_MPS;
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
void *data);
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event);
static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
{
if (link_type == LE_LINK) {
if (bdaddr_type == ADDR_LE_DEV_PUBLIC)
return BDADDR_LE_PUBLIC;
else
return BDADDR_LE_RANDOM;
}
return BDADDR_BREDR;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Marcel Holtmann | 30 | 81.08% | 1 | 50.00% |
Johan Hedberg | 7 | 18.92% | 1 | 50.00% |
Total | 37 | 100.00% | 2 | 100.00% |
static inline u8 bdaddr_src_type(struct hci_conn *hcon)
{
return bdaddr_type(hcon->type, hcon->src_type);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static inline u8 bdaddr_dst_type(struct hci_conn *hcon)
{
return bdaddr_type(hcon->type, hcon->dst_type);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->dcid == cid)
return c;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 19 | 42.22% | 4 | 50.00% |
Linus Torvalds | 11 | 24.44% | 1 | 12.50% |
Marcel Holtmann | 9 | 20.00% | 1 | 12.50% |
Andrei Emeltchenko | 4 | 8.89% | 1 | 12.50% |
Maksim Krasnyanskiy | 2 | 4.44% | 1 | 12.50% |
Total | 45 | 100.00% | 8 | 100.00% |
static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->scid == cid)
return c;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 18 | 40.00% | 3 | 42.86% |
Linus Torvalds | 9 | 20.00% | 1 | 14.29% |
Marcel Holtmann | 8 | 17.78% | 1 | 14.29% |
Maksim Krasnyanskiy | 6 | 13.33% | 1 | 14.29% |
Andrei Emeltchenko | 4 | 8.89% | 1 | 14.29% |
Total | 45 | 100.00% | 7 | 100.00% |
/* Find channel with given SCID.
* Returns locked channel. */
static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
u16 cid)
{
struct l2cap_chan *c;
mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid);
if (c)
l2cap_chan_lock(c);
mutex_unlock(&conn->chan_lock);
return c;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 21 | 36.21% | 2 | 28.57% |
Maxim Krasnyansky | 14 | 24.14% | 1 | 14.29% |
Marcel Holtmann | 8 | 13.79% | 1 | 14.29% |
Andrei Emeltchenko | 8 | 13.79% | 1 | 14.29% |
Gustavo Fernando Padovan | 7 | 12.07% | 2 | 28.57% |
Total | 58 | 100.00% | 7 | 100.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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 46 | 79.31% | 1 | 20.00% |
Marcel Holtmann | 5 | 8.62% | 1 | 20.00% |
Andrei Emeltchenko | 4 | 6.90% | 1 | 20.00% |
Gustavo Fernando Padovan | 2 | 3.45% | 1 | 20.00% |
Maxim Krasnyansky | 1 | 1.72% | 1 | 20.00% |
Total | 58 | 100.00% | 5 | 100.00% |
static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
u8 ident)
{
struct l2cap_chan *c;
list_for_each_entry(c, &conn->chan_l, list) {
if (c->ident == ident)
return c;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Marcel Holtmann | 23 | 51.11% | 2 | 33.33% |
Gustavo Fernando Padovan | 18 | 40.00% | 3 | 50.00% |
Andrei Emeltchenko | 4 | 8.89% | 1 | 16.67% |
Total | 45 | 100.00% | 6 | 100.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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 58 | 100.00% | 1 | 100.00% |
Total | 58 | 100.00% | 1 | 100.00% |
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{
struct l2cap_chan *c;
list_for_each_entry(c, &chan_list, global_l) {
if (c->sport == psm && !bacmp(&c->src, src))
return c;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 51 | 92.73% | 3 | 75.00% |
Szymon Janc | 4 | 7.27% | 1 | 25.00% |
Total | 55 | 100.00% | 4 | 100.00% |
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
{
int err;
write_lock(&chan_list_lock);
if (psm && __l2cap_global_chan_by_addr(psm, src)) {
err = -EADDRINUSE;
goto done;
}
if (psm) {
chan->psm = psm;
chan->sport = psm;
err = 0;
} else {
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;
}
err = -EINVAL;
for (p = start; p <= end; p += incr)
if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) {
chan->psm = cpu_to_le16(p);
chan->sport = cpu_to_le16(p);
err = 0;
break;
}
}
done:
write_unlock(&chan_list_lock);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 139 | 74.73% | 4 | 80.00% |
Johan Hedberg | 47 | 25.27% | 1 | 20.00% |
Total | 186 | 100.00% | 5 | 100.00% |
EXPORT_SYMBOL_GPL(l2cap_add_psm);
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
write_lock(&chan_list_lock);
/* Override the defaults (which are for conn-oriented) */
chan->omtu = L2CAP_DEFAULT_MTU;
chan->chan_type = L2CAP_CHAN_FIXED;
chan->scid = scid;
write_unlock(&chan_list_lock);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 34 | 72.34% | 3 | 75.00% |
Johan Hedberg | 13 | 27.66% | 1 | 25.00% |
Total | 47 | 100.00% | 4 | 100.00% |
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
{
u16 cid, dyn_end;
if (conn->hcon->type == LE_LINK)
dyn_end = L2CAP_CID_LE_DYN_END;
else
dyn_end = L2CAP_CID_DYN_END;
for (cid = L2CAP_CID_DYN_START; cid <= dyn_end; cid++) {
if (!__l2cap_get_chan_by_scid(conn, cid))
return cid;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Marcel Holtmann | 37 | 56.06% | 2 | 40.00% |
Johan Hedberg | 26 | 39.39% | 2 | 40.00% |
Gustavo Fernando Padovan | 3 | 4.55% | 1 | 20.00% |
Total | 66 | 100.00% | 5 | 100.00% |
static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
chan->ops->state_change(chan, state, 0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 51 | 98.08% | 4 | 80.00% |
Andrei Emeltchenko | 1 | 1.92% | 1 | 20.00% |
Total | 52 | 100.00% | 5 | 100.00% |
static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
int state, int err)
{
chan->state = state;
chan->ops->state_change(chan, chan->state, err);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 20 | 51.28% | 2 | 66.67% |
Andrei Emeltchenko | 19 | 48.72% | 1 | 33.33% |
Total | 39 | 100.00% | 3 | 100.00% |
static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
{
chan->ops->state_change(chan, chan->state, err);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrei Emeltchenko | 21 | 70.00% | 1 | 50.00% |
Gustavo Fernando Padovan | 9 | 30.00% | 1 | 50.00% |
Total | 30 | 100.00% | 2 | 100.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));
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
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));
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 41 | 100.00% | 1 | 100.00% |
Total | 41 | 100.00% | 1 | 100.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) {
if (bt_cb(skb)->l2cap.txseq == seq)
return skb;
}
return NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 47 | 97.92% | 1 | 50.00% |
Johan Hedberg | 1 | 2.08% | 1 | 50.00% |
Total | 48 | 100.00% | 2 | 100.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);
seq_list->list = kmalloc(sizeof(u16) * alloc_size, GFP_KERNEL);
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 99 | 100.00% | 1 | 100.00% |
Total | 99 | 100.00% | 1 | 100.00% |
static inline void l2cap_seq_list_free(struct l2cap_seq_list *seq_list)
{
kfree(seq_list->list);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 19 | 100.00% | 1 | 100.00% |
Total | 19 | 100.00% | 1 | 100.00% |
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
{
u16 seq = seq_list->head;
u16 mask = seq_list->mask;
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;
}
return seq;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 66 | 88.00% | 1 | 50.00% |
Johan Hedberg | 9 | 12.00% | 1 | 50.00% |
Total | 75 | 100.00% | 2 | 100.00% |
static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
{
u16 i;
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 54 | 91.53% | 1 | 50.00% |
Gustavo Fernando Padovan | 5 | 8.47% | 1 | 50.00% |
Total | 59 | 100.00% | 2 | 100.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 */
if (seq_list->list[seq & mask] != L2CAP_SEQ_LIST_CLEAR)
return;
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Mat Martineau | 79 | 97.53% | 1 | 50.00% |
Gustavo Fernando Padovan | 2 | 2.47% | 1 | 50.00% |
Total | 81 | 100.00% | 2 | 100.00% |
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
chan_timer.work);
struct l2cap_conn *conn = chan->conn;
int reason;
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
else if (chan->state == BT_CONNECT &&
chan->sec_level != BT_SECURITY_SDP)
reason = ECONNREFUSED;
else
reason = ETIMEDOUT;
l2cap_chan_close(chan, reason);
l2cap_chan_unlock(chan);
chan->ops->close(chan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(chan);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 110 | 76.92% | 6 | 60.00% |
Andrei Emeltchenko | 32 | 22.38% | 3 | 30.00% |
Ulisses Furquim | 1 | 0.70% | 1 | 10.00% |
Total | 143 | 100.00% | 10 | 100.00% |
struct l2cap_chan *l2cap_chan_create(void)
{
struct l2cap_chan *chan;
chan = kzalloc(sizeof(*chan), GFP_ATOMIC);
if (!chan)
return NULL;
mutex_init(&chan->lock);
/* Set default lock nesting level */
atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
write_lock(&chan_list_lock);
list_add(&chan->global_l, &chan_list);
write_unlock(&chan_list_lock);
INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout);
chan->state = BT_OPEN;
kref_init(&chan->kref);
/* This flag is cleared in l2cap_chan_ready() */
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
BT_DBG("chan %p", chan);
return chan;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 84 | 68.85% | 8 | 61.54% |
Mat Martineau | 11 | 9.02% | 1 | 7.69% |
Johan Hedberg | 11 | 9.02% | 1 | 7.69% |
Andrei Emeltchenko | 8 | 6.56% | 1 | 7.69% |
Szymon Janc | 6 | 4.92% | 1 | 7.69% |
Syam Sidhardhan | 2 | 1.64% | 1 | 7.69% |
Total | 122 | 100.00% | 13 | 100.00% |
EXPORT_SYMBOL_GPL(l2cap_chan_create);
static void l2cap_chan_destroy(struct kref *kref)
{
struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
BT_DBG("chan %p", chan);
write_lock(&chan_list_lock);
list_del(&chan->global_l);
write_unlock(&chan_list_lock);
kfree(chan);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 32 | 55.17% | 3 | 60.00% |
Syam Sidhardhan | 17 | 29.31% | 1 | 20.00% |
Jaganath Kanakkassery | 9 | 15.52% | 1 | 20.00% |
Total | 58 | 100.00% | 5 | 100.00% |
void l2cap_chan_hold(struct l2cap_chan *c)
{
BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
kref_get(&c->kref);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jaganath Kanakkassery | 29 | 87.88% | 1 | 33.33% |
Syam Sidhardhan | 3 | 9.09% | 1 | 33.33% |
Peter Zijlstra | 1 | 3.03% | 1 | 33.33% |
Total | 33 | 100.00% | 3 | 100.00% |
void l2cap_chan_put(struct l2cap_chan *c)
{
BT_DBG("chan %p orig refcnt %d", c, kref_read(&c->kref));
kref_put(&c->kref, l2cap_chan_destroy);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jaganath Kanakkassery | 30 | 85.71% | 2 | 50.00% |
Syam Sidhardhan | 4 | 11.43% | 1 | 25.00% |
Peter Zijlstra | 1 | 2.86% | 1 | 25.00% |
Total | 35 | 100.00% | 4 | 100.00% |
EXPORT_SYMBOL_GPL(l2cap_chan_put);
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;
chan->remote_max_tx = chan->max_tx;
chan->remote_tx_win = chan->tx_win;
chan->ack_win = L2CAP_DEFAULT_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW;
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;
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrei Emeltchenko | 50 | 52.08% | 1 | 33.33% |
Jukka Rissanen | 40 | 41.67% | 1 | 33.33% |
Mat Martineau | 6 | 6.25% | 1 | 33.33% |
Total | 96 | 100.00% | 3 | 100.00% |
EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
{
chan->sdu = NULL;
chan->sdu_last_frag = NULL;
chan->sdu_len = 0;
chan->tx_credits = 0;
chan->rx_credits = le_max_credits;
chan->mps = min_t(u16, chan->imtu, le_default_mps);
skb_queue_head_init(&chan->tx_q);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 64 | 100.00% | 7 | 100.00% |
Total | 64 | 100.00% | 7 | 100.00% |
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
__le16_to_cpu(chan->psm), chan->dcid);
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
chan->conn = conn;
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
/* Alloc CID for connection-oriented socket */
chan->scid = l2cap_alloc_cid(conn);
if (conn->hcon->type == ACL_LINK)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
case L2CAP_CHAN_CONN_LESS:
/* Connectionless socket */
chan->scid = L2CAP_CID_CONN_LESS;
chan->dcid = L2CAP_CID_CONN_LESS;
chan->omtu = L2CAP_DEFAULT_MTU;
break;
case L2CAP_CHAN_FIXED:
/* Caller will set CID and CID specific MTU values */
break;
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
chan->dcid = L2CAP_CID_SIGNALING;
chan->omtu = L2CAP_DEFAULT_MTU;
}
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;
chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO;
l2cap_chan_hold(chan);
/* 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);
list_add(&chan->list, &conn->chan_l);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrei Emeltchenko | 55 | 26.07% | 8 | 27.59% |
Marcel Holtmann | 51 | 24.17% | 2 | 6.90% |
Johan Hedberg | 38 | 18.01% | 4 | 13.79% |
Gustavo Fernando Padovan | 35 | 16.59% | 9 | 31.03% |
Maxim Krasnyansky | 12 | 5.69% | 1 | 3.45% |
Linus Torvalds | 9 | 4.27% | 2 | 6.90% |
Maksim Krasnyanskiy | 5 | 2.37% | 1 | 3.45% |
Ville Tervo | 5 | 2.37% | 1 | 3.45% |
Ulisses Furquim | 1 | 0.47% | 1 | 3.45% |
Total | 211 | 100.00% | 29 | 100.00% |
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->chan_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrei Emeltchenko | 37 | 97.37% | 2 | 66.67% |
Maksim Krasnyanskiy | 1 | 2.63% | 1 | 33.33% |
Total | 38 | 100.00% | 3 | 100.00% |
void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
__clear_chan_timer(chan);
BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
state_to_string(chan->state));
chan->ops->teardown(chan, err);
if (conn) {
struct amp_mgr *mgr = conn->hcon->amp_mgr;
/* Delete from channel list */
list_del(&chan->list);
l2cap_chan_put(chan);
chan->conn = NULL;
/* 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))
hci_conn_drop(conn->hcon);
if (mgr && mgr->bredr_chan == chan)
mgr->bredr_chan = NULL;
}
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);
}
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
switch(chan->mode) {
case L2CAP_MODE_BASIC:
break;
case L2CAP_MODE_LE_FLOWCTL:
skb_queue_purge(&chan->tx_q);
break;
case L2CAP_MODE_ERTM:
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
skb_queue_purge(&chan->srej_q);
l2cap_seq_list_free(&chan->srej_list);
l2cap_seq_list_free(&chan->retrans_list);
/* fall through */
case L2CAP_MODE_STREAMING:
skb_queue_purge(&chan->tx_q);
break;
}
return;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Gustavo Fernando Padovan | 81 | 31.27% | 17 | 50.00% |
Andrei Emeltchenko | 62 | 23.94% | 5 | 14.71% |
Johan Hedberg | 44 | 16.99% | 5 | 14.71% |
Marcel Holtmann | 32 | 12.36% | 1 | 2.94% |
Mat Martineau | 17 | 6.56% | 2 | 5.88% |
Maksim Krasnyanskiy | 14 | 5.41% | 1 | 2.94% |
Linus Torvalds | 7 | 2.70% | 1 | 2.94% |
David Herrmann | 1 | 0.39% | 1 | 2.94% |
Ulisses Furquim | 1 | 0.39% | 1 | 2.94% |
Total | 259 | 100.00% | 34 | 100.00% |
EXPORT_SYMBOL_GPL(l2cap_chan_del);
static void l2cap_conn_update_id_addr(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
id_addr_update_work);
struct hci_conn *hcon = conn->hcon;
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);
chan->dst_type = bdaddr_dst_type(hcon);
l2cap_chan_unlock(chan);
}
mutex_unlock(&conn->chan_lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 98 | 100.00% | 3 | 100.00% |
Total | 98 | 100.00% | 3 | 100.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))
result = L2CAP_CR_AUTHORIZATION;
else
result = L2CAP_CR_BAD_PSM;
l2cap_state_change(chan, BT_DISCONN);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.mtu = cpu_to_le16(chan->imtu);
rsp.mps = cpu_to_le16(chan->mps);
rsp.credits = cpu_to_le16(chan->rx_credits);
rsp.result = cpu_to_le16(result);
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
&rsp);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johan Hedberg | 127 | 100.00% | 3 | 100.00% |
Total | 127 | 100.00% | 3 | 100.00% |
static void