blob: 66d64f6f0139fba052f47187e93ed19f72501ea7 [file] [log] [blame]
/*
* 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 <stdint.h>
#include <assert.h>
#include <string.h>
#include "syscfg/syscfg.h"
#include "nimble/ble.h"
#include "nimble/hci_common.h"
#include "nimble/ble_hci_trans.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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_DATA_LEN_CHG)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_DATA_LEN_CHG_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_DATA_LEN_CHG;
put_le16(evbuf + 3, connsm->conn_handle);
put_le16(evbuf + 5, connsm->eff_max_tx_octets);
put_le16(evbuf + 7, connsm->eff_max_tx_time);
put_le16(evbuf + 9, connsm->eff_max_rx_octets);
put_le16(evbuf + 11, connsm->eff_max_rx_time);
ble_ll_hci_event_send(evbuf);
}
}
}
/**
* 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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_REM_CONN_PARM_REQ_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_REM_CONN_PARM_REQ;
put_le16(evbuf + 3, connsm->conn_handle);
put_le16(evbuf + 5, cp->interval_min);
put_le16(evbuf + 7, cp->interval_max);
put_le16(evbuf + 9, cp->latency);
put_le16(evbuf + 11, cp->timeout);
ble_ll_hci_event_send(evbuf);
}
}
}
/**
* 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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_CONN_UPD_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_CONN_UPD_COMPLETE;
evbuf[3] = status;
put_le16(evbuf + 4, connsm->conn_handle);
put_le16(evbuf + 6, connsm->conn_itvl);
put_le16(evbuf + 8, connsm->slave_latency);
put_le16(evbuf + 10, connsm->supervision_tmo);
ble_ll_hci_event_send(evbuf);
}
}
}
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1)
void
ble_ll_hci_ev_encrypt_chg(struct ble_ll_conn_sm *connsm, uint8_t status)
{
uint8_t evcode;
uint8_t *evbuf;
uint8_t evlen;
if (CONN_F_ENC_CHANGE_SENT(connsm) == 0) {
evcode = BLE_HCI_EVCODE_ENCRYPT_CHG;
evlen = BLE_HCI_EVENT_ENCRYPT_CHG_LEN;
} else {
evcode = BLE_HCI_EVCODE_ENC_KEY_REFRESH;
evlen = BLE_HCI_EVENT_ENC_KEY_REFRESH_LEN;
}
if (ble_ll_hci_is_event_enabled(evcode)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = evcode;
evbuf[1] = evlen;
evbuf[2] = status;
put_le16(evbuf + 3, connsm->conn_handle);
if (evcode == BLE_HCI_EVCODE_ENCRYPT_CHG) {
if (status == BLE_ERR_SUCCESS) {
evbuf[5] = 0x01;
} else {
evbuf[5] = 0;
}
}
ble_ll_hci_event_send(evbuf);
}
}
CONN_F_ENC_CHANGE_SENT(connsm) = 1;
}
/**
* 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)
{
int rc;
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_LT_KEY_REQ)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_LT_KEY_REQ_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_LT_KEY_REQ;
put_le16(evbuf + 3, connsm->conn_handle);
put_le64(evbuf + 5, connsm->enc_data.host_rand_num);
put_le16(evbuf + 13, connsm->enc_data.enc_div);
ble_ll_hci_event_send(evbuf);
}
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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_RD_REM_USED_FEAT_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_RD_REM_USED_FEAT;
evbuf[3] = status;
put_le16(evbuf + 4, connsm->conn_handle);
memset(evbuf + 6, 0, BLE_HCI_RD_LOC_SUPP_FEAT_RSPLEN);
evbuf[6] = connsm->conn_features;
memcpy(evbuf + 7, connsm->remote_features, 7);
ble_ll_hci_event_send(evbuf);
}
}
}
void
ble_ll_hci_ev_rd_rem_ver(struct ble_ll_conn_sm *connsm, uint8_t status)
{
uint8_t *evbuf;
if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_RD_REM_VER_INFO_CMP;
evbuf[1] = BLE_HCI_EVENT_RD_RM_VER_LEN;
evbuf[2] = status;
put_le16(evbuf + 3, connsm->conn_handle);
evbuf[5] = connsm->vers_nr;
put_le16(evbuf + 6, connsm->comp_id);
put_le16(evbuf + 8, connsm->sub_vers_nr);
ble_ll_hci_event_send(evbuf);
}
}
}
/**
* 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)
{
int rc;
uint8_t *evbuf;
rc = 0;
if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_HW_ERROR)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_HW_ERROR;
evbuf[1] = BLE_HCI_EVENT_HW_ERROR_LEN;
evbuf[2] = hw_err;
ble_ll_hci_event_send(evbuf);
} else {
rc = -1;
}
}
return rc;
}
void
ble_ll_hci_ev_databuf_overflow(void)
{
uint8_t *evbuf;
if (ble_ll_hci_is_event_enabled(BLE_HCI_EVCODE_DATA_BUF_OVERFLOW)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_DATA_BUF_OVERFLOW;
evbuf[1] = BLE_HCI_EVENT_DATABUF_OVERFLOW_LEN;
evbuf[2] = BLE_HCI_EVENT_ACL_BUF_OVERFLOW;
ble_ll_hci_event_send(evbuf);
}
}
}
/**
* Send a LE Channel Selection Algorithm event.
*
* @param connsm Pointer to connection state machine
*/
#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CSA2) == 1)
void
ble_ll_hci_ev_le_csa(struct ble_ll_conn_sm *connsm)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_CHAN_SEL_ALG)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_SUBEV_CHAN_SEL_ALG_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_CHAN_SEL_ALG;
put_le16(evbuf + 3, connsm->conn_handle);
evbuf[5] = connsm->csmflags.cfbit.csa2_supp ? 0x01 : 0x00;
ble_ll_hci_event_send(evbuf);
}
}
}
#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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_SCAN_REQ_RCVD;
evbuf[3] = adv_handle;
evbuf[4] = peer_addr_type;
memcpy(&evbuf[5], peer, BLE_DEV_ADDR_LEN);
ble_ll_hci_event_send(evbuf);
}
}
}
#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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_SCAN_TIMEOUT)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_SCAN_TIMEOUT;
ble_ll_hci_event_send(evbuf);
}
}
}
#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)
{
uint8_t *evbuf;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_ADV_SET_TERMINATED;
evbuf[3] = status;
evbuf[4] = adv_handle;
put_le16(evbuf + 5, conn_handle);
evbuf[7] = events;
ble_ll_hci_event_send(evbuf);
}
}
}
#endif
/**
* Send a PHY update complete event
*
* @param connsm Pointer to connection state machine
* @param status error status of event
*/
#if (BLE_LL_BT5_PHY_SUPPORTED == 1)
int
ble_ll_hci_ev_phy_update(struct ble_ll_conn_sm *connsm, uint8_t status)
{
int rc;
uint8_t *evbuf;
rc = 0;
if (ble_ll_hci_is_le_event_enabled(BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE)) {
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (evbuf) {
evbuf[0] = BLE_HCI_EVCODE_LE_META;
evbuf[1] = BLE_HCI_LE_PHY_UPD_LEN;
evbuf[2] = BLE_HCI_LE_SUBEV_PHY_UPDATE_COMPLETE;
evbuf[3] = status;
put_le16(evbuf + 4, connsm->conn_handle);
evbuf[6] = connsm->phy_data.cur_tx_phy;
evbuf[7] = connsm->phy_data.cur_rx_phy;
ble_ll_hci_event_send(evbuf);
} else {
rc = BLE_ERR_MEM_CAPACITY;
}
}
return rc;
}
#endif
void
ble_ll_hci_ev_send_vendor_err(char *file, uint32_t line)
{
uint8_t *evbuf;
uint8_t file_len = strlen(file);
evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI);
if (!evbuf) {
return;
}
evbuf[0] = BLE_HCI_EVCODE_VENDOR_DEBUG;
evbuf[1] = file_len + sizeof(line) + 1;
/* Debug id for future use */
evbuf[2] = 0x00;
memcpy(&evbuf[3], file, file_len);
put_le32(&evbuf[3] + file_len, line);
ble_ll_hci_event_send(evbuf);
}