incubator-nuttx/wireless/bluetooth/bt_conn.c

1065 lines
27 KiB
C

/****************************************************************************
* wireless/bluetooth/bt_conn.c
* Bluetooth connection handling.
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Ported from the Intel/Zephyr arduino101_firmware_source-v1.tar package
* where the code was released with a compatible 3-clause BSD license:
*
* Copyright (c) 2016, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kthread.h>
#include <nuttx/mm/iob.h>
#include <nuttx/wireless/bluetooth/bt_hci.h>
#include <nuttx/wireless/bluetooth/bt_core.h>
#include "bt_atomic.h"
#include "bt_queue.h"
#include "bt_hcicore.h"
#include "bt_conn.h"
#include "bt_l2cap.h"
#include "bt_keys.h"
#include "bt_smp.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct bt_conn_handoff_s
{
sem_t sync_sem;
FAR struct bt_conn_s *conn;
};
/****************************************************************************
* Private Data
****************************************************************************/
static struct bt_conn_s g_conns[CONFIG_BLUETOOTH_MAX_CONN];
static struct bt_conn_handoff_s g_conn_handoff =
{
SEM_INITIALIZER(1),
NULL
};
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_DEBUG_WIRELESS_INFO
static const char *state2str(enum bt_conn_state_e state)
{
switch (state)
{
case BT_CONN_DISCONNECTED:
return "disconnected";
case BT_CONN_CONNECT_SCAN:
return "connect-scan";
case BT_CONN_CONNECT:
return "connect";
case BT_CONN_CONNECTED:
return "connected";
case BT_CONN_DISCONNECT:
return "disconnect";
default:
return "(unknown)";
}
}
#endif
static void bt_conn_reset_rx_state(FAR struct bt_conn_s *conn)
{
if (!conn->rx_len)
{
return;
}
bt_buf_release(conn->rx);
conn->rx = NULL;
conn->rx_len = 0;
}
static int conn_tx_kthread(int argc, FAR char *argv[])
{
FAR struct bt_conn_s *conn;
FAR struct bt_buf_s *buf;
int ret;
/* Get the connection instance */
conn = g_conn_handoff.conn;
DEBUGASSERT(conn != NULL);
nxsem_post(&g_conn_handoff.sync_sem);
wlinfo("Started for handle %u\n", conn->handle);
while (conn->state == BT_CONN_CONNECTED)
{
/* Wait until the controller can accept ACL packets */
wlinfo("calling nxsem_wait\n");
do
{
ret = nxsem_wait(&g_btdev.le_pkts_sem);
}
while (ret == -EINTR);
DEBUGASSERT(ret == OK);
/* Check for disconnection */
if (conn->state != BT_CONN_CONNECTED)
{
nxsem_post(&g_btdev.le_pkts_sem);
break;
}
/* Get next ACL packet for connection */
ret = bt_queue_receive(conn->tx_queue, &buf);
DEBUGASSERT(ret >= 0 && buf != NULL);
UNUSED(ret);
if (conn->state != BT_CONN_CONNECTED)
{
nxsem_post(&g_btdev.le_pkts_sem);
bt_buf_release(buf);
break;
}
wlinfo("passing buf %p len %u to driver\n", buf, buf->len);
g_btdev.btdev->send(g_btdev.btdev, buf);
bt_buf_release(buf);
}
wlinfo("handle %u disconnected - cleaning up\n", conn->handle);
/* Give back any allocated buffers */
do
{
buf = NULL;
ret = bt_queue_receive(conn->tx_queue, &buf);
if (ret >= 0)
{
DEBUGASSERT(buf != NULL);
bt_buf_release(buf);
}
}
while (ret >= OK);
bt_conn_reset_rx_state(conn);
wlinfo("handle %u exiting\n", conn->handle);
bt_conn_release(conn);
return EXIT_SUCCESS;
}
static int bt_hci_disconnect(FAR struct bt_conn_s *conn, uint8_t reason)
{
FAR struct bt_buf_s *buf;
FAR struct bt_hci_cp_disconnect_s *disconn;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_DISCONNECT, sizeof(*disconn));
if (!buf)
{
return -ENOBUFS;
}
disconn = bt_buf_extend(buf, sizeof(*disconn));
disconn->handle = BT_HOST2LE16(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(FAR struct bt_conn_s *conn)
{
int err;
err = bt_hci_cmd_send(BT_HCI_OP_LE_CREATE_CONN_CANCEL, NULL);
if (err)
{
return err;
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bt_conn_receive
*
* Description:
* Receive packets from the HCI core on a registered connection.
*
* Input Parameters:
* conn - The registered connection
* buf - The buffer structure containing the packet
* flags - Packet boundary flags
*
* Returned Value:
* None
*
****************************************************************************/
void bt_conn_receive(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf,
uint8_t flags)
{
FAR struct bt_l2cap_hdr_s *hdr;
uint16_t len;
wlinfo("handle %u len %u flags %02x\n", conn->handle, buf->len, flags);
/* Check packet boundary flags */
switch (flags)
{
case 0x02:
/* First packet */
hdr = (void *)buf->data;
len = BT_LE162HOST(hdr->len);
wlinfo("First, len %u final %u\n", buf->len, len);
if (conn->rx_len)
{
wlerr("ERROR: Unexpected first L2CAP frame\n");
bt_conn_reset_rx_state(conn);
}
conn->rx_len = (sizeof(*hdr) + len) - buf->len;
wlinfo("rx_len %u\n", conn->rx_len);
if (conn->rx_len)
{
conn->rx = buf;
return;
}
break;
case 0x01:
/* Continuation */
if (!conn->rx_len)
{
wlerr("ERROR: Unexpected L2CAP continuation\n");
bt_conn_reset_rx_state(conn);
bt_buf_release(buf);
return;
}
if (buf->len > conn->rx_len)
{
wlerr("ERROR: L2CAP data overflow\n");
bt_conn_reset_rx_state(conn);
bt_buf_release(buf);
return;
}
wlinfo("Cont, len %u rx_len %u\n", buf->len, conn->rx_len);
if (buf->len > bt_buf_tailroom(conn->rx))
{
wlerr("ERROR: Not enough buffer space for L2CAP data\n");
bt_conn_reset_rx_state(conn);
bt_buf_release(buf);
return;
}
memcpy(bt_buf_extend(conn->rx, buf->len), buf->data, buf->len);
conn->rx_len -= buf->len;
bt_buf_release(buf);
if (conn->rx_len)
{
return;
}
buf = conn->rx;
conn->rx = NULL;
conn->rx_len = 0;
break;
default:
wlerr("ERROR: Unexpected ACL flags (0x%02x)\n", flags);
bt_conn_reset_rx_state(conn);
bt_buf_release(buf);
return;
}
hdr = (void *)buf->data;
len = BT_LE162HOST(hdr->len);
if (sizeof(*hdr) + len != buf->len)
{
wlerr("ERROR: ACL len mismatch (%u != %u)\n", len, buf->len);
bt_buf_release(buf);
return;
}
wlinfo("Successfully parsed %u byte L2CAP packet\n", buf->len);
bt_l2cap_receive(conn, buf);
}
/****************************************************************************
* Name: bt_conn_send
*
* Description:
* Send data over a connection
*
* Input Parameters:
* conn - The registered connection
* buf - The buffer structure containing the packet to be sent
*
* Returned Value:
* None
*
****************************************************************************/
void bt_conn_send(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf)
{
FAR struct bt_hci_acl_hdr_s *hdr;
sq_queue_t fraglist;
uint16_t len;
uint16_t remaining = buf->len;
FAR uint8_t *ptr;
DEBUGASSERT(conn != NULL && buf != NULL);
wlinfo("conn handle %u buf len %u\n", conn->handle, buf->len);
if (conn->state != BT_CONN_CONNECTED)
{
wlerr("ERROR: not connected!\n");
return;
}
len = remaining;
if (len > g_btdev.le_mtu)
{
len = g_btdev.le_mtu;
}
hdr = bt_buf_provide(buf, sizeof(*hdr));
hdr->handle = BT_HOST2LE16(conn->handle);
hdr->len = BT_HOST2LE16(len);
buf->len -= remaining - len;
ptr = bt_buf_tail(buf);
/* Add the fragment to the end of the list */
sq_addlast((FAR sq_entry_t *)buf, &fraglist);
remaining -= len;
while (remaining)
{
buf = bt_l2cap_create_pdu(conn);
len = remaining;
if (len < g_btdev.le_mtu)
{
len = g_btdev.le_mtu;
}
/* Copy from original buffer */
memcpy(bt_buf_extend(buf, len), ptr, len);
ptr += len;
hdr = bt_buf_provide(buf, sizeof(*hdr));
hdr->handle = BT_HOST2LE16(conn->handle | (1 << 12));
hdr->len = BT_HOST2LE16(len);
/* Add the fragment to the end of the list */
sq_addlast((FAR sq_entry_t *)buf, &fraglist);
remaining -= len;
}
/* Then send each fragment in the correct order */
while ((buf = (FAR struct bt_buf_s *)sq_remfirst(&fraglist)) != NULL)
{
bt_queue_send(conn->tx_queue, buf, BT_NORMAL_PRIO);
}
}
/****************************************************************************
* Name: bt_conn_add
*
* Description:
* Add a new connection
*
* Input Parameters:
* peer - The address of the Bluetooth peer
* role - Either BT_HCI_ROLE_MASTER or BT_HCI_ROLE_SLAVE
*
* Returned Value:
* A reference to the new connection structure is returned on success.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_add(FAR const bt_addr_le_t *peer,
uint8_t role)
{
FAR struct bt_conn_s *conn = NULL;
int i;
for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++)
{
if (!bt_addr_le_cmp(&g_conns[i].dst, BT_ADDR_LE_ANY))
{
conn = &g_conns[i];
break;
}
}
if (!conn)
{
return NULL;
}
memset(conn, 0, sizeof(*conn));
bt_atomic_set(&conn->ref, 1);
conn->role = role;
bt_addr_le_copy(&conn->dst, peer);
return conn;
}
/****************************************************************************
* Name: bt_conn_set_state
*
* Description:
* Set connection object in certain state and perform actions related to
* state change.
*
* Input Parameters:
* conn - The connection whose state will be changed.
* state - The new state of the connection.
*
* Returned Value:
* None
*
****************************************************************************/
void bt_conn_set_state(FAR struct bt_conn_s *conn,
enum bt_conn_state_e state)
{
enum bt_conn_state_e old_state;
wlinfo("%s -> %s\n", state2str(conn->state), state2str(state));
if (conn->state == state)
{
wlwarn("no transition\n");
return;
}
old_state = conn->state;
conn->state = state;
/* Take a reference for the first state transition after bt_conn_add() and
* keep it until reaching DISCONNECTED again.
*/
if (old_state == BT_CONN_DISCONNECTED)
{
bt_conn_addref(conn);
}
switch (conn->state)
{
case BT_CONN_CONNECTED:
{
pid_t pid;
int ret;
ret = bt_queue_open(BT_CONN_TX, O_RDWR | O_CREAT,
CONFIG_BLUETOOTH_TXCONN_NMSGS, &conn->tx_queue);
DEBUGASSERT(ret >= 0 && g_btdev.tx_queue != 0);
UNUSED(ret);
/* Get exclusive access to the handoff structure. The count will be
* zero when we complete this.
*/
do
{
ret = nxsem_wait(&g_conn_handoff.sync_sem);
}
while (ret == -EINTR);
DEBUGASSERT(ret == OK);
/* Start the Tx connection kernel thread */
g_conn_handoff.conn = bt_conn_addref(conn);
pid = kthread_create("BT Conn Tx", CONFIG_BLUETOOTH_TXCONN_PRIORITY,
CONFIG_BLUETOOTH_TXCONN_STACKSIZE,
conn_tx_kthread, NULL);
DEBUGASSERT(pid > 0);
UNUSED(pid);
/* Take the semaphore again. This will force us to wait with the
* sem_count at -1. It will be zero again when we continue.
*/
do
{
ret = nxsem_wait(&g_conn_handoff.sync_sem);
}
while (ret == -EINTR);
DEBUGASSERT(ret == OK);
nxsem_post(&g_conn_handoff.sync_sem);
}
break;
case BT_CONN_DISCONNECTED:
/* Send dummy buffer to wake up and stop the Tx thread for states where it
* was running.
*/
if (old_state == BT_CONN_CONNECTED || old_state == BT_CONN_DISCONNECT)
{
bt_queue_send(conn->tx_queue, bt_buf_alloc(BT_DUMMY, NULL, 0),
BT_NORMAL_PRIO);
}
/* Release the reference we took for the very first state transition. */
bt_conn_release(conn);
break;
case BT_CONN_CONNECT_SCAN:
case BT_CONN_CONNECT:
case BT_CONN_DISCONNECT:
break;
default:
wlwarn("no valid (%u) state was set\n", state);
break;
}
}
/****************************************************************************
* Name: bt_conn_lookup_handle
*
* Description:
* Look up an existing connection
*
* Input Parameters:
* handle - The handle to be used to perform the lookup
*
* Returned Value:
* A reference to the connection state instance is returned on success.
* NULL is returned if the connection is not found. On success, the
* caller gets a new reference to the connection object which must be
* released with bt_conn_release() once done using the connection.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_lookup_handle(uint16_t handle)
{
int i;
for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++)
{
/* We only care about connections with a valid handle */
if (g_conns[i].state != BT_CONN_CONNECTED &&
g_conns[i].state != BT_CONN_DISCONNECT)
{
continue;
}
if (g_conns[i].handle == handle)
{
return bt_conn_addref(&g_conns[i]);
}
}
return NULL;
}
/****************************************************************************
* Name: bt_conn_lookup_addr_le
*
* Description:
* Look up an existing connection based on the remote address.
*
* Input Parameters:
* peer - Remote address.
*
* Returned Value:
* A reference to the connection state instance is returned on success.
* NULL is returned if the connection is not found. On success, the
* caller gets a new reference to the connection object which must be
* released with bt_conn_release() once done using the connection.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_lookup_addr_le(FAR const bt_addr_le_t * peer)
{
int i;
for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++)
{
if (!bt_addr_le_cmp(peer, &g_conns[i].dst))
{
return bt_conn_addref(&g_conns[i]);
}
}
return NULL;
}
/****************************************************************************
* Name: bt_conn_lookup_state
*
* Description:
* Look up a connection state. For BT_ADDR_LE_ANY, returns the first
* connection with the specific state
*
* Input Parameters:
* peer - The peer address to match
* state - The connection state to match
*
* Returned Value:
* A reference to the connection state instance is returned on success.
* NULL is returned if the connection is not found. On success, the
* caller gets a new reference to the connection object which must be
* released with bt_conn_release() once done using the connection.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_lookup_state(FAR const bt_addr_le_t * peer,
enum bt_conn_state_e state)
{
int i;
for (i = 0; i < CONFIG_BLUETOOTH_MAX_CONN; i++)
{
if (!bt_addr_le_cmp(&g_conns[i].dst, BT_ADDR_LE_ANY))
{
continue;
}
if (bt_addr_le_cmp(peer, BT_ADDR_LE_ANY) &&
bt_addr_le_cmp(peer, &g_conns[i].dst))
{
continue;
}
if (g_conns[i].state == state)
{
return bt_conn_addref(&g_conns[i]);
}
}
return NULL;
}
/****************************************************************************
* Name: bt_conn_addref
*
* Description:
* Increment the reference count of a connection object.
*
* Input Parameters:
* conn - Connection object.
*
* Returned Value:
* Connection object with incremented reference count.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_addref(FAR struct bt_conn_s *conn)
{
bt_atomic_incr(&conn->ref);
wlinfo("handle %u ref %u\n", conn->handle, bt_atomic_get(&conn->ref));
return conn;
}
/****************************************************************************
* Name: bt_conn_release
*
* Description:
* Decrement the reference count of a connection object.
*
* Input Parameters:
* conn - Connection object.
*
* Returned Value:
* None
*
****************************************************************************/
void bt_conn_release(FAR struct bt_conn_s *conn)
{
bt_atomic_t old_ref;
old_ref = bt_atomic_decr(&conn->ref);
wlinfo("handle %u ref %u\n", conn->handle, bt_atomic_get(&conn->ref));
if (old_ref > 1)
{
return;
}
bt_addr_le_copy(&conn->dst, BT_ADDR_LE_ANY);
}
/****************************************************************************
* Name: bt_conn_get_dst
*
* Description:
* Get destination (peer) address of a connection.
*
* Input Parameters:
* conn - Connection object.
*
* Returned Value:
* Destination address.
*
****************************************************************************/
FAR const bt_addr_le_t *bt_conn_get_dst(FAR const struct bt_conn_s *conn)
{
return &conn->dst;
}
/****************************************************************************
* Name: bt_conn_security
*
* Description:
* This function enable security (encryption) for a connection. If device is
* already paired with sufficiently strong key encryption will be enabled. If
* link is already encrypted with sufficiently strong key this function does
* nothing.
*
* If device is not paired pairing will be initiated. If device is paired and
* keys are too weak but input output capabilities allow for strong enough keys
* pairing will be initiated.
*
* This function may return error if required level of security is not possible
* to achieve due to local or remote device limitation (eg input output
* capabilities).
*
* Input Parameters:
* conn - Connection object.
* sec - Requested security level.
*
* Returned Value:
* 0 on success or negative error
*
****************************************************************************/
int bt_conn_security(FAR struct bt_conn_s *conn, enum bt_security_e sec)
{
FAR struct bt_keys_s *keys;
if (conn->state != BT_CONN_CONNECTED)
{
return -ENOTCONN;
}
/* Nothing to do */
if (sec == BT_SECURITY_LOW)
{
return 0;
}
/* For now we only support JustWorks */
if (sec > BT_SECURITY_MEDIUM)
{
return -EINVAL;
}
if (conn->encrypt)
{
return 0;
}
if (conn->role == BT_HCI_ROLE_SLAVE)
{
return bt_smp_send_security_req(conn);
}
keys = bt_keys_find(BT_KEYS_LTK, &conn->dst);
if (keys)
{
return bt_conn_le_start_encryption(conn, keys->ltk.rand,
keys->ltk.ediv, keys->ltk.val);
}
return bt_smp_send_pairing_req(conn);
}
/****************************************************************************
* Name:bt_conn_set_auto_conn
*
* Description:
* This function enables/disables automatic connection initiation.
* Every time the device looses the connection with peer, this connection
* will be re-established if connectible advertisement from peer is
* received.
*
* Input Parameters:
* conn - Existing connection object.
* auto_conn - boolean value. If true, auto connect is enabled, if false,
* auto connect is disabled.
*
* Returned Value:
* None
*
****************************************************************************/
void bt_conn_set_auto_conn(FAR struct bt_conn_s *conn, bool auto_conn)
{
if (auto_conn)
{
bt_atomic_setbit(conn->flags, BT_CONN_AUTO_CONNECT);
}
else
{
bt_atomic_clrbit(conn->flags, BT_CONN_AUTO_CONNECT);
}
}
/****************************************************************************
* Name: bt_conn_disconnect
*
* Description:
* Disconnect an active connection with the specified reason code or cancel
* pending outgoing connection.
*
* Input Parameters:
* conn - Connection to disconnect.
* reason - Reason code for the disconnection.
*
* Returned Value:
* Zero on success or (negative) error code on failure.
*
****************************************************************************/
int bt_conn_disconnect(FAR struct bt_conn_s *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;
}
}
/****************************************************************************
* Name: bt_conn_create_le
*
* Description:
* Allows initiate new LE link to remote peer using its address.
* Returns a new reference that the the caller is responsible for managing.
*
* Input Parameters:
* peer - Remote address.
*
* Returned Value:
* Valid connection object on success or NULL otherwise.
*
****************************************************************************/
FAR struct bt_conn_s *bt_conn_create_le(FAR const bt_addr_le_t *peer)
{
FAR struct bt_conn_s *conn;
/* First check if this connection exists and that it is in a proper
* state.
*/
conn = bt_conn_lookup_addr_le(peer);
if (conn != NULL)
{
switch (conn->state)
{
case BT_CONN_CONNECT_SCAN:
case BT_CONN_CONNECT:
case BT_CONN_CONNECTED:
return conn;
default:
bt_conn_release(conn);
return NULL;
}
}
/* No.. the connection does not exist. Create it assuming MASTER role
* and put it in the BT_CONNECT_SCAN state.
*/
conn = bt_conn_add(peer, BT_HCI_ROLE_MASTER);
if (!conn)
{
return NULL;
}
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
bt_le_scan_update();
return conn;
}
/****************************************************************************
* Name: bt_conn_le_start_encryption
*
* Description:
* See the HCI start encryption command.
*
* NOTE: rand and ediv should be in BT order.
*
* Input Parameters:
* conn - The connection to send the command on.
* rand, ediv - Values to use for the encryption key
* ltk -
*
* Returned Value:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int bt_conn_le_start_encryption(FAR struct bt_conn_s *conn, uint64_t rand,
uint16_t ediv, FAR const uint8_t *ltk)
{
FAR struct bt_hci_cp_le_start_encryption_s *cp;
FAR struct bt_buf_s *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_START_ENCRYPTION, sizeof(*cp));
if (!buf)
{
return -ENOBUFS;
}
cp = bt_buf_extend(buf, sizeof(*cp));
cp->handle = BT_HOST2LE16(conn->handle);
cp->rand = rand;
cp->ediv = ediv;
memcpy(cp->ltk, ltk, sizeof(cp->ltk));
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_START_ENCRYPTION, buf, NULL);
}
int bt_conn_le_conn_update(FAR struct bt_conn_s *conn, uint16_t min,
uint16_t max, uint16_t latency, uint16_t timeout)
{
FAR struct hci_cp_le_conn_update_s *conn_update;
FAR struct bt_buf_s *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE, sizeof(*conn_update));
if (!buf)
{
return -ENOBUFS;
}
conn_update = bt_buf_extend(buf, sizeof(*conn_update));
memset(conn_update, 0, sizeof(*conn_update));
conn_update->handle = BT_HOST2LE16(conn->handle);
conn_update->conn_interval_min = BT_HOST2LE16(min);
conn_update->conn_interval_max = BT_HOST2LE16(max);
conn_update->conn_latency = BT_HOST2LE16(latency);
conn_update->supervision_timeout = BT_HOST2LE16(timeout);
return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf);
}