2015-04-28 10:39:50 +03:00
|
|
|
/* conn.c - Bluetooth connection handling */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2015 Intel Corporation
|
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2015-04-28 10:39:50 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-04-28 10:39:50 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2015-04-28 10:39:50 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <nanokernel.h>
|
2015-06-03 10:35:58 -04:00
|
|
|
#include <arch/cpu.h>
|
2015-04-28 10:39:50 +03:00
|
|
|
#include <toolchain.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2015-05-25 09:13:33 +03:00
|
|
|
#include <stdbool.h>
|
2015-09-15 10:08:04 +02:00
|
|
|
#include <atomic.h>
|
2015-04-28 10:39:50 +03:00
|
|
|
#include <misc/byteorder.h>
|
2015-05-08 09:00:20 +02:00
|
|
|
#include <misc/util.h>
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-06-16 17:25:37 +03:00
|
|
|
#include <bluetooth/log.h>
|
2015-04-28 10:39:50 +03:00
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
2015-09-15 10:08:04 +02:00
|
|
|
#include <bluetooth/driver.h>
|
2015-04-28 10:39:50 +03:00
|
|
|
|
|
|
|
#include "hci_core.h"
|
2015-06-15 11:05:35 +03:00
|
|
|
#include "conn_internal.h"
|
2015-04-28 10:39:50 +03:00
|
|
|
#include "l2cap.h"
|
2015-06-25 11:08:57 +02:00
|
|
|
#include "keys.h"
|
|
|
|
#include "smp.h"
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-05-13 14:06:34 +03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_CONN)
|
|
|
|
#undef BT_DBG
|
|
|
|
#define BT_DBG(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2015-07-22 13:03:51 +03:00
|
|
|
/* How long until we cancel HCI_LE_Create_Connection */
|
|
|
|
#define CONN_TIMEOUT (3 * sys_clock_ticks_per_sec)
|
|
|
|
|
2015-05-21 13:21:08 +03:00
|
|
|
static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN];
|
2015-08-23 20:47:29 +02:00
|
|
|
static struct bt_conn_cb *callback_list;
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-06-19 23:03:18 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_DEBUG_CONN)
|
|
|
|
static const char *state2str(bt_conn_state_t state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case BT_CONN_DISCONNECTED:
|
|
|
|
return "disconnected";
|
2015-07-02 11:16:55 +02:00
|
|
|
case BT_CONN_CONNECT_SCAN:
|
|
|
|
return "connect-scan";
|
2015-06-19 23:03:18 +02:00
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
return "connect";
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
return "connected";
|
|
|
|
case BT_CONN_DISCONNECT:
|
|
|
|
return "disconnect";
|
|
|
|
default:
|
|
|
|
return "(unknown)";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-10-05 15:46:52 +02:00
|
|
|
static void notify_connected(struct bt_conn *conn)
|
2015-08-23 20:47:29 +02:00
|
|
|
{
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->connected) {
|
|
|
|
cb->connected(conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-05 15:46:52 +02:00
|
|
|
static void notify_disconnected(struct bt_conn *conn)
|
2015-08-23 20:47:29 +02:00
|
|
|
{
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->disconnected) {
|
|
|
|
cb->disconnected(conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-09 17:27:16 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
2015-09-01 18:06:35 +02:00
|
|
|
void bt_conn_identity_resolved(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
const bt_addr_le_t *rpa;
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
if (conn->role == BT_HCI_ROLE_MASTER) {
|
|
|
|
rpa = &conn->resp_addr;
|
|
|
|
} else {
|
|
|
|
rpa = &conn->init_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->identity_resolved) {
|
|
|
|
cb->identity_resolved(conn, rpa, &conn->dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-01 18:16:54 +02:00
|
|
|
void bt_conn_security_changed(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct bt_conn_cb *cb;
|
|
|
|
|
|
|
|
for (cb = callback_list; cb; cb = cb->_next) {
|
|
|
|
if (cb->security_changed) {
|
|
|
|
cb->security_changed(conn, conn->sec_level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-02 12:39:11 +02:00
|
|
|
|
|
|
|
int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand,
|
2015-10-07 15:41:19 +02:00
|
|
|
uint16_t ediv, const uint8_t *ltk, size_t len)
|
2015-10-02 12:39:11 +02:00
|
|
|
{
|
|
|
|
struct bt_hci_cp_le_start_encryption *cp;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp = bt_buf_add(buf, sizeof(*cp));
|
|
|
|
cp->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
cp->rand = rand;
|
|
|
|
cp->ediv = ediv;
|
2015-10-07 15:41:19 +02:00
|
|
|
|
|
|
|
memcpy(cp->ltk, ltk, len);
|
|
|
|
if (len < sizeof(cp->ltk)) {
|
|
|
|
memset(cp->ltk + len, 0, sizeof(cp->ltk) - len);
|
|
|
|
}
|
2015-10-02 12:39:11 +02:00
|
|
|
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_conn_security(struct bt_conn *conn, bt_security_t sec)
|
|
|
|
{
|
2015-10-07 17:09:24 +02:00
|
|
|
int err = 0;
|
|
|
|
|
2015-10-02 12:39:11 +02:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* nothing to do */
|
|
|
|
if (conn->sec_level >= sec || conn->required_sec_level >= sec) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for now we only support legacy pairing */
|
|
|
|
if (sec > BT_SECURITY_HIGH) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->required_sec_level = sec;
|
|
|
|
|
|
|
|
#if defined(CONFIG_BLUETOOTH_CENTRAL)
|
|
|
|
if (conn->role == BT_HCI_ROLE_MASTER) {
|
|
|
|
struct bt_keys *keys;
|
|
|
|
|
|
|
|
keys = bt_keys_find(BT_KEYS_LTK, &conn->dst);
|
|
|
|
if (keys) {
|
|
|
|
if (sec > BT_SECURITY_MEDIUM &&
|
|
|
|
keys->type != BT_KEYS_AUTHENTICATED) {
|
2015-10-07 17:09:24 +02:00
|
|
|
err = bt_smp_send_pairing_req(conn);
|
|
|
|
goto done;
|
2015-10-02 12:39:11 +02:00
|
|
|
}
|
|
|
|
|
2015-10-07 17:09:24 +02:00
|
|
|
err = bt_conn_le_start_encryption(conn, keys->ltk.rand,
|
|
|
|
keys->ltk.ediv,
|
2015-10-07 15:41:19 +02:00
|
|
|
keys->ltk.val,
|
Bluetooth: SMP: Add support for encryption key size reduction
This allows to pair with devices that use reduced encryption key size.
Encryption key size is stored with keys for future use. LTKs are kept
in full form (16 bytes) and are reduced only when used.
As master:
< ACL Data TX: Handle 64 flags 0x00 dlen 11
SMP: Pairing Request (0x01) len 6
IO capability: NoInputNoOutput (0x03)
OOB data: Authentication data not present (0x00)
Authentication requirement: Bonding, No MITM, Legacy,
No Keypresses (0x01)
Max encryption key size: 16
Initiator key distribution: EncKey Sign (0x05)
Responder key distribution: EncKey IdKey Sign (0x07)
> ACL Data RX: Handle 64 flags 0x02 dlen 11
SMP: Pairing Response (0x02) len 6
IO capability: KeyboardDisplay (0x04)
OOB data: Authentication data not present (0x00)
Authentication requirement: No bonding, No MITM, Legacy,
No Keypresses (0x00)
Max encryption key size: 7
Initiator key distribution: <none> (0x00)
Responder key distribution: <none> (0x00)
...
< HCI Command: LE Start Encryption (0x08|0x0019) plen 28
Handle: 64
Random number: 0x0000000000000000
Encrypted diversifier: 0x0000
Long term key: df3cff52a981d6000000000000000000
As slave:
> ACL Data RX: Handle 64 flags 0x02 dlen 11
SMP: Pairing Request (0x01) len 6
IO capability: KeyboardDisplay (0x04)
OOB data: Authentication data not present (0x00)
Authentication requirement: No bonding, No MITM, Legacy,
No Keypresses (0x00)
Max encryption key size: 7
Initiator key distribution: <none> (0x00)
Responder key distribution: <none> (0x00)
< ACL Data TX: Handle 64 flags 0x00 dlen 11
SMP: Pairing Response (0x02) len 6
IO capability: NoInputNoOutput (0x03)
OOB data: Authentication data not present (0x00)
Authentication requirement: No bonding, No MITM, Legacy,
No Keypresses (0x00)
Max encryption key size: 16
Initiator key distribution: <none> (0x00)
Responder key distribution: <none> (0x00)
...
> HCI Event: LE Meta Event (0x3e) plen 13
LE Long Term Key Request (0x05)
Handle: 64
Random number: 0x0000000000000000
Encrypted diversifier: 0x0000
< HCI Command: LE Long Term Key Request Reply (0x08|0x001a) plen 18
Handle: 64
Long term key: 701b431a9e17bb000000000000000000
Change-Id: Ibc70aa01c040aff0d39410d273d6880d35aa5ae0
Signed-off-by: Szymon Janc <ext.szymon.janc@tieto.com>
2015-10-07 12:33:45 +02:00
|
|
|
keys->enc_size);
|
2015-10-07 17:09:24 +02:00
|
|
|
goto done;
|
2015-10-02 12:39:11 +02:00
|
|
|
}
|
|
|
|
|
2015-10-07 17:09:24 +02:00
|
|
|
err = bt_smp_send_pairing_req(conn);
|
|
|
|
goto done;
|
2015-10-02 12:39:11 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_BLUETOOTH_CENTRAL */
|
|
|
|
|
|
|
|
#if defined(CONFIG_BLUETOOTH_PERIPHERAL)
|
2015-10-07 17:09:24 +02:00
|
|
|
err = bt_smp_send_security_req(conn);
|
2015-10-02 12:39:11 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_PERIPHERAL */
|
2015-10-07 17:09:24 +02:00
|
|
|
|
|
|
|
done:
|
|
|
|
/* reset required security level in case of error */
|
|
|
|
if (err) {
|
|
|
|
conn->required_sec_level = conn->sec_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
2015-10-02 12:39:11 +02:00
|
|
|
}
|
2015-09-09 17:27:16 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
2015-09-01 18:16:54 +02:00
|
|
|
|
2015-08-23 20:47:29 +02:00
|
|
|
void bt_conn_cb_register(struct bt_conn_cb *cb)
|
|
|
|
{
|
|
|
|
cb->_next = callback_list;
|
|
|
|
callback_list = cb;
|
|
|
|
}
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
static void bt_conn_reset_rx_state(struct bt_conn *conn)
|
|
|
|
{
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!conn->rx_len) {
|
2015-04-28 10:39:50 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
|
|
|
bt_buf_put(conn->rx);
|
|
|
|
conn->rx = NULL;
|
|
|
|
conn->rx_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_conn_recv(struct bt_conn *conn, struct bt_buf *buf, uint8_t flags)
|
|
|
|
{
|
|
|
|
struct bt_l2cap_hdr *hdr;
|
|
|
|
uint16_t len;
|
|
|
|
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_DBG("handle %u len %u flags %02x\n", conn->handle, buf->len, flags);
|
2015-04-28 10:39:50 +03:00
|
|
|
|
|
|
|
/* Check packet boundary flags */
|
|
|
|
switch (flags) {
|
|
|
|
case 0x02:
|
|
|
|
/* First packet */
|
|
|
|
hdr = (void *)buf->data;
|
|
|
|
len = sys_le16_to_cpu(hdr->len);
|
|
|
|
|
|
|
|
BT_DBG("First, len %u final %u\n", buf->len, len);
|
|
|
|
|
|
|
|
if (conn->rx_len) {
|
|
|
|
BT_ERR("Unexpected first L2CAP frame\n");
|
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
conn->rx_len = (sizeof(*hdr) + len) - buf->len;
|
|
|
|
BT_DBG("rx_len %u\n", conn->rx_len);
|
|
|
|
if (conn->rx_len) {
|
|
|
|
conn->rx = buf;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
/* Continuation */
|
|
|
|
if (!conn->rx_len) {
|
|
|
|
BT_ERR("Unexpected L2CAP continuation\n");
|
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf->len > conn->rx_len) {
|
|
|
|
BT_ERR("L2CAP data overflow\n");
|
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Cont, len %u rx_len %u\n", buf->len, conn->rx_len);
|
|
|
|
|
|
|
|
if (buf->len > bt_buf_tailroom(conn->rx)) {
|
|
|
|
BT_ERR("Not enough buffer space for L2CAP data\n");
|
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(bt_buf_add(conn->rx, buf->len), buf->data, buf->len);
|
|
|
|
conn->rx_len -= buf->len;
|
|
|
|
bt_buf_put(buf);
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (conn->rx_len) {
|
2015-04-28 10:39:50 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
|
|
|
buf = conn->rx;
|
|
|
|
conn->rx = NULL;
|
|
|
|
conn->rx_len = 0;
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
2015-05-21 15:58:32 +03:00
|
|
|
BT_ERR("Unexpected ACL flags (0x%02x)\n", flags);
|
2015-04-28 10:39:50 +03:00
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (void *)buf->data;
|
|
|
|
len = sys_le16_to_cpu(hdr->len);
|
|
|
|
|
|
|
|
if (sizeof(*hdr) + len != buf->len) {
|
|
|
|
BT_ERR("ACL len mismatch (%u != %u)\n", len, buf->len);
|
|
|
|
bt_buf_put(buf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Successfully parsed %u byte L2CAP packet\n", buf->len);
|
|
|
|
|
2015-05-23 20:38:23 +03:00
|
|
|
bt_l2cap_recv(conn, buf);
|
2015-04-28 10:39:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void bt_conn_send(struct bt_conn *conn, struct bt_buf *buf)
|
|
|
|
{
|
2015-05-05 12:26:59 +03:00
|
|
|
uint16_t len, remaining = buf->len;
|
2015-05-05 11:06:54 +03:00
|
|
|
struct bt_hci_acl_hdr *hdr;
|
2015-05-05 12:26:59 +03:00
|
|
|
struct nano_fifo frags;
|
|
|
|
uint8_t *ptr;
|
2015-05-05 11:06:54 +03:00
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
BT_DBG("conn handle %u buf len %u\n", conn->handle, buf->len);
|
|
|
|
|
2015-07-15 13:53:17 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
BT_ERR("not connected!\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-05 12:26:59 +03:00
|
|
|
nano_fifo_init(&frags);
|
|
|
|
|
2015-07-15 13:53:17 +03:00
|
|
|
len = min(remaining, bt_dev.le_mtu);
|
2015-05-05 12:26:59 +03:00
|
|
|
|
2015-05-18 10:24:58 +03:00
|
|
|
hdr = bt_buf_push(buf, sizeof(*hdr));
|
2015-05-05 11:06:54 +03:00
|
|
|
hdr->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
hdr->len = sys_cpu_to_le16(len);
|
|
|
|
|
2015-05-05 12:26:59 +03:00
|
|
|
buf->len -= remaining - len;
|
|
|
|
ptr = bt_buf_tail(buf);
|
|
|
|
|
|
|
|
nano_fifo_put(&frags, buf);
|
|
|
|
remaining -= len;
|
|
|
|
|
|
|
|
while (remaining) {
|
2015-05-20 14:58:18 +03:00
|
|
|
buf = bt_l2cap_create_pdu(conn);
|
2015-05-05 12:26:59 +03:00
|
|
|
|
2015-07-15 13:53:17 +03:00
|
|
|
len = min(remaining, bt_dev.le_mtu);
|
2015-05-05 12:26:59 +03:00
|
|
|
|
|
|
|
/* Copy from original buffer */
|
|
|
|
memcpy(bt_buf_add(buf, len), ptr, len);
|
|
|
|
ptr += len;
|
|
|
|
|
2015-05-18 10:24:58 +03:00
|
|
|
hdr = bt_buf_push(buf, sizeof(*hdr));
|
2015-05-05 12:26:59 +03:00
|
|
|
hdr->handle = sys_cpu_to_le16(conn->handle | (1 << 12));
|
|
|
|
hdr->len = sys_cpu_to_le16(len);
|
|
|
|
|
|
|
|
nano_fifo_put(&frags, buf);
|
|
|
|
remaining -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((buf = nano_fifo_get(&frags))) {
|
|
|
|
nano_fifo_put(&conn->tx_queue, buf);
|
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void conn_tx_fiber(int arg1, int arg2)
|
|
|
|
{
|
|
|
|
struct bt_conn *conn = (struct bt_conn *)arg1;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
BT_DBG("Started for handle %u\n", conn->handle);
|
|
|
|
|
|
|
|
while (conn->state == BT_CONN_CONNECTED) {
|
2015-09-15 17:57:00 +03:00
|
|
|
int err;
|
|
|
|
|
2015-05-12 14:57:38 +03:00
|
|
|
/* Wait until the controller can accept ACL packets */
|
2015-05-20 13:44:05 +03:00
|
|
|
BT_DBG("calling sem_take_wait\n");
|
2015-07-15 13:53:17 +03:00
|
|
|
nano_fiber_sem_take_wait(&bt_dev.le_pkts_sem);
|
2015-05-12 14:57:38 +03:00
|
|
|
|
|
|
|
/* check for disconnection */
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
2015-07-15 13:53:17 +03:00
|
|
|
nano_fiber_sem_give(&bt_dev.le_pkts_sem);
|
2015-05-12 14:57:38 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
/* Get next ACL packet for connection */
|
|
|
|
buf = nano_fifo_get_wait(&conn->tx_queue);
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
2015-07-15 13:53:17 +03:00
|
|
|
nano_fiber_sem_give(&bt_dev.le_pkts_sem);
|
2015-04-28 10:39:50 +03:00
|
|
|
bt_buf_put(buf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-05-20 13:18:08 +03:00
|
|
|
BT_DBG("passing buf %p len %u to driver\n", buf, buf->len);
|
2015-09-15 17:57:00 +03:00
|
|
|
err = bt_dev.drv->send(buf);
|
|
|
|
if (err) {
|
|
|
|
BT_ERR("Unable to send to driver (err %d)\n", err);
|
|
|
|
} else {
|
|
|
|
conn->pending_pkts++;
|
|
|
|
}
|
2015-09-15 17:19:45 +03:00
|
|
|
|
2015-09-15 17:57:00 +03:00
|
|
|
bt_buf_put(buf);
|
2015-04-28 10:39:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("handle %u disconnected - cleaning up\n", conn->handle);
|
|
|
|
|
|
|
|
/* Give back any allocated buffers */
|
2015-05-05 10:50:14 +03:00
|
|
|
while ((buf = nano_fifo_get(&conn->tx_queue))) {
|
2015-04-28 10:39:50 +03:00
|
|
|
bt_buf_put(buf);
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-09-15 17:19:45 +03:00
|
|
|
/* Return any unacknowledged packets */
|
|
|
|
if (conn->pending_pkts) {
|
|
|
|
while (conn->pending_pkts--) {
|
|
|
|
nano_fiber_sem_give(&bt_dev.le_pkts_sem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-23 20:38:23 +03:00
|
|
|
bt_conn_reset_rx_state(conn);
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
BT_DBG("handle %u exiting\n", conn->handle);
|
|
|
|
bt_conn_put(conn);
|
|
|
|
}
|
|
|
|
|
2015-09-02 10:47:35 +02:00
|
|
|
struct bt_conn *bt_conn_add(const bt_addr_le_t *peer)
|
2015-04-28 10:39:50 +03:00
|
|
|
{
|
|
|
|
struct bt_conn *conn = NULL;
|
|
|
|
int i;
|
|
|
|
|
2015-05-21 13:21:08 +03:00
|
|
|
for (i = 0; i < ARRAY_SIZE(conns); i++) {
|
2015-08-19 18:44:32 +02:00
|
|
|
if (!atomic_get(&conns[i].ref)) {
|
2015-04-28 10:39:50 +03:00
|
|
|
conn = &conns[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-05 10:50:14 +03:00
|
|
|
if (!conn) {
|
2015-04-28 10:39:50 +03:00
|
|
|
return NULL;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
|
|
|
memset(conn, 0, sizeof(*conn));
|
|
|
|
|
2015-06-23 14:38:24 +02:00
|
|
|
atomic_set(&conn->ref, 1);
|
2015-06-23 11:56:50 +02:00
|
|
|
bt_addr_le_copy(&conn->dst, peer);
|
2015-09-09 17:27:16 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
2015-08-05 16:18:05 +02:00
|
|
|
conn->sec_level = BT_SECURITY_LOW;
|
|
|
|
conn->required_sec_level = BT_SECURITY_LOW;
|
2015-09-09 17:27:16 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
2015-04-28 10:43:14 +03:00
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2015-07-22 13:03:51 +03:00
|
|
|
static void timeout_fiber(int arg1, int arg2)
|
|
|
|
{
|
|
|
|
struct bt_conn *conn = (struct bt_conn *)arg1;
|
|
|
|
ARG_UNUSED(arg2);
|
|
|
|
|
|
|
|
conn->timeout = NULL;
|
|
|
|
|
|
|
|
bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
|
|
|
bt_conn_put(conn);
|
|
|
|
}
|
|
|
|
|
2015-06-19 18:56:13 +02:00
|
|
|
void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state)
|
2015-04-28 10:39:50 +03:00
|
|
|
{
|
2015-06-23 14:38:24 +02:00
|
|
|
bt_conn_state_t old_state;
|
|
|
|
|
2015-06-19 23:03:18 +02:00
|
|
|
BT_DBG("%s -> %s\n", state2str(conn->state), state2str(state));
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-06-19 18:56:13 +02:00
|
|
|
if (conn->state == state) {
|
|
|
|
BT_WARN("no transition\n");
|
2015-04-28 10:39:50 +03:00
|
|
|
return;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-06-23 14:38:24 +02:00
|
|
|
old_state = conn->state;
|
2015-06-19 18:56:13 +02:00
|
|
|
conn->state = state;
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-07-22 13:03:51 +03:00
|
|
|
/* Actions needed for exiting the old state */
|
|
|
|
switch (old_state) {
|
|
|
|
case BT_CONN_DISCONNECTED:
|
|
|
|
/* Take a reference for the first state transition after
|
|
|
|
* bt_conn_add() and keep it until reaching DISCONNECTED
|
|
|
|
* again.
|
|
|
|
*/
|
2015-07-08 10:55:51 +03:00
|
|
|
bt_conn_get(conn);
|
2015-07-22 13:03:51 +03:00
|
|
|
break;
|
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
if (conn->timeout) {
|
|
|
|
fiber_fiber_delayed_start_cancel(conn->timeout);
|
|
|
|
conn->timeout = NULL;
|
|
|
|
|
|
|
|
/* Drop the reference taken by timeout fiber */
|
|
|
|
bt_conn_put(conn);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2015-07-08 10:55:51 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 13:03:51 +03:00
|
|
|
/* Actions needed for entering the new state */
|
2015-06-19 18:56:13 +02:00
|
|
|
switch (conn->state){
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
nano_fifo_init(&conn->tx_queue);
|
2015-07-17 13:11:53 +02:00
|
|
|
fiber_start(conn->stack, sizeof(conn->stack), conn_tx_fiber,
|
|
|
|
(int)bt_conn_get(conn), 0, 7, 0);
|
2015-08-23 20:47:29 +02:00
|
|
|
|
|
|
|
bt_l2cap_connected(conn);
|
2015-10-05 15:46:52 +02:00
|
|
|
notify_connected(conn);
|
2015-06-19 18:56:13 +02:00
|
|
|
break;
|
|
|
|
case BT_CONN_DISCONNECTED:
|
2015-07-03 12:05:23 +02:00
|
|
|
/* Send dummy buffer to wake up and stop the tx fiber
|
|
|
|
* for states where it was running
|
|
|
|
*/
|
|
|
|
if (old_state == BT_CONN_CONNECTED ||
|
|
|
|
old_state == BT_CONN_DISCONNECT) {
|
2015-08-23 20:47:29 +02:00
|
|
|
bt_l2cap_disconnected(conn);
|
2015-10-05 15:46:52 +02:00
|
|
|
notify_disconnected(conn);
|
2015-08-23 20:47:29 +02:00
|
|
|
|
2015-07-03 12:05:23 +02:00
|
|
|
nano_fifo_put(&conn->tx_queue, bt_buf_get(BT_DUMMY, 0));
|
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
|
2015-07-08 10:55:51 +03:00
|
|
|
/* Release the reference we took for the very first
|
|
|
|
* state transition.
|
|
|
|
*/
|
2015-06-19 18:56:13 +02:00
|
|
|
bt_conn_put(conn);
|
2015-06-19 10:52:58 +02:00
|
|
|
|
2015-06-19 18:56:13 +02:00
|
|
|
break;
|
2015-07-02 11:16:55 +02:00
|
|
|
case BT_CONN_CONNECT_SCAN:
|
2015-07-22 13:03:51 +03:00
|
|
|
break;
|
2015-06-19 18:56:13 +02:00
|
|
|
case BT_CONN_CONNECT:
|
2015-07-22 13:03:51 +03:00
|
|
|
/* Add LE Create Connection timeout */
|
|
|
|
conn->timeout = fiber_delayed_start(conn->stack,
|
|
|
|
sizeof(conn->stack),
|
|
|
|
timeout_fiber,
|
|
|
|
(int)bt_conn_get(conn),
|
|
|
|
0, 7, 0, CONN_TIMEOUT);
|
|
|
|
break;
|
2015-06-19 18:56:13 +02:00
|
|
|
case BT_CONN_DISCONNECT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BT_WARN("no valid (%u) state was set\n", state);
|
2015-06-19 10:52:58 +02:00
|
|
|
|
2015-06-19 18:56:13 +02:00
|
|
|
break;
|
|
|
|
}
|
2015-06-19 10:52:58 +02:00
|
|
|
}
|
|
|
|
|
2015-05-27 21:08:39 +03:00
|
|
|
struct bt_conn *bt_conn_lookup_handle(uint16_t handle)
|
2015-04-28 10:39:50 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2015-05-21 13:21:08 +03:00
|
|
|
for (i = 0; i < ARRAY_SIZE(conns); i++) {
|
2015-08-19 18:44:32 +02:00
|
|
|
if (!atomic_get(&conns[i].ref)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-08 11:00:13 +03:00
|
|
|
/* We only care about connections with a valid handle */
|
|
|
|
if (conns[i].state != BT_CONN_CONNECTED &&
|
|
|
|
conns[i].state != BT_CONN_DISCONNECT) {
|
2015-04-28 10:39:50 +03:00
|
|
|
continue;
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (conns[i].handle == handle) {
|
2015-06-17 12:42:54 +03:00
|
|
|
return bt_conn_get(&conns[i]);
|
2015-05-05 10:50:14 +03:00
|
|
|
}
|
2015-04-28 10:39:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-27 21:11:34 +03:00
|
|
|
struct bt_conn *bt_conn_lookup_addr_le(const bt_addr_le_t *peer)
|
2015-05-26 14:25:03 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(conns); i++) {
|
2015-08-19 18:44:32 +02:00
|
|
|
if (!atomic_get(&conns[i].ref)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:25:03 +03:00
|
|
|
if (!bt_addr_le_cmp(peer, &conns[i].dst)) {
|
2015-06-17 12:42:54 +03:00
|
|
|
return bt_conn_get(&conns[i]);
|
2015-05-26 14:25:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-25 11:31:17 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct bt_conn *bt_conn_lookup_state(const bt_addr_le_t *peer,
|
|
|
|
const bt_conn_state_t state)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(conns); i++) {
|
2015-08-19 18:44:32 +02:00
|
|
|
if (!atomic_get(&conns[i].ref)) {
|
2015-07-01 18:16:35 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-06-25 11:31:17 +02:00
|
|
|
if (bt_addr_le_cmp(peer, BT_ADDR_LE_ANY) &&
|
|
|
|
bt_addr_le_cmp(peer, &conns[i].dst)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conns[i].state == state) {
|
|
|
|
return bt_conn_get(&conns[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:25:03 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
struct bt_conn *bt_conn_get(struct bt_conn *conn)
|
|
|
|
{
|
2015-06-23 14:38:24 +02:00
|
|
|
atomic_inc(&conn->ref);
|
|
|
|
|
2015-06-26 14:52:55 +02:00
|
|
|
BT_DBG("handle %u ref %u\n", conn->handle, atomic_get(&conn->ref));
|
|
|
|
|
2015-04-28 10:39:50 +03:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bt_conn_put(struct bt_conn *conn)
|
|
|
|
{
|
2015-08-19 18:44:32 +02:00
|
|
|
atomic_dec(&conn->ref);
|
2015-06-26 14:52:55 +02:00
|
|
|
|
|
|
|
BT_DBG("handle %u ref %u\n", conn->handle, atomic_get(&conn->ref));
|
2015-04-28 10:39:50 +03:00
|
|
|
}
|
2015-06-18 09:36:59 +02:00
|
|
|
|
|
|
|
const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
return &conn->dst;
|
|
|
|
}
|
2015-06-25 11:08:57 +02:00
|
|
|
|
2015-07-13 12:43:02 +02:00
|
|
|
void bt_conn_set_auto_conn(struct bt_conn *conn, bool auto_conn)
|
|
|
|
{
|
|
|
|
if (auto_conn) {
|
|
|
|
atomic_set_bit(conn->flags, BT_CONN_AUTO_CONNECT);
|
|
|
|
} else {
|
|
|
|
atomic_clear_bit(conn->flags, BT_CONN_AUTO_CONNECT);
|
|
|
|
}
|
|
|
|
}
|
2015-07-15 12:31:08 +03:00
|
|
|
|
|
|
|
static int bt_hci_disconnect(struct bt_conn *conn, uint8_t reason)
|
|
|
|
{
|
|
|
|
struct bt_buf *buf;
|
|
|
|
struct bt_hci_cp_disconnect *disconn;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
disconn = bt_buf_add(buf, sizeof(*disconn));
|
|
|
|
disconn->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
disconn->reason = reason;
|
|
|
|
|
|
|
|
err = bt_hci_cmd_send(BT_HCI_OP_DISCONNECT, buf);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bt_hci_connect_le_cancel(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2015-07-17 13:11:53 +02:00
|
|
|
if (conn->timeout) {
|
|
|
|
fiber_fiber_delayed_start_cancel(conn->timeout);
|
|
|
|
conn->timeout = NULL;
|
|
|
|
|
|
|
|
/* Drop the reference took by timeout fiber */
|
|
|
|
bt_conn_put(conn);
|
|
|
|
}
|
|
|
|
|
2015-07-15 12:31:08 +03:00
|
|
|
err = bt_hci_cmd_send(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason)
|
|
|
|
{
|
|
|
|
/* Disconnection is initiated by us, so auto connection shall
|
|
|
|
* be disabled. Otherwise the passive scan would be enabled
|
|
|
|
* and we could send LE Create Connection as soon as the remote
|
|
|
|
* starts advertising.
|
|
|
|
*/
|
|
|
|
bt_conn_set_auto_conn(conn, false);
|
|
|
|
|
|
|
|
switch (conn->state) {
|
|
|
|
case BT_CONN_CONNECT_SCAN:
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
bt_le_scan_update();
|
|
|
|
return 0;
|
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
return bt_hci_connect_le_cancel(conn);
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
return bt_hci_disconnect(conn, reason);
|
|
|
|
case BT_CONN_DISCONNECT:
|
|
|
|
return 0;
|
|
|
|
case BT_CONN_DISCONNECTED:
|
|
|
|
default:
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
}
|
2015-07-15 13:55:32 +03:00
|
|
|
|
|
|
|
struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer)
|
|
|
|
{
|
|
|
|
struct bt_conn *conn;
|
|
|
|
|
|
|
|
conn = bt_conn_lookup_addr_le(peer);
|
|
|
|
if (conn) {
|
|
|
|
switch (conn->state) {
|
|
|
|
case BT_CONN_CONNECT_SCAN:
|
|
|
|
case BT_CONN_CONNECT:
|
|
|
|
case BT_CONN_CONNECTED:
|
|
|
|
return conn;
|
|
|
|
default:
|
|
|
|
bt_conn_put(conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-02 10:47:35 +02:00
|
|
|
conn = bt_conn_add(peer);
|
2015-07-15 13:55:32 +03:00
|
|
|
if (!conn) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
|
|
|
|
|
|
|
|
bt_le_scan_update();
|
|
|
|
|
|
|
|
return conn;
|
|
|
|
}
|
2015-07-15 14:04:52 +03:00
|
|
|
|
2015-07-15 14:08:53 +03:00
|
|
|
int bt_conn_le_conn_update(struct bt_conn *conn, uint16_t min, uint16_t max,
|
|
|
|
uint16_t latency, uint16_t timeout)
|
|
|
|
{
|
|
|
|
struct hci_cp_le_conn_update *conn_update;
|
|
|
|
struct bt_buf *buf;
|
|
|
|
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE,
|
|
|
|
sizeof(*conn_update));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn_update = bt_buf_add(buf, sizeof(*conn_update));
|
|
|
|
memset(conn_update, 0, sizeof(*conn_update));
|
|
|
|
conn_update->handle = sys_cpu_to_le16(conn->handle);
|
|
|
|
conn_update->conn_interval_min = sys_cpu_to_le16(min);
|
|
|
|
conn_update->conn_interval_max = sys_cpu_to_le16(max);
|
|
|
|
conn_update->conn_latency = sys_cpu_to_le16(latency);
|
|
|
|
conn_update->supervision_timeout = sys_cpu_to_le16(timeout);
|
|
|
|
|
|
|
|
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf);
|
|
|
|
}
|