/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you 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
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* 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.
|
*/
|
|
#include <assert.h>
|
#include <errno.h>
|
#include <string.h>
|
#include "sysinit/sysinit.h"
|
#include "nimble_syscfg.h"
|
#include "stats/stats.h"
|
#include "host/ble_hs.h"
|
#include "ble_hs_priv.h"
|
#include "nimble/nimble_npl.h"
|
#ifndef MYNEWT
|
#include "nimble/nimble_port.h"
|
#endif
|
|
#define BLE_HS_HCI_EVT_COUNT MYNEWT_VAL(BLE_TRANSPORT_EVT_COUNT)
|
|
static void ble_hs_event_rx_hci_ev(struct ble_npl_event *ev);
|
#if NIMBLE_BLE_CONNECT
|
static void ble_hs_event_tx_notify(struct ble_npl_event *ev);
|
#endif
|
static void ble_hs_event_reset(struct ble_npl_event *ev);
|
static void ble_hs_event_start_stage1(struct ble_npl_event *ev);
|
static void ble_hs_event_start_stage2(struct ble_npl_event *ev);
|
static void ble_hs_timer_sched(int32_t ticks_from_now);
|
|
struct os_mempool ble_hs_hci_ev_pool;
|
static os_membuf_t ble_hs_hci_os_event_buf[
|
OS_MEMPOOL_SIZE(BLE_HS_HCI_EVT_COUNT, sizeof (struct ble_npl_event))
|
];
|
|
/** OS event - triggers tx of pending notifications and indications. */
|
static struct ble_npl_event ble_hs_ev_tx_notifications;
|
|
/** OS event - triggers a full reset. */
|
static struct ble_npl_event ble_hs_ev_reset;
|
|
static struct ble_npl_event ble_hs_ev_start_stage1;
|
static struct ble_npl_event ble_hs_ev_start_stage2;
|
|
static struct ble_npl_event ble_hs_ev_user;
|
|
uint8_t ble_hs_sync_state;
|
uint8_t ble_hs_enabled_state;
|
static int ble_hs_reset_reason;
|
|
#define BLE_HS_SYNC_RETRY_TIMEOUT_MS 100 /* ms */
|
|
static void *ble_hs_parent_task;
|
|
/**
|
* Handles unresponsive timeouts and periodic retries in case of resource
|
* shortage.
|
*/
|
static struct ble_npl_callout ble_hs_timer;
|
|
/* Shared queue that the host uses for work items. */
|
static struct ble_npl_eventq *ble_hs_evq;
|
|
static struct ble_mqueue ble_hs_rx_q;
|
|
static struct ble_npl_mutex ble_hs_mutex;
|
|
/** These values keep track of required ATT and GATT resources counts. They
|
* increase as services are added, and are read when the ATT server and GATT
|
* server are started.
|
*/
|
uint16_t ble_hs_max_attrs;
|
uint16_t ble_hs_max_services;
|
uint16_t ble_hs_max_client_configs;
|
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
static uint8_t ble_hs_dbg_mutex_locked;
|
#endif
|
|
STATS_SECT_DECL(ble_hs_stats) ble_hs_stats;
|
STATS_NAME_START(ble_hs_stats)
|
STATS_NAME(ble_hs_stats, conn_create)
|
STATS_NAME(ble_hs_stats, conn_delete)
|
STATS_NAME(ble_hs_stats, hci_cmd)
|
STATS_NAME(ble_hs_stats, hci_event)
|
STATS_NAME(ble_hs_stats, hci_invalid_ack)
|
STATS_NAME(ble_hs_stats, hci_unknown_event)
|
STATS_NAME(ble_hs_stats, hci_timeout)
|
STATS_NAME(ble_hs_stats, reset)
|
STATS_NAME(ble_hs_stats, sync)
|
STATS_NAME(ble_hs_stats, pvcy_add_entry)
|
STATS_NAME(ble_hs_stats, pvcy_add_entry_fail)
|
STATS_NAME_END(ble_hs_stats)
|
|
struct ble_npl_eventq *
|
ble_hs_evq_get(void)
|
{
|
return ble_hs_evq;
|
}
|
|
void
|
ble_hs_evq_set(struct ble_npl_eventq *evq)
|
{
|
ble_hs_evq = evq;
|
}
|
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
int
|
ble_hs_locked_by_cur_task(void)
|
{
|
#if MYNEWT
|
struct os_task *owner;
|
|
if (!ble_npl_os_started()) {
|
return ble_hs_dbg_mutex_locked;
|
}
|
|
owner = ble_hs_mutex.mu.mu_owner;
|
return owner != NULL && owner == os_sched_get_current_task();
|
#else
|
return 1;
|
#endif
|
}
|
#endif
|
|
/**
|
* Indicates whether the host's parent task is currently running.
|
*/
|
int
|
ble_hs_is_parent_task(void)
|
{
|
return !ble_npl_os_started() ||
|
ble_npl_get_current_task_id() == ble_hs_parent_task;
|
}
|
|
/**
|
* Locks the BLE host mutex. Nested locks allowed.
|
*/
|
void
|
ble_hs_lock_nested(void)
|
{
|
int rc;
|
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
if (!ble_npl_os_started()) {
|
ble_hs_dbg_mutex_locked = 1;
|
return;
|
}
|
#endif
|
|
rc = ble_npl_mutex_pend(&ble_hs_mutex, 0xffffffff);
|
BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
|
}
|
|
/**
|
* Unlocks the BLE host mutex. Nested locks allowed.
|
*/
|
void
|
ble_hs_unlock_nested(void)
|
{
|
int rc;
|
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
if (!ble_npl_os_started()) {
|
ble_hs_dbg_mutex_locked = 0;
|
return;
|
}
|
#endif
|
|
rc = ble_npl_mutex_release(&ble_hs_mutex);
|
BLE_HS_DBG_ASSERT_EVAL(rc == 0 || rc == OS_NOT_STARTED);
|
}
|
|
/**
|
* Locks the BLE host mutex. Nested locks not allowed.
|
*/
|
void
|
ble_hs_lock(void)
|
{
|
BLE_HS_DBG_ASSERT(!ble_hs_locked_by_cur_task());
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
if (!ble_npl_os_started()) {
|
BLE_HS_DBG_ASSERT(!ble_hs_dbg_mutex_locked);
|
}
|
#endif
|
|
ble_hs_lock_nested();
|
}
|
|
/**
|
* Unlocks the BLE host mutex. Nested locks not allowed.
|
*/
|
void
|
ble_hs_unlock(void)
|
{
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
if (!ble_npl_os_started()) {
|
BLE_HS_DBG_ASSERT(ble_hs_dbg_mutex_locked);
|
}
|
#endif
|
|
ble_hs_unlock_nested();
|
}
|
|
void
|
ble_hs_process_rx_data_queue(void)
|
{
|
struct os_mbuf *om;
|
|
while ((om = ble_mqueue_get(&ble_hs_rx_q)) != NULL) {
|
ble_hs_hci_evt_acl_process(om);
|
}
|
}
|
|
static int
|
ble_hs_wakeup_tx_conn(struct ble_hs_conn *conn)
|
{
|
struct os_mbuf_pkthdr *omp;
|
struct os_mbuf *om;
|
int rc;
|
|
while ((omp = STAILQ_FIRST(&conn->bhc_tx_q)) != NULL) {
|
STAILQ_REMOVE_HEAD(&conn->bhc_tx_q, omp_next);
|
|
om = OS_MBUF_PKTHDR_TO_MBUF(omp);
|
rc = ble_hs_hci_acl_tx_now(conn, &om);
|
if (rc == BLE_HS_EAGAIN) {
|
/* Controller is at capacity. This packet will be the first to
|
* get transmitted next time around.
|
*/
|
STAILQ_INSERT_HEAD(&conn->bhc_tx_q, OS_MBUF_PKTHDR(om), omp_next);
|
return BLE_HS_EAGAIN;
|
}
|
}
|
|
return 0;
|
}
|
|
/**
|
* Schedules the transmission of all queued ACL data packets to the controller.
|
*/
|
void
|
ble_hs_wakeup_tx(void)
|
{
|
struct ble_hs_conn *conn;
|
int rc;
|
|
ble_hs_lock();
|
|
/* If there is a connection with a partially transmitted packet, it has to
|
* be serviced first. The controller is waiting for the remainder so it
|
* can reassemble it.
|
*/
|
for (conn = ble_hs_conn_first();
|
conn != NULL;
|
conn = SLIST_NEXT(conn, bhc_next)) {
|
|
if (conn->bhc_flags & BLE_HS_CONN_F_TX_FRAG) {
|
rc = ble_hs_wakeup_tx_conn(conn);
|
if (rc != 0) {
|
goto done;
|
}
|
break;
|
}
|
}
|
|
/* For each connection, transmit queued packets until there are no more
|
* packets to send or the controller's buffers are exhausted.
|
*/
|
for (conn = ble_hs_conn_first();
|
conn != NULL;
|
conn = SLIST_NEXT(conn, bhc_next)) {
|
|
rc = ble_hs_wakeup_tx_conn(conn);
|
if (rc != 0) {
|
goto done;
|
}
|
}
|
|
done:
|
ble_hs_unlock();
|
}
|
|
static void
|
ble_hs_clear_rx_queue(void)
|
{
|
struct os_mbuf *om;
|
|
while ((om = ble_mqueue_get(&ble_hs_rx_q)) != NULL) {
|
os_mbuf_free_chain(om);
|
}
|
}
|
|
int
|
ble_hs_is_enabled(void)
|
{
|
return ble_hs_enabled_state == BLE_HS_ENABLED_STATE_ON;
|
}
|
|
int
|
ble_hs_synced(void)
|
{
|
return ble_hs_sync_state == BLE_HS_SYNC_STATE_GOOD;
|
}
|
|
static int
|
ble_hs_sync(void)
|
{
|
ble_npl_time_t retry_tmo_ticks;
|
int rc;
|
|
/* Set the sync state to "bringup." This allows the parent task to send
|
* the startup sequence to the controller. No other tasks are allowed to
|
* send any commands.
|
*/
|
ble_hs_sync_state = BLE_HS_SYNC_STATE_BRINGUP;
|
|
rc = ble_hs_startup_go();
|
if (rc == 0) {
|
ble_hs_sync_state = BLE_HS_SYNC_STATE_GOOD;
|
} else {
|
ble_hs_sync_state = BLE_HS_SYNC_STATE_BAD;
|
}
|
|
retry_tmo_ticks = ble_npl_time_ms_to_ticks32(BLE_HS_SYNC_RETRY_TIMEOUT_MS);
|
ble_hs_timer_sched(retry_tmo_ticks);
|
|
if (rc == 0) {
|
rc = ble_hs_misc_restore_irks();
|
if (rc != 0) {
|
BLE_HS_LOG(INFO, "Failed to restore IRKs from store; status=%d\n",
|
rc);
|
}
|
|
if (ble_hs_cfg.sync_cb != NULL) {
|
ble_hs_cfg.sync_cb();
|
}
|
|
STATS_INC(ble_hs_stats, sync);
|
}
|
|
return rc;
|
}
|
|
static int
|
ble_hs_reset(void)
|
{
|
int rc;
|
|
STATS_INC(ble_hs_stats, reset);
|
|
ble_hs_sync_state = 0;
|
|
ble_hs_clear_rx_queue();
|
|
/* Clear adverising and scanning states. */
|
ble_gap_reset_state(ble_hs_reset_reason);
|
|
/* Clear configured addresses. */
|
ble_hs_id_reset();
|
|
if (ble_hs_cfg.reset_cb != NULL && ble_hs_reset_reason != 0) {
|
ble_hs_cfg.reset_cb(ble_hs_reset_reason);
|
}
|
ble_hs_reset_reason = 0;
|
|
rc = ble_hs_sync();
|
return rc;
|
}
|
|
/**
|
* Called when the host timer expires. Handles unresponsive timeouts and
|
* periodic retries in case of resource shortage.
|
*/
|
static void
|
ble_hs_timer_exp(struct ble_npl_event *ev)
|
{
|
int32_t ticks_until_next;
|
|
switch (ble_hs_sync_state) {
|
case BLE_HS_SYNC_STATE_GOOD:
|
#if NIMBLE_BLE_CONNECT
|
ticks_until_next = ble_gattc_timer();
|
ble_hs_timer_sched(ticks_until_next);
|
|
ticks_until_next = ble_l2cap_sig_timer();
|
ble_hs_timer_sched(ticks_until_next);
|
|
ticks_until_next = ble_sm_timer();
|
ble_hs_timer_sched(ticks_until_next);
|
|
ticks_until_next = ble_hs_conn_timer();
|
ble_hs_timer_sched(ticks_until_next);
|
#endif
|
|
ticks_until_next = ble_gap_timer();
|
ble_hs_timer_sched(ticks_until_next);
|
|
break;
|
|
case BLE_HS_SYNC_STATE_BAD:
|
ble_hs_reset();
|
break;
|
|
case BLE_HS_SYNC_STATE_BRINGUP:
|
default:
|
/* The timer should not be set in this state. */
|
assert(0);
|
break;
|
}
|
|
}
|
|
static void
|
ble_hs_timer_reset(uint32_t ticks)
|
{
|
int rc;
|
|
if (!ble_hs_is_enabled()) {
|
ble_npl_callout_stop(&ble_hs_timer);
|
} else {
|
rc = ble_npl_callout_reset(&ble_hs_timer, ticks);
|
BLE_HS_DBG_ASSERT_EVAL(rc == 0);
|
}
|
}
|
|
static void
|
ble_hs_timer_sched(int32_t ticks_from_now)
|
{
|
ble_npl_time_t abs_time;
|
|
if (ticks_from_now == BLE_HS_FOREVER) {
|
return;
|
}
|
|
/* Reset timer if it is not currently scheduled or if the specified time is
|
* sooner than the previous expiration time.
|
*/
|
abs_time = ble_npl_time_get() + ticks_from_now;
|
if (!ble_npl_callout_is_active(&ble_hs_timer) ||
|
((ble_npl_stime_t)(abs_time -
|
ble_npl_callout_get_ticks(&ble_hs_timer))) < 0) {
|
ble_hs_timer_reset(ticks_from_now);
|
}
|
}
|
|
void
|
ble_hs_timer_resched(void)
|
{
|
/* Reschedule the timer to run immediately. The timer callback will query
|
* each module for an up-to-date expiration time.
|
*/
|
ble_hs_timer_reset(0);
|
}
|
|
static void
|
ble_hs_sched_start_stage2(void)
|
{
|
ble_npl_eventq_put((struct ble_npl_eventq *)ble_hs_evq_get(),
|
&ble_hs_ev_start_stage2);
|
}
|
|
void
|
ble_hs_sched_start(void)
|
{
|
#ifdef MYNEWT
|
ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
|
&ble_hs_ev_start_stage1);
|
#else
|
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &ble_hs_ev_start_stage1);
|
#endif
|
}
|
|
static void
|
ble_hs_event_rx_hci_ev(struct ble_npl_event *ev)
|
{
|
struct ble_hci_ev *hci_ev;
|
int rc;
|
|
hci_ev = ble_npl_event_get_arg(ev);
|
|
rc = os_memblock_put(&ble_hs_hci_ev_pool, ev);
|
BLE_HS_DBG_ASSERT_EVAL(rc == 0);
|
|
ble_hs_hci_evt_process(hci_ev);
|
}
|
|
#if NIMBLE_BLE_CONNECT
|
static void
|
ble_hs_event_tx_notify(struct ble_npl_event *ev)
|
{
|
ble_gatts_tx_notifications();
|
}
|
#endif
|
|
|
void __attribute__((weak)) ble_app_user_evt(struct ble_npl_event *ev)
|
{
|
return;
|
}
|
|
void ble_app_user_evt_post(void)
|
{
|
ble_npl_eventq_put(ble_hs_evq, &ble_hs_ev_user);
|
}
|
|
static void
|
ble_hs_event_rx_data(struct ble_npl_event *ev)
|
{
|
ble_hs_process_rx_data_queue();
|
}
|
|
static void
|
ble_hs_event_reset(struct ble_npl_event *ev)
|
{
|
ble_hs_reset();
|
}
|
|
/**
|
* Implements the first half of the start process. This just enqueues another
|
* event on the host parent task's event queue.
|
*
|
* Starting is done in two stages to allow the application time to configure
|
* the event queue to use after system initialization but before the host
|
* starts.
|
*/
|
static void
|
ble_hs_event_start_stage1(struct ble_npl_event *ev)
|
{
|
ble_hs_sched_start_stage2();
|
}
|
|
/**
|
* Implements the second half of the start process. This actually starts the
|
* host.
|
*
|
* Starting is done in two stages to allow the application time to configure
|
* the event queue to use after system initialization but before the host
|
* starts.
|
*/
|
static void
|
ble_hs_event_start_stage2(struct ble_npl_event *ev)
|
{
|
int rc;
|
|
rc = ble_hs_start();
|
assert(rc == 0);
|
}
|
|
void
|
ble_hs_enqueue_hci_event(uint8_t *hci_evt)
|
{
|
struct ble_npl_event *ev;
|
|
ev = os_memblock_get(&ble_hs_hci_ev_pool);
|
if (ev == NULL) {
|
ble_transport_free(hci_evt);
|
} else {
|
ble_npl_event_init(ev, ble_hs_event_rx_hci_ev, hci_evt);
|
ble_npl_eventq_put(ble_hs_evq, ev);
|
}
|
}
|
|
/**
|
* Schedules for all pending notifications and indications to be sent in the
|
* host parent task.
|
*/
|
void
|
ble_hs_notifications_sched(void)
|
{
|
#if !MYNEWT_VAL(BLE_HS_REQUIRE_OS)
|
if (!ble_npl_os_started()) {
|
ble_gatts_tx_notifications();
|
return;
|
}
|
#endif
|
|
ble_npl_eventq_put(ble_hs_evq, &ble_hs_ev_tx_notifications);
|
}
|
|
void
|
ble_hs_sched_reset(int reason)
|
{
|
BLE_HS_DBG_ASSERT(ble_hs_reset_reason == 0);
|
|
ble_hs_reset_reason = reason;
|
ble_npl_eventq_put(ble_hs_evq, &ble_hs_ev_reset);
|
}
|
|
void
|
ble_hs_hw_error(uint8_t hw_code)
|
{
|
ble_hs_sched_reset(BLE_HS_HW_ERR(hw_code));
|
}
|
|
int
|
ble_hs_start(void)
|
{
|
int rc;
|
|
ble_hs_lock();
|
switch (ble_hs_enabled_state) {
|
case BLE_HS_ENABLED_STATE_ON:
|
rc = BLE_HS_EALREADY;
|
break;
|
|
case BLE_HS_ENABLED_STATE_STOPPING:
|
rc = BLE_HS_EBUSY;
|
break;
|
|
case BLE_HS_ENABLED_STATE_OFF:
|
ble_hs_enabled_state = BLE_HS_ENABLED_STATE_ON;
|
rc = 0;
|
break;
|
|
default:
|
assert(0);
|
rc = BLE_HS_EUNKNOWN;
|
break;
|
}
|
ble_hs_unlock();
|
|
if (rc != 0) {
|
return rc;
|
}
|
|
ble_hs_parent_task = ble_npl_get_current_task_id();
|
|
#if MYNEWT_VAL(SELFTEST)
|
/* Stop the timer just in case the host was already running (e.g., unit
|
* tests).
|
*/
|
ble_npl_callout_stop(&ble_hs_timer);
|
#endif
|
|
ble_npl_callout_init(&ble_hs_timer, ble_hs_evq, ble_hs_timer_exp, NULL);
|
|
#if NIMBLE_BLE_CONNECT
|
rc = ble_gatts_start();
|
if (rc != 0) {
|
return rc;
|
}
|
#endif
|
ble_hs_sync();
|
|
return 0;
|
}
|
|
/**
|
* Called when a data packet is received from the controller. This function
|
* consumes the supplied mbuf, regardless of the outcome.
|
*
|
* @param om The incoming data packet, beginning with the
|
* HCI ACL data header.
|
*
|
* @return 0 on success; nonzero on failure.
|
*/
|
static int
|
ble_hs_rx_data(struct os_mbuf *om, void *arg)
|
{
|
int rc;
|
|
/* If flow control is enabled, mark this packet with its corresponding
|
* connection handle.
|
*/
|
ble_hs_flow_track_data_mbuf(om);
|
|
rc = ble_mqueue_put(&ble_hs_rx_q, ble_hs_evq, om);
|
if (rc != 0) {
|
os_mbuf_free_chain(om);
|
return BLE_HS_EOS;
|
}
|
|
return 0;
|
}
|
|
/**
|
* Enqueues an ACL data packet for transmission. This function consumes the
|
* supplied mbuf, regardless of the outcome.
|
*
|
* @param om The outgoing data packet, beginning with the
|
* HCI ACL data header.
|
*
|
* @return 0 on success; nonzero on failure.
|
*/
|
int
|
ble_hs_tx_data(struct os_mbuf *om)
|
{
|
return ble_transport_to_ll_acl(om);
|
}
|
|
void
|
ble_hs_init(void)
|
{
|
int rc;
|
|
/* Ensure this function only gets called by sysinit. */
|
SYSINIT_ASSERT_ACTIVE();
|
|
/* Create memory pool of OS events */
|
rc = os_mempool_init(&ble_hs_hci_ev_pool, BLE_HS_HCI_EVT_COUNT,
|
sizeof (struct ble_npl_event), ble_hs_hci_os_event_buf,
|
"ble_hs_hci_ev_pool");
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
/* These get initialized here to allow unit tests to run without a zeroed
|
* bss.
|
*/
|
ble_hs_reset_reason = 0;
|
ble_hs_enabled_state = BLE_HS_ENABLED_STATE_OFF;
|
|
#if NIMBLE_BLE_CONNECT
|
ble_npl_event_init(&ble_hs_ev_tx_notifications, ble_hs_event_tx_notify,
|
NULL);
|
#endif
|
ble_npl_event_init(&ble_hs_ev_reset, ble_hs_event_reset, NULL);
|
ble_npl_event_init(&ble_hs_ev_start_stage1, ble_hs_event_start_stage1,
|
NULL);
|
ble_npl_event_init(&ble_hs_ev_start_stage2, ble_hs_event_start_stage2,
|
NULL);
|
|
ble_npl_event_init(&ble_hs_ev_user, ble_app_user_evt, NULL);
|
|
ble_hs_hci_init();
|
|
rc = ble_hs_conn_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
#if MYNEWT_VAL(BLE_PERIODIC_ADV)
|
rc = ble_hs_periodic_sync_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
#endif
|
|
#if NIMBLE_BLE_CONNECT
|
rc = ble_l2cap_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
rc = ble_att_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
rc = ble_att_svr_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
rc = ble_gattc_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
rc = ble_gatts_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
#endif
|
rc = ble_gap_init();
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
ble_hs_stop_init();
|
|
ble_mqueue_init(&ble_hs_rx_q, ble_hs_event_rx_data, NULL);
|
|
rc = stats_init_and_reg(
|
STATS_HDR(ble_hs_stats), STATS_SIZE_INIT_PARMS(ble_hs_stats,
|
STATS_SIZE_32), STATS_NAME_INIT_PARMS(ble_hs_stats), "ble_hs");
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
rc = ble_npl_mutex_init(&ble_hs_mutex);
|
SYSINIT_PANIC_ASSERT(rc == 0);
|
|
#if MYNEWT_VAL(BLE_HS_DEBUG)
|
ble_hs_dbg_mutex_locked = 0;
|
#endif
|
|
#ifdef MYNEWT
|
ble_hs_evq_set((struct ble_npl_eventq *)os_eventq_dflt_get());
|
#else
|
ble_hs_evq_set(nimble_port_get_dflt_eventq());
|
#endif
|
|
/* Enqueue the start event to the default event queue. Using the default
|
* queue ensures the event won't run until the end of main(). This allows
|
* the application to configure this package in the meantime.
|
*/
|
#if MYNEWT_VAL(BLE_HS_AUTO_START)
|
#ifdef MYNEWT
|
ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
|
&ble_hs_ev_start_stage1);
|
#else
|
ble_npl_eventq_put(nimble_port_get_dflt_eventq(), &ble_hs_ev_start_stage1);
|
#endif
|
#endif
|
}
|
|
/* Transport APIs for HS side */
|
|
int
|
ble_transport_to_hs_evt_impl(void *buf)
|
{
|
return ble_hs_hci_rx_evt(buf, NULL);
|
}
|
|
int
|
ble_transport_to_hs_acl_impl(struct os_mbuf *om)
|
{
|
return ble_hs_rx_data(om, NULL);
|
}
|
|
void
|
ble_transport_hs_init(void)
|
{
|
ble_hs_init();
|
}
|