6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
2104 lines
52 KiB
C
2104 lines
52 KiB
C
/****************************************************************************
|
|
* wireless/bluetooth/bt_hdicore.c
|
|
* HCI core Bluetooth 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/clock.h>
|
|
#include <nuttx/kthread.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/wqueue.h>
|
|
#include <nuttx/net/bluetooth.h>
|
|
#include <nuttx/wireless/bluetooth/bt_core.h>
|
|
#include <nuttx/wireless/bluetooth/bt_hci.h>
|
|
|
|
#include "bt_queue.h"
|
|
#include "bt_buf.h"
|
|
#include "bt_keys.h"
|
|
#include "bt_conn.h"
|
|
#include "bt_l2cap.h"
|
|
#include "bt_hcicore.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
|
|
/* Wait up to 2.5 seconds for a response. This delay is arbitrary and
|
|
* intended only to avoid hangs while waiting for a response. It may need
|
|
* to be adjusted.
|
|
*/
|
|
|
|
#define TIMEOUT_SEC 2
|
|
#define TIMEOUT_NSEC 500 * 1024 * 1024
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* State of connected HCI device.
|
|
*
|
|
* NOTE: Because this is a global singleton, multiple HCI devices may not
|
|
* be supported.
|
|
*/
|
|
|
|
struct bt_dev_s g_btdev;
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static FAR struct bt_conn_cb_s *g_callback_list;
|
|
static bt_le_scan_cb_t *g_scan_dev_found_cb;
|
|
|
|
/* Lists of pending received messages. One for low priority input that is
|
|
* processed on the low priority work queue and one for high priority
|
|
* input that is processed on high priority work queue.
|
|
*/
|
|
|
|
static FAR struct bt_bufferlist_s g_lp_rxlist;
|
|
static FAR struct bt_bufferlist_s g_hp_rxlist;
|
|
|
|
/* Work structures: One for high priority and one for low priority work */
|
|
|
|
static struct work_s g_lp_work;
|
|
static struct work_s g_hp_work;
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: bt_enqueue_bufwork
|
|
*
|
|
* Description:
|
|
* Add the provided buffer 'buf' to the head selected buffer list 'list'
|
|
*
|
|
* Input Parameters:
|
|
* list - The buffer list to use
|
|
* buf - The buffer to be added to the head of the buffer list
|
|
*
|
|
* Returned Value:
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void bt_enqueue_bufwork(FAR struct bt_bufferlist_s *list,
|
|
FAR struct bt_buf_s *buf)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
flags = spin_lock_irqsave();
|
|
buf->flink = list->head;
|
|
if (list->head == NULL)
|
|
{
|
|
list->tail = buf;
|
|
}
|
|
|
|
list->head = buf;
|
|
spin_unlock_irqrestore(flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_dequeue_bufwork
|
|
*
|
|
* Description:
|
|
* Remove and return the buffer at the tail of the buffer list specified
|
|
* by 'list'.
|
|
*
|
|
* Input Parameters:
|
|
* list - The buffer list to use
|
|
*
|
|
* Returned Value:
|
|
* A pointer to the buffer that was at the tail of the buffer list. NULL
|
|
* is returned if the list was empty.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static FAR struct bt_buf_s *bt_dequeue_bufwork(FAR struct bt_bufferlist_s *list)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
irqstate_t flags;
|
|
|
|
flags = spin_lock_irqsave();
|
|
buf = list->tail;
|
|
if (buf != NULL)
|
|
{
|
|
if (list->head == list->tail)
|
|
{
|
|
list->head = NULL;
|
|
list->tail = NULL;
|
|
}
|
|
else
|
|
{
|
|
FAR struct bt_buf_s *prev;
|
|
|
|
for (prev = list->head;
|
|
prev && prev->flink != buf;
|
|
prev = prev->flink)
|
|
{
|
|
}
|
|
|
|
if (prev != NULL)
|
|
{
|
|
prev->flink = NULL;
|
|
list->tail = prev;
|
|
}
|
|
}
|
|
|
|
buf->flink = NULL;
|
|
}
|
|
|
|
spin_unlock_irqrestore(flags);
|
|
return buf;
|
|
}
|
|
|
|
static void bt_connected(FAR struct bt_conn_s *conn)
|
|
{
|
|
FAR struct bt_conn_cb_s *cb;
|
|
|
|
for (cb = g_callback_list; cb; cb = cb->flink)
|
|
{
|
|
if (cb->connected)
|
|
{
|
|
cb->connected(conn, cb->context);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bt_disconnected(FAR struct bt_conn_s *conn)
|
|
{
|
|
FAR struct bt_conn_cb_s *cb;
|
|
|
|
for (cb = g_callback_list; cb; cb = cb->flink)
|
|
{
|
|
if (cb->disconnected)
|
|
{
|
|
cb->disconnected(conn, cb->context);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hci_acl(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_acl_hdr_s *hdr = (FAR void *)buf->data;
|
|
FAR struct bt_conn_s *conn;
|
|
uint16_t handle;
|
|
uint16_t len = BT_LE162HOST(hdr->len);
|
|
uint8_t flags;
|
|
|
|
wlinfo("buf %p\n", buf);
|
|
|
|
handle = BT_LE162HOST(hdr->handle);
|
|
flags = (handle >> 12);
|
|
buf->u.acl.handle = bt_acl_handle(handle);
|
|
|
|
bt_buf_consume(buf, sizeof(*hdr));
|
|
|
|
wlinfo("handle %u len %u flags %u\n", buf->u.acl.handle, len, flags);
|
|
|
|
if (buf->len != len)
|
|
{
|
|
wlerr("ERROR: ACL data length mismatch (%u != %u)\n", buf->len, len);
|
|
bt_buf_release(buf);
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(buf->u.acl.handle);
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to find conn for handle %u\n", buf->u.acl.handle);
|
|
bt_buf_release(buf);
|
|
return;
|
|
}
|
|
|
|
bt_conn_receive(conn, buf, flags);
|
|
bt_conn_release(conn);
|
|
}
|
|
|
|
/* HCI event processing */
|
|
|
|
static void hci_encrypt_change(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_encrypt_change_s *evt = (FAR void *)buf->data;
|
|
FAR struct bt_conn_s *conn;
|
|
uint16_t handle = BT_LE162HOST(evt->handle);
|
|
|
|
wlinfo("status %u handle %u encrypt 0x%02x\n",
|
|
evt->status, handle, evt->encrypt);
|
|
|
|
if (evt->status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to look up conn with handle %u\n", handle);
|
|
return;
|
|
}
|
|
|
|
conn->encrypt = evt->encrypt;
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
|
bt_conn_release(conn);
|
|
}
|
|
|
|
static void hci_reset_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
uint8_t status = buf->data[0];
|
|
|
|
wlinfo("status %u\n", status);
|
|
|
|
if (status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_scan_dev_found_cb = NULL;
|
|
g_btdev.scan_enable = BT_LE_SCAN_DISABLE;
|
|
g_btdev.scan_filter = BT_LE_SCAN_FILTER_DUP_ENABLE;
|
|
}
|
|
|
|
static void hci_cmd_done(uint16_t opcode, uint8_t status,
|
|
FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_buf_s *sent = g_btdev.sent_cmd;
|
|
|
|
if (sent == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g_btdev.sent_cmd->u.hci.opcode != opcode)
|
|
{
|
|
wlerr("ERROR: Unexpected completion of opcode 0x%04x\n", opcode);
|
|
return;
|
|
}
|
|
|
|
g_btdev.sent_cmd = NULL;
|
|
|
|
/* If the command was synchronous wake up bt_hci_cmd_send_sync() */
|
|
|
|
if (sent->u.hci.sync != NULL)
|
|
{
|
|
FAR sem_t *sem = sent->u.hci.sync;
|
|
|
|
if (status != 0)
|
|
{
|
|
wlwarn("WARNING: status %u\n", status);
|
|
sent->u.hci.sync = NULL;
|
|
}
|
|
else
|
|
{
|
|
sent->u.hci.sync = bt_buf_addref(buf);
|
|
}
|
|
|
|
nxsem_post(sem);
|
|
}
|
|
else
|
|
{
|
|
bt_buf_release(sent);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct hci_evt_cmd_complete_s *evt = (FAR void *)buf->data;
|
|
uint16_t opcode = BT_LE162HOST(evt->opcode);
|
|
FAR uint8_t *status;
|
|
|
|
wlinfo("opcode %04x\n", opcode);
|
|
|
|
bt_buf_consume(buf, sizeof(*evt));
|
|
|
|
/* All command return parameters have a 1-byte status in the beginning, so we
|
|
* can safely make this generalization.
|
|
*/
|
|
|
|
status = buf->data;
|
|
|
|
switch (opcode)
|
|
{
|
|
case BT_HCI_OP_RESET:
|
|
hci_reset_complete(buf);
|
|
break;
|
|
|
|
default:
|
|
wlinfo("Unhandled opcode %04x\n", opcode);
|
|
break;
|
|
}
|
|
|
|
hci_cmd_done(opcode, *status, buf);
|
|
|
|
if (evt->ncmd > 0 && g_btdev.ncmd == 0)
|
|
{
|
|
/* Allow next command to be sent */
|
|
|
|
g_btdev.ncmd = 1;
|
|
nxsem_post(&g_btdev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
static void hci_cmd_status(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_cmd_status_s *evt = (FAR void *)buf->data;
|
|
uint16_t opcode = BT_LE162HOST(evt->opcode);
|
|
|
|
wlinfo("opcode %04x\n", opcode);
|
|
|
|
bt_buf_consume(buf, sizeof(*evt));
|
|
|
|
switch (opcode)
|
|
{
|
|
default:
|
|
wlinfo("Unhandled opcode %04x\n", opcode);
|
|
break;
|
|
}
|
|
|
|
hci_cmd_done(opcode, evt->status, buf);
|
|
|
|
if (evt->ncmd && !g_btdev.ncmd)
|
|
{
|
|
/* Allow next command to be sent */
|
|
|
|
g_btdev.ncmd = 1;
|
|
nxsem_post(&g_btdev.ncmd_sem);
|
|
}
|
|
}
|
|
|
|
static void hci_num_completed_packets(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_num_completed_packets_s *evt = (FAR void *)buf->data;
|
|
uint16_t num_handles = BT_LE162HOST(evt->num_handles);
|
|
uint16_t i;
|
|
|
|
wlinfo("num_handles %u\n", num_handles);
|
|
|
|
for (i = 0; i < num_handles; i++)
|
|
{
|
|
uint16_t handle;
|
|
uint16_t count;
|
|
|
|
handle = BT_LE162HOST(evt->h[i].handle);
|
|
count = BT_LE162HOST(evt->h[i].count);
|
|
|
|
wlinfo("handle %u count %u\n", handle, count);
|
|
UNUSED(handle);
|
|
|
|
while (count--)
|
|
{
|
|
nxsem_post(&g_btdev.le_pkts_sem);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hci_encrypt_key_refresh_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_encrypt_key_refresh_complete_s *evt = (FAR void *)buf->data;
|
|
FAR struct bt_conn_s *conn;
|
|
uint16_t handle;
|
|
|
|
handle = BT_LE162HOST(evt->handle);
|
|
|
|
wlinfo("status %u handle %u\n", evt->status, handle);
|
|
|
|
if (evt->status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to look up conn with handle %u\n", handle);
|
|
return;
|
|
}
|
|
|
|
bt_l2cap_encrypt_change(conn);
|
|
bt_conn_release(conn);
|
|
}
|
|
|
|
static void copy_id_addr(FAR struct bt_conn_s *conn,
|
|
FAR const bt_addr_le_t *addr)
|
|
{
|
|
FAR struct bt_keys_s *keys;
|
|
|
|
/* If we have a keys struct we already know the identity */
|
|
|
|
if (conn->keys)
|
|
{
|
|
return;
|
|
}
|
|
|
|
keys = bt_keys_find_irk(addr);
|
|
if (keys)
|
|
{
|
|
bt_addr_le_copy(&conn->dst, &keys->addr);
|
|
conn->keys = keys;
|
|
}
|
|
else
|
|
{
|
|
bt_addr_le_copy(&conn->dst, addr);
|
|
}
|
|
}
|
|
|
|
static int bt_hci_start_scanning(uint8_t scan_type, uint8_t scan_filter)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
FAR struct bt_buf_s *rsp;
|
|
FAR struct bt_hci_cp_le_set_scan_params_s *set_param;
|
|
FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable;
|
|
int ret;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS, sizeof(*set_param));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_param = bt_buf_extend(buf, sizeof(*set_param));
|
|
memset(set_param, 0, sizeof(*set_param));
|
|
set_param->scan_type = scan_type;
|
|
|
|
/* for the rest parameters apply default values according to spec 4.2, vol2,
|
|
* part E, 7.8.10
|
|
*/
|
|
|
|
set_param->interval = BT_HOST2LE16(0x0010);
|
|
set_param->window = BT_HOST2LE16(0x0010);
|
|
set_param->filter_policy = 0x00;
|
|
set_param->addr_type = 0x00;
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_PARAMS, buf);
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, sizeof(*scan_enable));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
scan_enable = bt_buf_extend(buf, sizeof(*scan_enable));
|
|
memset(scan_enable, 0, sizeof(*scan_enable));
|
|
scan_enable->filter_dup = scan_filter;
|
|
scan_enable->enable = BT_LE_SCAN_ENABLE;
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Update scan state in case of success (0) status */
|
|
|
|
ret = rsp->data[0];
|
|
if (!ret)
|
|
{
|
|
g_btdev.scan_enable = BT_LE_SCAN_ENABLE;
|
|
}
|
|
|
|
bt_buf_release(rsp);
|
|
return ret;
|
|
}
|
|
|
|
static int bt_hci_stop_scanning(void)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
FAR struct bt_buf_s *rsp;
|
|
FAR struct bt_hci_cp_le_set_scan_enable_s *scan_enable;
|
|
int ret;
|
|
|
|
if (g_btdev.scan_enable == BT_LE_SCAN_DISABLE)
|
|
{
|
|
wlwarn("WARNING: Scan already disabled\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE,
|
|
sizeof(*scan_enable));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
scan_enable = bt_buf_extend(buf, sizeof(*scan_enable));
|
|
memset(scan_enable, 0x0, sizeof(*scan_enable));
|
|
scan_enable->filter_dup = 0x00;
|
|
scan_enable->enable = BT_LE_SCAN_DISABLE;
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Update scan state in case of success (0) status */
|
|
|
|
ret = rsp->data[0];
|
|
if (!ret)
|
|
{
|
|
g_btdev.scan_enable = BT_LE_SCAN_DISABLE;
|
|
}
|
|
|
|
bt_buf_release(rsp);
|
|
return ret;
|
|
}
|
|
|
|
static int hci_le_create_conn(FAR const bt_addr_le_t *addr)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
FAR struct bt_hci_cp_le_create_conn_s *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_CREATE_CONN, sizeof(*cp));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
cp = bt_buf_extend(buf, sizeof(*cp));
|
|
memset(cp, 0x0, sizeof(*cp));
|
|
bt_addr_le_copy(&cp->peer_addr, addr);
|
|
cp->conn_interval_max = BT_HOST2LE16(0x0028);
|
|
cp->conn_interval_min = BT_HOST2LE16(0x0018);
|
|
cp->scan_interval = BT_HOST2LE16(0x0060);
|
|
cp->scan_window = BT_HOST2LE16(0x0030);
|
|
cp->supervision_timeout = BT_HOST2LE16(0x07d0);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_CREATE_CONN, buf, NULL);
|
|
}
|
|
|
|
static void hci_disconn_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_disconn_complete_s *evt = (FAR void *)buf->data;
|
|
uint16_t handle = BT_LE162HOST(evt->handle);
|
|
FAR struct bt_conn_s *conn;
|
|
|
|
wlinfo("status %u handle %u reason %u\n", evt->status, handle, evt->reason);
|
|
|
|
if (evt->status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to look up conn with handle %u\n", handle);
|
|
return;
|
|
}
|
|
|
|
bt_l2cap_disconnected(conn);
|
|
bt_disconnected(conn);
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
conn->handle = 0;
|
|
|
|
if (bt_atomic_testbit(conn->flags, BT_CONN_AUTO_CONNECT))
|
|
{
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN);
|
|
bt_le_scan_update();
|
|
}
|
|
|
|
bt_conn_release(conn);
|
|
|
|
if (g_btdev.adv_enable)
|
|
{
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (buf)
|
|
{
|
|
memcpy(bt_buf_extend(buf, 1), &g_btdev.adv_enable, 1);
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_ENABLE, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void le_conn_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_le_conn_complete_s *evt = (FAR void *)buf->data;
|
|
uint16_t handle = BT_LE162HOST(evt->handle);
|
|
FAR struct bt_conn_s *conn;
|
|
FAR struct bt_keys_s *keys;
|
|
|
|
wlinfo("status %u handle %u role %u %s\n", evt->status, handle,
|
|
evt->role, bt_addr_le_str(&evt->peer_addr));
|
|
|
|
/* Make lookup to check if there's a connection object in CONNECT state
|
|
* associated with passed peer LE address.
|
|
*/
|
|
|
|
keys = bt_keys_find_irk(&evt->peer_addr);
|
|
if (keys)
|
|
{
|
|
conn = bt_conn_lookup_state(&keys->addr, BT_CONN_CONNECT);
|
|
}
|
|
else
|
|
{
|
|
conn = bt_conn_lookup_state(&evt->peer_addr, BT_CONN_CONNECT);
|
|
}
|
|
|
|
if (evt->status)
|
|
{
|
|
if (!conn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
|
|
|
|
/* Drop the reference got by lookup call in CONNECT state. We are now in
|
|
* DISCONNECTED state since no successful LE link been made.
|
|
*/
|
|
|
|
bt_conn_release(conn);
|
|
return;
|
|
}
|
|
|
|
if (!conn)
|
|
{
|
|
conn = bt_conn_add(&evt->peer_addr, evt->role);
|
|
}
|
|
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to add new conn for handle %u\n", handle);
|
|
return;
|
|
}
|
|
|
|
conn->handle = handle;
|
|
conn->src.type = BT_ADDR_LE_PUBLIC;
|
|
memcpy(conn->src.val, g_btdev.bdaddr.val, sizeof(g_btdev.bdaddr.val));
|
|
copy_id_addr(conn, &evt->peer_addr);
|
|
conn->le_conn_interval = BT_LE162HOST(evt->interval);
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECTED);
|
|
|
|
bt_l2cap_connected(conn);
|
|
|
|
if (evt->role == BT_HCI_ROLE_SLAVE)
|
|
{
|
|
bt_l2cap_update_conn_param(conn);
|
|
}
|
|
|
|
bt_connected(conn);
|
|
bt_conn_release(conn);
|
|
bt_le_scan_update();
|
|
}
|
|
|
|
static void check_pending_conn(FAR const bt_addr_le_t *addr, uint8_t evtype,
|
|
FAR struct bt_keys_s *keys)
|
|
{
|
|
FAR struct bt_conn_s *conn;
|
|
|
|
/* Return if event is not connectible */
|
|
|
|
if (evtype != BT_LE_ADV_IND && evtype != BT_LE_ADV_DIRECT_IND)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (keys)
|
|
{
|
|
conn = bt_conn_lookup_state(&keys->addr, BT_CONN_CONNECT_SCAN);
|
|
}
|
|
else
|
|
{
|
|
conn = bt_conn_lookup_state(addr, BT_CONN_CONNECT_SCAN);
|
|
}
|
|
|
|
if (!conn)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (bt_hci_stop_scanning())
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (hci_le_create_conn(addr))
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
bt_conn_set_state(conn, BT_CONN_CONNECT);
|
|
|
|
done:
|
|
bt_conn_release(conn);
|
|
}
|
|
|
|
static void le_adv_report(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_ev_le_advertising_info_s *info;
|
|
uint8_t num_reports = buf->data[0];
|
|
|
|
wlinfo("Adv number of reports %u\n", num_reports);
|
|
|
|
info = bt_buf_consume(buf, sizeof(num_reports));
|
|
|
|
while (num_reports--)
|
|
{
|
|
int8_t rssi = info->data[info->length];
|
|
FAR struct bt_keys_s *keys;
|
|
bt_addr_le_t addr;
|
|
|
|
wlinfo("%s event %u, len %u, rssi %d dBm\n",
|
|
bt_addr_le_str(&info->addr), info->evt_type, info->length,
|
|
rssi);
|
|
|
|
keys = bt_keys_find_irk(&info->addr);
|
|
if (keys)
|
|
{
|
|
bt_addr_le_copy(&addr, &keys->addr);
|
|
wlinfo("Identity %s matched RPA %s\n",
|
|
bt_addr_le_str(&keys->addr), bt_addr_le_str(&info->addr));
|
|
}
|
|
else
|
|
{
|
|
bt_addr_le_copy(&addr, &info->addr);
|
|
}
|
|
|
|
if (g_scan_dev_found_cb)
|
|
{
|
|
g_scan_dev_found_cb(&addr, rssi, info->evt_type,
|
|
info->data, info->length);
|
|
}
|
|
|
|
check_pending_conn(&info->addr, info->evt_type, keys);
|
|
|
|
/* Get next report iteration by moving pointer to right offset in buf
|
|
* according to spec 4.2, Vol 2, Part E, 7.7.65.2.
|
|
*/
|
|
|
|
info = bt_buf_consume(buf, sizeof(*info) + info->length + sizeof(rssi));
|
|
}
|
|
}
|
|
|
|
static void le_ltk_request(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_le_ltk_request_s *evt = (FAR void *)buf->data;
|
|
FAR struct bt_conn_s *conn;
|
|
uint16_t handle;
|
|
|
|
handle = BT_LE162HOST(evt->handle);
|
|
|
|
wlinfo("handle %u\n", handle);
|
|
|
|
conn = bt_conn_lookup_handle(handle);
|
|
if (!conn)
|
|
{
|
|
wlerr("ERROR: Unable to lookup conn for handle %u\n", handle);
|
|
return;
|
|
}
|
|
|
|
if (!conn->keys)
|
|
{
|
|
conn->keys = bt_keys_find(BT_KEYS_SLAVE_LTK, &conn->dst);
|
|
}
|
|
|
|
if (conn->keys && (conn->keys->keys & BT_KEYS_SLAVE_LTK) &&
|
|
conn->keys->slave_ltk.rand == evt->rand &&
|
|
conn->keys->slave_ltk.ediv == evt->ediv)
|
|
{
|
|
FAR struct bt_hci_cp_le_ltk_req_reply_s *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_REPLY, sizeof(*cp));
|
|
if (!buf)
|
|
{
|
|
wlerr("ERROR: Out of command buffers\n");
|
|
goto done;
|
|
}
|
|
|
|
cp = bt_buf_extend(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
memcpy(cp->ltk, conn->keys->slave_ltk.val, 16);
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_REPLY, buf);
|
|
}
|
|
else
|
|
{
|
|
FAR struct bt_hci_cp_le_ltk_req_neg_reply_s *cp;
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, sizeof(*cp));
|
|
if (!buf)
|
|
{
|
|
wlerr("ERROR: Out of command buffers\n");
|
|
goto done;
|
|
}
|
|
|
|
cp = bt_buf_extend(buf, sizeof(*cp));
|
|
cp->handle = evt->handle;
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_LTK_REQ_NEG_REPLY, buf);
|
|
}
|
|
|
|
done:
|
|
bt_conn_release(conn);
|
|
}
|
|
|
|
static void hci_le_meta_event(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_le_meta_event_s *evt = (FAR void *)buf->data;
|
|
|
|
bt_buf_consume(buf, sizeof(*evt));
|
|
|
|
switch (evt->subevent)
|
|
{
|
|
case BT_HCI_EVT_LE_CONN_COMPLETE:
|
|
le_conn_complete(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_LE_ADVERTISING_REPORT:
|
|
le_adv_report(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_LE_LTK_REQUEST:
|
|
le_ltk_request(buf);
|
|
break;
|
|
|
|
default:
|
|
wlinfo("Unhandled LE event %04x\n", evt->subevent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void hci_event(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_hdr_s *hdr = (FAR void *)buf->data;
|
|
|
|
wlinfo("event %u\n", hdr->evt);
|
|
|
|
bt_buf_consume(buf, sizeof(struct bt_hci_evt_hdr_s));
|
|
|
|
switch (hdr->evt)
|
|
{
|
|
case BT_HCI_EVT_DISCONN_COMPLETE:
|
|
hci_disconn_complete(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_ENCRYPT_CHANGE:
|
|
hci_encrypt_change(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE:
|
|
hci_encrypt_key_refresh_complete(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_LE_META_EVENT:
|
|
hci_le_meta_event(buf);
|
|
break;
|
|
|
|
default:
|
|
wlwarn("WARNING: Unhandled event 0x%02x\n", hdr->evt);
|
|
break;
|
|
}
|
|
|
|
bt_buf_release(buf);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hci_tx_kthread
|
|
*
|
|
* Description:
|
|
* This is a kernel thread that handles sending of commands.
|
|
*
|
|
* Input Parameters:
|
|
* Standard kernel thread arguments
|
|
*
|
|
* Returned Value:
|
|
* Doesn't normally return.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int hci_tx_kthread(int argc, FAR char *argv[])
|
|
{
|
|
FAR const struct bt_driver_s *btdev = g_btdev.btdev;
|
|
int ret;
|
|
|
|
wlinfo("started\n");
|
|
|
|
for (; ; )
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
|
|
/* Wait until ncmd > 0 */
|
|
|
|
nxsem_wait_uninterruptible(&g_btdev.ncmd_sem);
|
|
|
|
/* Get next command - wait if necessary */
|
|
|
|
buf = NULL;
|
|
ret = bt_queue_receive(g_btdev.tx_queue, &buf);
|
|
DEBUGASSERT(ret >= 0 && buf != NULL);
|
|
UNUSED(ret);
|
|
|
|
g_btdev.ncmd = 0;
|
|
|
|
wlinfo("Sending command %04x buf %p to driver\n",
|
|
buf->u.hci.opcode, buf);
|
|
|
|
btdev->send(btdev, buf);
|
|
|
|
/* Clear out any existing sent command */
|
|
|
|
if (g_btdev.sent_cmd)
|
|
{
|
|
wlerr("ERROR: Uncleared pending sent_cmd\n");
|
|
bt_buf_release(g_btdev.sent_cmd);
|
|
g_btdev.sent_cmd = NULL;
|
|
}
|
|
|
|
g_btdev.sent_cmd = buf;
|
|
}
|
|
|
|
return EXIT_SUCCESS; /* Can't get here */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hci_rx_work
|
|
*
|
|
* Description:
|
|
* This work function operates on the low priority work queue using the
|
|
* low priority buffer queue.
|
|
*
|
|
* Input Parameters:
|
|
* arg - Indicates which buffer queue should be used
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void hci_rx_work(FAR void *arg)
|
|
{
|
|
FAR struct bt_bufferlist_s *list = (FAR struct bt_bufferlist_s *)arg;
|
|
FAR struct bt_buf_s *buf;
|
|
|
|
wlinfo("list %p\n", list);
|
|
DEBUGASSERT(list != NULL);
|
|
|
|
while ((buf = bt_dequeue_bufwork(list)) != NULL)
|
|
{
|
|
wlinfo("buf %p type %u len %u\n", buf, buf->type, buf->len);
|
|
|
|
switch (buf->type)
|
|
{
|
|
case BT_ACL_IN:
|
|
hci_acl(buf);
|
|
break;
|
|
|
|
case BT_EVT:
|
|
hci_event(buf);
|
|
break;
|
|
|
|
default:
|
|
wlerr("ERROR: Unknown buf type %u\n", buf->type);
|
|
bt_buf_release(buf);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: priority_rx_work
|
|
*
|
|
* Description:
|
|
* This work function operates on the high priority work thread using the
|
|
* high priority buffer queue.
|
|
*
|
|
* Input Parameters:
|
|
* arg - Indicates which buffer queue should be used
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void priority_rx_work(FAR void *arg)
|
|
{
|
|
FAR struct bt_bufferlist_s *list = (FAR struct bt_bufferlist_s *)arg;
|
|
FAR struct bt_buf_s *buf;
|
|
|
|
wlinfo("list %p\n", list);
|
|
DEBUGASSERT(list != NULL);
|
|
|
|
while ((buf = bt_dequeue_bufwork(list)) != NULL)
|
|
{
|
|
FAR struct bt_hci_evt_hdr_s *hdr = (FAR void *)buf->data;
|
|
|
|
wlinfo("buf %p type %u len %u\n", buf, buf->type, buf->len);
|
|
|
|
if (buf->type != BT_EVT)
|
|
{
|
|
wlerr("Unknown buf type %u\n", buf->type);
|
|
bt_buf_release(buf);
|
|
continue;
|
|
}
|
|
|
|
bt_buf_consume(buf, sizeof(struct bt_hci_evt_hdr_s));
|
|
|
|
switch (hdr->evt)
|
|
{
|
|
case BT_HCI_EVT_CMD_COMPLETE:
|
|
hci_cmd_complete(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_CMD_STATUS:
|
|
hci_cmd_status(buf);
|
|
break;
|
|
|
|
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
|
|
hci_num_completed_packets(buf);
|
|
break;
|
|
|
|
default:
|
|
wlerr("Unknown event 0x%02x\n", hdr->evt);
|
|
break;
|
|
}
|
|
|
|
bt_buf_release(buf);
|
|
}
|
|
}
|
|
|
|
static void read_local_features_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_read_local_features_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
memcpy(g_btdev.features, rp->features, sizeof(g_btdev.features));
|
|
}
|
|
|
|
static void read_local_ver_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_read_local_version_info_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
g_btdev.hci_version = rp->hci_version;
|
|
g_btdev.hci_revision = BT_LE162HOST(rp->hci_revision);
|
|
g_btdev.manufacturer = BT_LE162HOST(rp->manufacturer);
|
|
}
|
|
|
|
static void read_bdaddr_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_read_bd_addr_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
bt_addr_copy(&g_btdev.bdaddr, &rp->bdaddr);
|
|
}
|
|
|
|
static void read_le_features_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_le_read_local_features_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
memcpy(g_btdev.le_features, rp->features, sizeof(g_btdev.le_features));
|
|
}
|
|
|
|
static void read_buffer_size_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_read_buffer_size_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
/* If LE-side has buffers we can ignore the BR/EDR values */
|
|
|
|
if (g_btdev.le_mtu)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_btdev.le_mtu = BT_LE162HOST(rp->acl_max_len);
|
|
g_btdev.le_pkts = BT_LE162HOST(rp->acl_max_num);
|
|
}
|
|
|
|
static void le_read_buffer_size_complete(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_rp_le_read_buffer_size_s *rp = (FAR void *)buf->data;
|
|
|
|
wlinfo("status %u\n", rp->status);
|
|
|
|
g_btdev.le_mtu = BT_LE162HOST(rp->le_max_len);
|
|
g_btdev.le_pkts = rp->le_max_num;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: hci_initialize()
|
|
*
|
|
* Description:
|
|
*
|
|
* Input Parameters:
|
|
* none
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int hci_initialize(void)
|
|
{
|
|
FAR struct bt_hci_cp_host_buffer_size_s *hbs;
|
|
FAR struct bt_hci_cp_set_event_mask_s *ev;
|
|
FAR struct bt_buf_s *buf;
|
|
FAR struct bt_buf_s *rsp;
|
|
FAR uint8_t *enable;
|
|
int ret;
|
|
|
|
/* Send HCI_RESET */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: BT_HCI_OP_RESET failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
bt_buf_release(rsp);
|
|
|
|
/* Read Local Supported Features */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_FEATURES, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: BT_HCI_OP_READ_LOCAL_FEATURES failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
read_local_features_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
|
|
/* Read Local Version Information */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_LOCAL_VERSION_INFO, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
read_local_ver_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
|
|
/* Read Bluetooth Address */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BD_ADDR, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
read_bdaddr_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
|
|
/* For now we only support LE capable controllers */
|
|
|
|
if (!lmp_le_capable(g_btdev))
|
|
{
|
|
wlerr("ERROR: Non-LE capable controller detected!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Read Low Energy Supported Features */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_LOCAL_FEATURES, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: BT_HCI_OP_LE_READ_LOCAL_FEATURES failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
read_le_features_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
|
|
/* Read LE Buffer Size */
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_BUFFER_SIZE, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: BT_HCI_OP_LE_READ_BUFFER_SIZE failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
le_read_buffer_size_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_EVENT_MASK, sizeof(*ev));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
ev = bt_buf_extend(buf, sizeof(*ev));
|
|
memset(ev, 0, sizeof(*ev));
|
|
|
|
ev->events[0] |= 0x10; /* Disconnection Complete */
|
|
ev->events[1] |= 0x08; /* Read Remote Version Information Complete */
|
|
ev->events[1] |= 0x20; /* Command Complete */
|
|
ev->events[1] |= 0x40; /* Command Status */
|
|
ev->events[1] |= 0x80; /* Hardware Error */
|
|
ev->events[2] |= 0x04; /* Number of Completed Packets */
|
|
ev->events[3] |= 0x02; /* Data Buffer Overflow */
|
|
ev->events[7] |= 0x20; /* LE Meta-Event */
|
|
|
|
if (g_btdev.le_features[0] & BT_HCI_LE_ENCRYPTION)
|
|
{
|
|
ev->events[0] |= 0x80; /* Encryption Change */
|
|
ev->events[5] |= 0x80; /* Encryption Key Refresh Complete */
|
|
}
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_SET_EVENT_MASK, buf, NULL);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_HOST_BUFFER_SIZE, sizeof(*hbs));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
hbs = bt_buf_extend(buf, sizeof(*hbs));
|
|
memset(hbs, 0, sizeof(*hbs));
|
|
hbs->acl_mtu = BT_HOST2LE16(BLUETOOTH_MAX_FRAMELEN -
|
|
sizeof(struct bt_hci_acl_hdr_s) -
|
|
g_btdev.btdev->head_reserve);
|
|
hbs->acl_pkts = BT_HOST2LE16(CONFIG_BLUETOOTH_BUFFER_PREALLOC);
|
|
|
|
ret = bt_hci_cmd_send(BT_HCI_OP_HOST_BUFFER_SIZE, buf);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, 1);
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
enable = bt_buf_extend(buf, sizeof(*enable));
|
|
*enable = 0x01;
|
|
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_SET_CTL_TO_HOST_FLOW, buf, NULL);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (lmp_bredr_capable(g_btdev))
|
|
{
|
|
FAR struct bt_hci_cp_write_le_host_supp_s *cp;
|
|
|
|
/* Use BR/EDR buffer size if LE reports zero buffers */
|
|
|
|
if (!g_btdev.le_mtu)
|
|
{
|
|
ret = bt_hci_cmd_send_sync(BT_HCI_OP_READ_BUFFER_SIZE, NULL, &rsp);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_hci_cmd_send_sync failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
read_buffer_size_complete(rsp);
|
|
bt_buf_release(rsp);
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, sizeof(*cp));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
/* Explicitly enable LE for dual-mode controllers */
|
|
|
|
cp = bt_buf_extend(buf, sizeof *cp);
|
|
cp->le = 0x01;
|
|
cp->simul = 0x00;
|
|
|
|
bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf, NULL);
|
|
}
|
|
|
|
wlinfo("HCI ver %u rev %u, manufacturer %u\n", g_btdev.hci_version,
|
|
g_btdev.hci_revision, g_btdev.manufacturer);
|
|
wlinfo("ACL buffers: pkts %u mtu %u\n", g_btdev.le_pkts, g_btdev.le_mtu);
|
|
|
|
/* Initialize & prime the semaphore for counting controller-side available
|
|
* ACL packet buffers.
|
|
*/
|
|
|
|
nxsem_init(&g_btdev.le_pkts_sem, 0, g_btdev.le_pkts);
|
|
return 0;
|
|
}
|
|
|
|
/* threads, fifos and semaphores initialization */
|
|
|
|
static void cmd_queue_init(void)
|
|
{
|
|
pid_t pid;
|
|
int ret;
|
|
|
|
/* When there is a command to be sent to the Bluetooth driver, it queued on
|
|
* the Tx queue and received by logic on the Tx kernel thread.
|
|
*/
|
|
|
|
g_btdev.tx_queue = NULL;
|
|
ret = bt_queue_open(BT_HCI_TX, O_RDWR | O_CREAT,
|
|
CONFIG_BLUETOOTH_TXCMD_NMSGS, &g_btdev.tx_queue);
|
|
DEBUGASSERT(ret >= 0 && g_btdev.tx_queue != NULL);
|
|
UNUSED(ret);
|
|
|
|
nxsem_init(&g_btdev.ncmd_sem, 0, 1);
|
|
nxsem_setprotocol(&g_btdev.ncmd_sem, SEM_PRIO_NONE);
|
|
|
|
g_btdev.ncmd = 1;
|
|
pid = kthread_create("BT HCI Tx", CONFIG_BLUETOOTH_TXCMD_PRIORITY,
|
|
CONFIG_BLUETOOTH_TXCMD_STACKSIZE,
|
|
hci_tx_kthread, NULL);
|
|
DEBUGASSERT(pid > 0);
|
|
UNUSED(pid);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: bt_initialize
|
|
*
|
|
* Description:
|
|
* Initialize Bluetooth. Must be the called before anything else.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or (negative) error code otherwise.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_initialize(void)
|
|
{
|
|
FAR const struct bt_driver_s *btdev = g_btdev.btdev;
|
|
int ret;
|
|
|
|
wlinfo("btdev %p\n", btdev);
|
|
|
|
DEBUGASSERT(btdev != NULL);
|
|
bt_buf_initialize();
|
|
|
|
cmd_queue_init();
|
|
|
|
ret = btdev->open(btdev);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: HCI driver open failed (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = hci_initialize();
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: hci_initialize failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return bt_l2cap_init();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_driver_register
|
|
*
|
|
* Description:
|
|
* Register the Bluetooth low-level driver with the Bluetooth stack.
|
|
* This is called from the low-level driver and is part of the driver
|
|
* interface prototyped in include/nuttx/wireless/bluetooth/bt_driver.h
|
|
*
|
|
* This function associates the Bluetooth driver with the Bluetooth stack.
|
|
*
|
|
* Input Parameters:
|
|
* btdev - An instance of the low-level drivers interface structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero is returned on success; a negated errno value is returned on any
|
|
* failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_driver_register(FAR const struct bt_driver_s *btdev)
|
|
{
|
|
DEBUGASSERT(btdev != NULL && btdev->open != NULL && btdev->send != NULL);
|
|
|
|
if (g_btdev.btdev != NULL)
|
|
{
|
|
wlwarn("WARNING: Already registered\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
g_btdev.btdev = btdev;
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_driver_unregister
|
|
*
|
|
* Description:
|
|
* Unregister a Bluetooth low-level driver previously registered with
|
|
* bt_driver_register. This may be called from the low-level driver and
|
|
* is part of the driver interface prototyped in
|
|
* include/nuttx/wireless/bluetooth/bt_driver.h
|
|
*
|
|
* Input Parameters:
|
|
* btdev - An instance of the low-level drivers interface structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void bt_driver_unregister(FAR const struct bt_driver_s *btdev)
|
|
{
|
|
g_btdev.btdev = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_hci_receive
|
|
*
|
|
* Description:
|
|
* Called by the Bluetooth low-level driver when new data is received from
|
|
* the radio. This may be called from the low-level driver and is part of
|
|
* the driver interface prototyped in include/nuttx/wireless/bluetooth/bt_driver.h
|
|
*
|
|
* NOTE: This function will defer all real work to the low or to the high
|
|
* priority work queues. Therefore, this function may safely be called
|
|
* from interrupt handling logic.
|
|
*
|
|
* Input Parameters:
|
|
* buf - An instance of the buffer structure providing the received frame.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
void bt_hci_receive(FAR struct bt_buf_s *buf)
|
|
{
|
|
FAR struct bt_hci_evt_hdr_s *hdr;
|
|
int ret;
|
|
|
|
wlinfo("buf %p len %u\n", buf, buf->len);
|
|
|
|
/* Critical command complete/status events use the high priority work
|
|
* queue.
|
|
*/
|
|
|
|
if (buf->type != BT_ACL_IN)
|
|
{
|
|
if (buf->type != BT_EVT)
|
|
{
|
|
wlerr("ERROR: Invalid buf type %u\n", buf->type);
|
|
bt_buf_release(buf);
|
|
return;
|
|
}
|
|
|
|
/* Command Complete/Status events use high priority messages. */
|
|
|
|
hdr = (FAR void *)buf->data;
|
|
if (hdr->evt == BT_HCI_EVT_CMD_COMPLETE ||
|
|
hdr->evt == BT_HCI_EVT_CMD_STATUS ||
|
|
hdr->evt == BT_HCI_EVT_NUM_COMPLETED_PACKETS)
|
|
{
|
|
/* Add the buffer to the high priority Rx buffer list */
|
|
|
|
bt_enqueue_bufwork(&g_hp_rxlist, buf);
|
|
|
|
/* If there is already pending work, then do nothing. Otherwise,
|
|
* schedule processing of the Rx buffer list on the high priority
|
|
* work queue.
|
|
*/
|
|
|
|
if (work_available(&g_hp_work))
|
|
{
|
|
ret = work_queue(HPWORK, &g_hp_work, priority_rx_work,
|
|
&g_hp_rxlist, 0);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: Failed to schedule HPWORK: %d\n", ret);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* All others use the low priority work queue */
|
|
|
|
/* Add the buffer to the low priority Rx buffer list */
|
|
|
|
bt_enqueue_bufwork(&g_lp_rxlist, buf);
|
|
|
|
/* If there is already pending work, then do nothing. Otherwise, schedule
|
|
* processing of the Rx buffer list on the low priority work queue.
|
|
*/
|
|
|
|
if (work_available(&g_lp_work))
|
|
{
|
|
ret = work_queue(LPWORK, &g_lp_work, hci_rx_work, &g_lp_rxlist, 0);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: Failed to schedule LPWORK: %d\n", ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_hci_cmd_create
|
|
*
|
|
* Description:
|
|
* Allocate and initialize a buffer for a command
|
|
*
|
|
* Returned Value:
|
|
* A reference to the allocated buffer. NULL could possibly be returned
|
|
* on any failure to allocate.
|
|
*
|
|
****************************************************************************/
|
|
|
|
FAR struct bt_buf_s *bt_hci_cmd_create(uint16_t opcode, uint8_t param_len)
|
|
{
|
|
FAR struct bt_hci_cmd_hdr_s *hdr;
|
|
FAR struct bt_buf_s *buf;
|
|
|
|
wlinfo("opcode %04x param_len %u\n", opcode, param_len);
|
|
|
|
buf = bt_buf_alloc(BT_CMD, NULL, g_btdev.btdev->head_reserve);
|
|
if (!buf)
|
|
{
|
|
wlerr("ERROR: Cannot get free buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
wlinfo("buf %p\n", buf);
|
|
|
|
buf->u.hci.opcode = opcode;
|
|
buf->u.hci.sync = NULL;
|
|
|
|
hdr = bt_buf_extend(buf, sizeof(*hdr));
|
|
hdr->opcode = BT_HOST2LE16(opcode);
|
|
hdr->param_len = param_len;
|
|
|
|
return buf;
|
|
}
|
|
|
|
int bt_hci_cmd_send(uint16_t opcode, FAR struct bt_buf_s *buf)
|
|
{
|
|
int ret;
|
|
|
|
if (buf == NULL)
|
|
{
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
wlinfo("opcode %04x len %u\n", opcode, buf->len);
|
|
|
|
/* Host Number of Completed Packets can ignore the ncmd value and does not
|
|
* generate any cmd complete/status events.
|
|
*/
|
|
|
|
if (opcode == BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS)
|
|
{
|
|
g_btdev.btdev->send(g_btdev.btdev, buf);
|
|
bt_buf_release(buf);
|
|
return 0;
|
|
}
|
|
|
|
ret = bt_queue_send(g_btdev.tx_queue, buf, BT_NORMAL_PRIO);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_queue_send() failed: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bt_hci_cmd_send_sync(uint16_t opcode, FAR struct bt_buf_s *buf,
|
|
FAR struct bt_buf_s **rsp)
|
|
{
|
|
sem_t sync_sem;
|
|
int ret;
|
|
|
|
/* NOTE: This function cannot be called from the rx thread since it relies
|
|
* on the very same thread in processing the cmd_complete event and giving
|
|
* back the blocking semaphore.
|
|
*/
|
|
|
|
if (buf == NULL)
|
|
{
|
|
buf = bt_hci_cmd_create(opcode, 0);
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
}
|
|
|
|
wlinfo("opcode %04x len %u\n", opcode, buf->len);
|
|
|
|
/* Set up for the wait */
|
|
|
|
nxsem_init(&sync_sem, 0, 0);
|
|
nxsem_setprotocol(&sync_sem, SEM_PRIO_NONE);
|
|
buf->u.hci.sync = &sync_sem;
|
|
|
|
/* Send the frame */
|
|
|
|
ret = bt_queue_send(g_btdev.tx_queue, buf, BT_NORMAL_PRIO);
|
|
if (ret < 0)
|
|
{
|
|
wlerr("ERROR: bt_queue_send() failed: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
struct timespec abstime;
|
|
|
|
/* Wait for the response to the command. An I/O error will be
|
|
* declared if the response does not occur within the timeout
|
|
* interval.
|
|
*
|
|
* REVISIT: The cause of the timeout could be a failure to receive a
|
|
* response to a sent frame or, perhaps, a failure to send the frame.
|
|
* Should there also be logic to flush any unsent Tx packets?
|
|
*
|
|
* Get the current time. Not that we lock the scheduler here so that
|
|
* we can be assured that there will be no context switches will occur
|
|
* between the time that we calculate the delay time and until we get
|
|
* to the wait.
|
|
*/
|
|
|
|
sched_lock();
|
|
ret = clock_gettime(CLOCK_REALTIME, &abstime);
|
|
if (ret >= 0)
|
|
{
|
|
/* Add the offset to the time in the future */
|
|
|
|
abstime.tv_sec += TIMEOUT_SEC;
|
|
abstime.tv_nsec += TIMEOUT_NSEC;
|
|
|
|
/* Handle carry from nanoseconds to seconds */
|
|
|
|
if (abstime.tv_nsec >= NSEC_PER_SEC)
|
|
{
|
|
abstime.tv_nsec -= NSEC_PER_SEC;
|
|
abstime.tv_sec++;
|
|
}
|
|
|
|
/* Now wait for the response. The scheduler lock will be
|
|
* released while we are waiting.
|
|
*/
|
|
|
|
nxsem_timedwait_uninterruptible(&sync_sem, &abstime);
|
|
}
|
|
|
|
sched_unlock();
|
|
}
|
|
|
|
/* Indicate failure if we failed to get the response */
|
|
|
|
if (ret >= 0)
|
|
{
|
|
if (buf->u.hci.sync == NULL)
|
|
{
|
|
wlerr("ERROR: Failed get return parameters\n");
|
|
ret = -EIO;
|
|
}
|
|
else
|
|
{
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (rsp != NULL)
|
|
{
|
|
*rsp = buf->u.hci.sync;
|
|
}
|
|
else if (buf->u.hci.sync != NULL)
|
|
{
|
|
bt_buf_release(buf->u.hci.sync);
|
|
}
|
|
|
|
bt_buf_release(buf);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_start_advertising
|
|
*
|
|
* Description:
|
|
* Set advertisement data, scan response data, advertisement parameters
|
|
* and start advertising.
|
|
*
|
|
* Input Parameters:
|
|
* type - Advertising type.
|
|
* ad - Data to be used in advertisement packets.
|
|
* sd - Data to be used in scan response packets.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or (negative) error code otherwise.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_start_advertising(uint8_t type, FAR const struct bt_eir_s *ad,
|
|
FAR const struct bt_eir_s *sd)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
FAR struct bt_hci_cp_le_set_adv_data_s *set_data;
|
|
FAR struct bt_hci_cp_le_set_adv_data_s *scan_rsp;
|
|
FAR struct bt_hci_cp_le_set_adv_parameters_s *set_param;
|
|
int i;
|
|
|
|
if (ad == NULL)
|
|
{
|
|
goto send_scan_rsp;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_DATA, sizeof(*set_data));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_data = bt_buf_extend(buf, sizeof(*set_data));
|
|
|
|
memset(set_data, 0, sizeof(*set_data));
|
|
|
|
for (i = 0; ad[i].len > 0; i++)
|
|
{
|
|
/* Check if ad fit in the remaining buffer */
|
|
|
|
if (set_data->len + ad[i].len + 1 > 29)
|
|
{
|
|
break;
|
|
}
|
|
|
|
memcpy(&set_data->data[set_data->len], &ad[i], ad[i].len + 1);
|
|
set_data->len += ad[i].len + 1;
|
|
}
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_DATA, buf);
|
|
|
|
send_scan_rsp:
|
|
if (sd == NULL)
|
|
{
|
|
goto send_set_param;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_RSP_DATA,
|
|
sizeof(*scan_rsp));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
scan_rsp = bt_buf_extend(buf, sizeof(*scan_rsp));
|
|
|
|
memset(scan_rsp, 0, sizeof(*scan_rsp));
|
|
|
|
for (i = 0; sd[i].len > 0; i++)
|
|
{
|
|
/* Check if ad fit in the remaining buffer */
|
|
|
|
if (scan_rsp->len + sd[i].len + 1 > 29)
|
|
{
|
|
break;
|
|
}
|
|
|
|
memcpy(&scan_rsp->data[scan_rsp->len], &sd[i], sd[i].len + 1);
|
|
scan_rsp->len += sd[i].len + 1;
|
|
}
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, buf);
|
|
|
|
send_set_param:
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAMETERS,
|
|
sizeof(*set_param));
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
set_param = bt_buf_extend(buf, sizeof(*set_param));
|
|
|
|
memset(set_param, 0, sizeof(*set_param));
|
|
set_param->min_interval = BT_HOST2LE16(0x0800);
|
|
set_param->max_interval = BT_HOST2LE16(0x0800);
|
|
set_param->type = type;
|
|
set_param->channel_map = 0x07;
|
|
|
|
bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_PARAMETERS, buf);
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
g_btdev.adv_enable = 0x01;
|
|
memcpy(bt_buf_extend(buf, 1), &g_btdev.adv_enable, 1);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_stop_advertising
|
|
*
|
|
* Description:
|
|
* Stops ongoing advertising.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or (negative) error code otherwise.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_stop_advertising(void)
|
|
{
|
|
FAR struct bt_buf_s *buf;
|
|
|
|
if (!g_btdev.adv_enable)
|
|
{
|
|
wlwarn("WARNING: Already advertising\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
|
|
if (buf == NULL)
|
|
{
|
|
wlerr("ERROR: Failed to create buffer\n");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
g_btdev.adv_enable = 0x00;
|
|
memcpy(bt_buf_extend(buf, 1), &g_btdev.adv_enable, 1);
|
|
|
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_start_scanning
|
|
*
|
|
* Description:
|
|
* Start LE scanning with and provide results through the specified
|
|
* callback.
|
|
*
|
|
* Input Parameters:
|
|
* filter_dups - Enable duplicate filtering (or not).
|
|
* cb - Callback to notify scan results.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or error code otherwise, positive in case
|
|
* of protocol error or negative (POSIX) in case of stack internal error
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_start_scanning(uint8_t scan_filter, bt_le_scan_cb_t cb)
|
|
{
|
|
/* Return if active scan is already enabled */
|
|
|
|
if (g_scan_dev_found_cb)
|
|
{
|
|
wlwarn("WARNING: Already scanning\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
g_scan_dev_found_cb = cb;
|
|
g_btdev.scan_filter = scan_filter;
|
|
|
|
return bt_le_scan_update();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_stop_scanning
|
|
*
|
|
* Description:
|
|
* Stops ongoing LE scanning.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or error code otherwise, positive in case
|
|
* of protocol error or negative (POSIX) in case of stack internal error
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_stop_scanning(void)
|
|
{
|
|
/* Return if active scanning is already disabled */
|
|
|
|
if (g_scan_dev_found_cb == NULL)
|
|
{
|
|
wlwarn("WARNING: Not scanning\n");
|
|
return -EALREADY;
|
|
}
|
|
|
|
g_scan_dev_found_cb = NULL;
|
|
g_btdev.scan_filter = BT_LE_SCAN_FILTER_DUP_ENABLE;
|
|
|
|
return bt_le_scan_update();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_le_scan_update
|
|
*
|
|
* Description:
|
|
* Used to determine whether to start scan and which scan type should be
|
|
* used.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success or error code otherwise, positive in case
|
|
* of protocol error or negative (POSIX) in case of stack internal error
|
|
*
|
|
****************************************************************************/
|
|
|
|
int bt_le_scan_update(void)
|
|
{
|
|
FAR struct bt_conn_s *conn;
|
|
int ret;
|
|
|
|
if (g_btdev.scan_enable)
|
|
{
|
|
if (g_scan_dev_found_cb)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ret = bt_hci_stop_scanning();
|
|
if (ret)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (g_scan_dev_found_cb)
|
|
{
|
|
return bt_hci_start_scanning(BT_LE_SCAN_ACTIVE, g_btdev.scan_filter);
|
|
}
|
|
|
|
conn = bt_conn_lookup_state(BT_ADDR_LE_ANY, BT_CONN_CONNECT_SCAN);
|
|
if (!conn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bt_conn_release(conn);
|
|
return bt_hci_start_scanning(BT_LE_SCAN_PASSIVE, g_btdev.scan_filter);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: bt_conn_cb_register
|
|
*
|
|
* Description:
|
|
* Register callbacks to monitor the state of connections.
|
|
*
|
|
* Input Parameters:
|
|
* cb - Instance of the callback structure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void bt_conn_cb_register(FAR struct bt_conn_cb_s *cb)
|
|
{
|
|
cb->flink = g_callback_list;
|
|
g_callback_list = cb;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_WIRELESS_ERROR
|
|
FAR const char *bt_addr_str(FAR const bt_addr_t *addr)
|
|
{
|
|
static char bufs[2][18];
|
|
static uint8_t cur;
|
|
FAR char *str;
|
|
|
|
str = bufs[cur++];
|
|
cur %= ARRAY_SIZE(bufs);
|
|
bt_addr_to_str(addr, str, sizeof(bufs[cur]));
|
|
|
|
return str;
|
|
}
|
|
|
|
FAR const char *bt_addr_le_str(FAR const bt_addr_le_t *addr)
|
|
{
|
|
static char bufs[2][27];
|
|
static uint8_t cur;
|
|
FAR char *str;
|
|
|
|
str = bufs[cur++];
|
|
cur %= ARRAY_SIZE(bufs);
|
|
bt_addr_le_to_str(addr, str, sizeof(bufs[cur]));
|
|
|
|
return str;
|
|
}
|
|
#endif /* CONFIG_DEBUG_WIRELESS_ERROR */
|