| /* |
| * 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 <stdarg.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "syscfg/syscfg.h" |
| #include "nimble/ble.h" |
| #include "nimble/hci_common.h" |
| #include "controller/ble_ll.h" |
| #include "controller/ble_ll_hci.h" |
| #include "controller/ble_ll_ctrl.h" |
| #include "ble_ll_conn_priv.h" |
| |
| #if (BLETEST_CONCURRENT_CONN_TEST == 1) |
| extern void bletest_ltk_req_reply(uint16_t handle); |
| #endif |
| |
| /** |
| * Send a data length change event for a connection to the host. |
| * |
| * @param connsm Pointer to connection state machine |
| */ |
| void |
| ble_ll_hci_ev_datalen_chg(struct ble_ll_conn_sm *connsm) |
| { |
| struct ble_hci_ev_le_subev_data_len_chg *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DATA_LEN_CHG)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_DATA_LEN_CHG; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| |
| ev->max_tx_octets = htole16(connsm->eff_max_tx_octets); |
| ev->max_tx_time = htole16(connsm->eff_max_tx_time); |
| ev->max_rx_octets = htole16(connsm->eff_max_rx_octets); |
| ev->max_rx_time = htole16(connsm->eff_max_rx_time); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| /** |
| * Send a connection parameter request event for a connection to the host. |
| * |
| * @param connsm Pointer to connection state machine |
| */ |
| void |
| ble_ll_hci_ev_rem_conn_parm_req(struct ble_ll_conn_sm *connsm, |
| struct ble_ll_conn_params *cp) |
| { |
| struct ble_hci_ev_le_subev_rem_conn_param_req *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->min_interval = htole16(cp->interval_min); |
| ev->max_interval = htole16(cp->interval_max); |
| ev->latency = htole16(cp->latency); |
| ev->timeout = htole16(cp->timeout); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| /** |
| * Send a connection update event. |
| * |
| * @param connsm Pointer to connection state machine |
| * @param status The error code. |
| */ |
| void |
| ble_ll_hci_ev_conn_update(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_le_subev_conn_upd_complete *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE; |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->conn_itvl = htole16(connsm->conn_itvl); |
| ev->conn_latency = htole16(connsm->periph_latency); |
| ev->supervision_timeout = htole16(connsm->supervision_tmo); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) |
| void |
| ble_ll_hci_ev_encrypt_chg(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_enc_key_refresh *ev_key_refresh; |
| struct ble_hci_ev_enrypt_chg *ev_enc_chf; |
| struct ble_hci_ev *hci_ev; |
| |
| if (connsm->flags.encrypt_event_sent == 0) { |
| if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_ENCRYPT_CHG)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_ENCRYPT_CHG; |
| hci_ev->length = sizeof(*ev_enc_chf); |
| ev_enc_chf = (void *) hci_ev->data; |
| |
| ev_enc_chf->status = status; |
| ev_enc_chf->connection_handle = htole16(connsm->conn_handle); |
| ev_enc_chf->enabled = (status == BLE_ERR_SUCCESS) ? 0x01 : 0x00; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| |
| connsm->flags.encrypt_event_sent = 1; |
| return; |
| } |
| |
| if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_ENC_KEY_REFRESH)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_ENC_KEY_REFRESH; |
| hci_ev->length = sizeof(*ev_key_refresh); |
| ev_key_refresh = (void *) hci_ev->data; |
| |
| ev_key_refresh->status = status; |
| ev_key_refresh->conn_handle = htole16(connsm->conn_handle); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| /** |
| * Send a long term key request event for a connection to the host. |
| * |
| * @param connsm Pointer to connection state machine |
| */ |
| int |
| ble_ll_hci_ev_ltk_req(struct ble_ll_conn_sm *connsm) |
| { |
| struct ble_hci_ev_le_subev_lt_key_req *ev; |
| struct ble_hci_ev *hci_ev; |
| int rc; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_LT_KEY_REQ)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_LT_KEY_REQ; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->rand = htole64(connsm->enc_data.host_rand_num); |
| ev->div = htole16(connsm->enc_data.enc_div); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| rc = 0; |
| } else { |
| rc = -1; |
| } |
| |
| #if (BLETEST_CONCURRENT_CONN_TEST == 1) |
| if (rc == 0) { |
| bletest_ltk_req_reply(connsm->conn_handle); |
| } |
| #endif |
| return rc; |
| } |
| #endif |
| |
| void |
| ble_ll_hci_ev_rd_rem_used_feat(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_le_subev_rd_rem_used_feat *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT; |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->features[0] = connsm->conn_features; |
| memcpy(ev->features + 1, connsm->remote_features, 7); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| void |
| ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_rd_rem_ver_info_cmp *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->version = connsm->vers_nr; |
| ev->manufacturer = htole16(connsm->comp_id); |
| ev->subversion = htole16(connsm->sub_vers_nr); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| /** |
| * Send a HW error to the host. |
| * |
| * @param hw_err |
| * |
| * @return int 0: event masked or event sent, -1 otherwise |
| */ |
| int |
| ble_ll_hci_ev_hw_err(uint8_t hw_err) |
| { |
| struct ble_hci_ev_hw_error *ev; |
| struct ble_hci_ev *hci_ev; |
| int rc; |
| |
| rc = 0; |
| if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_HW_ERROR)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_HW_ERROR; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->hw_code = hw_err; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } else { |
| rc = -1; |
| } |
| } |
| return rc; |
| } |
| |
| void |
| ble_ll_hci_ev_databuf_overflow(void) |
| { |
| struct ble_hci_ev_data_buf_overflow *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_DATA_BUF_OVERFLOW)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_DATA_BUF_OVERFLOW; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->link_type = BLE_HCI_EVENT_ACL_BUF_OVERFLOW; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| |
| /** |
| * Send a LE Channel Selection Algorithm event. |
| * |
| * @param connsm Pointer to connection state machine |
| */ |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) |
| void |
| ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm) |
| { |
| struct ble_hci_ev_le_subev_chan_sel_alg *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CHAN_SEL_ALG)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_CHAN_SEL_ALG; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->csa = connsm->flags.csa2 ? 0x01 : 0x00; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| #endif |
| |
| /** |
| * Sends the LE Scan Request Received event |
| * |
| */ |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| void |
| ble_ll_hci_ev_send_scan_req_recv(uint8_t adv_handle, const uint8_t *peer, |
| uint8_t peer_addr_type) |
| { |
| struct ble_hci_ev_le_subev_scan_req_rcvd *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD; |
| ev->adv_handle = adv_handle; |
| ev->peer_addr_type = peer_addr_type; |
| memcpy(ev->peer_addr, peer, BLE_DEV_ADDR_LEN); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| #endif |
| |
| /** |
| * Sends the LE Scan Timeout Event |
| * |
| */ |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| void |
| ble_ll_hci_ev_send_scan_timeout(void) |
| { |
| struct ble_hci_ev_le_subev_scan_timeout *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_TIMEOUT)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| #endif |
| |
| /** |
| * Sends the LE Advertising Set Terminated event |
| * |
| */ |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_EXT_ADV) |
| void |
| ble_ll_hci_ev_send_adv_set_terminated(uint8_t status, uint8_t adv_handle, |
| uint16_t conn_handle, uint8_t events) |
| { |
| struct ble_hci_ev_le_subev_adv_set_terminated *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED; |
| ev->status = status; |
| ev->adv_handle = adv_handle; |
| ev->conn_handle = htole16(conn_handle); |
| ev->num_events = events; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| } |
| #endif |
| |
| /** |
| * Send a PHY update complete event |
| * |
| * @param connsm Pointer to connection state machine |
| * @param status error status of event |
| */ |
| #if MYNEWT_VAL(BLE_LL_PHY) |
| int |
| ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_le_subev_phy_update_complete *ev; |
| struct ble_hci_ev *hci_ev; |
| int rc; |
| |
| rc = 0; |
| if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE)) { |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE; |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->tx_phy = connsm->phy_data.cur_tx_phy; |
| ev->rx_phy = connsm->phy_data.cur_rx_phy; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } else { |
| rc = BLE_ERR_MEM_CAPACITY; |
| } |
| } |
| return rc; |
| } |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_SCA_UPDATE) |
| void |
| ble_ll_hci_ev_sca_update(struct ble_ll_conn_sm *connsm, uint8_t status, |
| uint8_t peer_sca) |
| { |
| struct ble_hci_ev_le_subev_peer_sca_complete *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_REQ_PEER_SCA_COMP)) { |
| return; |
| } |
| |
| hci_ev = ble_transport_alloc_evt(0); |
| if (!hci_ev) { |
| return; |
| } |
| |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_REQ_PEER_SCA_COMP; |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->sca = peer_sca; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_ENHANCED_CONN_UPDATE) |
| void |
| ble_ll_hci_ev_subrate_change(struct ble_ll_conn_sm *connsm, uint8_t status) |
| { |
| struct ble_hci_ev_le_subev_subrate_change *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| if (!ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SUBRATE_CHANGE)) { |
| return; |
| } |
| |
| hci_ev = ble_transport_alloc_evt(0); |
| if (!hci_ev) { |
| return; |
| } |
| |
| hci_ev->opcode = BLE_HCI_EVCODE_LE_META; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *)hci_ev->data; |
| |
| ev->subev_code = BLE_HCI_LE_SUBEV_SUBRATE_CHANGE; |
| ev->status = status; |
| ev->conn_handle = htole16(connsm->conn_handle); |
| ev->subrate_factor = htole16(connsm->subrate_factor); |
| ev->periph_latency = htole16(connsm->periph_latency); |
| ev->cont_num = htole16(connsm->cont_num); |
| ev->supervision_tmo = htole16(connsm->supervision_tmo); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| #endif |
| |
| #if MYNEWT_VAL(BLE_LL_HCI_VS_CONN_STRICT_SCHED) |
| void |
| ble_ll_hci_ev_send_vs_css_slot_changed(uint16_t conn_handle, uint16_t slot_idx) |
| { |
| struct ble_hci_ev_vs_css_slot_changed *ev; |
| struct ble_hci_ev_vs *ev_vs; |
| struct ble_hci_ev *hci_ev; |
| |
| hci_ev = ble_transport_alloc_evt(0); |
| if (!hci_ev) { |
| return; |
| |
| } |
| |
| hci_ev->opcode = BLE_HCI_EVCODE_VS; |
| hci_ev->length = sizeof(*ev_vs) + sizeof(*ev); |
| ev_vs = (void *)hci_ev->data; |
| ev_vs->id = BLE_HCI_VS_SUBEV_ID_CSS_SLOT_CHANGED; |
| ev = (void *)ev_vs->data; |
| ev->conn_handle = htole16(conn_handle); |
| ev->slot_idx = htole16(slot_idx); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| #endif |
| |
| void |
| ble_ll_hci_ev_send_vs_assert(const char *file, uint32_t line) |
| { |
| struct ble_hci_ev_vs *ev; |
| struct ble_hci_ev *hci_ev; |
| unsigned int str_len; |
| bool skip = true; |
| uint8_t digit; |
| int max_len; |
| int i; |
| |
| /* 6 is for line number ":00000" , we assume files have no more than 64k of |
| * lines |
| */ |
| max_len = BLE_HCI_MAX_DATA_LEN - sizeof(*ev) - 6; |
| |
| hci_ev = ble_transport_alloc_evt(0); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_VS; |
| hci_ev->length = sizeof(*ev); |
| ev = (void *) hci_ev->data; |
| |
| /* Debug id for future use */ |
| ev->id = BLE_HCI_VS_SUBEV_ID_ASSERT; |
| |
| /* snprintf would be nicer but this is heavy on flash |
| * len = snprintf((char *) ev->data, max_len, "%s:%u", file, line); |
| * if (len < 0) { |
| * len = 0; |
| * } else if (len > max_len) { |
| * len = max_len; |
| * } |
| * |
| * hci_ev->length += len; |
| */ |
| str_len = strlen(file); |
| if (str_len > (unsigned int)max_len) { |
| str_len = max_len; |
| } |
| |
| memcpy(ev->data, file, str_len); |
| ev->data[str_len++] = ':'; |
| |
| for (i = 100000; i >= 10; i /= 10) { |
| digit = (line % i) / (i/10); |
| |
| if (!digit && skip) { |
| continue; |
| } |
| |
| skip = false; |
| ev->data[str_len++] = '0' + digit; |
| } |
| |
| hci_ev->length += str_len; |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| |
| void |
| ble_ll_hci_ev_send_vs_printf(uint8_t id, const char *fmt, ...) |
| { |
| struct ble_hci_ev_vs *ev; |
| struct ble_hci_ev *hci_ev; |
| va_list ap; |
| |
| hci_ev = ble_transport_alloc_evt(1); |
| if (!hci_ev) { |
| return; |
| } |
| |
| hci_ev->opcode = BLE_HCI_EVCODE_VS; |
| hci_ev->length = sizeof(*ev); |
| |
| ev = (void *) hci_ev->data; |
| ev->id = id; |
| |
| va_start(ap, fmt); |
| hci_ev->length += vsnprintf((void *)ev->data, |
| BLE_HCI_MAX_DATA_LEN - sizeof(*ev), fmt, ap); |
| va_end(ap); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| |
| #if MYNEWT_VAL(BLE_LL_HCI_LLCP_TRACE) |
| void |
| ble_ll_hci_ev_send_vs_llcp_trace(uint8_t type, uint16_t handle, uint16_t count, |
| void *pdu, size_t length) |
| { |
| struct ble_hci_ev_vs *ev; |
| struct ble_hci_ev *hci_ev; |
| |
| hci_ev = ble_transport_alloc_evt(1); |
| if (hci_ev) { |
| hci_ev->opcode = BLE_HCI_EVCODE_VS; |
| hci_ev->length = sizeof(*ev) + 8 + length; |
| ev = (void *) hci_ev->data; |
| |
| ev->id = BLE_HCI_VS_SUBEV_ID_LLCP_TRACE; |
| ev->data[0] = type; |
| put_le16(&ev->data[1], handle); |
| put_le16(&ev->data[3], count); |
| ev->data[5] = 0; |
| ev->data[6] = 0; |
| ev->data[7] = 0; |
| memcpy(&ev->data[8], pdu, length); |
| |
| ble_ll_hci_event_send(hci_ev); |
| } |
| } |
| #endif |