forked from mirrors/linux
		
	Bluetooth: Add basic LE L2CAP connect request receiving support
This patch adds the necessary boiler plate code to handle receiving L2CAP connect requests over LE and respond to them with a proper connect response. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
							parent
							
								
									791d60f71a
								
							
						
					
					
						commit
						27e2d4c8d2
					
				
					 2 changed files with 153 additions and 0 deletions
				
			
		|  | @ -846,6 +846,7 @@ int l2cap_init_sockets(void); | |||
| void l2cap_cleanup_sockets(void); | ||||
| bool l2cap_is_socket(struct socket *sock); | ||||
| 
 | ||||
| void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan); | ||||
| void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); | ||||
| 
 | ||||
| int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); | ||||
|  |  | |||
|  | @ -617,6 +617,29 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) | |||
| 	return; | ||||
| } | ||||
| 
 | ||||
| static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) | ||||
| { | ||||
| 	struct l2cap_conn *conn = chan->conn; | ||||
| 	struct l2cap_le_conn_rsp rsp; | ||||
| 	u16 result; | ||||
| 
 | ||||
| 	if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) | ||||
| 		result = L2CAP_CR_AUTHORIZATION; | ||||
| 	else | ||||
| 		result = L2CAP_CR_BAD_PSM; | ||||
| 
 | ||||
| 	l2cap_state_change(chan, BT_DISCONN); | ||||
| 
 | ||||
| 	rsp.dcid    = cpu_to_le16(chan->scid); | ||||
| 	rsp.mtu     = cpu_to_le16(chan->imtu); | ||||
| 	rsp.mps     = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||||
| 	rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||||
| 	rsp.result  = cpu_to_le16(result); | ||||
| 
 | ||||
| 	l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), | ||||
| 		       &rsp); | ||||
| } | ||||
| 
 | ||||
| static void l2cap_chan_connect_reject(struct l2cap_chan *chan) | ||||
| { | ||||
| 	struct l2cap_conn *conn = chan->conn; | ||||
|  | @ -663,6 +686,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) | |||
| 		if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { | ||||
| 			if (conn->hcon->type == ACL_LINK) | ||||
| 				l2cap_chan_connect_reject(chan); | ||||
| 			else if (conn->hcon->type == LE_LINK) | ||||
| 				l2cap_chan_le_connect_reject(chan); | ||||
| 		} | ||||
| 
 | ||||
| 		l2cap_chan_del(chan, reason); | ||||
|  | @ -3641,6 +3666,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, | |||
| 	return ptr - data; | ||||
| } | ||||
| 
 | ||||
| void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) | ||||
| { | ||||
| 	struct l2cap_le_conn_rsp rsp; | ||||
| 	struct l2cap_conn *conn = chan->conn; | ||||
| 
 | ||||
| 	BT_DBG("chan %p", chan); | ||||
| 
 | ||||
| 	rsp.dcid    = cpu_to_le16(chan->scid); | ||||
| 	rsp.mtu     = cpu_to_le16(chan->imtu); | ||||
| 	rsp.mps     = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||||
| 	rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||||
| 	rsp.result  = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); | ||||
| 
 | ||||
| 	l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), | ||||
| 		       &rsp); | ||||
| } | ||||
| 
 | ||||
| void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) | ||||
| { | ||||
| 	struct l2cap_conn_rsp rsp; | ||||
|  | @ -5382,6 +5424,113 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, | |||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int l2cap_le_connect_req(struct l2cap_conn *conn, | ||||
| 				struct l2cap_cmd_hdr *cmd, u16 cmd_len, | ||||
| 				u8 *data) | ||||
| { | ||||
| 	struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data; | ||||
| 	struct l2cap_le_conn_rsp rsp; | ||||
| 	struct l2cap_chan *chan, *pchan; | ||||
| 	u16 dcid, scid, mtu, mps; | ||||
| 	__le16 psm; | ||||
| 	u8 result; | ||||
| 
 | ||||
| 	if (cmd_len != sizeof(*req)) | ||||
| 		return -EPROTO; | ||||
| 
 | ||||
| 	scid = __le16_to_cpu(req->scid); | ||||
| 	mtu  = __le16_to_cpu(req->mtu); | ||||
| 	mps  = __le16_to_cpu(req->mps); | ||||
| 	psm  = req->psm; | ||||
| 	dcid = 0; | ||||
| 
 | ||||
| 	if (mtu < 23 || mps < 23) | ||||
| 		return -EPROTO; | ||||
| 
 | ||||
| 	BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm), | ||||
| 	       scid, mtu, mps); | ||||
| 
 | ||||
| 	/* Check if we have socket listening on psm */ | ||||
| 	pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, | ||||
| 					 &conn->hcon->dst, LE_LINK); | ||||
| 	if (!pchan) { | ||||
| 		result = L2CAP_CR_BAD_PSM; | ||||
| 		chan = NULL; | ||||
| 		goto response; | ||||
| 	} | ||||
| 
 | ||||
| 	mutex_lock(&conn->chan_lock); | ||||
| 	l2cap_chan_lock(pchan); | ||||
| 
 | ||||
| 	if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) { | ||||
| 		result = L2CAP_CR_AUTHENTICATION; | ||||
| 		chan = NULL; | ||||
| 		goto response_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Check if we already have channel with that dcid */ | ||||
| 	if (__l2cap_get_chan_by_dcid(conn, scid)) { | ||||
| 		result = L2CAP_CR_NO_MEM; | ||||
| 		chan = NULL; | ||||
| 		goto response_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	chan = pchan->ops->new_connection(pchan); | ||||
| 	if (!chan) { | ||||
| 		result = L2CAP_CR_NO_MEM; | ||||
| 		goto response_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	bacpy(&chan->src, &conn->hcon->src); | ||||
| 	bacpy(&chan->dst, &conn->hcon->dst); | ||||
| 	chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); | ||||
| 	chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); | ||||
| 	chan->psm  = psm; | ||||
| 	chan->dcid = scid; | ||||
| 	chan->omtu = mtu; | ||||
| 	chan->remote_mps = mps; | ||||
| 
 | ||||
| 	__l2cap_chan_add(conn, chan); | ||||
| 	dcid = chan->scid; | ||||
| 
 | ||||
| 	__set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); | ||||
| 
 | ||||
| 	chan->ident = cmd->ident; | ||||
| 
 | ||||
| 	if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { | ||||
| 		l2cap_state_change(chan, BT_CONNECT2); | ||||
| 		result = L2CAP_CR_PEND; | ||||
| 		chan->ops->defer(chan); | ||||
| 	} else { | ||||
| 		l2cap_chan_ready(chan); | ||||
| 		result = L2CAP_CR_SUCCESS; | ||||
| 	} | ||||
| 
 | ||||
| response_unlock: | ||||
| 	l2cap_chan_unlock(pchan); | ||||
| 	mutex_unlock(&conn->chan_lock); | ||||
| 
 | ||||
| 	if (result == L2CAP_CR_PEND) | ||||
| 		return 0; | ||||
| 
 | ||||
| response: | ||||
| 	if (chan) { | ||||
| 		rsp.mtu = cpu_to_le16(chan->imtu); | ||||
| 		rsp.mps = __constant_cpu_to_le16(L2CAP_LE_DEFAULT_MPS); | ||||
| 	} else { | ||||
| 		rsp.mtu = 0; | ||||
| 		rsp.mps = 0; | ||||
| 	} | ||||
| 
 | ||||
| 	rsp.dcid    = cpu_to_le16(dcid); | ||||
| 	rsp.credits = __constant_cpu_to_le16(L2CAP_LE_MAX_CREDITS); | ||||
| 	rsp.result  = cpu_to_le16(result); | ||||
| 
 | ||||
| 	l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, | ||||
| 				   struct l2cap_cmd_hdr *cmd, u16 cmd_len, | ||||
| 				   u8 *data) | ||||
|  | @ -5400,6 +5549,9 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, | |||
| 		l2cap_le_connect_rsp(conn, cmd, cmd_len, data); | ||||
| 		return 0; | ||||
| 
 | ||||
| 	case L2CAP_LE_CONN_REQ: | ||||
| 		return l2cap_le_connect_req(conn, cmd, cmd_len, data); | ||||
| 
 | ||||
| 	default: | ||||
| 		BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); | ||||
| 		return -EINVAL; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Johan Hedberg
						Johan Hedberg