8036 lines · 190.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/*   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>    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" #define LE_FLOWCTL_MAX_CREDITS 65535 bool disable_ertm;bool enable_ecred = IS_ENABLED(CONFIG_BT_LE_L2CAP_ECRED); static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; static LIST_HEAD(chan_list);static DEFINE_RWLOCK(chan_list_lock); 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, size_t data_size);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 void l2cap_retrans_timeout(struct work_struct *work);static void l2cap_monitor_timeout(struct work_struct *work);static void l2cap_ack_timeout(struct work_struct *work); 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;} 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);} /* ---- 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;} 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;} /* Find channel with given SCID. * Returns a reference locked channel. */static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,						 u16 cid){	struct l2cap_chan *c; 	c = __l2cap_get_chan_by_scid(conn, cid);	if (c) {		/* Only lock if chan reference is not 0 */		c = l2cap_chan_hold_unless_zero(c);		if (c)			l2cap_chan_lock(c);	} 	return c;} /* Find channel with given DCID. * Returns a reference locked channel. */static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,						 u16 cid){	struct l2cap_chan *c; 	c = __l2cap_get_chan_by_dcid(conn, cid);	if (c) {		/* Only lock if chan reference is not 0 */		c = l2cap_chan_hold_unless_zero(c);		if (c)			l2cap_chan_lock(c);	} 	return c;} 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;} static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src,						      u8 src_type){	struct l2cap_chan *c; 	list_for_each_entry(c, &chan_list, global_l) {		if (src_type == BDADDR_BREDR && c->src_type != BDADDR_BREDR)			continue; 		if (src_type != BDADDR_BREDR && c->src_type == BDADDR_BREDR)			continue; 		if (c->sport == psm && !bacmp(&c->src, src))			return c;	}	return NULL;} 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, chan->src_type)) {		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->src_type)) {				chan->psm   = cpu_to_le16(p);				chan->sport = cpu_to_le16(p);				err = 0;				break;			}	} done:	write_unlock(&chan_list_lock);	return err;}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;} 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;} 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);} 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);} static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err){	chan->ops->state_change(chan, chan->state, err);} 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));	}} 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;} /* ---- 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_array(alloc_size, sizeof(u16), 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;} 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;} 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;} 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;} 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;} 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)); 	if (test_bit(FLAG_DEL, &chan->flags)) {		l2cap_chan_put(chan);		return;	} 	mutex_lock(&conn->lock);	/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling	 * this work. No need to call l2cap_chan_hold(chan) here again.	 */	l2cap_chan_lock(chan); 	if (test_bit(FLAG_DEL, &chan->flags))		goto unlock; 	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); 	chan->ops->close(chan); unlock:	l2cap_chan_unlock(chan);	mutex_unlock(&conn->lock);	l2cap_chan_put(chan);} struct l2cap_chan *l2cap_chan_create(void){	struct l2cap_chan *chan; 	chan = kzalloc_obj(*chan, GFP_ATOMIC);	if (!chan)		return NULL; 	skb_queue_head_init(&chan->tx_q);	skb_queue_head_init(&chan->srej_q);	mutex_init(&chan->lock); 	/* Set default lock nesting level */	atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL); 	/* Available receive buffer space is initially unknown */	chan->rx_avail = -1; 	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);	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); 	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;}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); 	if (chan->conn)		l2cap_conn_put(chan->conn); 	kfree(chan);} void l2cap_chan_hold(struct l2cap_chan *c){

Lines 1–500 of 8036.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t