| /* |
| * 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 "syscfg/syscfg.h" |
| #include "stats/stats.h" |
| #include "host/ble_hs.h" |
| #include "host/ble_audio_broadcast_source.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) + \ |
| MYNEWT_VAL(BLE_TRANSPORT_EVT_DISCARDABLE_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; |
| |
| 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) |
| { |
| #ifdef 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 |
| |
| 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_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); |
| |
| #endif |
| rc = ble_gap_init(); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| #if NIMBLE_BLE_CONNECT |
| |
| 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 |
| |
| #if MYNEWT_VAL(BLE_ISO) |
| rc = ble_iso_init(); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| #if MYNEWT_VAL(BLE_ISO_BROADCAST_SOURCE) |
| rc = ble_audio_broadcast_init(); |
| SYSINIT_PANIC_ASSERT(rc == 0); |
| #endif |
| #endif |
| |
| 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); |
| } |
| |
| int |
| ble_transport_to_hs_iso_impl(struct os_mbuf *om) |
| { |
| os_mbuf_free_chain(om); |
| |
| return 0; |
| } |
| |
| void |
| ble_transport_hs_init(void) |
| { |
| ble_hs_init(); |
| } |