mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
This commit is contained in:
commit
f647a52e15
222 changed files with 5029 additions and 4302 deletions
|
|
@ -31,7 +31,7 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#define VERSION "2.17"
|
||||
#define VERSION "2.18"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
|
|
|
|||
|
|
@ -1228,7 +1228,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
|
|||
/* If Connectionless Slave Broadcast master role is supported
|
||||
* enable all necessary events for it.
|
||||
*/
|
||||
if (hdev->features[2][0] & 0x01) {
|
||||
if (lmp_csb_master_capable(hdev)) {
|
||||
events[1] |= 0x40; /* Triggered Clock Capture */
|
||||
events[1] |= 0x80; /* Synchronization Train Complete */
|
||||
events[2] |= 0x10; /* Slave Page Response Timeout */
|
||||
|
|
@ -1238,7 +1238,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
|
|||
/* If Connectionless Slave Broadcast slave role is supported
|
||||
* enable all necessary events for it.
|
||||
*/
|
||||
if (hdev->features[2][0] & 0x02) {
|
||||
if (lmp_csb_slave_capable(hdev)) {
|
||||
events[2] |= 0x01; /* Synchronization Train Received */
|
||||
events[2] |= 0x02; /* CSB Receive */
|
||||
events[2] |= 0x04; /* CSB Timeout */
|
||||
|
|
@ -1275,15 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
|||
hci_setup_link_policy(req);
|
||||
|
||||
if (lmp_le_capable(hdev)) {
|
||||
/* If the controller has a public BD_ADDR, then by
|
||||
* default use that one. If this is a LE only
|
||||
* controller without one, default to the random
|
||||
* address.
|
||||
*/
|
||||
if (bacmp(&hdev->bdaddr, BDADDR_ANY))
|
||||
hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
else
|
||||
hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
|
||||
/* If the controller has a public BD_ADDR, then
|
||||
* by default use that one. If this is a LE only
|
||||
* controller without a public address, default
|
||||
* to the random address.
|
||||
*/
|
||||
if (bacmp(&hdev->bdaddr, BDADDR_ANY))
|
||||
hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
else
|
||||
hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
}
|
||||
|
||||
hci_set_le_support(req);
|
||||
}
|
||||
|
|
@ -1307,7 +1309,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
|
|||
hci_set_event_mask_page_2(req);
|
||||
|
||||
/* Check for Synchronization Train support */
|
||||
if (hdev->features[2][0] & 0x04)
|
||||
if (lmp_sync_train_capable(hdev))
|
||||
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -486,7 +486,10 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev,
|
|||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (!rp->status)
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
if (test_bit(HCI_SETUP, &hdev->dev_flags))
|
||||
memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
|
||||
}
|
||||
|
||||
|
|
@ -538,12 +541,6 @@ static void hci_cc_read_local_features(struct hci_dev *hdev,
|
|||
|
||||
if (hdev->features[0][5] & LMP_EDR_3S_ESCO)
|
||||
hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
|
||||
|
||||
BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
|
||||
hdev->features[0][0], hdev->features[0][1],
|
||||
hdev->features[0][2], hdev->features[0][3],
|
||||
hdev->features[0][4], hdev->features[0][5],
|
||||
hdev->features[0][6], hdev->features[0][7]);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
|
||||
|
|
@ -1782,7 +1779,9 @@ static u8 hci_to_mgmt_reason(u8 err)
|
|||
static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_disconn_complete *ev = (void *) skb->data;
|
||||
u8 reason = hci_to_mgmt_reason(ev->reason);
|
||||
struct hci_conn *conn;
|
||||
u8 type;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||
|
||||
|
|
@ -1792,43 +1791,38 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (ev->status == 0)
|
||||
conn->state = BT_CLOSED;
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
|
||||
(conn->type == ACL_LINK || conn->type == LE_LINK)) {
|
||||
if (ev->status) {
|
||||
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, ev->status);
|
||||
} else {
|
||||
u8 reason = hci_to_mgmt_reason(ev->reason);
|
||||
|
||||
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, reason);
|
||||
}
|
||||
if (ev->status) {
|
||||
mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, ev->status);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (ev->status == 0) {
|
||||
u8 type = conn->type;
|
||||
conn->state = BT_CLOSED;
|
||||
|
||||
if (type == ACL_LINK && conn->flush_key)
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
|
||||
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
|
||||
conn->dst_type, reason);
|
||||
|
||||
/* Re-enable advertising if necessary, since it might
|
||||
* have been disabled by the connection. From the
|
||||
* HCI_LE_Set_Advertise_Enable command description in
|
||||
* the core specification (v4.0):
|
||||
* "The Controller shall continue advertising until the Host
|
||||
* issues an LE_Set_Advertise_Enable command with
|
||||
* Advertising_Enable set to 0x00 (Advertising is disabled)
|
||||
* or until a connection is created or until the Advertising
|
||||
* is timed out due to Directed Advertising."
|
||||
*/
|
||||
if (type == LE_LINK)
|
||||
mgmt_reenable_advertising(hdev);
|
||||
}
|
||||
if (conn->type == ACL_LINK && conn->flush_key)
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
type = conn->type;
|
||||
|
||||
hci_proto_disconn_cfm(conn, ev->reason);
|
||||
hci_conn_del(conn);
|
||||
|
||||
/* Re-enable advertising if necessary, since it might
|
||||
* have been disabled by the connection. From the
|
||||
* HCI_LE_Set_Advertise_Enable command description in
|
||||
* the core specification (v4.0):
|
||||
* "The Controller shall continue advertising until the Host
|
||||
* issues an LE_Set_Advertise_Enable command with
|
||||
* Advertising_Enable set to 0x00 (Advertising is disabled)
|
||||
* or until a connection is created or until the Advertising
|
||||
* is timed out due to Directed Advertising."
|
||||
*/
|
||||
if (type == LE_LINK)
|
||||
mgmt_reenable_advertising(hdev);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
|
|||
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,
|
||||
|
|
@ -213,9 +216,14 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
|
|||
|
||||
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
||||
{
|
||||
u16 cid = L2CAP_CID_DYN_START;
|
||||
u16 cid, dyn_end;
|
||||
|
||||
for (; cid < L2CAP_CID_DYN_END; cid++) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -490,6 +498,18 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
|
|||
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
|
||||
}
|
||||
|
||||
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, L2CAP_LE_DEFAULT_MPS);
|
||||
|
||||
skb_queue_head_init(&chan->tx_q);
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
@ -502,12 +522,12 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
|||
switch (chan->chan_type) {
|
||||
case L2CAP_CHAN_CONN_ORIENTED:
|
||||
if (conn->hcon->type == LE_LINK) {
|
||||
/* LE connection */
|
||||
chan->omtu = L2CAP_DEFAULT_MTU;
|
||||
if (chan->dcid == L2CAP_CID_ATT)
|
||||
if (chan->dcid == L2CAP_CID_ATT) {
|
||||
chan->omtu = L2CAP_DEFAULT_MTU;
|
||||
chan->scid = L2CAP_CID_ATT;
|
||||
else
|
||||
} else {
|
||||
chan->scid = l2cap_alloc_cid(conn);
|
||||
}
|
||||
} else {
|
||||
/* Alloc CID for connection-oriented socket */
|
||||
chan->scid = l2cap_alloc_cid(conn);
|
||||
|
|
@ -597,6 +617,10 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
|||
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);
|
||||
|
|
@ -617,6 +641,50 @@ 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 = 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);
|
||||
}
|
||||
|
||||
static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
struct l2cap_conn_rsp rsp;
|
||||
u16 result;
|
||||
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
else
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
|
||||
l2cap_state_change(chan, BT_DISCONN);
|
||||
|
||||
rsp.scid = cpu_to_le16(chan->dcid);
|
||||
rsp.dcid = cpu_to_le16(chan->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||
}
|
||||
|
||||
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
|
|
@ -630,8 +698,10 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
|
|||
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
/* ATT uses L2CAP_CHAN_CONN_ORIENTED so we must also
|
||||
* check for chan->psm.
|
||||
*/
|
||||
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && chan->psm) {
|
||||
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
||||
l2cap_send_disconn_req(chan, reason);
|
||||
} else
|
||||
|
|
@ -639,24 +709,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
|
|||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
result = L2CAP_CR_SEC_BLOCK;
|
||||
else
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
|
||||
l2cap_state_change(chan, BT_DISCONN);
|
||||
|
||||
rsp.scid = cpu_to_le16(chan->dcid);
|
||||
rsp.dcid = cpu_to_le16(chan->scid);
|
||||
rsp.result = cpu_to_le16(result);
|
||||
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
|
||||
sizeof(rsp), &rsp);
|
||||
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);
|
||||
|
|
@ -726,6 +783,9 @@ int l2cap_chan_check_security(struct l2cap_chan *chan)
|
|||
struct l2cap_conn *conn = chan->conn;
|
||||
__u8 auth_type;
|
||||
|
||||
if (conn->hcon->type == LE_LINK)
|
||||
return smp_conn_security(conn->hcon, chan->sec_level);
|
||||
|
||||
auth_type = l2cap_get_auth_type(chan);
|
||||
|
||||
return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
|
||||
|
|
@ -1152,16 +1212,57 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
|
|||
chan->conf_state = 0;
|
||||
__clear_chan_timer(chan);
|
||||
|
||||
if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits)
|
||||
chan->ops->suspend(chan);
|
||||
|
||||
chan->state = BT_CONNECTED;
|
||||
|
||||
chan->ops->ready(chan);
|
||||
}
|
||||
|
||||
static void l2cap_le_connect(struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
struct l2cap_le_conn_req req;
|
||||
|
||||
if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
|
||||
return;
|
||||
|
||||
req.psm = chan->psm;
|
||||
req.scid = cpu_to_le16(chan->scid);
|
||||
req.mtu = cpu_to_le16(chan->imtu);
|
||||
req.mps = cpu_to_le16(chan->mps);
|
||||
req.credits = cpu_to_le16(chan->rx_credits);
|
||||
|
||||
chan->ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_REQ,
|
||||
sizeof(req), &req);
|
||||
}
|
||||
|
||||
static void l2cap_le_start(struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
|
||||
if (!smp_conn_security(conn->hcon, chan->sec_level))
|
||||
return;
|
||||
|
||||
if (!chan->psm) {
|
||||
l2cap_chan_ready(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan->state == BT_CONNECT)
|
||||
l2cap_le_connect(chan);
|
||||
}
|
||||
|
||||
static void l2cap_start_connection(struct l2cap_chan *chan)
|
||||
{
|
||||
if (__amp_capable(chan)) {
|
||||
BT_DBG("chan %p AMP capable: discover AMPs", chan);
|
||||
a2mp_discover_amp(chan);
|
||||
} else if (chan->conn->hcon->type == LE_LINK) {
|
||||
l2cap_le_start(chan);
|
||||
} else {
|
||||
l2cap_send_conn_req(chan);
|
||||
}
|
||||
|
|
@ -1172,7 +1273,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
|
|||
struct l2cap_conn *conn = chan->conn;
|
||||
|
||||
if (conn->hcon->type == LE_LINK) {
|
||||
l2cap_chan_ready(chan);
|
||||
l2cap_le_start(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1430,9 +1531,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|||
}
|
||||
|
||||
if (hcon->type == LE_LINK) {
|
||||
if (smp_conn_security(hcon, chan->sec_level))
|
||||
l2cap_chan_ready(chan);
|
||||
|
||||
l2cap_le_start(chan);
|
||||
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
|
||||
l2cap_chan_ready(chan);
|
||||
|
||||
|
|
@ -1703,7 +1802,8 @@ EXPORT_SYMBOL(l2cap_conn_put);
|
|||
*/
|
||||
static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
|
||||
bdaddr_t *src,
|
||||
bdaddr_t *dst)
|
||||
bdaddr_t *dst,
|
||||
u8 link_type)
|
||||
{
|
||||
struct l2cap_chan *c, *c1 = NULL;
|
||||
|
||||
|
|
@ -1713,6 +1813,12 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
|
|||
if (state && c->state != state)
|
||||
continue;
|
||||
|
||||
if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
|
||||
continue;
|
||||
|
||||
if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
|
||||
continue;
|
||||
|
||||
if (c->psm == psm) {
|
||||
int src_match, dst_match;
|
||||
int src_any, dst_any;
|
||||
|
|
@ -1739,6 +1845,18 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
|
|||
return c1;
|
||||
}
|
||||
|
||||
static bool is_valid_psm(u16 psm, u8 dst_type)
|
||||
{
|
||||
if (!psm)
|
||||
return false;
|
||||
|
||||
if (bdaddr_type_is_le(dst_type))
|
||||
return (psm <= 0x00ff);
|
||||
|
||||
/* PSM must be odd and lsb of upper byte must be 0 */
|
||||
return ((psm & 0x0101) == 0x0001);
|
||||
}
|
||||
|
||||
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
||||
bdaddr_t *dst, u8 dst_type)
|
||||
{
|
||||
|
|
@ -1759,8 +1877,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
/* PSM must be odd and lsb of upper byte must be 0 */
|
||||
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
|
||||
if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
|
||||
chan->chan_type != L2CAP_CHAN_RAW) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
|
|
@ -1774,6 +1891,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
break;
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
l2cap_le_flowctl_init(chan);
|
||||
break;
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
if (!disable_ertm)
|
||||
|
|
@ -2432,6 +2552,89 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
|
||||
struct msghdr *msg,
|
||||
size_t len, u16 sdulen)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
struct sk_buff *skb;
|
||||
int err, count, hlen;
|
||||
struct l2cap_hdr *lh;
|
||||
|
||||
BT_DBG("chan %p len %zu", chan, len);
|
||||
|
||||
if (!conn)
|
||||
return ERR_PTR(-ENOTCONN);
|
||||
|
||||
hlen = L2CAP_HDR_SIZE;
|
||||
|
||||
if (sdulen)
|
||||
hlen += L2CAP_SDULEN_SIZE;
|
||||
|
||||
count = min_t(unsigned int, (conn->mtu - hlen), len);
|
||||
|
||||
skb = chan->ops->alloc_skb(chan, count + hlen,
|
||||
msg->msg_flags & MSG_DONTWAIT);
|
||||
if (IS_ERR(skb))
|
||||
return skb;
|
||||
|
||||
/* Create L2CAP header */
|
||||
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||
lh->cid = cpu_to_le16(chan->dcid);
|
||||
lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
|
||||
|
||||
if (sdulen)
|
||||
put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
|
||||
|
||||
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
|
||||
if (unlikely(err < 0)) {
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
|
||||
struct sk_buff_head *seg_queue,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
size_t pdu_len;
|
||||
u16 sdu_len;
|
||||
|
||||
BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
|
||||
|
||||
pdu_len = chan->conn->mtu - L2CAP_HDR_SIZE;
|
||||
|
||||
pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
|
||||
|
||||
sdu_len = len;
|
||||
pdu_len -= L2CAP_SDULEN_SIZE;
|
||||
|
||||
while (len > 0) {
|
||||
if (len <= pdu_len)
|
||||
pdu_len = len;
|
||||
|
||||
skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
|
||||
if (IS_ERR(skb)) {
|
||||
__skb_queue_purge(seg_queue);
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
__skb_queue_tail(seg_queue, skb);
|
||||
|
||||
len -= pdu_len;
|
||||
|
||||
if (sdu_len) {
|
||||
sdu_len = 0;
|
||||
pdu_len += L2CAP_SDULEN_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
||||
u32 priority)
|
||||
{
|
||||
|
|
@ -2453,6 +2656,40 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
|
|||
}
|
||||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
/* Check outgoing MTU */
|
||||
if (len > chan->omtu)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (!chan->tx_credits)
|
||||
return -EAGAIN;
|
||||
|
||||
__skb_queue_head_init(&seg_queue);
|
||||
|
||||
err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
|
||||
|
||||
if (chan->state != BT_CONNECTED) {
|
||||
__skb_queue_purge(&seg_queue);
|
||||
err = -ENOTCONN;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
|
||||
|
||||
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
||||
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
||||
chan->tx_credits--;
|
||||
}
|
||||
|
||||
if (!chan->tx_credits)
|
||||
chan->ops->suspend(chan);
|
||||
|
||||
err = len;
|
||||
|
||||
break;
|
||||
|
||||
case L2CAP_MODE_BASIC:
|
||||
/* Check outgoing MTU */
|
||||
if (len > chan->omtu)
|
||||
|
|
@ -3592,6 +3829,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 = cpu_to_le16(chan->mps);
|
||||
rsp.credits = cpu_to_le16(chan->rx_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;
|
||||
|
|
@ -3713,7 +3967,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
|||
|
||||
/* Check if we have socket listening on psm */
|
||||
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
|
||||
&conn->hcon->dst);
|
||||
&conn->hcon->dst, ACL_LINK);
|
||||
if (!pchan) {
|
||||
result = L2CAP_CR_BAD_PSM;
|
||||
goto sendresp;
|
||||
|
|
@ -5155,18 +5409,17 @@ static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
|
|||
|
||||
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd,
|
||||
u8 *data)
|
||||
u16 cmd_len, u8 *data)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct l2cap_conn_param_update_req *req;
|
||||
struct l2cap_conn_param_update_rsp rsp;
|
||||
u16 min, max, latency, to_multiplier, cmd_len;
|
||||
u16 min, max, latency, to_multiplier;
|
||||
int err;
|
||||
|
||||
if (!(hcon->link_mode & HCI_LM_MASTER))
|
||||
return -EINVAL;
|
||||
|
||||
cmd_len = __le16_to_cpu(cmd->len);
|
||||
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
|
||||
return -EPROTO;
|
||||
|
||||
|
|
@ -5196,6 +5449,65 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||
u8 *data)
|
||||
{
|
||||
struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
|
||||
u16 dcid, mtu, mps, credits, result;
|
||||
struct l2cap_chan *chan;
|
||||
int err;
|
||||
|
||||
if (cmd_len < sizeof(*rsp))
|
||||
return -EPROTO;
|
||||
|
||||
dcid = __le16_to_cpu(rsp->dcid);
|
||||
mtu = __le16_to_cpu(rsp->mtu);
|
||||
mps = __le16_to_cpu(rsp->mps);
|
||||
credits = __le16_to_cpu(rsp->credits);
|
||||
result = __le16_to_cpu(rsp->result);
|
||||
|
||||
if (result == L2CAP_CR_SUCCESS && (mtu < 23 || mps < 23))
|
||||
return -EPROTO;
|
||||
|
||||
BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
|
||||
dcid, mtu, mps, credits, result);
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
switch (result) {
|
||||
case L2CAP_CR_SUCCESS:
|
||||
chan->ident = 0;
|
||||
chan->dcid = dcid;
|
||||
chan->omtu = mtu;
|
||||
chan->remote_mps = mps;
|
||||
chan->tx_credits = credits;
|
||||
l2cap_chan_ready(chan);
|
||||
break;
|
||||
|
||||
default:
|
||||
l2cap_chan_del(chan, ECONNREFUSED);
|
||||
break;
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||
u8 *data)
|
||||
|
|
@ -5276,23 +5588,235 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||
return err;
|
||||
}
|
||||
|
||||
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u8 *data)
|
||||
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, credits, 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;
|
||||
credits = 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;
|
||||
}
|
||||
|
||||
l2cap_le_flowctl_init(chan);
|
||||
|
||||
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;
|
||||
chan->tx_credits = __le16_to_cpu(req->credits);
|
||||
|
||||
__l2cap_chan_add(conn, chan);
|
||||
dcid = chan->scid;
|
||||
credits = chan->rx_credits;
|
||||
|
||||
__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 = cpu_to_le16(chan->mps);
|
||||
} else {
|
||||
rsp.mtu = 0;
|
||||
rsp.mps = 0;
|
||||
}
|
||||
|
||||
rsp.dcid = cpu_to_le16(dcid);
|
||||
rsp.credits = cpu_to_le16(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_credits(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||
u8 *data)
|
||||
{
|
||||
struct l2cap_le_credits *pkt;
|
||||
struct l2cap_chan *chan;
|
||||
u16 cid, credits;
|
||||
|
||||
if (cmd_len != sizeof(*pkt))
|
||||
return -EPROTO;
|
||||
|
||||
pkt = (struct l2cap_le_credits *) data;
|
||||
cid = __le16_to_cpu(pkt->cid);
|
||||
credits = __le16_to_cpu(pkt->credits);
|
||||
|
||||
BT_DBG("cid 0x%4.4x credits 0x%4.4x", cid, credits);
|
||||
|
||||
chan = l2cap_get_chan_by_dcid(conn, cid);
|
||||
if (!chan)
|
||||
return -EBADSLT;
|
||||
|
||||
chan->tx_credits += credits;
|
||||
|
||||
while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
|
||||
l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
|
||||
chan->tx_credits--;
|
||||
}
|
||||
|
||||
if (chan->tx_credits)
|
||||
chan->ops->resume(chan);
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||
u8 *data)
|
||||
{
|
||||
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
if (cmd_len < sizeof(*rej))
|
||||
return -EPROTO;
|
||||
|
||||
mutex_lock(&conn->chan_lock);
|
||||
|
||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||
if (!chan)
|
||||
goto done;
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
l2cap_chan_del(chan, ECONNREFUSED);
|
||||
l2cap_chan_unlock(chan);
|
||||
|
||||
done:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
|
||||
u8 *data)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!enable_lecoc) {
|
||||
switch (cmd->code) {
|
||||
case L2CAP_LE_CONN_REQ:
|
||||
case L2CAP_LE_CONN_RSP:
|
||||
case L2CAP_LE_CREDITS:
|
||||
case L2CAP_DISCONN_REQ:
|
||||
case L2CAP_DISCONN_RSP:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (cmd->code) {
|
||||
case L2CAP_COMMAND_REJ:
|
||||
return 0;
|
||||
l2cap_le_command_rej(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_PARAM_UPDATE_REQ:
|
||||
return l2cap_conn_param_update_req(conn, cmd, data);
|
||||
err = l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_PARAM_UPDATE_RSP:
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case L2CAP_LE_CONN_RSP:
|
||||
l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_LE_CONN_REQ:
|
||||
err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_LE_CREDITS:
|
||||
err = l2cap_le_credits(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_REQ:
|
||||
err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_RSP:
|
||||
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
||||
|
|
@ -5321,7 +5845,7 @@ static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
|||
goto drop;
|
||||
}
|
||||
|
||||
err = l2cap_le_sig_cmd(conn, cmd, skb->data);
|
||||
err = l2cap_le_sig_cmd(conn, cmd, len, skb->data);
|
||||
if (err) {
|
||||
struct l2cap_cmd_rej_unk rej;
|
||||
|
||||
|
|
@ -6312,6 +6836,121 @@ drop:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_conn *conn = chan->conn;
|
||||
struct l2cap_le_credits pkt;
|
||||
u16 return_credits;
|
||||
|
||||
/* We return more credits to the sender only after the amount of
|
||||
* credits falls below half of the initial amount.
|
||||
*/
|
||||
if (chan->rx_credits >= (le_max_credits + 1) / 2)
|
||||
return;
|
||||
|
||||
return_credits = le_max_credits - chan->rx_credits;
|
||||
|
||||
BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
|
||||
|
||||
chan->rx_credits += return_credits;
|
||||
|
||||
pkt.cid = cpu_to_le16(chan->scid);
|
||||
pkt.credits = cpu_to_le16(return_credits);
|
||||
|
||||
chan->ident = l2cap_get_ident(conn);
|
||||
|
||||
l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
|
||||
}
|
||||
|
||||
static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!chan->rx_credits) {
|
||||
BT_ERR("No credits to receive LE L2CAP data");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
if (chan->imtu < skb->len) {
|
||||
BT_ERR("Too big LE L2CAP PDU");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
chan->rx_credits--;
|
||||
BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
|
||||
|
||||
l2cap_chan_le_send_credits(chan);
|
||||
|
||||
err = 0;
|
||||
|
||||
if (!chan->sdu) {
|
||||
u16 sdu_len;
|
||||
|
||||
sdu_len = get_unaligned_le16(skb->data);
|
||||
skb_pull(skb, L2CAP_SDULEN_SIZE);
|
||||
|
||||
BT_DBG("Start of new SDU. sdu_len %u skb->len %u imtu %u",
|
||||
sdu_len, skb->len, chan->imtu);
|
||||
|
||||
if (sdu_len > chan->imtu) {
|
||||
BT_ERR("Too big LE L2CAP SDU length received");
|
||||
err = -EMSGSIZE;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (skb->len > sdu_len) {
|
||||
BT_ERR("Too much LE L2CAP data received");
|
||||
err = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (skb->len == sdu_len)
|
||||
return chan->ops->recv(chan, skb);
|
||||
|
||||
chan->sdu = skb;
|
||||
chan->sdu_len = sdu_len;
|
||||
chan->sdu_last_frag = skb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_DBG("SDU fragment. chan->sdu->len %u skb->len %u chan->sdu_len %u",
|
||||
chan->sdu->len, skb->len, chan->sdu_len);
|
||||
|
||||
if (chan->sdu->len + skb->len > chan->sdu_len) {
|
||||
BT_ERR("Too much LE L2CAP data received");
|
||||
err = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag);
|
||||
skb = NULL;
|
||||
|
||||
if (chan->sdu->len == chan->sdu_len) {
|
||||
err = chan->ops->recv(chan, chan->sdu);
|
||||
if (!err) {
|
||||
chan->sdu = NULL;
|
||||
chan->sdu_last_frag = NULL;
|
||||
chan->sdu_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
if (err) {
|
||||
kfree_skb(skb);
|
||||
kfree_skb(chan->sdu);
|
||||
chan->sdu = NULL;
|
||||
chan->sdu_last_frag = NULL;
|
||||
chan->sdu_len = 0;
|
||||
}
|
||||
|
||||
/* We can't return an error here since we took care of the skb
|
||||
* freeing internally. An error return would cause the caller to
|
||||
* do a double-free of the skb.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -6341,6 +6980,12 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
|
|||
goto drop;
|
||||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
if (l2cap_le_data_rcv(chan, skb) < 0)
|
||||
goto drop;
|
||||
|
||||
goto done;
|
||||
|
||||
case L2CAP_MODE_BASIC:
|
||||
/* If socket recv buffers overflows we drop data here
|
||||
* which is *bad* because L2CAP has to be reliable.
|
||||
|
|
@ -6380,7 +7025,8 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
|||
if (hcon->type != ACL_LINK)
|
||||
goto drop;
|
||||
|
||||
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
|
||||
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
|
||||
ACL_LINK);
|
||||
if (!chan)
|
||||
goto drop;
|
||||
|
||||
|
|
@ -6612,11 +7258,10 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
}
|
||||
|
||||
if (chan->state == BT_CONNECT) {
|
||||
if (!status) {
|
||||
if (!status)
|
||||
l2cap_start_connection(chan);
|
||||
} else {
|
||||
else
|
||||
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
||||
}
|
||||
} else if (chan->state == BT_CONNECT2) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 res, stat;
|
||||
|
|
@ -6817,6 +7462,11 @@ int __init l2cap_init(void)
|
|||
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
|
||||
NULL, &l2cap_debugfs_fops);
|
||||
|
||||
debugfs_create_u16("l2cap_le_max_credits", 0466, bt_debugfs,
|
||||
&le_max_credits);
|
||||
debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
|
||||
&le_default_mps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
/* Bluetooth L2CAP sockets. */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
|
@ -35,6 +36,8 @@
|
|||
|
||||
#include "smp.h"
|
||||
|
||||
bool enable_lecoc;
|
||||
|
||||
static struct bt_sock_list l2cap_sk_list = {
|
||||
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
|
||||
};
|
||||
|
|
@ -50,6 +53,32 @@ bool l2cap_is_socket(struct socket *sock)
|
|||
}
|
||||
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 < 0x1001 && !capable(CAP_NET_BIND_SERVICE))
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_validate_le_psm(u16 psm)
|
||||
{
|
||||
/* Valid LE_PSM ranges are defined only until 0x00ff */
|
||||
if (psm > 0x00ff)
|
||||
return -EINVAL;
|
||||
|
||||
/* Restrict fixed, SIG assigned PSM values to CAP_NET_BIND_SERVICE */
|
||||
if (psm <= 0x007f && !capable(CAP_NET_BIND_SERVICE))
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
|
@ -73,11 +102,11 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||
return -EINVAL;
|
||||
|
||||
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
|
||||
/* Connection oriented channels are not supported on LE */
|
||||
if (la.l2_psm)
|
||||
if (!enable_lecoc && la.l2_psm)
|
||||
return -EINVAL;
|
||||
/* We only allow ATT user space socket */
|
||||
if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
|
||||
if (la.l2_cid &&
|
||||
la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -91,17 +120,13 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||
if (la.l2_psm) {
|
||||
__u16 psm = __le16_to_cpu(la.l2_psm);
|
||||
|
||||
/* PSM must be odd and lsb of upper byte must be 0 */
|
||||
if ((psm & 0x0101) != 0x0001) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (la.l2_bdaddr_type == BDADDR_BREDR)
|
||||
err = l2cap_validate_bredr_psm(psm);
|
||||
else
|
||||
err = l2cap_validate_le_psm(psm);
|
||||
|
||||
/* Restrict usage of well-known PSMs */
|
||||
if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {
|
||||
err = -EACCES;
|
||||
if (err)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (la.l2_cid)
|
||||
|
|
@ -127,6 +152,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||
bacpy(&chan->src, &la.l2_bdaddr);
|
||||
chan->src_type = la.l2_bdaddr_type;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -189,14 +217,17 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
|
|||
return -EINVAL;
|
||||
|
||||
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
|
||||
/* Connection oriented channels are not supported on LE */
|
||||
if (la.l2_psm)
|
||||
if (!enable_lecoc && la.l2_psm)
|
||||
return -EINVAL;
|
||||
/* We only allow ATT user space socket */
|
||||
if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
|
||||
if (la.l2_cid &&
|
||||
la.l2_cid != __constant_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)
|
||||
|
|
@ -234,6 +265,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
case L2CAP_MODE_LE_FLOWCTL:
|
||||
break;
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
|
|
@ -360,6 +392,16 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
|
|||
|
||||
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;
|
||||
|
|
@ -514,6 +556,41 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case BT_SNDMTU:
|
||||
if (!enable_lecoc) {
|
||||
err = -EPROTONOSUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
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 (!enable_lecoc) {
|
||||
err = -EPROTONOSUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -554,6 +631,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
|||
|
||||
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;
|
||||
|
|
@ -585,6 +667,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
|
|||
|
||||
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;
|
||||
|
|
@ -807,6 +891,47 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||
|
||||
break;
|
||||
|
||||
case BT_SNDMTU:
|
||||
if (!enable_lecoc) {
|
||||
err = -EPROTONOSUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bdaddr_type_is_le(chan->src_type)) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setting is not supported as it's the remote side that
|
||||
* decides this.
|
||||
*/
|
||||
err = -EPERM;
|
||||
break;
|
||||
|
||||
case BT_RCVMTU:
|
||||
if (!enable_lecoc) {
|
||||
err = -EPROTONOSUPPORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bdaddr_type_is_le(chan->src_type)) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED) {
|
||||
err = -EISCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_user(opt, (u32 __user *) optval)) {
|
||||
err = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
chan->imtu = opt;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
break;
|
||||
|
|
@ -859,10 +984,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||
|
||||
if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP,
|
||||
&bt_sk(sk)->flags)) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
pi->chan->state = BT_CONFIG;
|
||||
if (bdaddr_type_is_le(pi->chan->src_type)) {
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
pi->chan->state = BT_CONNECTED;
|
||||
__l2cap_le_connect_rsp_defer(pi->chan);
|
||||
} else {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
pi->chan->state = BT_CONFIG;
|
||||
__l2cap_connect_rsp_defer(pi->chan);
|
||||
}
|
||||
|
||||
__l2cap_connect_rsp_defer(pi->chan);
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1236,6 +1367,14 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
|
|||
return sk->sk_sndtimeo;
|
||||
}
|
||||
|
||||
static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
|
||||
{
|
||||
struct sock *sk = chan->data;
|
||||
|
||||
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
|
||||
static struct l2cap_ops l2cap_chan_ops = {
|
||||
.name = "L2CAP Socket Interface",
|
||||
.new_connection = l2cap_sock_new_connection_cb,
|
||||
|
|
@ -1246,6 +1385,7 @@ static struct l2cap_ops l2cap_chan_ops = {
|
|||
.ready = l2cap_sock_ready_cb,
|
||||
.defer = l2cap_sock_defer_cb,
|
||||
.resume = l2cap_sock_resume_cb,
|
||||
.suspend = l2cap_sock_suspend_cb,
|
||||
.set_shutdown = l2cap_sock_set_shutdown_cb,
|
||||
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
|
||||
.alloc_skb = l2cap_sock_alloc_skb_cb,
|
||||
|
|
@ -1303,6 +1443,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||
chan->tx_win_max = pchan->tx_win_max;
|
||||
chan->sec_level = pchan->sec_level;
|
||||
chan->flags = pchan->flags;
|
||||
chan->tx_credits = pchan->tx_credits;
|
||||
chan->rx_credits = pchan->rx_credits;
|
||||
|
||||
security_sk_clone(parent, sk);
|
||||
} else {
|
||||
|
|
@ -1469,3 +1611,6 @@ void l2cap_cleanup_sockets(void)
|
|||
bt_sock_unregister(BTPROTO_L2CAP);
|
||||
proto_unregister(&l2cap_proto);
|
||||
}
|
||||
|
||||
module_param(enable_lecoc, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC");
|
||||
|
|
|
|||
|
|
@ -1264,7 +1264,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
if (cp->val == 0x02) {
|
||||
/* Limited discoverable mode */
|
||||
hci_cp.num_iac = 2;
|
||||
hci_cp.num_iac = min_t(u8, hdev->num_iac, 2);
|
||||
hci_cp.iac_lap[0] = 0x00; /* LIAC */
|
||||
hci_cp.iac_lap[1] = 0x8b;
|
||||
hci_cp.iac_lap[2] = 0x9e;
|
||||
|
|
@ -4595,6 +4595,9 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
struct mgmt_ev_device_disconnected ev;
|
||||
struct sock *sk = NULL;
|
||||
|
||||
if (link_type != ACL_LINK && link_type != LE_LINK)
|
||||
return;
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
|
||||
|
||||
bacpy(&ev.addr.bdaddr, bdaddr);
|
||||
|
|
@ -4613,6 +4616,8 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 link_type, u8 addr_type, u8 status)
|
||||
{
|
||||
u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
|
||||
struct mgmt_cp_disconnect *cp;
|
||||
struct mgmt_rp_disconnect rp;
|
||||
struct pending_cmd *cmd;
|
||||
|
||||
|
|
@ -4623,8 +4628,16 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
if (!cmd)
|
||||
return;
|
||||
|
||||
cp = cmd->param;
|
||||
|
||||
if (bacmp(bdaddr, &cp->addr.bdaddr))
|
||||
return;
|
||||
|
||||
if (cp->addr.type != bdaddr_type)
|
||||
return;
|
||||
|
||||
bacpy(&rp.addr.bdaddr, bdaddr);
|
||||
rp.addr.type = link_to_bdaddr(link_type, addr_type);
|
||||
rp.addr.type = bdaddr_type;
|
||||
|
||||
cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
|||
{
|
||||
struct blkcipher_desc desc;
|
||||
struct scatterlist sg;
|
||||
int err, iv_len;
|
||||
unsigned char iv[128];
|
||||
int err;
|
||||
|
||||
if (tfm == NULL) {
|
||||
BT_ERR("tfm %p", tfm);
|
||||
|
|
@ -72,12 +71,6 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
|||
|
||||
sg_init_one(&sg, r, 16);
|
||||
|
||||
iv_len = crypto_blkcipher_ivsize(tfm);
|
||||
if (iv_len) {
|
||||
memset(&iv, 0xff, iv_len);
|
||||
crypto_blkcipher_set_iv(tfm, iv, iv_len);
|
||||
}
|
||||
|
||||
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error %d", err);
|
||||
|
|
@ -143,13 +136,6 @@ static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
|
|||
return err;
|
||||
}
|
||||
|
||||
static int smp_rand(u8 *buf)
|
||||
{
|
||||
get_random_bytes(buf, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
|
||||
u16 dlen, void *data)
|
||||
{
|
||||
|
|
@ -257,11 +243,11 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
|
||||
static void smp_failure(struct l2cap_conn *conn, u8 reason)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
|
||||
if (send)
|
||||
if (reason)
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
|
||||
&reason);
|
||||
|
||||
|
|
@ -406,7 +392,7 @@ static void confirm_work(struct work_struct *work)
|
|||
return;
|
||||
|
||||
error:
|
||||
smp_failure(conn, reason, 1);
|
||||
smp_failure(conn, reason);
|
||||
}
|
||||
|
||||
static void random_work(struct work_struct *work)
|
||||
|
|
@ -490,7 +476,7 @@ static void random_work(struct work_struct *work)
|
|||
return;
|
||||
|
||||
error:
|
||||
smp_failure(conn, reason, 1);
|
||||
smp_failure(conn, reason);
|
||||
}
|
||||
|
||||
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
||||
|
|
@ -555,10 +541,10 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
|
|||
break;
|
||||
case MGMT_OP_USER_PASSKEY_NEG_REPLY:
|
||||
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
|
||||
return 0;
|
||||
default:
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
|
@ -606,9 +592,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
ret = smp_rand(smp->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
get_random_bytes(smp->prnd, sizeof(smp->prnd));
|
||||
|
||||
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
|
||||
|
|
@ -644,9 +628,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
ret = smp_rand(smp->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
get_random_bytes(smp->prnd, sizeof(smp->prnd));
|
||||
|
||||
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
|
||||
|
|
@ -768,6 +750,17 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
|
||||
{
|
||||
if (sec_level == BT_SECURITY_LOW)
|
||||
return true;
|
||||
|
||||
if (hcon->sec_level >= sec_level)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
|
|
@ -779,10 +772,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
|
|||
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
|
||||
return 1;
|
||||
|
||||
if (sec_level == BT_SECURITY_LOW)
|
||||
return 1;
|
||||
|
||||
if (hcon->sec_level >= sec_level)
|
||||
if (smp_sufficient_security(hcon, sec_level))
|
||||
return 1;
|
||||
|
||||
if (hcon->link_mode & HCI_LM_MASTER)
|
||||
|
|
@ -895,7 +885,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_FAIL:
|
||||
smp_failure(conn, skb->data[0], 0);
|
||||
smp_failure(conn, 0);
|
||||
reason = 0;
|
||||
err = -EPERM;
|
||||
break;
|
||||
|
|
@ -941,7 +931,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
|
||||
done:
|
||||
if (reason)
|
||||
smp_failure(conn, reason, 1);
|
||||
smp_failure(conn, reason);
|
||||
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ struct smp_chan {
|
|||
};
|
||||
|
||||
/* SMP Commands */
|
||||
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
|
||||
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
||||
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
|
||||
|
|
|
|||
Loading…
Reference in a new issue