mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-07-06 13:55:22 +02:00
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
|
@ -846,6 +846,7 @@ int l2cap_init_sockets(void);
|
||||||
void l2cap_cleanup_sockets(void);
|
void l2cap_cleanup_sockets(void);
|
||||||
bool l2cap_is_socket(struct socket *sock);
|
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);
|
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan);
|
||||||
|
|
||||||
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
|
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;
|
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)
|
static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
struct l2cap_conn *conn = chan->conn;
|
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 (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
|
||||||
if (conn->hcon->type == ACL_LINK)
|
if (conn->hcon->type == ACL_LINK)
|
||||||
l2cap_chan_connect_reject(chan);
|
l2cap_chan_connect_reject(chan);
|
||||||
|
else if (conn->hcon->type == LE_LINK)
|
||||||
|
l2cap_chan_le_connect_reject(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
l2cap_chan_del(chan, reason);
|
l2cap_chan_del(chan, reason);
|
||||||
|
@ -3641,6 +3666,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
|
||||||
return ptr - 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)
|
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
struct l2cap_conn_rsp rsp;
|
struct l2cap_conn_rsp rsp;
|
||||||
|
@ -5382,6 +5424,113 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
||||||
return err;
|
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,
|
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
||||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||||
u8 *data)
|
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);
|
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case L2CAP_LE_CONN_REQ:
|
||||||
|
return l2cap_le_connect_req(conn, cmd, cmd_len, data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
|
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user