mirror of
https://github.com/Fishwaldo/bl_mcu_sdk.git
synced 2025-07-23 13:18:59 +00:00
[feat] add ble component
This commit is contained in:
parent
28f6fa8c50
commit
91a930f878
203 changed files with 82743 additions and 0 deletions
385
components/ble/ble_stack/host/a2dp.c
Normal file
385
components/ble/ble_stack/host/a2dp.c
Normal file
|
@ -0,0 +1,385 @@
|
|||
/** @file
|
||||
* @brief Advance Audio Distribution Profile.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <atomic.h>
|
||||
#include <byteorder.h>
|
||||
#include <util.h>
|
||||
#include <printk.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <l2cap.h>
|
||||
#include <avdtp.h>
|
||||
#include <a2dp.h>
|
||||
#include <sdp.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_A2DP)
|
||||
#define LOG_MODULE_NAME bt_a2dp
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "conn_internal.h"
|
||||
#include "avdtp_internal.h"
|
||||
#include "a2dp_internal.h"
|
||||
#include "a2dp-codec.h"
|
||||
#include "oi_codec_sbc.h"
|
||||
|
||||
#define A2DP_NO_SPACE (-1)
|
||||
|
||||
struct bt_a2dp {
|
||||
struct bt_avdtp session;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
OI_CODEC_SBC_DECODER_CONTEXT decoder_context;
|
||||
uint32_t context_data[CODEC_DATA_WORDS(SBC_MAX_CHANNELS, SBC_CODEC_FAST_FILTER_BUFFERS)];
|
||||
int16_t decode_buf[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS];
|
||||
} A2DP_SBC_DECODER;
|
||||
|
||||
static A2DP_SBC_DECODER sbc_decoder;
|
||||
|
||||
/* Connections */
|
||||
static struct bt_a2dp connection[CONFIG_BT_MAX_CONN];
|
||||
static struct bt_avdtp_stream stream[CONFIG_BT_MAX_CONN];
|
||||
|
||||
static struct bt_sdp_attribute a2dp_attrs[] = {
|
||||
BT_SDP_NEW_SERVICE,
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_SVCLASS_ID_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_AUDIO_SINK_SVCLASS)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_PROTO_DESC_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 16),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
|
||||
BT_SDP_ARRAY_16(BT_L2CAP_PSM_AVDTP)
|
||||
},
|
||||
)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_L2CAP_PSM_AVDTP)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
|
||||
BT_SDP_ARRAY_16(0x0102)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_PROFILE_DESC_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_ADVANCED_AUDIO_SVCLASS)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
|
||||
BT_SDP_ARRAY_16(0x0102)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_SERVICE_NAME("A2DP sink"),
|
||||
};
|
||||
|
||||
static struct bt_sdp_record a2dp_rec = BT_SDP_RECORD(a2dp_attrs);
|
||||
|
||||
struct bt_a2dp_endpoint endpoint_1;
|
||||
struct bt_a2dp_endpoint endpoint_2;
|
||||
|
||||
struct bt_a2dp_codec_sbc_params sbc_info;
|
||||
|
||||
void bt_a2dp_set_sbc_codec_info()
|
||||
{
|
||||
sbc_info.config[0] =
|
||||
//Sampling Frequency
|
||||
A2DP_SBC_SAMP_FREQ_48000 |
|
||||
A2DP_SBC_SAMP_FREQ_44100 |
|
||||
A2DP_SBC_SAMP_FREQ_32000 |
|
||||
A2DP_SBC_SAMP_FREQ_16000 |
|
||||
//Channel Mode
|
||||
A2DP_SBC_CH_MODE_JOINT |
|
||||
A2DP_SBC_CH_MODE_STREO |
|
||||
A2DP_SBC_CH_MODE_DUAL |
|
||||
A2DP_SBC_CH_MODE_MONO;
|
||||
sbc_info.config[1] =
|
||||
//Block Length
|
||||
A2DP_SBC_BLK_LEN_16 |
|
||||
A2DP_SBC_BLK_LEN_12 |
|
||||
A2DP_SBC_BLK_LEN_8 |
|
||||
A2DP_SBC_BLK_LEN_4 |
|
||||
//Subbands
|
||||
A2DP_SBC_SUBBAND_8 |
|
||||
A2DP_SBC_SUBBAND_4 |
|
||||
//Allocation Method
|
||||
A2DP_SBC_ALLOC_MTHD_SNR |
|
||||
A2DP_SBC_ALLOC_MTHD_LOUDNESS;
|
||||
sbc_info.min_bitpool = 2;
|
||||
sbc_info.max_bitpool = 53;
|
||||
}
|
||||
|
||||
void a2d_reset(struct bt_a2dp *a2dp_conn)
|
||||
{
|
||||
(void)memset(a2dp_conn, 0, sizeof(struct bt_a2dp));
|
||||
}
|
||||
|
||||
void stream_reset(struct bt_avdtp_stream *stream_conn)
|
||||
{
|
||||
(void)memset(stream_conn, 0, sizeof(struct bt_avdtp_stream));
|
||||
}
|
||||
|
||||
struct bt_a2dp *get_new_connection(struct bt_conn *conn)
|
||||
{
|
||||
int8_t i, free;
|
||||
|
||||
free = A2DP_NO_SPACE;
|
||||
|
||||
if (!conn) {
|
||||
BT_ERR("Invalid Input (err: %d)", -EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a space */
|
||||
for (i = 0; i < CONFIG_BT_MAX_CONN; i++) {
|
||||
if (connection[i].session.br_chan.chan.conn == conn) {
|
||||
BT_DBG("Conn already exists");
|
||||
if(!connection[i].session.streams->chan.chan.conn)
|
||||
{
|
||||
BT_DBG("Create AV stream");
|
||||
return &connection[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
BT_DBG("A2DP signal stream and AV stream already exists");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connection[i].session.br_chan.chan.conn &&
|
||||
free == A2DP_NO_SPACE) {
|
||||
BT_DBG("Create signal stream");
|
||||
free = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (free == A2DP_NO_SPACE) {
|
||||
BT_DBG("More connection cannot be supported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Clean the memory area before returning */
|
||||
a2d_reset(&connection[free]);
|
||||
stream_reset(&stream[free]);
|
||||
connection[free].session.streams = &stream[free];
|
||||
|
||||
return &connection[free];
|
||||
}
|
||||
|
||||
int a2dp_accept(struct bt_conn *conn, struct bt_avdtp **session)
|
||||
{
|
||||
struct bt_a2dp *a2dp_conn;
|
||||
|
||||
a2dp_conn = get_new_connection(conn);
|
||||
if (!a2dp_conn) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*session = &(a2dp_conn->session);
|
||||
BT_DBG("session: %p", &(a2dp_conn->session));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int a2dp_sbc_decode_init()
|
||||
{
|
||||
OI_STATUS status = OI_CODEC_SBC_DecoderReset(&sbc_decoder.decoder_context,
|
||||
sbc_decoder.context_data,
|
||||
sizeof(sbc_decoder.context_data),
|
||||
2,
|
||||
2,
|
||||
false,
|
||||
false);
|
||||
if (!OI_SUCCESS(status))
|
||||
{
|
||||
BT_ERR("decode init failed with error: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if PCM_PRINTF
|
||||
extern int16_t cool_edit[];
|
||||
extern uint32_t byte_index;
|
||||
#endif
|
||||
int a2dp_sbc_decode_process(uint8_t media_data[], uint16_t data_len)
|
||||
{
|
||||
//remove media header, expose sbc frame
|
||||
const OI_BYTE * data = media_data + 12 + 1;
|
||||
OI_UINT32 data_size = data_len - 12 - 1;
|
||||
|
||||
if (data_size <= 0) {
|
||||
BT_ERR("empty packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(data[0] != 0x9c)
|
||||
{
|
||||
BT_ERR("sbc frame syncword error \n");
|
||||
}
|
||||
|
||||
OI_INT16* pcm = sbc_decoder.decode_buf;
|
||||
OI_UINT32 pcm_size = sizeof(sbc_decoder.decode_buf);
|
||||
|
||||
OI_INT16 frame_count = OI_CODEC_SBC_FrameCount((OI_BYTE *)data, data_size);
|
||||
BT_DBG("frame_count: %d\n", frame_count);
|
||||
|
||||
for (int i = 0; i < frame_count; i++)
|
||||
{
|
||||
OI_STATUS status = OI_CODEC_SBC_DecodeFrame(&sbc_decoder.decoder_context,
|
||||
&data,
|
||||
&data_size,
|
||||
pcm,
|
||||
&pcm_size);
|
||||
if (!OI_SUCCESS(status))
|
||||
{
|
||||
BT_ERR("decoding failure with error: %d \n", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if PCM_PRINTF
|
||||
memcpy((OI_INT8 *)cool_edit + byte_index, pcm, pcm_size);
|
||||
byte_index += pcm_size;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Callback for incoming requests */
|
||||
static struct bt_avdtp_ind_cb cb_ind = {
|
||||
/*TODO*/
|
||||
};
|
||||
|
||||
/* The above callback structures need to be packed and passed to AVDTP */
|
||||
static struct bt_avdtp_event_cb avdtp_cb = {
|
||||
.ind = &cb_ind,
|
||||
.accept = a2dp_accept
|
||||
};
|
||||
|
||||
int bt_a2dp_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Register event handlers with AVDTP */
|
||||
err = bt_avdtp_register(&avdtp_cb);
|
||||
if (err < 0) {
|
||||
BT_ERR("A2DP registration failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Register SDP record */
|
||||
err = bt_sdp_register_service(&a2dp_rec);
|
||||
if(err < 0)
|
||||
{
|
||||
BT_ERR("A2DP regist sdp record failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
int reg_1 = bt_a2dp_register_endpoint(&endpoint_1, BT_A2DP_AUDIO, BT_A2DP_SINK);
|
||||
int reg_2 = bt_a2dp_register_endpoint(&endpoint_2, BT_A2DP_AUDIO, BT_A2DP_SINK);
|
||||
if (reg_1 || reg_2) {
|
||||
BT_ERR("A2DP registration endpoint 1 failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_a2dp_set_sbc_codec_info();
|
||||
|
||||
err = a2dp_sbc_decode_init();
|
||||
if (err < 0) {
|
||||
BT_ERR("sbc codec init failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_DBG("A2DP Initialized successfully.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bt_a2dp *bt_a2dp_connect(struct bt_conn *conn)
|
||||
{
|
||||
struct bt_a2dp *a2dp_conn;
|
||||
int err;
|
||||
|
||||
a2dp_conn = get_new_connection(conn);
|
||||
if (!a2dp_conn) {
|
||||
BT_ERR("Cannot allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
err = bt_avdtp_connect(conn, &(a2dp_conn->session));
|
||||
if (err < 0) {
|
||||
/* If error occurs, undo the saving and return the error */
|
||||
a2d_reset(a2dp_conn);
|
||||
BT_DBG("AVDTP Connect failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BT_DBG("Connect request sent");
|
||||
return a2dp_conn;
|
||||
}
|
||||
|
||||
int bt_a2dp_register_endpoint(struct bt_a2dp_endpoint *endpoint,
|
||||
uint8_t media_type, uint8_t role)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_ASSERT(endpoint);
|
||||
|
||||
err = bt_avdtp_register_sep(media_type, role, &(endpoint->info));
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
12
components/ble/ble_stack/host/a2dp_internal.h
Normal file
12
components/ble/ble_stack/host/a2dp_internal.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/** @file
|
||||
* @brief Advance Audio Distribution Profile Internal header.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* To be called when first SEP is being registered */
|
||||
int bt_a2dp_init(void);
|
537
components/ble/ble_stack/host/at.c
Normal file
537
components/ble/ble_stack/host/at.c
Normal file
|
@ -0,0 +1,537 @@
|
|||
/**
|
||||
* @file at.c
|
||||
* Generic AT command handling library implementation
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <net/buf.h>
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static void next_list(struct at_client *at)
|
||||
{
|
||||
if (at->buf[at->pos] == ',') {
|
||||
at->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
int at_check_byte(struct net_buf *buf, char check_byte)
|
||||
{
|
||||
const unsigned char *str = buf->data;
|
||||
|
||||
if (*str != check_byte) {
|
||||
return -EINVAL;
|
||||
}
|
||||
net_buf_pull(buf, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void skip_space(struct at_client *at)
|
||||
{
|
||||
while (at->buf[at->pos] == ' ') {
|
||||
at->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
int at_get_number(struct at_client *at, uint32_t *val)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
skip_space(at);
|
||||
|
||||
for (i = 0U, *val = 0U;
|
||||
isdigit((unsigned char)at->buf[at->pos]);
|
||||
at->pos++, i++) {
|
||||
*val = *val * 10U + at->buf[at->pos] - '0';
|
||||
}
|
||||
|
||||
if (i == 0U) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
next_list(at);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool str_has_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
if (strncmp(str, prefix, strlen(prefix)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int at_parse_result(const char *str, struct net_buf *buf,
|
||||
enum at_result *result)
|
||||
{
|
||||
/* Map the result and check for end lf */
|
||||
if ((!strncmp(str, "OK", 2)) && (at_check_byte(buf, '\n') == 0)) {
|
||||
*result = AT_RESULT_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!strncmp(str, "ERROR", 5)) && (at_check_byte(buf, '\n')) == 0) {
|
||||
*result = AT_RESULT_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
static int get_cmd_value(struct at_client *at, struct net_buf *buf,
|
||||
char stop_byte, enum at_cmd_state cmd_state)
|
||||
{
|
||||
int cmd_len = 0;
|
||||
uint8_t pos = at->pos;
|
||||
const char *str = (char *)buf->data;
|
||||
|
||||
while (cmd_len < buf->len && at->pos != at->buf_max_len) {
|
||||
if (*str != stop_byte) {
|
||||
at->buf[at->pos++] = *str;
|
||||
cmd_len++;
|
||||
str++;
|
||||
pos = at->pos;
|
||||
} else {
|
||||
cmd_len++;
|
||||
at->buf[at->pos] = '\0';
|
||||
at->pos = 0U;
|
||||
at->cmd_state = cmd_state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
net_buf_pull(buf, cmd_len);
|
||||
|
||||
if (pos == at->buf_max_len) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_response_string(struct at_client *at, struct net_buf *buf,
|
||||
char stop_byte, enum at_state state)
|
||||
{
|
||||
int cmd_len = 0;
|
||||
uint8_t pos = at->pos;
|
||||
const char *str = (char *)buf->data;
|
||||
|
||||
while (cmd_len < buf->len && at->pos != at->buf_max_len) {
|
||||
if (*str != stop_byte) {
|
||||
at->buf[at->pos++] = *str;
|
||||
cmd_len++;
|
||||
str++;
|
||||
pos = at->pos;
|
||||
} else {
|
||||
cmd_len++;
|
||||
at->buf[at->pos] = '\0';
|
||||
at->pos = 0U;
|
||||
at->state = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
net_buf_pull(buf, cmd_len);
|
||||
|
||||
if (pos == at->buf_max_len) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_buffer(struct at_client *at)
|
||||
{
|
||||
(void)memset(at->buf, 0, at->buf_max_len);
|
||||
at->pos = 0U;
|
||||
}
|
||||
|
||||
static int at_state_start(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = at_check_byte(buf, '\r');
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
at->state = AT_STATE_START_CR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_state_start_cr(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = at_check_byte(buf, '\n');
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
at->state = AT_STATE_START_LF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_state_start_lf(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
reset_buffer(at);
|
||||
if (at_check_byte(buf, '+') == 0) {
|
||||
at->state = AT_STATE_GET_CMD_STRING;
|
||||
return 0;
|
||||
} else if (isalpha(*buf->data)) {
|
||||
at->state = AT_STATE_GET_RESULT_STRING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
static int at_state_get_cmd_string(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
return get_response_string(at, buf, ':', AT_STATE_PROCESS_CMD);
|
||||
}
|
||||
|
||||
static bool is_cmer(struct at_client *at)
|
||||
{
|
||||
if (strncmp(at->buf, "CME ERROR", 9) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int at_state_process_cmd(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
if (is_cmer(at)) {
|
||||
at->state = AT_STATE_PROCESS_AG_NW_ERR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (at->resp) {
|
||||
at->resp(at, buf);
|
||||
at->resp = NULL;
|
||||
return 0;
|
||||
}
|
||||
at->state = AT_STATE_UNSOLICITED_CMD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_state_get_result_string(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
return get_response_string(at, buf, '\r', AT_STATE_PROCESS_RESULT);
|
||||
}
|
||||
|
||||
static bool is_ring(struct at_client *at)
|
||||
{
|
||||
if (strncmp(at->buf, "RING", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int at_state_process_result(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
enum at_cme cme_err;
|
||||
enum at_result result;
|
||||
|
||||
if (is_ring(at)) {
|
||||
at->state = AT_STATE_UNSOLICITED_CMD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (at_parse_result(at->buf, buf, &result) == 0) {
|
||||
if (at->finish) {
|
||||
/* cme_err is 0 - Is invalid until result is
|
||||
* AT_RESULT_CME_ERROR
|
||||
*/
|
||||
cme_err = 0;
|
||||
at->finish(at, result, cme_err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the state to process unsolicited response */
|
||||
at->cmd_state = AT_CMD_START;
|
||||
at->state = AT_STATE_START;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cme_handle(struct at_client *at)
|
||||
{
|
||||
enum at_cme cme_err;
|
||||
uint32_t val;
|
||||
|
||||
if (!at_get_number(at, &val) && val <= CME_ERROR_NETWORK_NOT_ALLOWED) {
|
||||
cme_err = val;
|
||||
} else {
|
||||
cme_err = CME_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (at->finish) {
|
||||
at->finish(at, AT_RESULT_CME_ERROR, cme_err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_state_process_ag_nw_err(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
at->cmd_state = AT_CMD_GET_VALUE;
|
||||
return at_parse_cmd_input(at, buf, NULL, cme_handle,
|
||||
AT_CMD_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
static int at_state_unsolicited_cmd(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
if (at->unsolicited) {
|
||||
return at->unsolicited(at, buf);
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* The order of handler function should match the enum at_state */
|
||||
static handle_parse_input_t parser_cb[] = {
|
||||
at_state_start, /* AT_STATE_START */
|
||||
at_state_start_cr, /* AT_STATE_START_CR */
|
||||
at_state_start_lf, /* AT_STATE_START_LF */
|
||||
at_state_get_cmd_string, /* AT_STATE_GET_CMD_STRING */
|
||||
at_state_process_cmd, /* AT_STATE_PROCESS_CMD */
|
||||
at_state_get_result_string, /* AT_STATE_GET_RESULT_STRING */
|
||||
at_state_process_result, /* AT_STATE_PROCESS_RESULT */
|
||||
at_state_process_ag_nw_err, /* AT_STATE_PROCESS_AG_NW_ERR */
|
||||
at_state_unsolicited_cmd /* AT_STATE_UNSOLICITED_CMD */
|
||||
};
|
||||
|
||||
int at_parse_input(struct at_client *at, struct net_buf *buf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (buf->len) {
|
||||
if (at->state < AT_STATE_START || at->state >= AT_STATE_END) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = parser_cb[at->state](at, buf);
|
||||
if (ret < 0) {
|
||||
/* Reset the state in case of error */
|
||||
at->cmd_state = AT_CMD_START;
|
||||
at->state = AT_STATE_START;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_cmd_start(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type)
|
||||
{
|
||||
if (!str_has_prefix(at->buf, prefix)) {
|
||||
if (type == AT_CMD_TYPE_NORMAL) {
|
||||
at->state = AT_STATE_UNSOLICITED_CMD;
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (type == AT_CMD_TYPE_OTHER) {
|
||||
/* Skip for Other type such as ..RING.. which does not have
|
||||
* values to get processed.
|
||||
*/
|
||||
at->cmd_state = AT_CMD_PROCESS_VALUE;
|
||||
} else {
|
||||
at->cmd_state = AT_CMD_GET_VALUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_cmd_get_value(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type)
|
||||
{
|
||||
/* Reset buffer before getting the values */
|
||||
reset_buffer(at);
|
||||
return get_cmd_value(at, buf, '\r', AT_CMD_PROCESS_VALUE);
|
||||
}
|
||||
|
||||
static int at_cmd_process_value(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = func(at);
|
||||
at->cmd_state = AT_CMD_STATE_END_LF;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int at_cmd_state_end_lf(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = at_check_byte(buf, '\n');
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
at->cmd_state = AT_CMD_START;
|
||||
at->state = AT_STATE_START;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The order of handler function should match the enum at_cmd_state */
|
||||
static handle_cmd_input_t cmd_parser_cb[] = {
|
||||
at_cmd_start, /* AT_CMD_START */
|
||||
at_cmd_get_value, /* AT_CMD_GET_VALUE */
|
||||
at_cmd_process_value, /* AT_CMD_PROCESS_VALUE */
|
||||
at_cmd_state_end_lf /* AT_CMD_STATE_END_LF */
|
||||
};
|
||||
|
||||
int at_parse_cmd_input(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (buf->len) {
|
||||
if (at->cmd_state < AT_CMD_START ||
|
||||
at->cmd_state >= AT_CMD_STATE_END) {
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = cmd_parser_cb[at->cmd_state](at, buf, prefix, func, type);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
/* Check for main state, the end of cmd parsing and return. */
|
||||
if (at->state == AT_STATE_START) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at_has_next_list(struct at_client *at)
|
||||
{
|
||||
return at->buf[at->pos] != '\0';
|
||||
}
|
||||
|
||||
int at_open_list(struct at_client *at)
|
||||
{
|
||||
skip_space(at);
|
||||
|
||||
/* The list shall start with '(' open parenthesis */
|
||||
if (at->buf[at->pos] != '(') {
|
||||
return -ENODATA;
|
||||
}
|
||||
at->pos++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at_close_list(struct at_client *at)
|
||||
{
|
||||
skip_space(at);
|
||||
|
||||
if (at->buf[at->pos] != ')') {
|
||||
return -ENODATA;
|
||||
}
|
||||
at->pos++;
|
||||
|
||||
next_list(at);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at_list_get_string(struct at_client *at, char *name, uint8_t len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
skip_space(at);
|
||||
|
||||
if (at->buf[at->pos] != '"') {
|
||||
return -ENODATA;
|
||||
}
|
||||
at->pos++;
|
||||
|
||||
while (at->buf[at->pos] != '\0' && at->buf[at->pos] != '"') {
|
||||
if (i == len) {
|
||||
return -ENODATA;
|
||||
}
|
||||
name[i++] = at->buf[at->pos++];
|
||||
}
|
||||
|
||||
if (i == len) {
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
name[i] = '\0';
|
||||
|
||||
if (at->buf[at->pos] != '"') {
|
||||
return -ENODATA;
|
||||
}
|
||||
at->pos++;
|
||||
|
||||
skip_space(at);
|
||||
next_list(at);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max)
|
||||
{
|
||||
uint32_t low, high;
|
||||
int ret;
|
||||
|
||||
ret = at_get_number(at, &low);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (at->buf[at->pos] == '-') {
|
||||
at->pos++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!isdigit((unsigned char)at->buf[at->pos])) {
|
||||
return -ENODATA;
|
||||
}
|
||||
out:
|
||||
ret = at_get_number(at, &high);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*min = low;
|
||||
*max = high;
|
||||
|
||||
next_list(at);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited)
|
||||
{
|
||||
at->unsolicited = unsolicited;
|
||||
}
|
||||
|
||||
void at_register(struct at_client *at, at_resp_cb_t resp, at_finish_cb_t finish)
|
||||
{
|
||||
at->resp = resp;
|
||||
at->finish = finish;
|
||||
at->state = AT_STATE_START;
|
||||
}
|
118
components/ble/ble_stack/host/at.h
Normal file
118
components/ble/ble_stack/host/at.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/** @file at.h
|
||||
* @brief Internal APIs for AT command handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
enum at_result {
|
||||
AT_RESULT_OK,
|
||||
AT_RESULT_ERROR,
|
||||
AT_RESULT_CME_ERROR
|
||||
};
|
||||
|
||||
enum at_cme {
|
||||
CME_ERROR_AG_FAILURE = 0,
|
||||
CME_ERROR_NO_CONNECTION_TO_PHONE = 1,
|
||||
CME_ERROR_OPERATION_NOT_ALLOWED = 3,
|
||||
CME_ERROR_OPERATION_NOT_SUPPORTED = 4,
|
||||
CME_ERROR_PH_SIM_PIN_REQUIRED = 5,
|
||||
CME_ERROR_SIM_NOT_INSERTED = 10,
|
||||
CME_ERROR_SIM_PIN_REQUIRED = 11,
|
||||
CME_ERROR_SIM_PUK_REQUIRED = 12,
|
||||
CME_ERROR_SIM_FAILURE = 13,
|
||||
CME_ERROR_SIM_BUSY = 14,
|
||||
CME_ERROR_INCORRECT_PASSWORD = 16,
|
||||
CME_ERROR_SIM_PIN2_REQUIRED = 17,
|
||||
CME_ERROR_SIM_PUK2_REQUIRED = 18,
|
||||
CME_ERROR_MEMORY_FULL = 20,
|
||||
CME_ERROR_INVALID_INDEX = 21,
|
||||
CME_ERROR_MEMORY_FAILURE = 23,
|
||||
CME_ERROR_TEXT_STRING_TOO_LONG = 24,
|
||||
CME_ERROR_INVALID_CHARS_IN_TEXT_STRING = 25,
|
||||
CME_ERROR_DIAL_STRING_TO_LONG = 26,
|
||||
CME_ERROR_INVALID_CHARS_IN_DIAL_STRING = 27,
|
||||
CME_ERROR_NO_NETWORK_SERVICE = 30,
|
||||
CME_ERROR_NETWORK_TIMEOUT = 31,
|
||||
CME_ERROR_NETWORK_NOT_ALLOWED = 32,
|
||||
CME_ERROR_UNKNOWN = 33,
|
||||
};
|
||||
|
||||
enum at_state {
|
||||
AT_STATE_START,
|
||||
AT_STATE_START_CR,
|
||||
AT_STATE_START_LF,
|
||||
AT_STATE_GET_CMD_STRING,
|
||||
AT_STATE_PROCESS_CMD,
|
||||
AT_STATE_GET_RESULT_STRING,
|
||||
AT_STATE_PROCESS_RESULT,
|
||||
AT_STATE_PROCESS_AG_NW_ERR,
|
||||
AT_STATE_UNSOLICITED_CMD,
|
||||
AT_STATE_END
|
||||
};
|
||||
|
||||
enum at_cmd_state {
|
||||
AT_CMD_START,
|
||||
AT_CMD_GET_VALUE,
|
||||
AT_CMD_PROCESS_VALUE,
|
||||
AT_CMD_STATE_END_LF,
|
||||
AT_CMD_STATE_END
|
||||
};
|
||||
|
||||
enum at_cmd_type {
|
||||
AT_CMD_TYPE_NORMAL,
|
||||
AT_CMD_TYPE_UNSOLICITED,
|
||||
AT_CMD_TYPE_OTHER
|
||||
};
|
||||
|
||||
struct at_client;
|
||||
|
||||
/* Callback at_resp_cb_t used to parse response value received for the
|
||||
* particular AT command. Eg: +CIND=<value>
|
||||
*/
|
||||
typedef int (*at_resp_cb_t)(struct at_client *at, struct net_buf *buf);
|
||||
|
||||
/* Callback at_finish_cb used to monitor the success or failure of the AT
|
||||
* command received from server.
|
||||
* Argument 'cme_err' is valid only when argument 'result' is equal to
|
||||
* AT_RESULT_CME_ERROR
|
||||
*/
|
||||
typedef int (*at_finish_cb_t)(struct at_client *at, enum at_result result,
|
||||
enum at_cme cme_err);
|
||||
typedef int (*parse_val_t)(struct at_client *at);
|
||||
typedef int (*handle_parse_input_t)(struct at_client *at, struct net_buf *buf);
|
||||
typedef int (*handle_cmd_input_t)(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type);
|
||||
|
||||
struct at_client {
|
||||
char *buf;
|
||||
uint8_t pos;
|
||||
uint8_t buf_max_len;
|
||||
uint8_t state;
|
||||
uint8_t cmd_state;
|
||||
at_resp_cb_t resp;
|
||||
at_resp_cb_t unsolicited;
|
||||
at_finish_cb_t finish;
|
||||
};
|
||||
|
||||
/* Register the callback functions */
|
||||
void at_register(struct at_client *at, at_resp_cb_t resp,
|
||||
at_finish_cb_t finish);
|
||||
void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited);
|
||||
int at_get_number(struct at_client *at, uint32_t *val);
|
||||
/* This parsing will only works for non-fragmented net_buf */
|
||||
int at_parse_input(struct at_client *at, struct net_buf *buf);
|
||||
/* This command parsing will only works for non-fragmented net_buf */
|
||||
int at_parse_cmd_input(struct at_client *at, struct net_buf *buf,
|
||||
const char *prefix, parse_val_t func,
|
||||
enum at_cmd_type type);
|
||||
int at_check_byte(struct net_buf *buf, char check_byte);
|
||||
int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max);
|
||||
int at_list_get_string(struct at_client *at, char *name, uint8_t len);
|
||||
int at_close_list(struct at_client *at);
|
||||
int at_open_list(struct at_client *at);
|
||||
int at_has_next_list(struct at_client *at);
|
2448
components/ble/ble_stack/host/att.c
Normal file
2448
components/ble/ble_stack/host/att.c
Normal file
File diff suppressed because it is too large
Load diff
265
components/ble/ble_stack/host/att_internal.h
Normal file
265
components/ble/ble_stack/host/att_internal.h
Normal file
|
@ -0,0 +1,265 @@
|
|||
/* att_internal.h - Attribute protocol handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define BT_ATT_DEFAULT_LE_MTU 23
|
||||
|
||||
#if BT_L2CAP_RX_MTU < CONFIG_BT_L2CAP_TX_MTU
|
||||
#define BT_ATT_MTU BT_L2CAP_RX_MTU
|
||||
#else
|
||||
#define BT_ATT_MTU CONFIG_BT_L2CAP_TX_MTU
|
||||
#endif
|
||||
|
||||
struct bt_att_hdr {
|
||||
u8_t code;
|
||||
} __packed;
|
||||
|
||||
#define BT_ATT_OP_ERROR_RSP 0x01
|
||||
struct bt_att_error_rsp {
|
||||
u8_t request;
|
||||
u16_t handle;
|
||||
u8_t error;
|
||||
} __packed;
|
||||
|
||||
#define BT_ATT_OP_MTU_REQ 0x02
|
||||
struct bt_att_exchange_mtu_req {
|
||||
u16_t mtu;
|
||||
} __packed;
|
||||
|
||||
#define BT_ATT_OP_MTU_RSP 0x03
|
||||
struct bt_att_exchange_mtu_rsp {
|
||||
u16_t mtu;
|
||||
} __packed;
|
||||
|
||||
/* Find Information Request */
|
||||
#define BT_ATT_OP_FIND_INFO_REQ 0x04
|
||||
struct bt_att_find_info_req {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
} __packed;
|
||||
|
||||
/* Format field values for BT_ATT_OP_FIND_INFO_RSP */
|
||||
#define BT_ATT_INFO_16 0x01
|
||||
#define BT_ATT_INFO_128 0x02
|
||||
|
||||
struct bt_att_info_16 {
|
||||
u16_t handle;
|
||||
u16_t uuid;
|
||||
} __packed;
|
||||
|
||||
struct bt_att_info_128 {
|
||||
u16_t handle;
|
||||
u8_t uuid[16];
|
||||
} __packed;
|
||||
|
||||
/* Find Information Response */
|
||||
#define BT_ATT_OP_FIND_INFO_RSP 0x05
|
||||
struct bt_att_find_info_rsp {
|
||||
u8_t format;
|
||||
u8_t info[0];
|
||||
} __packed;
|
||||
|
||||
/* Find By Type Value Request */
|
||||
#define BT_ATT_OP_FIND_TYPE_REQ 0x06
|
||||
struct bt_att_find_type_req {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u16_t type;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
struct bt_att_handle_group {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
} __packed;
|
||||
|
||||
/* Find By Type Value Response */
|
||||
#define BT_ATT_OP_FIND_TYPE_RSP 0x07
|
||||
struct bt_att_find_type_rsp {
|
||||
struct bt_att_handle_group list[0];
|
||||
} __packed;
|
||||
|
||||
/* Read By Type Request */
|
||||
#define BT_ATT_OP_READ_TYPE_REQ 0x08
|
||||
struct bt_att_read_type_req {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
struct bt_att_data {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Read By Type Response */
|
||||
#define BT_ATT_OP_READ_TYPE_RSP 0x09
|
||||
struct bt_att_read_type_rsp {
|
||||
u8_t len;
|
||||
struct bt_att_data data[0];
|
||||
} __packed;
|
||||
|
||||
/* Read Request */
|
||||
#define BT_ATT_OP_READ_REQ 0x0a
|
||||
struct bt_att_read_req {
|
||||
u16_t handle;
|
||||
} __packed;
|
||||
|
||||
/* Read Response */
|
||||
#define BT_ATT_OP_READ_RSP 0x0b
|
||||
struct bt_att_read_rsp {
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Read Blob Request */
|
||||
#define BT_ATT_OP_READ_BLOB_REQ 0x0c
|
||||
struct bt_att_read_blob_req {
|
||||
u16_t handle;
|
||||
u16_t offset;
|
||||
} __packed;
|
||||
|
||||
/* Read Blob Response */
|
||||
#define BT_ATT_OP_READ_BLOB_RSP 0x0d
|
||||
struct bt_att_read_blob_rsp {
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Read Multiple Request */
|
||||
#define BT_ATT_READ_MULT_MIN_LEN_REQ 0x04
|
||||
|
||||
#define BT_ATT_OP_READ_MULT_REQ 0x0e
|
||||
struct bt_att_read_mult_req {
|
||||
u16_t handles[0];
|
||||
} __packed;
|
||||
|
||||
/* Read Multiple Respose */
|
||||
#define BT_ATT_OP_READ_MULT_RSP 0x0f
|
||||
struct bt_att_read_mult_rsp {
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Read by Group Type Request */
|
||||
#define BT_ATT_OP_READ_GROUP_REQ 0x10
|
||||
struct bt_att_read_group_req {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
struct bt_att_group_data {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Read by Group Type Response */
|
||||
#define BT_ATT_OP_READ_GROUP_RSP 0x11
|
||||
struct bt_att_read_group_rsp {
|
||||
u8_t len;
|
||||
struct bt_att_group_data data[0];
|
||||
} __packed;
|
||||
|
||||
/* Write Request */
|
||||
#define BT_ATT_OP_WRITE_REQ 0x12
|
||||
struct bt_att_write_req {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Write Response */
|
||||
#define BT_ATT_OP_WRITE_RSP 0x13
|
||||
|
||||
/* Prepare Write Request */
|
||||
#define BT_ATT_OP_PREPARE_WRITE_REQ 0x16
|
||||
struct bt_att_prepare_write_req {
|
||||
u16_t handle;
|
||||
u16_t offset;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Prepare Write Respond */
|
||||
#define BT_ATT_OP_PREPARE_WRITE_RSP 0x17
|
||||
struct bt_att_prepare_write_rsp {
|
||||
u16_t handle;
|
||||
u16_t offset;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Execute Write Request */
|
||||
#define BT_ATT_FLAG_CANCEL 0x00
|
||||
#define BT_ATT_FLAG_EXEC 0x01
|
||||
|
||||
#define BT_ATT_OP_EXEC_WRITE_REQ 0x18
|
||||
struct bt_att_exec_write_req {
|
||||
u8_t flags;
|
||||
} __packed;
|
||||
|
||||
/* Execute Write Response */
|
||||
#define BT_ATT_OP_EXEC_WRITE_RSP 0x19
|
||||
|
||||
/* Handle Value Notification */
|
||||
#define BT_ATT_OP_NOTIFY 0x1b
|
||||
struct bt_att_notify {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Handle Value Indication */
|
||||
#define BT_ATT_OP_INDICATE 0x1d
|
||||
struct bt_att_indicate {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Handle Value Confirm */
|
||||
#define BT_ATT_OP_CONFIRM 0x1e
|
||||
|
||||
struct bt_att_signature {
|
||||
u8_t value[12];
|
||||
} __packed;
|
||||
|
||||
/* Write Command */
|
||||
#define BT_ATT_OP_WRITE_CMD 0x52
|
||||
struct bt_att_write_cmd {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* Signed Write Command */
|
||||
#define BT_ATT_OP_SIGNED_WRITE_CMD 0xd2
|
||||
struct bt_att_signed_write_cmd {
|
||||
u16_t handle;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
void att_pdu_sent(struct bt_conn *conn, void *user_data);
|
||||
|
||||
void att_cfm_sent(struct bt_conn *conn, void *user_data);
|
||||
|
||||
void att_rsp_sent(struct bt_conn *conn, void *user_data);
|
||||
|
||||
void att_req_sent(struct bt_conn *conn, void *user_data);
|
||||
|
||||
void bt_att_init(void);
|
||||
u16_t bt_att_get_mtu(struct bt_conn *conn);
|
||||
|
||||
#if defined(CONFIG_BLE_AT_CMD)
|
||||
void set_mtu_size(u16_t size);
|
||||
#endif
|
||||
|
||||
struct net_buf *bt_att_create_pdu(struct bt_conn *conn, u8_t op,
|
||||
size_t len);
|
||||
|
||||
/* Send ATT PDU over a connection */
|
||||
int bt_att_send(struct bt_conn *conn, struct net_buf *buf, bt_conn_tx_cb_t cb,
|
||||
void *user_data);
|
||||
|
||||
/* Send ATT Request over a connection */
|
||||
int bt_att_req_send(struct bt_conn *conn, struct bt_att_req *req);
|
||||
|
||||
/* Cancel ATT request */
|
||||
void bt_att_req_cancel(struct bt_conn *conn, struct bt_att_req *req);
|
739
components/ble/ble_stack/host/avdtp.c
Normal file
739
components/ble/ble_stack/host/avdtp.c
Normal file
|
@ -0,0 +1,739 @@
|
|||
/*
|
||||
* Audio Video Distribution Protocol
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#include <atomic.h>
|
||||
#include <byteorder.h>
|
||||
#include <util.h>
|
||||
|
||||
#include <hci_host.h>
|
||||
#include <bluetooth.h>
|
||||
#include <l2cap.h>
|
||||
#include <a2dp.h>
|
||||
#include <avdtp.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_AVDTP)
|
||||
#define LOG_MODULE_NAME bt_avdtp
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "conn_internal.h"
|
||||
#include "l2cap_internal.h"
|
||||
#include "avdtp_internal.h"
|
||||
#include "a2dp-codec.h"
|
||||
|
||||
#define AVDTP_MSG_POISTION 0x00
|
||||
#define AVDTP_PKT_POSITION 0x02
|
||||
#define AVDTP_TID_POSITION 0x04
|
||||
#define AVDTP_SIGID_MASK 0x3f
|
||||
|
||||
#define AVDTP_GET_TR_ID(hdr) ((hdr & 0xf0) >> AVDTP_TID_POSITION)
|
||||
#define AVDTP_GET_MSG_TYPE(hdr) (hdr & 0x03)
|
||||
#define AVDTP_GET_PKT_TYPE(hdr) ((hdr & 0x0c) >> AVDTP_PKT_POSITION)
|
||||
#define AVDTP_GET_SIG_ID(s) (s & AVDTP_SIGID_MASK)
|
||||
|
||||
static struct bt_avdtp_event_cb *event_cb;
|
||||
|
||||
static struct bt_avdtp_seid_lsep *lseps;
|
||||
|
||||
extern struct bt_a2dp_codec_sbc_params sbc_info;
|
||||
|
||||
#define AVDTP_CHAN(_ch) CONTAINER_OF(_ch, struct bt_avdtp, br_chan.chan)
|
||||
|
||||
#define AVDTP_KWORK(_work) CONTAINER_OF(_work, struct bt_avdtp_req,\
|
||||
timeout_work)
|
||||
|
||||
#define AVDTP_TIMEOUT K_SECONDS(6)
|
||||
|
||||
|
||||
static void handle_avdtp_discover_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_get_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_set_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_get_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_reconf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_open_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_start_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_close_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_suspend_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_abort_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_sec_ctrl_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_get_all_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
static void handle_avdtp_dly_rpt_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type);
|
||||
|
||||
static const struct {
|
||||
uint8_t sig_id;
|
||||
void (*func)(struct bt_avdtp *session, struct net_buf *buf,
|
||||
uint8_t msg_type);
|
||||
} handler[] = {
|
||||
{BT_AVDTP_DISCOVER, handle_avdtp_discover_cmd},
|
||||
{BT_AVDTP_GET_CAPABILITIES, handle_avdtp_get_cap_cmd},
|
||||
{BT_AVDTP_SET_CONFIGURATION, handle_avdtp_set_conf_cmd},
|
||||
{BT_AVDTP_GET_CONFIGURATION, handle_avdtp_get_conf_cmd},
|
||||
{BT_AVDTP_RECONFIGURE, handle_avdtp_reconf_cmd},
|
||||
{BT_AVDTP_OPEN, handle_avdtp_open_cmd},
|
||||
{BT_AVDTP_START, handle_avdtp_start_cmd},
|
||||
{BT_AVDTP_CLOSE, handle_avdtp_close_cmd},
|
||||
{BT_AVDTP_SUSPEND, handle_avdtp_suspend_cmd},
|
||||
{BT_AVDTP_ABORT, handle_avdtp_abort_cmd},
|
||||
{BT_AVDTP_SECURITY_CONTROL, handle_avdtp_sec_ctrl_cmd},
|
||||
{BT_AVDTP_GET_ALL_CAPABILITIES, handle_avdtp_get_all_cap_cmd},
|
||||
{BT_AVDTP_DELAYREPORT, handle_avdtp_dly_rpt_cmd},
|
||||
};
|
||||
|
||||
static int avdtp_send(struct bt_avdtp *session,
|
||||
struct net_buf *buf, struct bt_avdtp_req *req)
|
||||
{
|
||||
int result;
|
||||
struct bt_avdtp_single_sig_hdr *hdr;
|
||||
|
||||
hdr = (struct bt_avdtp_single_sig_hdr *)buf->data;
|
||||
|
||||
result = bt_l2cap_chan_send(&session->br_chan.chan, buf);
|
||||
if (result < 0) {
|
||||
BT_ERR("Error:L2CAP send fail - result = %d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*Save the sent request*/
|
||||
req->sig = AVDTP_GET_SIG_ID(hdr->signal_id);
|
||||
req->tid = AVDTP_GET_TR_ID(hdr->hdr);
|
||||
BT_DBG("sig 0x%02X, tid 0x%02X", req->sig, req->tid);
|
||||
|
||||
session->req = req;
|
||||
/* Start timeout work */
|
||||
k_delayed_work_submit(&session->req->timeout_work, AVDTP_TIMEOUT);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct net_buf *avdtp_create_pdu(uint8_t msg_type,
|
||||
uint8_t pkt_type,
|
||||
uint8_t sig_id)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
static uint8_t tid;
|
||||
struct bt_avdtp_single_sig_hdr *hdr;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
buf = bt_l2cap_create_pdu(NULL, 0);
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
|
||||
hdr->hdr = (msg_type | pkt_type << AVDTP_PKT_POSITION |
|
||||
tid++ << AVDTP_TID_POSITION);
|
||||
tid %= 16; /* Loop for 16*/
|
||||
hdr->signal_id = sig_id & AVDTP_SIGID_MASK;
|
||||
|
||||
BT_DBG("hdr = 0x%02X, Signal_ID = 0x%02X", hdr->hdr, hdr->signal_id);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Timeout handler */
|
||||
static void avdtp_timeout(struct k_work *work)
|
||||
{
|
||||
BT_DBG("Failed Signal_id = %d", (AVDTP_KWORK(work))->sig);
|
||||
|
||||
/* Gracefully Disconnect the Signalling and streaming L2cap chann*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief avdtp_parsing_capability : parsing avdtp capability content
|
||||
*/
|
||||
static int avdtp_parsing_capability(struct net_buf *buf)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
while (buf->len)
|
||||
{
|
||||
uint8_t svc_cat = net_buf_pull_u8(buf);
|
||||
uint8_t cat_len = net_buf_pull_u8(buf);;
|
||||
switch (svc_cat)
|
||||
{
|
||||
case BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_REPORTING:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_RECOVERY:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_HDR_COMPRESSION:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_MULTIPLEXING:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_MEDIA_CODEC:
|
||||
|
||||
break;
|
||||
|
||||
case BT_AVDTP_SERVICE_CAT_DELAYREPORTING:
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_avdtp_discover_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_DISCOVER);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
struct bt_avdtp_seid_lsep *disc_sep = lseps;
|
||||
uint8_t acp_endpoint[2];
|
||||
while(disc_sep)
|
||||
{
|
||||
acp_endpoint[0] = disc_sep->sep.id << 2 | disc_sep->sep.inuse << 1 |disc_sep->sep.rfa0;
|
||||
acp_endpoint[1] = disc_sep->sep.media_type << 4 | disc_sep->sep.tsep << 3 |disc_sep->sep.rfa1;
|
||||
memcpy(rsp_buf->data + rsp_buf->len, acp_endpoint, 2);
|
||||
rsp_buf->len += 2;
|
||||
disc_sep = disc_sep->next;
|
||||
}
|
||||
|
||||
#if 0
|
||||
BT_DBG("rsp_buf len: %d \n", rsp_buf->len);
|
||||
for(int i = 0; i < rsp_buf->len; i++)
|
||||
{
|
||||
BT_WARN("0x%02x, ", rsp_buf->data[i]);
|
||||
}
|
||||
BT_WARN("\n");
|
||||
#endif
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_get_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_GET_CAPABILITIES);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t svc_cat_1[2];
|
||||
svc_cat_1[0] = BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT;
|
||||
svc_cat_1[1] = 0;
|
||||
memcpy(rsp_buf->data + rsp_buf->len, svc_cat_1, 2);
|
||||
rsp_buf->len += 2;
|
||||
|
||||
uint8_t svc_cat_2[8];
|
||||
svc_cat_2[0] = BT_AVDTP_SERVICE_CAT_MEDIA_CODEC;
|
||||
svc_cat_2[1] = 6;
|
||||
svc_cat_2[2] = BT_A2DP_AUDIO;
|
||||
svc_cat_2[3] = BT_A2DP_CODEC_TYPE_SBC;
|
||||
svc_cat_2[4] = sbc_info.config[0];
|
||||
svc_cat_2[5] = sbc_info.config[1];
|
||||
svc_cat_2[6] = sbc_info.min_bitpool;
|
||||
svc_cat_2[7] = sbc_info.max_bitpool;
|
||||
memcpy(rsp_buf->data + rsp_buf->len, svc_cat_2, 8);
|
||||
rsp_buf->len += 8;
|
||||
|
||||
uint8_t svc_cat_3[4];
|
||||
svc_cat_3[0] = BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION;
|
||||
svc_cat_3[1] = 2;
|
||||
svc_cat_3[2] = BT_AVDTP_CONTENT_PROTECTION_LSB_SCMS_T;
|
||||
svc_cat_3[3] = BT_AVDTP_CONTENT_PROTECTION_MSB;
|
||||
memcpy(rsp_buf->data + rsp_buf->len, svc_cat_3, 4);
|
||||
rsp_buf->len += 4;
|
||||
|
||||
#if 0
|
||||
BT_DBG("rsp_buf len: %d \n", rsp_buf->len);
|
||||
for(int i = 0; i < rsp_buf->len; i++)
|
||||
{
|
||||
BT_WARN("0x%02x, ", rsp_buf->data[i]);
|
||||
}
|
||||
BT_WARN("\n");
|
||||
#endif
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_set_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t acp_seid = net_buf_pull_u8(buf) >> 2;
|
||||
uint8_t int_seid = net_buf_pull_u8(buf) >> 2;
|
||||
|
||||
int res_pars = avdtp_parsing_capability(buf);
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SET_CONFIGURATION);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_get_conf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
}
|
||||
|
||||
static void handle_avdtp_reconf_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
}
|
||||
|
||||
static void handle_avdtp_open_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_OPEN);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_start_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_START);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_close_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_CLOSE);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_suspend_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_SUSPEND);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_abort_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
if(!session)
|
||||
{
|
||||
BT_DBG("Error: Session not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
struct net_buf *rsp_buf;
|
||||
rsp_buf = avdtp_create_pdu(BT_AVDTP_ACCEPT, BT_AVDTP_PACKET_TYPE_SINGLE, BT_AVDTP_ABORT);
|
||||
if (!rsp_buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return;
|
||||
}
|
||||
|
||||
int result = bt_l2cap_chan_send(&session->br_chan.chan, rsp_buf);
|
||||
if (result < 0)
|
||||
{
|
||||
BT_ERR("Error: BT L2CAP send fail - result = %d", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_avdtp_sec_ctrl_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
}
|
||||
|
||||
static void handle_avdtp_get_all_cap_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
}
|
||||
|
||||
static void handle_avdtp_dly_rpt_cmd(struct bt_avdtp *session, struct net_buf *buf, uint8_t msg_type)
|
||||
{
|
||||
BT_DBG(" ");
|
||||
}
|
||||
|
||||
/* L2CAP Interface callbacks */
|
||||
void bt_avdtp_l2cap_connected(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
struct bt_avdtp *session;
|
||||
|
||||
if (!chan) {
|
||||
BT_ERR("Invalid AVDTP chan");
|
||||
return;
|
||||
}
|
||||
|
||||
session = AVDTP_CHAN(chan);
|
||||
BT_DBG("chan %p session %p", chan, session);
|
||||
/* Init the timer */
|
||||
k_delayed_work_init(&session->req->timeout_work, avdtp_timeout);
|
||||
|
||||
}
|
||||
|
||||
void bt_avdtp_l2cap_disconnected(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
struct bt_avdtp *session = AVDTP_CHAN(chan);
|
||||
|
||||
BT_DBG("chan %p session %p", chan, session);
|
||||
session->br_chan.chan.conn = NULL;
|
||||
/* Clear the Pending req if set*/
|
||||
}
|
||||
|
||||
void bt_avdtp_l2cap_encrypt_changed(struct bt_l2cap_chan *chan, uint8_t status)
|
||||
{
|
||||
BT_DBG("");
|
||||
}
|
||||
|
||||
int bt_avdtp_l2cap_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
struct bt_avdtp_single_sig_hdr *hdr;
|
||||
struct bt_avdtp *session = AVDTP_CHAN(chan);
|
||||
uint8_t i, msgtype, sigid, tid;
|
||||
|
||||
if (buf->len < sizeof(*hdr)) {
|
||||
BT_ERR("Recvd Wrong AVDTP Header");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdr = net_buf_pull_mem(buf, sizeof(*hdr));
|
||||
msgtype = AVDTP_GET_MSG_TYPE(hdr->hdr);
|
||||
sigid = AVDTP_GET_SIG_ID(hdr->signal_id);
|
||||
tid = AVDTP_GET_TR_ID(hdr->hdr);
|
||||
|
||||
BT_DBG("msg_type[0x%02x] sig_id[0x%02x] tid[0x%02x]",
|
||||
msgtype, sigid, tid);
|
||||
|
||||
#if 0
|
||||
BT_DBG("avdtp payload len: %d \n", buf->len);
|
||||
for(int i = 0; i < buf->len; i++)
|
||||
{
|
||||
BT_WARN("0x%02x, ", buf->data[i]);
|
||||
}
|
||||
BT_WARN("\n");
|
||||
#endif
|
||||
|
||||
/* validate if there is an outstanding resp expected*/
|
||||
if (msgtype != BT_AVDTP_CMD) {
|
||||
if (session->req == NULL) {
|
||||
BT_DBG("Unexpected peer response");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (session->req->sig != sigid ||
|
||||
session->req->tid != tid) {
|
||||
BT_DBG("Peer mismatch resp, expected sig[0x%02x]"
|
||||
"tid[0x%02x]", session->req->sig,
|
||||
session->req->tid);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0U; i < ARRAY_SIZE(handler); i++) {
|
||||
if (sigid == handler[i].sig_id) {
|
||||
handler[i].func(session, buf, msgtype);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_avdtp_l2cap_media_stream_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
#if 0
|
||||
BT_DBG("avdtp payload len: %d \n", buf->len);
|
||||
for(int i = 0; i < buf->len; i++)
|
||||
{
|
||||
BT_WARN("0x%02x, ", buf->data[i]);
|
||||
}
|
||||
BT_WARN("\n");
|
||||
#endif
|
||||
|
||||
int res = a2dp_sbc_decode_process(buf->data, buf->len);
|
||||
if(res)
|
||||
{
|
||||
BT_DBG("decode fail, error: %d \n", res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*A2DP Layer interface */
|
||||
int bt_avdtp_connect(struct bt_conn *conn, struct bt_avdtp *session)
|
||||
{
|
||||
static const struct bt_l2cap_chan_ops ops = {
|
||||
.connected = bt_avdtp_l2cap_connected,
|
||||
.disconnected = bt_avdtp_l2cap_disconnected,
|
||||
.encrypt_change = bt_avdtp_l2cap_encrypt_changed,
|
||||
.recv = bt_avdtp_l2cap_recv
|
||||
};
|
||||
|
||||
if (!session) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
session->br_chan.chan.ops = &ops;
|
||||
session->br_chan.chan.required_sec_level = BT_SECURITY_L2;
|
||||
|
||||
return bt_l2cap_chan_connect(conn, &session->br_chan.chan,
|
||||
BT_L2CAP_PSM_AVDTP);
|
||||
}
|
||||
|
||||
int bt_avdtp_disconnect(struct bt_avdtp *session)
|
||||
{
|
||||
if (!session) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("session %p", session);
|
||||
|
||||
return bt_l2cap_chan_disconnect(&session->br_chan.chan);
|
||||
}
|
||||
|
||||
int bt_avdtp_l2cap_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||
{
|
||||
struct bt_avdtp *session = NULL;
|
||||
int result;
|
||||
static const struct bt_l2cap_chan_ops ops = {
|
||||
.connected = bt_avdtp_l2cap_connected,
|
||||
.disconnected = bt_avdtp_l2cap_disconnected,
|
||||
.recv = bt_avdtp_l2cap_recv,
|
||||
};
|
||||
|
||||
static const struct bt_l2cap_chan_ops media_ops = {
|
||||
.connected = bt_avdtp_l2cap_connected,
|
||||
.disconnected = bt_avdtp_l2cap_disconnected,
|
||||
.recv = bt_avdtp_l2cap_media_stream_recv,
|
||||
};
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
/* Get the AVDTP session from upper layer */
|
||||
result = event_cb->accept(conn, &session);
|
||||
if (result < 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if(!session->br_chan.chan.conn)
|
||||
{
|
||||
BT_DBG("create l2cap_br signal stream, session %p", session);
|
||||
session->br_chan.chan.ops = &ops;
|
||||
session->br_chan.rx.mtu = BT_AVDTP_MAX_MTU;
|
||||
*chan = &session->br_chan.chan;
|
||||
}
|
||||
else
|
||||
{
|
||||
BT_DBG("create l2cap_br AV stream, session %p", session);
|
||||
session->streams->chan.chan.ops = &media_ops;
|
||||
session->streams->chan.rx.mtu = BT_AVDTP_MAX_MTU;
|
||||
*chan = &session->streams->chan.chan;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Application will register its callback */
|
||||
int bt_avdtp_register(struct bt_avdtp_event_cb *cb)
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
if (event_cb) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
event_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_avdtp_register_sep(uint8_t media_type, uint8_t role,
|
||||
struct bt_avdtp_seid_lsep *lsep)
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
static uint8_t bt_avdtp_seid = BT_AVDTP_MIN_SEID;
|
||||
|
||||
if (!lsep) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (bt_avdtp_seid == BT_AVDTP_MAX_SEID) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
lsep->sep.id = bt_avdtp_seid++;
|
||||
lsep->sep.inuse = 0U;
|
||||
lsep->sep.media_type = media_type;
|
||||
lsep->sep.tsep = role;
|
||||
|
||||
lsep->next = lseps;
|
||||
lseps = lsep;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init function */
|
||||
int bt_avdtp_init(void)
|
||||
{
|
||||
int err;
|
||||
static struct bt_l2cap_server avdtp_l2cap = {
|
||||
.psm = BT_L2CAP_PSM_AVDTP,
|
||||
.sec_level = BT_SECURITY_L2,
|
||||
.accept = bt_avdtp_l2cap_accept,
|
||||
};
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
/* Register AVDTP PSM with L2CAP */
|
||||
err = bt_l2cap_br_server_register(&avdtp_l2cap);
|
||||
if (err < 0) {
|
||||
BT_ERR("AVDTP L2CAP Registration failed %d", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* AVDTP Discover Request */
|
||||
int bt_avdtp_discover(struct bt_avdtp *session,
|
||||
struct bt_avdtp_discover_params *param)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
|
||||
BT_DBG("");
|
||||
if (!param || !session) {
|
||||
BT_DBG("Error: Callback/Session not valid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = avdtp_create_pdu(BT_AVDTP_CMD,
|
||||
BT_AVDTP_PACKET_TYPE_SINGLE,
|
||||
BT_AVDTP_DISCOVER);
|
||||
if (!buf) {
|
||||
BT_ERR("Error: No Buff available");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Body of the message */
|
||||
|
||||
return avdtp_send(session, buf, ¶m->req);
|
||||
}
|
175
components/ble/ble_stack/host/avdtp_internal.h
Normal file
175
components/ble/ble_stack/host/avdtp_internal.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* avdtp_internal.h - avdtp handling
|
||||
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <avdtp.h>
|
||||
|
||||
/* @brief A2DP ROLE's */
|
||||
#define A2DP_SRC_ROLE 0x00
|
||||
#define A2DP_SNK_ROLE 0x01
|
||||
|
||||
/* @brief AVDTP Role */
|
||||
#define BT_AVDTP_INT 0x00
|
||||
#define BT_AVDTP_ACP 0x01
|
||||
|
||||
#define BT_L2CAP_PSM_AVDTP 0x0019
|
||||
|
||||
/* AVDTP SIGNAL HEADER - Packet Type*/
|
||||
#define BT_AVDTP_PACKET_TYPE_SINGLE 0x00
|
||||
#define BT_AVDTP_PACKET_TYPE_START 0x01
|
||||
#define BT_AVDTP_PACKET_TYPE_CONTINUE 0x02
|
||||
#define BT_AVDTP_PACKET_TYPE_END 0x03
|
||||
|
||||
/* AVDTP SIGNAL HEADER - MESSAGE TYPE */
|
||||
#define BT_AVDTP_CMD 0x00
|
||||
#define BT_AVDTP_GEN_REJECT 0x01
|
||||
#define BT_AVDTP_ACCEPT 0x02
|
||||
#define BT_AVDTP_REJECT 0x03
|
||||
|
||||
/* @brief AVDTP SIGNAL HEADER - Signal Identifier */
|
||||
#define BT_AVDTP_DISCOVER 0x01
|
||||
#define BT_AVDTP_GET_CAPABILITIES 0x02
|
||||
#define BT_AVDTP_SET_CONFIGURATION 0x03
|
||||
#define BT_AVDTP_GET_CONFIGURATION 0x04
|
||||
#define BT_AVDTP_RECONFIGURE 0x05
|
||||
#define BT_AVDTP_OPEN 0x06
|
||||
#define BT_AVDTP_START 0x07
|
||||
#define BT_AVDTP_CLOSE 0x08
|
||||
#define BT_AVDTP_SUSPEND 0x09
|
||||
#define BT_AVDTP_ABORT 0x0a
|
||||
#define BT_AVDTP_SECURITY_CONTROL 0x0b
|
||||
#define BT_AVDTP_GET_ALL_CAPABILITIES 0x0c
|
||||
#define BT_AVDTP_DELAYREPORT 0x0d
|
||||
|
||||
/* @brief AVDTP STREAM STATE */
|
||||
#define BT_AVDTP_STREAM_STATE_IDLE 0x01
|
||||
#define BT_AVDTP_STREAM_STATE_CONFIGURED 0x02
|
||||
#define BT_AVDTP_STREAM_STATE_OPEN 0x03
|
||||
#define BT_AVDTP_STREAM_STATE_STREAMING 0x04
|
||||
#define BT_AVDTP_STREAM_STATE_CLOSING 0x05
|
||||
|
||||
/* @brief AVDTP Media TYPE */
|
||||
#define BT_AVDTP_SERVICE_CAT_MEDIA_TRANSPORT 0x01
|
||||
#define BT_AVDTP_SERVICE_CAT_REPORTING 0x02
|
||||
#define BT_AVDTP_SERVICE_CAT_RECOVERY 0x03
|
||||
#define BT_AVDTP_SERVICE_CAT_CONTENT_PROTECTION 0x04
|
||||
#define BT_AVDTP_SERVICE_CAT_HDR_COMPRESSION 0x05
|
||||
#define BT_AVDTP_SERVICE_CAT_MULTIPLEXING 0x06
|
||||
#define BT_AVDTP_SERVICE_CAT_MEDIA_CODEC 0x07
|
||||
#define BT_AVDTP_SERVICE_CAT_DELAYREPORTING 0x08
|
||||
|
||||
/* @brief AVDTP Content Protection Capabilities */
|
||||
#define BT_AVDTP_CONTENT_PROTECTION_MSB 0x00
|
||||
#define BT_AVDTP_CONTENT_PROTECTION_LSB_DTCP 0x01
|
||||
#define BT_AVDTP_CONTENT_PROTECTION_LSB_SCMS_T 0x02
|
||||
|
||||
/* AVDTP Error Codes */
|
||||
#define BT_AVDTP_SUCCESS 0x00
|
||||
#define BT_AVDTP_ERR_BAD_HDR_FORMAT 0x01
|
||||
#define BT_AVDTP_ERR_BAD_LENGTH 0x11
|
||||
#define BT_AVDTP_ERR_BAD_ACP_SEID 0x12
|
||||
#define BT_AVDTP_ERR_SEP_IN_USE 0x13
|
||||
#define BT_AVDTP_ERR_SEP_NOT_IN_USE 0x14
|
||||
#define BT_AVDTP_ERR_BAD_SERV_CATEGORY 0x17
|
||||
#define BT_AVDTP_ERR_BAD_PAYLOAD_FORMAT 0x18
|
||||
#define BT_AVDTP_ERR_NOT_SUPPORTED_COMMAND 0x19
|
||||
#define BT_AVDTP_ERR_INVALID_CAPABILITIES 0x1a
|
||||
#define BT_AVDTP_ERR_BAD_RECOVERY_TYPE 0x22
|
||||
#define BT_AVDTP_ERR_BAD_MEDIA_TRANSPORT_FORMAT 0x23
|
||||
#define BT_AVDTP_ERR_BAD_RECOVERY_FORMAT 0x25
|
||||
#define BT_AVDTP_ERR_BAD_ROHC_FORMAT 0x26
|
||||
#define BT_AVDTP_ERR_BAD_CP_FORMAT 0x27
|
||||
#define BT_AVDTP_ERR_BAD_MULTIPLEXING_FORMAT 0x28
|
||||
#define BT_AVDTP_ERR_UNSUPPORTED_CONFIGURAION 0x29
|
||||
#define BT_AVDTP_ERR_BAD_STATE 0x31
|
||||
|
||||
#define BT_AVDTP_MAX_MTU CONFIG_BT_L2CAP_RX_MTU
|
||||
|
||||
#define BT_AVDTP_MIN_SEID 0x01
|
||||
#define BT_AVDTP_MAX_SEID 0x3E
|
||||
|
||||
struct bt_avdtp;
|
||||
struct bt_avdtp_req;
|
||||
|
||||
typedef int (*bt_avdtp_func_t)(struct bt_avdtp *session,
|
||||
struct bt_avdtp_req *req);
|
||||
|
||||
struct bt_avdtp_req {
|
||||
uint8_t sig;
|
||||
uint8_t tid;
|
||||
bt_avdtp_func_t func;
|
||||
struct k_delayed_work timeout_work;
|
||||
};
|
||||
|
||||
struct bt_avdtp_single_sig_hdr {
|
||||
uint8_t hdr;
|
||||
uint8_t signal_id;
|
||||
} __packed;
|
||||
|
||||
#define BT_AVDTP_SIG_HDR_LEN sizeof(struct bt_avdtp_single_sig_hdr)
|
||||
|
||||
struct bt_avdtp_ind_cb {
|
||||
/*
|
||||
* discovery_ind;
|
||||
* get_capabilities_ind;
|
||||
* set_configuration_ind;
|
||||
* open_ind;
|
||||
* start_ind;
|
||||
* suspend_ind;
|
||||
* close_ind;
|
||||
*/
|
||||
};
|
||||
|
||||
struct bt_avdtp_cap {
|
||||
uint8_t cat;
|
||||
uint8_t len;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
struct bt_avdtp_sep {
|
||||
uint8_t seid;
|
||||
uint8_t len;
|
||||
struct bt_avdtp_cap caps[0];
|
||||
};
|
||||
|
||||
struct bt_avdtp_discover_params {
|
||||
struct bt_avdtp_req req;
|
||||
uint8_t status;
|
||||
struct bt_avdtp_sep *caps;
|
||||
};
|
||||
|
||||
/** @brief Global AVDTP session structure. */
|
||||
struct bt_avdtp {
|
||||
struct bt_l2cap_br_chan br_chan;
|
||||
struct bt_avdtp_stream *streams; /* List of AV streams */
|
||||
struct bt_avdtp_req *req;
|
||||
};
|
||||
|
||||
struct bt_avdtp_event_cb {
|
||||
struct bt_avdtp_ind_cb *ind;
|
||||
int (*accept)(struct bt_conn *conn, struct bt_avdtp **session);
|
||||
};
|
||||
|
||||
/* Initialize AVDTP layer*/
|
||||
int bt_avdtp_init(void);
|
||||
|
||||
/* Application register with AVDTP layer */
|
||||
int bt_avdtp_register(struct bt_avdtp_event_cb *cb);
|
||||
|
||||
/* AVDTP connect */
|
||||
int bt_avdtp_connect(struct bt_conn *conn, struct bt_avdtp *session);
|
||||
|
||||
/* AVDTP disconnect */
|
||||
int bt_avdtp_disconnect(struct bt_avdtp *session);
|
||||
|
||||
/* AVDTP SEP register function */
|
||||
int bt_avdtp_register_sep(uint8_t media_type, uint8_t role,
|
||||
struct bt_avdtp_seid_lsep *sep);
|
||||
|
||||
/* AVDTP Discover Request */
|
||||
int bt_avdtp_discover(struct bt_avdtp *session,
|
||||
struct bt_avdtp_discover_params *param);
|
372
components/ble/ble_stack/host/bl_host_assist.c
Normal file
372
components/ble/ble_stack/host/bl_host_assist.c
Normal file
|
@ -0,0 +1,372 @@
|
|||
#include "ble_lib_api.h"
|
||||
#include "bluetooth.h"
|
||||
#include "conn.h"
|
||||
#include "hci_core.h"
|
||||
#include "hci_driver.h"
|
||||
#include "byteorder.h"
|
||||
#include "log.h"
|
||||
#include "errno.h"
|
||||
|
||||
struct blhast_le_adv_data{
|
||||
u8_t ad[31];
|
||||
size_t ad_len;
|
||||
};
|
||||
|
||||
static struct bt_le_scan_param blhast_le_scan_param;
|
||||
static struct bt_le_adv_param blhast_le_adv_param;
|
||||
static struct blhast_le_adv_data blhast_le_ad;
|
||||
static struct blhast_le_adv_data blhast_le_sd;
|
||||
static bt_le_scan_cb_t *blhast_le_scan_cb;
|
||||
|
||||
static void blhast_ble_scan_assist_cb(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb);
|
||||
static void blhast_ble_adv_assist_cb(const struct bt_le_adv_param *param, const struct bt_data *ad,
|
||||
size_t ad_len, const struct bt_data *sd, size_t sd_len);
|
||||
|
||||
static struct blhast_cb assist_cb = {
|
||||
.le_scan_cb = blhast_ble_scan_assist_cb,
|
||||
.le_adv_cb = blhast_ble_adv_assist_cb,
|
||||
};
|
||||
|
||||
extern struct bt_dev bt_dev;
|
||||
|
||||
static void blhast_ble_scan_assist_cb(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb)
|
||||
{
|
||||
memcpy(&blhast_le_scan_param, param, sizeof(struct bt_le_scan_param));
|
||||
blhast_le_scan_cb = cb;
|
||||
}
|
||||
|
||||
static void blhast_ble_get_ad(const struct bt_data *ad, size_t ad_len, uint8_t *output)
|
||||
{
|
||||
int i;
|
||||
uint8_t data_len = 0;
|
||||
|
||||
for (i = 0; i < ad_len; i++) {
|
||||
*(output + data_len) = ad[i].type;
|
||||
data_len++;
|
||||
|
||||
*(output + data_len) = ad[i].data_len;
|
||||
data_len++;
|
||||
|
||||
memcpy(output + data_len, ad[i].data, ad[i].data_len);
|
||||
|
||||
data_len += ad[i].data_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void blhast_ble_construct_ad(struct blhast_le_adv_data *adv_data, struct bt_data *output)
|
||||
{
|
||||
int i;
|
||||
size_t ad_len = adv_data->ad_len;
|
||||
u8_t *p_ad = adv_data->ad;
|
||||
|
||||
|
||||
for(i = 0; i < ad_len; i++){
|
||||
memcpy(&output[i], p_ad, 2);//type, data_len
|
||||
p_ad += 2;
|
||||
output[i].data = (const u8_t *)p_ad;
|
||||
p_ad += output[i].data_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void blhast_ble_adv_assist_cb(const struct bt_le_adv_param *param, const struct bt_data *ad,
|
||||
size_t ad_len, const struct bt_data *sd, size_t sd_len)
|
||||
{
|
||||
memcpy(&blhast_le_adv_param, param, sizeof(struct bt_le_adv_param));
|
||||
|
||||
if(ad){
|
||||
blhast_le_ad.ad_len = ad_len;
|
||||
memset(blhast_le_ad.ad, 0, sizeof(blhast_le_ad.ad));
|
||||
blhast_ble_get_ad(ad, ad_len, blhast_le_ad.ad);
|
||||
}
|
||||
|
||||
if(sd){
|
||||
blhast_le_sd.ad_len = sd_len;
|
||||
memset(blhast_le_sd.ad, 0, sizeof(blhast_le_sd.ad));
|
||||
blhast_ble_get_ad(sd, sd_len, blhast_le_sd.ad);
|
||||
}
|
||||
}
|
||||
|
||||
static int blhast_common_reset(void)
|
||||
{
|
||||
|
||||
struct net_buf *rsp;
|
||||
int err;
|
||||
|
||||
if (!(bt_dev.drv->quirks & BT_QUIRK_NO_RESET)) {
|
||||
/* Send HCI_RESET */
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_RESET, NULL, &rsp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
bt_hci_reset_complete(rsp);
|
||||
net_buf_unref(rsp);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
|
||||
err = bt_set_flow_control();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_BT_HCI_ACL_FLOW_CONTROL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blhast_ble_reset(void)
|
||||
{
|
||||
struct bt_hci_cp_write_le_host_supp *cp_le;
|
||||
struct net_buf *buf, *rsp;
|
||||
int err;
|
||||
|
||||
if (!BT_FEAT_LE(bt_dev.features)) {
|
||||
BT_ERR("Non-LE capable controller detected!");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (BT_FEAT_BREDR(bt_dev.features)) {
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP,
|
||||
sizeof(*cp_le));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
cp_le = net_buf_add(buf, sizeof(*cp_le));
|
||||
|
||||
/* Explicitly enable LE for dual-mode controllers */
|
||||
cp_le->le = 0x01;
|
||||
cp_le->simul = 0x00;
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_LE_HOST_SUPP, buf,
|
||||
NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CONN) &&
|
||||
IS_ENABLED(CONFIG_BT_DATA_LEN_UPDATE) &&
|
||||
BT_FEAT_LE_DLE(bt_dev.le.features)) {
|
||||
struct bt_hci_cp_le_write_default_data_len *cp;
|
||||
struct bt_hci_rp_le_read_max_data_len *rp;
|
||||
u16_t tx_octets, tx_time;
|
||||
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_READ_MAX_DATA_LEN, NULL,
|
||||
&rsp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
rp = (void *)rsp->data;
|
||||
tx_octets = sys_le16_to_cpu(rp->max_tx_octets);
|
||||
tx_time = sys_le16_to_cpu(rp->max_tx_time);
|
||||
net_buf_unref(rsp);
|
||||
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN,
|
||||
sizeof(*cp));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
cp = net_buf_add(buf, sizeof(*cp));
|
||||
cp->max_tx_octets = sys_cpu_to_le16(tx_octets);
|
||||
cp->max_tx_time = sys_cpu_to_le16(tx_time);
|
||||
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_WRITE_DEFAULT_DATA_LEN,
|
||||
buf, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return bt_le_set_event_mask();
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
static int blhast_br_reset(void)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
struct bt_hci_cp_write_ssp_mode *ssp_cp;
|
||||
struct bt_hci_cp_write_inquiry_mode *inq_cp;
|
||||
struct bt_hci_write_local_name *name_cp;
|
||||
int err;
|
||||
|
||||
|
||||
/* Set SSP mode */
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SSP_MODE, sizeof(*ssp_cp));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
ssp_cp = net_buf_add(buf, sizeof(*ssp_cp));
|
||||
ssp_cp->mode = 0x01;
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SSP_MODE, buf, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable Inquiry results with RSSI or extended Inquiry */
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_INQUIRY_MODE, sizeof(*inq_cp));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
inq_cp = net_buf_add(buf, sizeof(*inq_cp));
|
||||
inq_cp->mode = 0x02;
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_INQUIRY_MODE, buf, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set local name */
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_LOCAL_NAME, sizeof(*name_cp));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
name_cp = net_buf_add(buf, sizeof(*name_cp));
|
||||
strncpy((char *)name_cp->local_name, CONFIG_BT_DEVICE_NAME,
|
||||
sizeof(name_cp->local_name));
|
||||
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_LOCAL_NAME, buf, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set page timeout*/
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_PAGE_TIMEOUT, sizeof(u16_t));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
net_buf_add_le16(buf, CONFIG_BT_PAGE_TIMEOUT);
|
||||
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_PAGE_TIMEOUT, buf, NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Enable BR/EDR SC if supported */
|
||||
if (BT_FEAT_SC(bt_dev.features)) {
|
||||
struct bt_hci_cp_write_sc_host_supp *sc_cp;
|
||||
|
||||
buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_SC_HOST_SUPP,
|
||||
sizeof(*sc_cp));
|
||||
if (!buf) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
sc_cp = net_buf_add(buf, sizeof(*sc_cp));
|
||||
sc_cp->sc_support = 0x01;
|
||||
|
||||
err = bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_SC_HOST_SUPP, buf,
|
||||
NULL);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int blhast_host_hci_reset(void)
|
||||
{
|
||||
int err;
|
||||
ATOMIC_DEFINE(old_flags, BT_DEV_NUM_FLAGS);
|
||||
memcpy(old_flags, bt_dev.flags, sizeof(old_flags));
|
||||
|
||||
err = blhast_common_reset();
|
||||
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = blhast_ble_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
if (BT_FEAT_BREDR(bt_dev.features)) {
|
||||
err = blhast_br_reset();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = bt_set_event_mask();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
memcpy(bt_dev.flags, old_flags, sizeof(old_flags));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blhast_host_state_restore(void)
|
||||
{
|
||||
struct bt_data *ad = NULL;
|
||||
struct bt_data *sd = NULL;
|
||||
k_sem_give(&bt_dev.ncmd_sem);
|
||||
net_buf_unref(bt_dev.sent_cmd);
|
||||
bt_dev.sent_cmd = NULL;
|
||||
|
||||
blhast_host_hci_reset();
|
||||
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
bt_notify_disconnected();
|
||||
#endif
|
||||
|
||||
atomic_set_bit(bt_dev.flags, BT_DEV_ASSIST_RUN);
|
||||
|
||||
#if defined(CONFIG_BT_OBSERVER)
|
||||
if(atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN))
|
||||
{
|
||||
BT_WARN("Restore BLE scan\r\n");
|
||||
atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN);
|
||||
atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING);
|
||||
bt_le_scan_start((const struct bt_le_scan_param *)&blhast_le_scan_param, blhast_le_scan_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING))
|
||||
{
|
||||
BT_WARN("Restore BLE advertising\r\n");
|
||||
if(blhast_le_ad.ad_len > 0)
|
||||
{
|
||||
ad = k_malloc(sizeof(struct bt_data) * blhast_le_ad.ad_len);
|
||||
blhast_ble_construct_ad(&blhast_le_ad, ad);
|
||||
}
|
||||
if(blhast_le_sd.ad_len > 0)
|
||||
{
|
||||
sd = k_malloc(sizeof(struct bt_data) * blhast_le_sd.ad_len);
|
||||
blhast_ble_construct_ad(&blhast_le_sd, sd);
|
||||
}
|
||||
|
||||
bt_le_adv_start((const struct bt_le_adv_param *)&blhast_le_adv_param, ad,
|
||||
blhast_le_ad.ad_len, sd, blhast_le_sd.ad_len);
|
||||
|
||||
if(ad)
|
||||
k_free(ad);
|
||||
if(sd)
|
||||
k_free(sd);
|
||||
}
|
||||
|
||||
atomic_clear_bit(bt_dev.flags, BT_DEV_ASSIST_RUN);
|
||||
}
|
||||
|
||||
void blhast_bt_reset(void)
|
||||
{
|
||||
ble_controller_reset();
|
||||
blhast_host_state_restore();
|
||||
}
|
||||
|
||||
void blhast_init(void)
|
||||
{
|
||||
memset(&blhast_le_ad, 0, sizeof(struct blhast_le_adv_data));
|
||||
memset(&blhast_le_sd, 0, sizeof(struct blhast_le_adv_data));
|
||||
bt_register_host_assist_cb(&assist_cb);
|
||||
}
|
2678
components/ble/ble_stack/host/conn.c
Normal file
2678
components/ble/ble_stack/host/conn.c
Normal file
File diff suppressed because it is too large
Load diff
344
components/ble/ble_stack/host/conn_internal.h
Normal file
344
components/ble/ble_stack/host/conn_internal.h
Normal file
|
@ -0,0 +1,344 @@
|
|||
/** @file
|
||||
* @brief Internal APIs for Bluetooth connection handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
typedef enum __packed {
|
||||
BT_CONN_DISCONNECTED,
|
||||
BT_CONN_CONNECT_SCAN,
|
||||
BT_CONN_CONNECT_DIR_ADV,
|
||||
BT_CONN_CONNECT,
|
||||
BT_CONN_CONNECTED,
|
||||
BT_CONN_DISCONNECT,
|
||||
} bt_conn_state_t;
|
||||
|
||||
/* bt_conn flags: the flags defined here represent connection parameters */
|
||||
enum {
|
||||
BT_CONN_AUTO_CONNECT,
|
||||
BT_CONN_BR_LEGACY_SECURE, /* 16 digits legacy PIN tracker */
|
||||
BT_CONN_USER, /* user I/O when pairing */
|
||||
BT_CONN_BR_PAIRING, /* BR connection in pairing context */
|
||||
BT_CONN_BR_NOBOND, /* SSP no bond pairing tracker */
|
||||
BT_CONN_BR_PAIRING_INITIATOR, /* local host starts authentication */
|
||||
BT_CONN_CLEANUP, /* Disconnected, pending cleanup */
|
||||
BT_CONN_AUTO_PHY_UPDATE, /* Auto-update PHY */
|
||||
BT_CONN_SLAVE_PARAM_UPDATE, /* If slave param update timer fired */
|
||||
BT_CONN_SLAVE_PARAM_SET, /* If slave param were set from app */
|
||||
BT_CONN_SLAVE_PARAM_L2CAP, /* If should force L2CAP for CPUP */
|
||||
BT_CONN_FORCE_PAIR, /* Pairing even with existing keys. */
|
||||
|
||||
BT_CONN_AUTO_PHY_COMPLETE, /* Auto-initiated PHY procedure done */
|
||||
BT_CONN_AUTO_FEATURE_EXCH, /* Auto-initiated LE Feat done */
|
||||
BT_CONN_AUTO_VERSION_INFO, /* Auto-initiated LE version done */
|
||||
|
||||
/* Total number of flags - must be at the end of the enum */
|
||||
BT_CONN_NUM_FLAGS,
|
||||
};
|
||||
|
||||
struct bt_conn_le {
|
||||
bt_addr_le_t dst;
|
||||
|
||||
bt_addr_le_t init_addr;
|
||||
bt_addr_le_t resp_addr;
|
||||
|
||||
u16_t interval;
|
||||
u16_t interval_min;
|
||||
u16_t interval_max;
|
||||
|
||||
u16_t latency;
|
||||
u16_t timeout;
|
||||
u16_t pending_latency;
|
||||
u16_t pending_timeout;
|
||||
|
||||
u8_t features[8];
|
||||
|
||||
struct bt_keys *keys;
|
||||
|
||||
#if defined(CONFIG_BT_STACK_PTS)
|
||||
u8_t own_adder_type;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
/* For now reserve space for 2 pages of LMP remote features */
|
||||
#define LMP_MAX_PAGES 2
|
||||
|
||||
struct bt_conn_br {
|
||||
bt_addr_t dst;
|
||||
u8_t remote_io_capa;
|
||||
u8_t remote_auth;
|
||||
u8_t pairing_method;
|
||||
/* remote LMP features pages per 8 bytes each */
|
||||
u8_t features[LMP_MAX_PAGES][8];
|
||||
|
||||
struct bt_keys_link_key *link_key;
|
||||
};
|
||||
|
||||
struct bt_conn_sco {
|
||||
/* Reference to ACL Connection */
|
||||
struct bt_conn *acl;
|
||||
u16_t pkt_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct bt_conn_iso {
|
||||
/* Reference to ACL Connection */
|
||||
struct bt_conn *acl;
|
||||
/* CIG ID */
|
||||
uint8_t cig_id;
|
||||
/* CIS ID */
|
||||
uint8_t cis_id;
|
||||
};
|
||||
|
||||
typedef void (*bt_conn_tx_cb_t)(struct bt_conn *conn, void *user_data);
|
||||
|
||||
struct bt_conn_tx {
|
||||
sys_snode_t node;
|
||||
|
||||
bt_conn_tx_cb_t cb;
|
||||
void *user_data;
|
||||
|
||||
/* Number of pending packets without a callback after this one */
|
||||
u32_t pending_no_cb;
|
||||
};
|
||||
|
||||
struct bt_conn {
|
||||
u16_t handle;
|
||||
u8_t type;
|
||||
u8_t role;
|
||||
|
||||
ATOMIC_DEFINE(flags, BT_CONN_NUM_FLAGS);
|
||||
|
||||
/* Which local identity address this connection uses */
|
||||
u8_t id;
|
||||
|
||||
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
||||
bt_security_t sec_level;
|
||||
bt_security_t required_sec_level;
|
||||
u8_t encrypt;
|
||||
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
||||
|
||||
/* Connection error or reason for disconnect */
|
||||
u8_t err;
|
||||
|
||||
bt_conn_state_t state;
|
||||
|
||||
u16_t rx_len;
|
||||
struct net_buf *rx;
|
||||
|
||||
/* Sent but not acknowledged TX packets with a callback */
|
||||
sys_slist_t tx_pending;
|
||||
/* Sent but not acknowledged TX packets without a callback before
|
||||
* the next packet (if any) in tx_pending.
|
||||
*/
|
||||
u32_t pending_no_cb;
|
||||
|
||||
/* Completed TX for which we need to call the callback */
|
||||
sys_slist_t tx_complete;
|
||||
struct k_work tx_complete_work;
|
||||
|
||||
|
||||
/* Queue for outgoing ACL data */
|
||||
struct k_fifo tx_queue;
|
||||
|
||||
/* Active L2CAP channels */
|
||||
sys_slist_t channels;
|
||||
|
||||
atomic_t ref;
|
||||
|
||||
/* Delayed work for connection update and other deferred tasks */
|
||||
struct k_delayed_work update_work;
|
||||
|
||||
union {
|
||||
struct bt_conn_le le;
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
struct bt_conn_br br;
|
||||
struct bt_conn_sco sco;
|
||||
#endif
|
||||
#if defined(CONFIG_BT_AUDIO)
|
||||
struct bt_conn_iso iso;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BT_REMOTE_VERSION)
|
||||
struct bt_conn_rv {
|
||||
u8_t version;
|
||||
u16_t manufacturer;
|
||||
u16_t subversion;
|
||||
} rv;
|
||||
#endif
|
||||
};
|
||||
|
||||
void bt_conn_reset_rx_state(struct bt_conn *conn);
|
||||
|
||||
/* Process incoming data for a connection */
|
||||
void bt_conn_recv(struct bt_conn *conn, struct net_buf *buf, u8_t flags);
|
||||
|
||||
/* Send data over a connection */
|
||||
int bt_conn_send_cb(struct bt_conn *conn, struct net_buf *buf,
|
||||
bt_conn_tx_cb_t cb, void *user_data);
|
||||
|
||||
static inline int bt_conn_send(struct bt_conn *conn, struct net_buf *buf)
|
||||
{
|
||||
return bt_conn_send_cb(conn, buf, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Add a new LE connection */
|
||||
struct bt_conn *bt_conn_add_le(u8_t id, const bt_addr_le_t *peer);
|
||||
|
||||
/** Connection parameters for ISO connections */
|
||||
struct bt_iso_create_param {
|
||||
uint8_t id;
|
||||
uint8_t num_conns;
|
||||
struct bt_conn **conns;
|
||||
struct bt_iso_chan **chans;
|
||||
};
|
||||
|
||||
/* Bind ISO connections parameters */
|
||||
int bt_conn_bind_iso(struct bt_iso_create_param *param);
|
||||
|
||||
/* Connect ISO connections */
|
||||
int bt_conn_connect_iso(struct bt_conn **conns, uint8_t num_conns);
|
||||
|
||||
/* Add a new ISO connection */
|
||||
struct bt_conn *bt_conn_add_iso(struct bt_conn *acl);
|
||||
|
||||
/* Cleanup ISO references */
|
||||
void bt_iso_cleanup(struct bt_conn *iso_conn);
|
||||
|
||||
/* Allocate new ISO connection */
|
||||
struct bt_conn *iso_conn_new(struct bt_conn *conns, size_t size);
|
||||
|
||||
/* Add a new BR/EDR connection */
|
||||
struct bt_conn *bt_conn_add_br(const bt_addr_t *peer);
|
||||
|
||||
/* Add a new SCO connection */
|
||||
struct bt_conn *bt_conn_add_sco(const bt_addr_t *peer, int link_type);
|
||||
|
||||
/* Cleanup SCO references */
|
||||
void bt_sco_cleanup(struct bt_conn *sco_conn);
|
||||
|
||||
/* Look up an existing sco connection by BT address */
|
||||
struct bt_conn *bt_conn_lookup_addr_sco(const bt_addr_t *peer);
|
||||
|
||||
/* Look up an existing connection by BT address */
|
||||
struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer);
|
||||
|
||||
void bt_conn_pin_code_req(struct bt_conn *conn);
|
||||
u8_t bt_conn_get_io_capa(void);
|
||||
u8_t bt_conn_ssp_get_auth(const struct bt_conn *conn);
|
||||
void bt_conn_ssp_auth(struct bt_conn *conn, u32_t passkey);
|
||||
void bt_conn_ssp_auth_complete(struct bt_conn *conn, u8_t status);
|
||||
|
||||
void bt_conn_disconnect_all(u8_t id);
|
||||
|
||||
/* Look up an existing connection */
|
||||
struct bt_conn *bt_conn_lookup_handle(u16_t handle);
|
||||
|
||||
/* Compare an address with bt_conn destination address */
|
||||
int bt_conn_addr_le_cmp(const struct bt_conn *conn, const bt_addr_le_t *peer);
|
||||
|
||||
|
||||
/* Helpers for identifying & looking up connections based on the the index to
|
||||
* the connection list. This is useful for O(1) lookups, but can't be used
|
||||
* e.g. as the handle since that's assigned to us by the controller.
|
||||
*/
|
||||
#define BT_CONN_ID_INVALID 0xff
|
||||
struct bt_conn *bt_conn_lookup_id(u8_t id);
|
||||
|
||||
/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection
|
||||
* with the specific state
|
||||
*/
|
||||
struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer,
|
||||
const bt_conn_state_t state);
|
||||
|
||||
/* Set connection object in certain state and perform action related to state */
|
||||
void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state);
|
||||
|
||||
int bt_conn_le_conn_update(struct bt_conn *conn,
|
||||
const struct bt_le_conn_param *param);
|
||||
|
||||
void notify_remote_info(struct bt_conn *conn);
|
||||
|
||||
void notify_le_param_updated(struct bt_conn *conn);
|
||||
|
||||
bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param);
|
||||
|
||||
#if defined(CONFIG_BT_SMP)
|
||||
/* rand and ediv should be in BT order */
|
||||
int bt_conn_le_start_encryption(struct bt_conn *conn, u8_t rand[8],
|
||||
u8_t ediv[2], const u8_t *ltk, size_t len);
|
||||
|
||||
/* Notify higher layers that RPA was resolved */
|
||||
void bt_conn_identity_resolved(struct bt_conn *conn);
|
||||
#endif /* CONFIG_BT_SMP */
|
||||
|
||||
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
||||
/* Notify higher layers that connection security changed */
|
||||
void bt_conn_security_changed(struct bt_conn *conn, enum bt_security_err err);
|
||||
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
||||
|
||||
/* Prepare a PDU to be sent over a connection */
|
||||
#if defined(CONFIG_NET_BUF_LOG)
|
||||
struct net_buf *bt_conn_create_pdu_timeout_debug(struct net_buf_pool *pool,
|
||||
size_t reserve, s32_t timeout,
|
||||
const char *func, int line);
|
||||
#define bt_conn_create_pdu_timeout(_pool, _reserve, _timeout) \
|
||||
bt_conn_create_pdu_timeout_debug(_pool, _reserve, _timeout, \
|
||||
__func__, __LINE__)
|
||||
|
||||
#define bt_conn_create_pdu(_pool, _reserve) \
|
||||
bt_conn_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \
|
||||
__func__, __line__)
|
||||
#else
|
||||
struct net_buf *bt_conn_create_pdu_timeout(struct net_buf_pool *pool,
|
||||
size_t reserve, s32_t timeout);
|
||||
|
||||
#define bt_conn_create_pdu(_pool, _reserve) \
|
||||
bt_conn_create_pdu_timeout(_pool, _reserve, K_FOREVER)
|
||||
#endif
|
||||
|
||||
/* Prepare a PDU to be sent over a connection */
|
||||
#if defined(CONFIG_NET_BUF_LOG)
|
||||
struct net_buf *bt_conn_create_frag_timeout_debug(size_t reserve, s32_t timeout,
|
||||
const char *func, int line);
|
||||
|
||||
#define bt_conn_create_frag_timeout(_reserve, _timeout) \
|
||||
bt_conn_create_frag_timeout_debug(_reserve, _timeout, \
|
||||
__func__, __LINE__)
|
||||
|
||||
#define bt_conn_create_frag(_reserve) \
|
||||
bt_conn_create_frag_timeout_debug(_reserve, K_FOREVER, \
|
||||
__func__, __LINE__)
|
||||
#else
|
||||
struct net_buf *bt_conn_create_frag_timeout(size_t reserve, s32_t timeout);
|
||||
|
||||
#define bt_conn_create_frag(_reserve) \
|
||||
bt_conn_create_frag_timeout(_reserve, K_FOREVER)
|
||||
#endif
|
||||
|
||||
/* Initialize connection management */
|
||||
int bt_conn_init(void);
|
||||
|
||||
/* Selects based on connecton type right semaphore for ACL packets */
|
||||
struct k_sem *bt_conn_get_pkts(struct bt_conn *conn);
|
||||
|
||||
/* k_poll related helpers for the TX thread */
|
||||
int bt_conn_prepare_events(struct k_poll_event events[]);
|
||||
void bt_conn_process_tx(struct bt_conn *conn);
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
/** @brief Get connection handle for a connection.
|
||||
*
|
||||
* @param conn Connection object.
|
||||
* @param conn_handle Place to store the Connection handle.
|
||||
*
|
||||
* @return 0 on success or negative error value on failure.
|
||||
*/
|
||||
int bt_hci_get_conn_handle(const struct bt_conn *conn, u16_t *conn_handle);
|
||||
#endif
|
167
components/ble/ble_stack/host/crypto.c
Normal file
167
components/ble/ble_stack/host/crypto.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <misc/byteorder.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <hci_host.h>
|
||||
#include <conn.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <hmac_prng.h>
|
||||
#include <aes.h>
|
||||
#include <utils.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
||||
#define LOG_MODULE_NAME bt_crypto
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
|
||||
static struct tc_hmac_prng_struct prng;
|
||||
|
||||
static int prng_reseed(struct tc_hmac_prng_struct *h)
|
||||
{
|
||||
u8_t seed[32];
|
||||
s64_t extra;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < (sizeof(seed) / 8); i++) {
|
||||
struct bt_hci_rp_le_rand *rp;
|
||||
struct net_buf *rsp;
|
||||
|
||||
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
rp = (void *)rsp->data;
|
||||
memcpy(&seed[i * 8], rp->rand, 8);
|
||||
|
||||
net_buf_unref(rsp);
|
||||
}
|
||||
|
||||
extra = k_uptime_get();
|
||||
|
||||
ret = tc_hmac_prng_reseed(h, seed, sizeof(seed), (u8_t *)&extra,
|
||||
sizeof(extra));
|
||||
if (ret == TC_CRYPTO_FAIL) {
|
||||
BT_ERR("Failed to re-seed PRNG");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prng_init(void)
|
||||
{
|
||||
struct bt_hci_rp_le_rand *rp;
|
||||
struct net_buf *rsp;
|
||||
int ret;
|
||||
|
||||
/* Check first that HCI_LE_Rand is supported */
|
||||
if (!BT_CMD_TEST(bt_dev.supported_commands, 27, 7)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
rp = (void *)rsp->data;
|
||||
|
||||
ret = tc_hmac_prng_init(&prng, rp->rand, sizeof(rp->rand));
|
||||
|
||||
net_buf_unref(rsp);
|
||||
|
||||
if (ret == TC_CRYPTO_FAIL) {
|
||||
BT_ERR("Failed to initialize PRNG");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* re-seed is needed after init */
|
||||
return prng_reseed(&prng);
|
||||
}
|
||||
|
||||
int bt_rand(void *buf, size_t len)
|
||||
{
|
||||
#if !defined(CONFIG_BT_GEN_RANDOM_BY_SW)
|
||||
k_get_random_byte_array(buf, len);
|
||||
return 0;
|
||||
#else
|
||||
int ret;
|
||||
ret = tc_hmac_prng_generate(buf, len, &prng);
|
||||
if (ret == TC_HMAC_PRNG_RESEED_REQ) {
|
||||
ret = prng_reseed(&prng);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = tc_hmac_prng_generate(buf, len, &prng);
|
||||
}
|
||||
|
||||
if (ret == TC_CRYPTO_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
int bt_encrypt_le(const u8_t key[16], const u8_t plaintext[16],
|
||||
u8_t enc_data[16])
|
||||
{
|
||||
struct tc_aes_key_sched_struct s;
|
||||
u8_t tmp[16];
|
||||
|
||||
BT_DBG("key %s", bt_hex(key, 16));
|
||||
BT_DBG("plaintext %s", bt_hex(plaintext, 16));
|
||||
|
||||
sys_memcpy_swap(tmp, key, 16);
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sys_memcpy_swap(tmp, plaintext, 16);
|
||||
|
||||
if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sys_mem_swap(enc_data, 16);
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16],
|
||||
u8_t enc_data[16])
|
||||
{
|
||||
struct tc_aes_key_sched_struct s;
|
||||
|
||||
BT_DBG("key %s", bt_hex(key, 16));
|
||||
BT_DBG("plaintext %s", bt_hex(plaintext, 16));
|
||||
|
||||
if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("enc_data %s", bt_hex(enc_data, 16));
|
||||
|
||||
return 0;
|
||||
}
|
8
components/ble/ble_stack/host/crypto.h
Normal file
8
components/ble/ble_stack/host/crypto.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
int prng_init(void);
|
63
components/ble/ble_stack/host/ecc.h
Normal file
63
components/ble/ble_stack/host/ecc.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* ecc.h - ECDH helpers */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* @brief Container for public key callback */
|
||||
struct bt_pub_key_cb {
|
||||
/** @brief Callback type for Public Key generation.
|
||||
*
|
||||
* Used to notify of the local public key or that the local key is not
|
||||
* available (either because of a failure to read it or because it is
|
||||
* being regenerated).
|
||||
*
|
||||
* @param key The local public key, or NULL in case of no key.
|
||||
*/
|
||||
void (*func)(const u8_t key[64]);
|
||||
|
||||
struct bt_pub_key_cb *_next;
|
||||
};
|
||||
|
||||
/* @brief Generate a new Public Key.
|
||||
*
|
||||
* Generate a new ECC Public Key. The callback will persist even after the
|
||||
* key has been generated, and will be used to notify of new generation
|
||||
* processes (NULL as key).
|
||||
*
|
||||
* @param cb Callback to notify the new key, or NULL to request an update
|
||||
* without registering any new callback.
|
||||
*
|
||||
* @return Zero on success or negative error code otherwise
|
||||
*/
|
||||
int bt_pub_key_gen(struct bt_pub_key_cb *cb);
|
||||
|
||||
/* @brief Get the current Public Key.
|
||||
*
|
||||
* Get the current ECC Public Key.
|
||||
*
|
||||
* @return Current key, or NULL if not available.
|
||||
*/
|
||||
const u8_t *bt_pub_key_get(void);
|
||||
|
||||
/* @typedef bt_dh_key_cb_t
|
||||
* @brief Callback type for DH Key calculation.
|
||||
*
|
||||
* Used to notify of the calculated DH Key.
|
||||
*
|
||||
* @param key The DH Key, or NULL in case of failure.
|
||||
*/
|
||||
typedef void (*bt_dh_key_cb_t)(const u8_t key[32]);
|
||||
|
||||
/* @brief Calculate a DH Key from a remote Public Key.
|
||||
*
|
||||
* Calculate a DH Key from the remote Public Key.
|
||||
*
|
||||
* @param remote_pk Remote Public Key.
|
||||
* @param cb Callback to notify the calculated key.
|
||||
*
|
||||
* @return Zero on success or negative error code otherwise
|
||||
*/
|
||||
int bt_dh_key_gen(const u8_t remote_pk[64], bt_dh_key_cb_t cb);
|
4683
components/ble/ble_stack/host/gatt.c
Normal file
4683
components/ble/ble_stack/host/gatt.c
Normal file
File diff suppressed because it is too large
Load diff
60
components/ble/ble_stack/host/gatt_internal.h
Normal file
60
components/ble/ble_stack/host/gatt_internal.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/** @file
|
||||
* @brief Internal API for Generic Attribute Profile handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define BT_GATT_CENTRAL_ADDR_RES_NOT_SUPP 0
|
||||
#define BT_GATT_CENTRAL_ADDR_RES_SUPP 1
|
||||
|
||||
#include <gatt.h>
|
||||
|
||||
#define BT_GATT_PERM_READ_MASK (BT_GATT_PERM_READ | \
|
||||
BT_GATT_PERM_READ_ENCRYPT | \
|
||||
BT_GATT_PERM_READ_AUTHEN)
|
||||
#define BT_GATT_PERM_WRITE_MASK (BT_GATT_PERM_WRITE | \
|
||||
BT_GATT_PERM_WRITE_ENCRYPT | \
|
||||
BT_GATT_PERM_WRITE_AUTHEN)
|
||||
#define BT_GATT_PERM_ENCRYPT_MASK (BT_GATT_PERM_READ_ENCRYPT | \
|
||||
BT_GATT_PERM_WRITE_ENCRYPT)
|
||||
#define BT_GATT_PERM_AUTHEN_MASK (BT_GATT_PERM_READ_AUTHEN | \
|
||||
BT_GATT_PERM_WRITE_AUTHEN)
|
||||
|
||||
void bt_gatt_init(void);
|
||||
#if defined(BFLB_BLE)
|
||||
void bt_gatt_deinit(void);
|
||||
#endif
|
||||
void bt_gatt_connected(struct bt_conn *conn);
|
||||
void bt_gatt_encrypt_change(struct bt_conn *conn);
|
||||
void bt_gatt_disconnected(struct bt_conn *conn);
|
||||
|
||||
bool bt_gatt_change_aware(struct bt_conn *conn, bool req);
|
||||
|
||||
int bt_gatt_store_ccc(u8_t id, const bt_addr_le_t *addr);
|
||||
|
||||
int bt_gatt_clear(u8_t id, const bt_addr_le_t *addr);
|
||||
|
||||
#if defined(BFLB_BLE_MTU_CHANGE_CB)
|
||||
void bt_gatt_mtu_changed(struct bt_conn *conn, u16_t mtu);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_BT_GATT_CLIENT)
|
||||
void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
||||
const void *data, u16_t length);
|
||||
#else
|
||||
static inline void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
||||
const void *data, u16_t length)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BT_GATT_CLIENT */
|
||||
|
||||
struct bt_gatt_attr;
|
||||
|
||||
/* Check attribute permission */
|
||||
u8_t bt_gatt_check_perm(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
u8_t mask);
|
7814
components/ble/ble_stack/host/hci_core.c
Normal file
7814
components/ble/ble_stack/host/hci_core.c
Normal file
File diff suppressed because it is too large
Load diff
287
components/ble/ble_stack/host/hci_core.h
Normal file
287
components/ble/ble_stack/host/hci_core.h
Normal file
|
@ -0,0 +1,287 @@
|
|||
/* hci_core.h - Bluetooth HCI core access */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* LL connection parameters */
|
||||
#define LE_CONN_LATENCY 0x0000
|
||||
#define LE_CONN_TIMEOUT 0x002a
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
#define LMP_FEAT_PAGES_COUNT 3
|
||||
#else
|
||||
#define LMP_FEAT_PAGES_COUNT 1
|
||||
#endif
|
||||
|
||||
/* SCO settings */
|
||||
#define BT_VOICE_CVSD_16BIT 0x0060
|
||||
#define BT_VOICE_MSBC_16BIT 0x0063
|
||||
|
||||
/* k_poll event tags */
|
||||
enum {
|
||||
BT_EVENT_CMD_TX,
|
||||
BT_EVENT_CONN_TX_QUEUE,
|
||||
};
|
||||
|
||||
/* bt_dev flags: the flags defined here represent BT controller state */
|
||||
enum {
|
||||
BT_DEV_ENABLE,
|
||||
BT_DEV_READY,
|
||||
BT_DEV_PRESET_ID,
|
||||
BT_DEV_USER_ID_ADDR,
|
||||
BT_DEV_HAS_PUB_KEY,
|
||||
BT_DEV_PUB_KEY_BUSY,
|
||||
|
||||
BT_DEV_ADVERTISING,
|
||||
BT_DEV_ADVERTISING_NAME,
|
||||
BT_DEV_ADVERTISING_CONNECTABLE,
|
||||
BT_DEV_KEEP_ADVERTISING,
|
||||
BT_DEV_SCANNING,
|
||||
BT_DEV_EXPLICIT_SCAN,
|
||||
BT_DEV_ACTIVE_SCAN,
|
||||
BT_DEV_SCAN_FILTER_DUP,
|
||||
BT_DEV_SCAN_WL,
|
||||
BT_DEV_AUTO_CONN,
|
||||
|
||||
BT_DEV_RPA_VALID,
|
||||
|
||||
BT_DEV_ID_PENDING,
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
BT_DEV_ISCAN,
|
||||
BT_DEV_PSCAN,
|
||||
BT_DEV_INQUIRY,
|
||||
#endif /* CONFIG_BT_BREDR */
|
||||
|
||||
#if defined(CONFIG_BT_STACK_PTS)
|
||||
BT_DEV_ADV_ADDRESS_IS_PUBLIC,
|
||||
#endif
|
||||
|
||||
#if defined(BFLB_HOST_ASSISTANT)
|
||||
BT_DEV_ASSIST_RUN,
|
||||
#endif
|
||||
|
||||
|
||||
/* Total number of flags - must be at the end of the enum */
|
||||
BT_DEV_NUM_FLAGS,
|
||||
};
|
||||
|
||||
/* Flags which should not be cleared upon HCI_Reset */
|
||||
#define BT_DEV_PERSISTENT_FLAGS (BIT(BT_DEV_ENABLE) | \
|
||||
BIT(BT_DEV_PRESET_ID) | \
|
||||
BIT(BT_DEV_USER_ID_ADDR))
|
||||
|
||||
struct bt_dev_le {
|
||||
/* LE features */
|
||||
u8_t features[8];
|
||||
/* LE states */
|
||||
u64_t states;
|
||||
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
/* Controller buffer information */
|
||||
u16_t mtu;
|
||||
struct k_sem pkts;
|
||||
#endif /* CONFIG_BT_CONN */
|
||||
|
||||
#if defined(CONFIG_BT_SMP)
|
||||
/* Size of the the controller resolving list */
|
||||
u8_t rl_size;
|
||||
/* Number of entries in the resolving list. rl_entries > rl_size
|
||||
* means that host-side resolving is used.
|
||||
*/
|
||||
u8_t rl_entries;
|
||||
#endif /* CONFIG_BT_SMP */
|
||||
|
||||
#if defined(CONFIG_BT_WHITELIST)
|
||||
/* Size of the controller whitelist. */
|
||||
u8_t wl_size;
|
||||
/* Number of entries in the resolving list. */
|
||||
u8_t wl_entries;
|
||||
#endif /* CONFIG_BT_WHITELIST */
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
struct bt_dev_br {
|
||||
/* Max controller's acceptable ACL packet length */
|
||||
u16_t mtu;
|
||||
struct k_sem pkts;
|
||||
u16_t esco_pkt_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* The theoretical max for these is 8 and 64, but there's no point
|
||||
* in allocating the full memory if we only support a small subset.
|
||||
* These values must be updated whenever the host implementation is
|
||||
* extended beyond the current values.
|
||||
*/
|
||||
#define BT_DEV_VS_FEAT_MAX 1
|
||||
#define BT_DEV_VS_CMDS_MAX 2
|
||||
|
||||
/* State tracking for the local Bluetooth controller */
|
||||
struct bt_dev {
|
||||
/* Local Identity Address(es) */
|
||||
bt_addr_le_t id_addr[CONFIG_BT_ID_MAX];
|
||||
u8_t id_count;
|
||||
|
||||
/* ID Address used for advertising */
|
||||
u8_t adv_id;
|
||||
|
||||
/* Current local Random Address */
|
||||
bt_addr_le_t random_addr;
|
||||
|
||||
/* Controller version & manufacturer information */
|
||||
u8_t hci_version;
|
||||
u8_t lmp_version;
|
||||
u16_t hci_revision;
|
||||
u16_t lmp_subversion;
|
||||
u16_t manufacturer;
|
||||
|
||||
/* LMP features (pages 0, 1, 2) */
|
||||
u8_t features[LMP_FEAT_PAGES_COUNT][8];
|
||||
|
||||
/* Supported commands */
|
||||
u8_t supported_commands[64];
|
||||
|
||||
#if defined(CONFIG_BT_HCI_VS_EXT)
|
||||
/* Vendor HCI support */
|
||||
u8_t vs_features[BT_DEV_VS_FEAT_MAX];
|
||||
u8_t vs_commands[BT_DEV_VS_CMDS_MAX];
|
||||
#endif
|
||||
|
||||
struct k_work init;
|
||||
|
||||
ATOMIC_DEFINE(flags, BT_DEV_NUM_FLAGS);
|
||||
|
||||
/* LE controller specific features */
|
||||
struct bt_dev_le le;
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
/* BR/EDR controller specific features */
|
||||
struct bt_dev_br br;
|
||||
#endif
|
||||
|
||||
/* Number of commands controller can accept */
|
||||
struct k_sem ncmd_sem;
|
||||
|
||||
/* Last sent HCI command */
|
||||
struct net_buf *sent_cmd;
|
||||
|
||||
#if !defined(CONFIG_BT_RECV_IS_RX_THREAD)
|
||||
/* Queue for incoming HCI events & ACL data */
|
||||
struct k_fifo rx_queue;
|
||||
#endif
|
||||
|
||||
/* Queue for outgoing HCI commands */
|
||||
struct k_fifo cmd_tx_queue;
|
||||
|
||||
/* Registered HCI driver */
|
||||
const struct bt_hci_driver *drv;
|
||||
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
/* Local Identity Resolving Key */
|
||||
u8_t irk[CONFIG_BT_ID_MAX][16];
|
||||
|
||||
/* Work used for RPA rotation */
|
||||
struct k_delayed_work rpa_update;
|
||||
#endif
|
||||
|
||||
/* Local Name */
|
||||
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
||||
char name[CONFIG_BT_DEVICE_NAME_MAX + 1];
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined (CONFIG_BT_STACK_PTS)
|
||||
typedef enum __packed{
|
||||
dir_connect_req = 0x01, /*Send a direct connection require while the Lower test enters direct mode .*/
|
||||
|
||||
ad_type_service_uuid = 0x02,
|
||||
ad_type_local_name = 0x03,
|
||||
ad_type_flags = 0x04,
|
||||
ad_type_manu_data = 0x05,
|
||||
ad_type_tx_power_level = 0x06,
|
||||
ad_type_service_data = 0x07,
|
||||
ad_type_appearance = 0x08,
|
||||
|
||||
gatt_discover_chara = 0x09,
|
||||
gatt_exec_write_req = 0x0a,
|
||||
gatt_cancel_write_req = 0x0b,
|
||||
att_read_by_group_type_ind = 0x0c, /* CASE : GATT/SR/GAD/BV-01-C. Indicate PTS sends a GATT discover all primary services request to iut */
|
||||
att_find_by_type_value_ind = 0x0d, /* CASE : GATT/SR/GAD/BV-02-C. Indicate PTS sends a request to iut for discover it contains Primary Services by Service UUID */
|
||||
att_read_by_type_ind = 0x0e, /* CASE : GATT/SR/GAD/BV-04-C. Indicate PTS sends a request to iut for discover all characteristics of a specified service.*/
|
||||
|
||||
own_addr_type_random = 0x0f
|
||||
}event_id;
|
||||
|
||||
#endif
|
||||
|
||||
extern struct bt_dev bt_dev;
|
||||
#if defined(CONFIG_BT_SMP) || defined(CONFIG_BT_BREDR)
|
||||
extern const struct bt_conn_auth_cb *bt_auth;
|
||||
#endif /* CONFIG_BT_SMP || CONFIG_BT_BREDR */
|
||||
|
||||
bool bt_le_conn_params_valid(const struct bt_le_conn_param *param);
|
||||
|
||||
int bt_le_scan_update(bool fast_scan);
|
||||
|
||||
int bt_le_auto_conn(const struct bt_le_conn_param *conn_param);
|
||||
int bt_le_auto_conn_cancel(void);
|
||||
|
||||
bool bt_addr_le_is_bonded(u8_t id, const bt_addr_le_t *addr);
|
||||
const bt_addr_le_t *bt_lookup_id_addr(u8_t id, const bt_addr_le_t *addr);
|
||||
|
||||
int bt_send(struct net_buf *buf);
|
||||
|
||||
/* Don't require everyone to include keys.h */
|
||||
struct bt_keys;
|
||||
void bt_id_add(struct bt_keys *keys);
|
||||
void bt_id_del(struct bt_keys *keys);
|
||||
|
||||
int bt_setup_id_addr(void);
|
||||
void bt_finalize_init(void);
|
||||
|
||||
int bt_le_adv_start_internal(const struct bt_le_adv_param *param,
|
||||
const struct bt_data *ad, size_t ad_len,
|
||||
const struct bt_data *sd, size_t sd_len,
|
||||
const bt_addr_le_t *peer);
|
||||
#if defined(CONFIG_BLE_MULTI_ADV)
|
||||
int bt_le_adv_start_instant(const struct bt_le_adv_param *param,
|
||||
const uint8_t *ad_data, size_t ad_len,
|
||||
const uint8_t *sd_data, size_t sd_len);
|
||||
#endif
|
||||
|
||||
#if defined (BFLB_BLE)
|
||||
|
||||
int bt_le_read_rssi(u16_t handle,int8_t *rssi);
|
||||
int set_ad_and_rsp_d(u16_t hci_op, u8_t *data, u32_t ad_len);
|
||||
int set_adv_enable(bool enable);
|
||||
int set_adv_param(const struct bt_le_adv_param *param);
|
||||
int set_adv_channel_map(u8_t channel);
|
||||
int bt_get_local_public_address(bt_addr_le_t *adv_addr);
|
||||
int bt_get_local_ramdon_address(bt_addr_le_t *adv_addr);
|
||||
int bt_le_set_data_len(struct bt_conn *conn, u16_t tx_octets, u16_t tx_time);
|
||||
int hci_le_set_phy(struct bt_conn *conn);
|
||||
int hci_le_set_default_phy(struct bt_conn *conn,u8_t default_phy);
|
||||
|
||||
|
||||
#if defined(CONFIG_SET_TX_PWR)
|
||||
int bt_set_tx_pwr(int8_t power);
|
||||
#endif
|
||||
|
||||
#if defined(BFLB_HOST_ASSISTANT)
|
||||
struct blhast_cb{
|
||||
void (*le_scan_cb)(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb);
|
||||
void (*le_adv_cb)(const struct bt_le_adv_param *param, const struct bt_data *ad,
|
||||
size_t ad_len, const struct bt_data *sd, size_t sd_len);
|
||||
};
|
||||
int bt_set_flow_control(void);
|
||||
int bt_set_event_mask(void);
|
||||
int bt_le_set_event_mask(void);
|
||||
void bt_hci_reset_complete(struct net_buf *buf);
|
||||
void bt_register_host_assist_cb(struct blhast_cb *cb);
|
||||
#endif
|
||||
|
||||
#endif
|
340
components/ble/ble_stack/host/hci_ecc.c
Normal file
340
components/ble/ble_stack/host/hci_ecc.c
Normal file
|
@ -0,0 +1,340 @@
|
|||
/**
|
||||
* @file hci_ecc.c
|
||||
* HCI ECC emulation
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <atomic.h>
|
||||
#include <misc/stack.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <constants.h>
|
||||
#include <utils.h>
|
||||
#include <ecc.h>
|
||||
#include <ecc_dh.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <conn.h>
|
||||
#include <hci_host.h>
|
||||
#include <hci_driver.h>
|
||||
#include <../include/bluetooth/crypto.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_ecc.h"
|
||||
#ifdef CONFIG_BT_HCI_RAW
|
||||
#include <bluetooth/hci_raw.h>
|
||||
#include "hci_raw_internal.h"
|
||||
#else
|
||||
#include "hci_core.h"
|
||||
#endif
|
||||
|
||||
static struct k_thread ecc_thread_data;
|
||||
#if !defined(BFLB_BLE)
|
||||
static BT_STACK_NOINIT(ecc_thread_stack, 1024);
|
||||
#endif
|
||||
|
||||
/* based on Core Specification 4.2 Vol 3. Part H 2.3.5.6.1 */
|
||||
static const u32_t debug_private_key[8] = {
|
||||
0xcd3c1abd, 0x5899b8a6, 0xeb40b799, 0x4aff607b, 0xd2103f50, 0x74c9b3e3,
|
||||
0xa3c55f38, 0x3f49f6d4
|
||||
};
|
||||
|
||||
#if defined(CONFIG_BT_USE_DEBUG_KEYS)
|
||||
static const u8_t debug_public_key[64] = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc, 0xdb, 0xfd, 0xf4, 0xac,
|
||||
0x11, 0x91, 0xf4, 0xef, 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
||||
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20, 0x8b, 0xd2, 0x89, 0x15,
|
||||
0xd0, 0x8e, 0x1c, 0x74, 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
|
||||
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63, 0x6d, 0xeb, 0x2a, 0x65,
|
||||
0x49, 0x9c, 0x80, 0xdc
|
||||
};
|
||||
#endif
|
||||
|
||||
enum {
|
||||
PENDING_PUB_KEY,
|
||||
PENDING_DHKEY,
|
||||
|
||||
/* Total number of flags - must be at the end of the enum */
|
||||
NUM_FLAGS,
|
||||
};
|
||||
|
||||
static ATOMIC_DEFINE(flags, NUM_FLAGS);
|
||||
|
||||
static K_SEM_DEFINE(cmd_sem, 0, 1);
|
||||
|
||||
static struct {
|
||||
u8_t private_key[32];
|
||||
|
||||
union {
|
||||
u8_t pk[64];
|
||||
u8_t dhkey[32];
|
||||
};
|
||||
} ecc;
|
||||
|
||||
static void send_cmd_status(u16_t opcode, u8_t status)
|
||||
{
|
||||
struct bt_hci_evt_cmd_status *evt;
|
||||
struct bt_hci_evt_hdr *hdr;
|
||||
struct net_buf *buf;
|
||||
|
||||
BT_DBG("opcode %x status %x", opcode, status);
|
||||
|
||||
buf = bt_buf_get_evt(BT_HCI_EVT_CMD_STATUS, false, K_FOREVER);
|
||||
bt_buf_set_type(buf, BT_BUF_EVT);
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
hdr->evt = BT_HCI_EVT_CMD_STATUS;
|
||||
hdr->len = sizeof(*evt);
|
||||
|
||||
evt = net_buf_add(buf, sizeof(*evt));
|
||||
evt->ncmd = 1U;
|
||||
evt->opcode = sys_cpu_to_le16(opcode);
|
||||
evt->status = status;
|
||||
|
||||
bt_recv_prio(buf);
|
||||
}
|
||||
|
||||
static u8_t generate_keys(void)
|
||||
{
|
||||
#if !defined(CONFIG_BT_USE_DEBUG_KEYS)
|
||||
do {
|
||||
int rc;
|
||||
|
||||
rc = uECC_make_key(ecc.pk, ecc.private_key, &curve_secp256r1);
|
||||
if (rc == TC_CRYPTO_FAIL) {
|
||||
BT_ERR("Failed to create ECC public/private pair");
|
||||
return BT_HCI_ERR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
/* make sure generated key isn't debug key */
|
||||
} while (memcmp(ecc.private_key, debug_private_key, 32) == 0);
|
||||
#else
|
||||
sys_memcpy_swap(&ecc.pk, debug_public_key, 32);
|
||||
sys_memcpy_swap(&ecc.pk[32], &debug_public_key[32], 32);
|
||||
sys_memcpy_swap(ecc.private_key, debug_private_key, 32);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emulate_le_p256_public_key_cmd(void)
|
||||
{
|
||||
struct bt_hci_evt_le_p256_public_key_complete *evt;
|
||||
struct bt_hci_evt_le_meta_event *meta;
|
||||
struct bt_hci_evt_hdr *hdr;
|
||||
struct net_buf *buf;
|
||||
u8_t status;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
status = generate_keys();
|
||||
|
||||
buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
hdr->evt = BT_HCI_EVT_LE_META_EVENT;
|
||||
hdr->len = sizeof(*meta) + sizeof(*evt);
|
||||
|
||||
meta = net_buf_add(buf, sizeof(*meta));
|
||||
meta->subevent = BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE;
|
||||
|
||||
evt = net_buf_add(buf, sizeof(*evt));
|
||||
evt->status = status;
|
||||
|
||||
if (status) {
|
||||
(void)memset(evt->key, 0, sizeof(evt->key));
|
||||
} else {
|
||||
/* Convert X and Y coordinates from big-endian (provided
|
||||
* by crypto API) to little endian HCI.
|
||||
*/
|
||||
sys_memcpy_swap(evt->key, ecc.pk, 32);
|
||||
sys_memcpy_swap(&evt->key[32], &ecc.pk[32], 32);
|
||||
}
|
||||
|
||||
atomic_clear_bit(flags, PENDING_PUB_KEY);
|
||||
|
||||
bt_recv(buf);
|
||||
}
|
||||
|
||||
static void emulate_le_generate_dhkey(void)
|
||||
{
|
||||
struct bt_hci_evt_le_generate_dhkey_complete *evt;
|
||||
struct bt_hci_evt_le_meta_event *meta;
|
||||
struct bt_hci_evt_hdr *hdr;
|
||||
struct net_buf *buf;
|
||||
int ret;
|
||||
|
||||
ret = uECC_valid_public_key(ecc.pk, &curve_secp256r1);
|
||||
if (ret < 0) {
|
||||
BT_ERR("public key is not valid (ret %d)", ret);
|
||||
ret = TC_CRYPTO_FAIL;
|
||||
} else {
|
||||
ret = uECC_shared_secret(ecc.pk, ecc.private_key, ecc.dhkey,
|
||||
&curve_secp256r1);
|
||||
}
|
||||
|
||||
buf = bt_buf_get_rx(BT_BUF_EVT, K_FOREVER);
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
hdr->evt = BT_HCI_EVT_LE_META_EVENT;
|
||||
hdr->len = sizeof(*meta) + sizeof(*evt);
|
||||
|
||||
meta = net_buf_add(buf, sizeof(*meta));
|
||||
meta->subevent = BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE;
|
||||
|
||||
evt = net_buf_add(buf, sizeof(*evt));
|
||||
|
||||
if (ret == TC_CRYPTO_FAIL) {
|
||||
evt->status = BT_HCI_ERR_UNSPECIFIED;
|
||||
(void)memset(evt->dhkey, 0, sizeof(evt->dhkey));
|
||||
} else {
|
||||
evt->status = 0U;
|
||||
/* Convert from big-endian (provided by crypto API) to
|
||||
* little-endian HCI.
|
||||
*/
|
||||
sys_memcpy_swap(evt->dhkey, ecc.dhkey, sizeof(ecc.dhkey));
|
||||
}
|
||||
|
||||
atomic_clear_bit(flags, PENDING_DHKEY);
|
||||
|
||||
bt_recv(buf);
|
||||
}
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
static void ecc_thread(void *p1)
|
||||
#else
|
||||
static void ecc_thread(void *p1, void *p2, void *p3)
|
||||
#endif
|
||||
{
|
||||
while (true) {
|
||||
k_sem_take(&cmd_sem, K_FOREVER);
|
||||
|
||||
if (atomic_test_bit(flags, PENDING_PUB_KEY)) {
|
||||
emulate_le_p256_public_key_cmd();
|
||||
} else if (atomic_test_bit(flags, PENDING_DHKEY)) {
|
||||
emulate_le_generate_dhkey();
|
||||
} else {
|
||||
__ASSERT(0, "Unhandled ECC command");
|
||||
}
|
||||
#if !defined(BFLB_BLE)
|
||||
STACK_ANALYZE("ecc stack", ecc_thread_stack);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_ecc_events(struct net_buf *buf)
|
||||
{
|
||||
struct bt_hci_cp_le_set_event_mask *cmd;
|
||||
|
||||
cmd = (void *)(buf->data + sizeof(struct bt_hci_cmd_hdr));
|
||||
|
||||
/*
|
||||
* don't enable controller ECC events as those will be generated from
|
||||
* emulation code
|
||||
*/
|
||||
cmd->events[0] &= ~0x80; /* LE Read Local P-256 PKey Compl */
|
||||
cmd->events[1] &= ~0x01; /* LE Generate DHKey Compl Event */
|
||||
}
|
||||
|
||||
static void le_gen_dhkey(struct net_buf *buf)
|
||||
{
|
||||
struct bt_hci_cp_le_generate_dhkey *cmd;
|
||||
u8_t status;
|
||||
|
||||
if (atomic_test_bit(flags, PENDING_PUB_KEY)) {
|
||||
status = BT_HCI_ERR_CMD_DISALLOWED;
|
||||
goto send_status;
|
||||
}
|
||||
|
||||
if (buf->len < sizeof(struct bt_hci_cp_le_generate_dhkey)) {
|
||||
status = BT_HCI_ERR_INVALID_PARAM;
|
||||
goto send_status;
|
||||
}
|
||||
|
||||
if (atomic_test_and_set_bit(flags, PENDING_DHKEY)) {
|
||||
status = BT_HCI_ERR_CMD_DISALLOWED;
|
||||
goto send_status;
|
||||
}
|
||||
|
||||
cmd = (void *)buf->data;
|
||||
/* Convert X and Y coordinates from little-endian HCI to
|
||||
* big-endian (expected by the crypto API).
|
||||
*/
|
||||
sys_memcpy_swap(ecc.pk, cmd->key, 32);
|
||||
sys_memcpy_swap(&ecc.pk[32], &cmd->key[32], 32);
|
||||
k_sem_give(&cmd_sem);
|
||||
status = BT_HCI_ERR_SUCCESS;
|
||||
|
||||
send_status:
|
||||
net_buf_unref(buf);
|
||||
send_cmd_status(BT_HCI_OP_LE_GENERATE_DHKEY, status);
|
||||
}
|
||||
|
||||
static void le_p256_pub_key(struct net_buf *buf)
|
||||
{
|
||||
u8_t status;
|
||||
|
||||
net_buf_unref(buf);
|
||||
|
||||
if (atomic_test_bit(flags, PENDING_DHKEY)) {
|
||||
status = BT_HCI_ERR_CMD_DISALLOWED;
|
||||
} else if (atomic_test_and_set_bit(flags, PENDING_PUB_KEY)) {
|
||||
status = BT_HCI_ERR_CMD_DISALLOWED;
|
||||
} else {
|
||||
k_sem_give(&cmd_sem);
|
||||
status = BT_HCI_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
send_cmd_status(BT_HCI_OP_LE_P256_PUBLIC_KEY, status);
|
||||
}
|
||||
|
||||
int bt_hci_ecc_send(struct net_buf *buf)
|
||||
{
|
||||
if (bt_buf_get_type(buf) == BT_BUF_CMD) {
|
||||
struct bt_hci_cmd_hdr *chdr = (void *)buf->data;
|
||||
|
||||
switch (sys_le16_to_cpu(chdr->opcode)) {
|
||||
case BT_HCI_OP_LE_P256_PUBLIC_KEY:
|
||||
net_buf_pull(buf, sizeof(*chdr));
|
||||
le_p256_pub_key(buf);
|
||||
return 0;
|
||||
case BT_HCI_OP_LE_GENERATE_DHKEY:
|
||||
net_buf_pull(buf, sizeof(*chdr));
|
||||
le_gen_dhkey(buf);
|
||||
return 0;
|
||||
case BT_HCI_OP_LE_SET_EVENT_MASK:
|
||||
clear_ecc_events(buf);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bt_dev.drv->send(buf);
|
||||
}
|
||||
|
||||
int default_CSPRNG(u8_t *dst, unsigned int len)
|
||||
{
|
||||
return !bt_rand(dst, len);
|
||||
}
|
||||
|
||||
void bt_hci_ecc_init(void)
|
||||
{
|
||||
#if defined(BFLB_BLE)
|
||||
k_sem_init(&cmd_sem, 0, 1);
|
||||
k_thread_create(&ecc_thread_data, "ecc_thread",
|
||||
CONFIG_BT_HCI_ECC_STACK_SIZE, ecc_thread,
|
||||
CONFIG_BT_WORK_QUEUE_PRIO);
|
||||
#else
|
||||
k_thread_create(&ecc_thread_data, ecc_thread_stack,
|
||||
K_THREAD_STACK_SIZEOF(ecc_thread_stack), ecc_thread,
|
||||
NULL, NULL, NULL, K_PRIO_PREEMPT(10), 0, K_NO_WAIT);
|
||||
k_thread_name_set(&ecc_thread_data, "BT ECC");
|
||||
#endif
|
||||
}
|
10
components/ble/ble_stack/host/hci_ecc.h
Normal file
10
components/ble/ble_stack/host/hci_ecc.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* hci_ecc.h - HCI ECC emulation */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
void bt_hci_ecc_init(void);
|
||||
int bt_hci_ecc_send(struct net_buf *buf);
|
929
components/ble/ble_stack/host/hfp_hf.c
Normal file
929
components/ble/ble_stack/host/hfp_hf.c
Normal file
|
@ -0,0 +1,929 @@
|
|||
/* hfp_hf.c - Hands free Profile - Handsfree side handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr.h>
|
||||
#include <errno.h>
|
||||
#include <atomic.h>
|
||||
#include <byteorder.h>
|
||||
#include <util.h>
|
||||
#include <printk.h>
|
||||
|
||||
#include <conn.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HFP_HF)
|
||||
#define LOG_MODULE_NAME bt_hfp_hf
|
||||
#include "log.h"
|
||||
|
||||
#include <rfcomm.h>
|
||||
#include <hfp_hf.h>
|
||||
#include <sdp.h>
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "conn_internal.h"
|
||||
#include "l2cap_internal.h"
|
||||
#include "rfcomm_internal.h"
|
||||
#include "at.h"
|
||||
#include "hfp_internal.h"
|
||||
|
||||
#define MAX_IND_STR_LEN 17
|
||||
|
||||
struct bt_hfp_hf_cb *bt_hf;
|
||||
bool hfp_codec_msbc = 0;
|
||||
|
||||
#if !defined(BFLB_DYNAMIC_ALLOC_MEM)
|
||||
NET_BUF_POOL_FIXED_DEFINE(hf_pool, CONFIG_BT_MAX_CONN + 1,
|
||||
BT_RFCOMM_BUF_SIZE(BT_HF_CLIENT_MAX_PDU), NULL);
|
||||
#else
|
||||
struct net_buf_pool hf_pool;
|
||||
#endif
|
||||
|
||||
static struct bt_hfp_hf bt_hfp_hf_pool[CONFIG_BT_MAX_CONN];
|
||||
|
||||
static struct bt_sdp_attribute hfp_attrs[] = {
|
||||
BT_SDP_NEW_SERVICE,
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_SVCLASS_ID_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 10),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_HANDSFREE_SVCLASS)
|
||||
},
|
||||
)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_GENERIC_AUDIO_SVCLASS)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_PROTO_DESC_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 12),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 3),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_PROTO_L2CAP)
|
||||
},
|
||||
)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 5),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_PROTO_RFCOMM)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UINT8),
|
||||
BT_SDP_ARRAY_16(BT_RFCOMM_CHAN_HFP_HF)
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_LIST(
|
||||
BT_SDP_ATTR_PROFILE_DESC_LIST,
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 8),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE_VAR(BT_SDP_SEQ8, 6),
|
||||
BT_SDP_DATA_ELEM_LIST(
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UUID16),
|
||||
BT_SDP_ARRAY_16(BT_SDP_HANDSFREE_SVCLASS)
|
||||
},
|
||||
{
|
||||
BT_SDP_TYPE_SIZE(BT_SDP_UINT16),
|
||||
BT_SDP_ARRAY_16(0x0107)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
),
|
||||
BT_SDP_SERVICE_NAME("hands-free"),
|
||||
/*
|
||||
"SupportedFeatures" attribute bit mapping for the HF
|
||||
bit 0: EC and/or NR function
|
||||
bit 1: Call waiting or three-way calling
|
||||
bit 2: CLI presentation capability
|
||||
bit 3: Voice recognition activation
|
||||
bit 4: Remote volume control
|
||||
bit 5: Wide band speech
|
||||
bit 6: Enhanced Voice Recognition Status
|
||||
bit 7: Voice Recognition Text
|
||||
*/
|
||||
BT_SDP_SUPPORTED_FEATURES(0x0035),
|
||||
};
|
||||
|
||||
static struct bt_sdp_record hfp_rec = BT_SDP_RECORD(hfp_attrs);
|
||||
|
||||
/* The order should follow the enum hfp_hf_ag_indicators */
|
||||
static const struct {
|
||||
char *name;
|
||||
uint32_t min;
|
||||
uint32_t max;
|
||||
} ag_ind[] = {
|
||||
{"service", 0, 1}, /* HF_SERVICE_IND */
|
||||
{"call", 0, 1}, /* HF_CALL_IND */
|
||||
{"callsetup", 0, 3}, /* HF_CALL_SETUP_IND */
|
||||
{"callheld", 0, 2}, /* HF_CALL_HELD_IND */
|
||||
{"signal", 0, 5}, /* HF_SINGNAL_IND */
|
||||
{"roam", 0, 1}, /* HF_ROAM_IND */
|
||||
{"battchg", 0, 5} /* HF_BATTERY_IND */
|
||||
};
|
||||
|
||||
|
||||
static void connected(struct bt_conn *conn)
|
||||
{
|
||||
BT_DBG("HFP HF Connected!");
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn)
|
||||
{
|
||||
BT_DBG("HFP HF Disconnected!");
|
||||
}
|
||||
|
||||
static void service(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Service indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void call(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Call indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void call_setup(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Call Setup indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void call_held(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Call Held indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void signal(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Signal indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void roam(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Roaming indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void battery(struct bt_conn *conn, uint32_t value)
|
||||
{
|
||||
BT_DBG("Battery indicator value: %u", value);
|
||||
}
|
||||
|
||||
static void ring_cb(struct bt_conn *conn)
|
||||
{
|
||||
BT_DBG("Incoming Call...");
|
||||
}
|
||||
|
||||
static struct bt_hfp_hf_cb hf_cb = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.service = service,
|
||||
.call = call,
|
||||
.call_setup = call_setup,
|
||||
.call_held = call_held,
|
||||
.signal = signal,
|
||||
.roam = roam,
|
||||
.battery = battery,
|
||||
.ring_indication = ring_cb,
|
||||
};
|
||||
|
||||
|
||||
void hf_slc_error(struct at_client *hf_at)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int err;
|
||||
|
||||
BT_ERR("SLC error: disconnecting");
|
||||
err = bt_rfcomm_dlc_disconnect(&hf->rfcomm_dlc);
|
||||
if (err) {
|
||||
BT_ERR("Rfcomm: Unable to disconnect :%d", -err);
|
||||
}
|
||||
}
|
||||
|
||||
int hfp_hf_send_cmd(struct bt_hfp_hf *hf, at_resp_cb_t resp,
|
||||
at_finish_cb_t finish, const char *format, ...)
|
||||
{
|
||||
struct net_buf *buf;
|
||||
va_list vargs;
|
||||
int ret;
|
||||
|
||||
/* register the callbacks */
|
||||
at_register(&hf->at, resp, finish);
|
||||
|
||||
buf = bt_rfcomm_create_pdu(&hf_pool);
|
||||
if (!buf) {
|
||||
BT_ERR("No Buffers!");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
va_start(vargs, format);
|
||||
ret = vsnprintf((char*)buf->data, (net_buf_tailroom(buf) - 1), format, vargs);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Unable to format variable arguments");
|
||||
return ret;
|
||||
}
|
||||
va_end(vargs);
|
||||
|
||||
net_buf_add(buf, ret);
|
||||
net_buf_add_u8(buf, '\r');
|
||||
|
||||
ret = bt_rfcomm_dlc_send(&hf->rfcomm_dlc, buf);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Rfcomm send error :(%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int brsf_handle(struct at_client *hf_at)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
uint32_t val;
|
||||
int ret;
|
||||
|
||||
ret = at_get_number(hf_at, &val);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Error getting value");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hf->ag_features = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int brsf_resp(struct at_client *hf_at, struct net_buf *buf)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
err = at_parse_cmd_input(hf_at, buf, "BRSF", brsf_handle,
|
||||
AT_CMD_TYPE_NORMAL);
|
||||
if (err < 0) {
|
||||
/* Returning negative value is avoided before SLC connection
|
||||
* established.
|
||||
*/
|
||||
BT_ERR("Error parsing CMD input");
|
||||
hf_slc_error(hf_at);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cind_handle_values(struct at_client *hf_at, uint32_t index,
|
||||
char *name, uint32_t min, uint32_t max)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int i;
|
||||
|
||||
BT_DBG("index: %u, name: %s, min: %u, max:%u", index, name, min, max);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ag_ind); i++) {
|
||||
if (strcmp(name, ag_ind[i].name) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (min != ag_ind[i].min || max != ag_ind[i].max) {
|
||||
BT_ERR("%s indicator min/max value not matching", name);
|
||||
}
|
||||
|
||||
hf->ind_table[index] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cind_handle(struct at_client *hf_at)
|
||||
{
|
||||
uint32_t index = 0U;
|
||||
|
||||
/* Parsing Example: CIND: ("call",(0,1)) etc.. */
|
||||
while (at_has_next_list(hf_at)) {
|
||||
char name[MAX_IND_STR_LEN];
|
||||
uint32_t min, max;
|
||||
|
||||
if (at_open_list(hf_at) < 0) {
|
||||
BT_ERR("Could not get open list");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (at_list_get_string(hf_at, name, sizeof(name)) < 0) {
|
||||
BT_ERR("Could not get string");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (at_open_list(hf_at) < 0) {
|
||||
BT_ERR("Could not get open list");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (at_list_get_range(hf_at, &min, &max) < 0) {
|
||||
BT_ERR("Could not get range");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (at_close_list(hf_at) < 0) {
|
||||
BT_ERR("Could not get close list");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (at_close_list(hf_at) < 0) {
|
||||
BT_ERR("Could not get close list");
|
||||
goto error;
|
||||
}
|
||||
|
||||
cind_handle_values(hf_at, index, name, min, max);
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
BT_ERR("Error on CIND response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int cind_resp(struct at_client *hf_at, struct net_buf *buf)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = at_parse_cmd_input(hf_at, buf, "CIND", cind_handle,
|
||||
AT_CMD_TYPE_NORMAL);
|
||||
if (err < 0) {
|
||||
BT_ERR("Error parsing CMD input");
|
||||
hf_slc_error(hf_at);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ag_indicator_handle_values(struct at_client *hf_at, uint32_t index,
|
||||
uint32_t value)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn;
|
||||
|
||||
BT_DBG("Index :%u, Value :%u", index, value);
|
||||
|
||||
if (index >= ARRAY_SIZE(ag_ind)) {
|
||||
BT_ERR("Max only %lu indicators are supported",
|
||||
ARRAY_SIZE(ag_ind));
|
||||
return;
|
||||
}
|
||||
|
||||
if (value > ag_ind[hf->ind_table[index]].max ||
|
||||
value < ag_ind[hf->ind_table[index]].min) {
|
||||
BT_ERR("Indicators out of range - value: %u", value);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (hf->ind_table[index]) {
|
||||
case HF_SERVICE_IND:
|
||||
if (bt_hf->service) {
|
||||
bt_hf->service(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_CALL_IND:
|
||||
if (bt_hf->call) {
|
||||
bt_hf->call(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_CALL_SETUP_IND:
|
||||
if (bt_hf->call_setup) {
|
||||
bt_hf->call_setup(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_CALL_HELD_IND:
|
||||
if (bt_hf->call_held) {
|
||||
bt_hf->call_held(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_SINGNAL_IND:
|
||||
if (bt_hf->signal) {
|
||||
bt_hf->signal(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_ROAM_IND:
|
||||
if (bt_hf->roam) {
|
||||
bt_hf->roam(conn, value);
|
||||
}
|
||||
break;
|
||||
case HF_BATTERY_IND:
|
||||
if (bt_hf->battery) {
|
||||
bt_hf->battery(conn, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unknown AG indicator");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cind_status_handle(struct at_client *hf_at)
|
||||
{
|
||||
uint32_t index = 0U;
|
||||
|
||||
while (at_has_next_list(hf_at)) {
|
||||
uint32_t value;
|
||||
int ret;
|
||||
|
||||
ret = at_get_number(hf_at, &value);
|
||||
if (ret < 0) {
|
||||
BT_ERR("could not get the value");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ag_indicator_handle_values(hf_at, index, value);
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cind_status_resp(struct at_client *hf_at, struct net_buf *buf)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = at_parse_cmd_input(hf_at, buf, "CIND", cind_status_handle,
|
||||
AT_CMD_TYPE_NORMAL);
|
||||
if (err < 0) {
|
||||
BT_ERR("Error parsing CMD input");
|
||||
hf_slc_error(hf_at);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ciev_handle(struct at_client *hf_at)
|
||||
{
|
||||
uint32_t index, value;
|
||||
int ret;
|
||||
|
||||
ret = at_get_number(hf_at, &index);
|
||||
if (ret < 0) {
|
||||
BT_ERR("could not get the Index");
|
||||
return ret;
|
||||
}
|
||||
/* The first element of the list shall have 1 */
|
||||
if (!index) {
|
||||
BT_ERR("Invalid index value '0'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = at_get_number(hf_at, &value);
|
||||
if (ret < 0) {
|
||||
BT_ERR("could not get the value");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ag_indicator_handle_values(hf_at, (index - 1), value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ring_handle(struct at_client *hf_at)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn;
|
||||
|
||||
if (bt_hf->ring_indication) {
|
||||
bt_hf->ring_indication(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bcs_handle(struct at_client *hf_at)
|
||||
{
|
||||
uint32_t value;
|
||||
int ret;
|
||||
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
|
||||
ret = at_get_number(hf_at, &value);
|
||||
if (ret < 0) {
|
||||
BT_ERR("could not get the value");
|
||||
return ret;
|
||||
}
|
||||
if (value == 1) {
|
||||
if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+BCS=1") < 0) {
|
||||
BT_ERR("Error Sending AT+BCS=1");
|
||||
}
|
||||
}
|
||||
else if (value == 2) {
|
||||
if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+BCS=2") < 0) {
|
||||
BT_ERR("Error Sending AT+BCS=2");
|
||||
} else {
|
||||
hfp_codec_msbc = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BT_WARN("Invail BCS value !");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct unsolicited {
|
||||
const char *cmd;
|
||||
enum at_cmd_type type;
|
||||
int (*func)(struct at_client *hf_at);
|
||||
} handlers[] = {
|
||||
{ "CIEV", AT_CMD_TYPE_UNSOLICITED, ciev_handle },
|
||||
{ "RING", AT_CMD_TYPE_OTHER, ring_handle },
|
||||
{ "BCS", AT_CMD_TYPE_UNSOLICITED, bcs_handle }
|
||||
};
|
||||
|
||||
static const struct unsolicited *hfp_hf_unsol_lookup(struct at_client *hf_at)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
|
||||
if (!strncmp(hf_at->buf, handlers[i].cmd,
|
||||
strlen(handlers[i].cmd))) {
|
||||
return &handlers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int unsolicited_cb(struct at_client *hf_at, struct net_buf *buf)
|
||||
{
|
||||
const struct unsolicited *handler;
|
||||
|
||||
handler = hfp_hf_unsol_lookup(hf_at);
|
||||
if (!handler) {
|
||||
BT_ERR("Unhandled unsolicited response");
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
if (!at_parse_cmd_input(hf_at, buf, handler->cmd, handler->func,
|
||||
handler->type)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOMSG;
|
||||
}
|
||||
|
||||
int cmd_complete(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn;
|
||||
struct bt_hfp_hf_cmd_complete cmd = { 0 };
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
switch (result) {
|
||||
case AT_RESULT_OK:
|
||||
cmd.type = HFP_HF_CMD_OK;
|
||||
break;
|
||||
case AT_RESULT_ERROR:
|
||||
cmd.type = HFP_HF_CMD_ERROR;
|
||||
break;
|
||||
case AT_RESULT_CME_ERROR:
|
||||
cmd.type = HFP_HF_CMD_CME_ERROR;
|
||||
cmd.cme = cme_err;
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Unknown error code");
|
||||
cmd.type = HFP_HF_CMD_UNKNOWN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bt_hf->cmd_complete_cb) {
|
||||
bt_hf->cmd_complete_cb(conn, &cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmee_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hfp_hf_send_cmd(hf, NULL, NULL, "AT+NREC=0") < 0) {
|
||||
BT_ERR("Error Sending AT+NREC");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void slc_completed(struct at_client *hf_at)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
struct bt_conn *conn = hf->rfcomm_dlc.session->br_chan.chan.conn;
|
||||
|
||||
if (bt_hf->connected) {
|
||||
bt_hf->connected(conn);
|
||||
}
|
||||
|
||||
if (hfp_hf_send_cmd(hf, NULL, cmee_finish, "AT+CMEE=1") < 0) {
|
||||
BT_ERR("Error Sending AT+CMEE");
|
||||
}
|
||||
}
|
||||
|
||||
int cmer_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
slc_completed(hf_at);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cind_status_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int err;
|
||||
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
at_register_unsolicited(hf_at, unsolicited_cb);
|
||||
err = hfp_hf_send_cmd(hf, NULL, cmer_finish, "AT+CMER=3,0,0,1");
|
||||
if (err < 0) {
|
||||
hf_slc_error(hf_at);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cind_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int err;
|
||||
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hfp_hf_send_cmd(hf, cind_status_resp, cind_status_finish,
|
||||
"AT+CIND?");
|
||||
if (err < 0) {
|
||||
hf_slc_error(hf_at);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bac_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int err;
|
||||
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hfp_hf_send_cmd(hf, cind_resp, cind_finish, "AT+CIND=?");
|
||||
if (err < 0) {
|
||||
hf_slc_error(hf_at);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int brsf_finish(struct at_client *hf_at, enum at_result result,
|
||||
enum at_cme cme_err)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(hf_at, struct bt_hfp_hf, at);
|
||||
int err;
|
||||
|
||||
if (result != AT_RESULT_OK) {
|
||||
BT_ERR("SLC Connection ERROR in response");
|
||||
hf_slc_error(hf_at);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = hfp_hf_send_cmd(hf, NULL, bac_finish, "AT+BAC=1,2");
|
||||
if (err < 0) {
|
||||
hf_slc_error(hf_at);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hf_slc_establish(struct bt_hfp_hf *hf)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
err = hfp_hf_send_cmd(hf, brsf_resp, brsf_finish, "AT+BRSF=%u",
|
||||
hf->hf_features);
|
||||
if (err < 0) {
|
||||
hf_slc_error(&hf->at);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bt_hfp_hf *bt_hfp_hf_lookup_bt_conn(struct bt_conn *conn)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) {
|
||||
struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i];
|
||||
|
||||
if (hf->rfcomm_dlc.session->br_chan.chan.conn == conn) {
|
||||
return hf;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bt_hfp_hf_send_cmd(struct bt_conn *conn, enum bt_hfp_hf_at_cmd cmd)
|
||||
{
|
||||
struct bt_hfp_hf *hf;
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
if (!conn) {
|
||||
BT_ERR("Invalid connection");
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
hf = bt_hfp_hf_lookup_bt_conn(conn);
|
||||
if (!hf) {
|
||||
BT_ERR("No HF connection found");
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case BT_HFP_HF_ATA:
|
||||
err = hfp_hf_send_cmd(hf, NULL, cmd_complete, "ATA");
|
||||
if (err < 0) {
|
||||
BT_ERR("Failed ATA");
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case BT_HFP_HF_AT_CHUP:
|
||||
err = hfp_hf_send_cmd(hf, NULL, cmd_complete, "AT+CHUP");
|
||||
if (err < 0) {
|
||||
BT_ERR("Failed AT+CHUP");
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_ERR("Invalid AT Command");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hfp_hf_connected(struct bt_rfcomm_dlc *dlc)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc);
|
||||
|
||||
BT_DBG("hf connected");
|
||||
|
||||
BT_ASSERT(hf);
|
||||
hf_slc_establish(hf);
|
||||
}
|
||||
|
||||
static void hfp_hf_disconnected(struct bt_rfcomm_dlc *dlc)
|
||||
{
|
||||
struct bt_conn *conn = dlc->session->br_chan.chan.conn;
|
||||
|
||||
BT_DBG("hf disconnected!");
|
||||
if (bt_hf->disconnected) {
|
||||
bt_hf->disconnected(conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void hfp_hf_recv(struct bt_rfcomm_dlc *dlc, struct net_buf *buf)
|
||||
{
|
||||
struct bt_hfp_hf *hf = CONTAINER_OF(dlc, struct bt_hfp_hf, rfcomm_dlc);
|
||||
|
||||
if (at_parse_input(&hf->at, buf) < 0) {
|
||||
BT_ERR("Parsing failed");
|
||||
}
|
||||
}
|
||||
|
||||
static int bt_hfp_hf_accept(struct bt_conn *conn, struct bt_rfcomm_dlc **dlc)
|
||||
{
|
||||
int i;
|
||||
static struct bt_rfcomm_dlc_ops ops = {
|
||||
.connected = hfp_hf_connected,
|
||||
.disconnected = hfp_hf_disconnected,
|
||||
.recv = hfp_hf_recv,
|
||||
};
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bt_hfp_hf_pool); i++) {
|
||||
struct bt_hfp_hf *hf = &bt_hfp_hf_pool[i];
|
||||
int j;
|
||||
|
||||
if (hf->rfcomm_dlc.session) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hf->at.buf = hf->hf_buffer;
|
||||
hf->at.buf_max_len = HF_MAX_BUF_LEN;
|
||||
|
||||
hf->rfcomm_dlc.ops = &ops;
|
||||
hf->rfcomm_dlc.mtu = BT_HFP_MAX_MTU;
|
||||
|
||||
*dlc = &hf->rfcomm_dlc;
|
||||
|
||||
/* Set the supported features*/
|
||||
hf->hf_features = BT_HFP_HF_SUPPORTED_FEATURES;
|
||||
|
||||
for (j = 0; j < HF_MAX_AG_INDICATORS; j++) {
|
||||
hf->ind_table[j] = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_ERR("Unable to establish HF connection (%p)", conn);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int bt_hfp_hf_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
#if defined(BFLB_DYNAMIC_ALLOC_MEM)
|
||||
k_lifo_init(&hf_pool.free, CONFIG_BT_MAX_CONN + 1);
|
||||
net_buf_init(&hf_pool, CONFIG_BT_MAX_CONN + 1, BT_RFCOMM_BUF_SIZE(BT_HF_CLIENT_MAX_PDU), NULL);
|
||||
#endif
|
||||
|
||||
bt_hf = &hf_cb;
|
||||
|
||||
static struct bt_rfcomm_server chan = {
|
||||
.channel = BT_RFCOMM_CHAN_HFP_HF,
|
||||
.accept = bt_hfp_hf_accept,
|
||||
};
|
||||
|
||||
bt_rfcomm_server_register(&chan);
|
||||
|
||||
/* Register SDP record */
|
||||
err = bt_sdp_register_service(&hfp_rec);
|
||||
if(err < 0)
|
||||
{
|
||||
BT_ERR("HFP regist sdp record failed");
|
||||
}
|
||||
BT_DBG("HFP initialized successfully.");
|
||||
return err;
|
||||
}
|
||||
|
66
components/ble/ble_stack/host/hfp_internal.h
Normal file
66
components/ble/ble_stack/host/hfp_internal.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/** @file
|
||||
* @brief Internal APIs for Bluetooth Handsfree profile handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define BT_HFP_MAX_MTU 140
|
||||
#define BT_HF_CLIENT_MAX_PDU BT_HFP_MAX_MTU
|
||||
|
||||
/* HFP AG Features */
|
||||
#define BT_HFP_AG_FEATURE_3WAY_CALL 0x00000001 /* Three-way calling */
|
||||
#define BT_HFP_AG_FEATURE_ECNR 0x00000002 /* EC and/or NR function */
|
||||
#define BT_HFP_AG_FEATURE_VOICE_RECG 0x00000004 /* Voice recognition */
|
||||
#define BT_HFP_AG_INBAND_RING_TONE 0x00000008 /* In-band ring capability */
|
||||
#define BT_HFP_AG_VOICE_TAG 0x00000010 /* Attach no. to voice tag */
|
||||
#define BT_HFP_AG_FEATURE_REJECT_CALL 0x00000020 /* Ability to reject call */
|
||||
#define BT_HFP_AG_FEATURE_ECS 0x00000040 /* Enhanced call status */
|
||||
#define BT_HFP_AG_FEATURE_ECC 0x00000080 /* Enhanced call control */
|
||||
#define BT_HFP_AG_FEATURE_EXT_ERR 0x00000100 /* Extented error codes */
|
||||
#define BT_HFP_AG_FEATURE_CODEC_NEG 0x00000200 /* Codec negotiation */
|
||||
#define BT_HFP_AG_FEATURE_HF_IND 0x00000400 /* HF Indicators */
|
||||
#define BT_HFP_AG_FEARTURE_ESCO_S4 0x00000800 /* eSCO S4 Settings */
|
||||
|
||||
/* HFP HF Features */
|
||||
#define BT_HFP_HF_FEATURE_ECNR 0x00000001 /* EC and/or NR */
|
||||
#define BT_HFP_HF_FEATURE_3WAY_CALL 0x00000002 /* Three-way calling */
|
||||
#define BT_HFP_HF_FEATURE_CLI 0x00000004 /* CLI presentation */
|
||||
#define BT_HFP_HF_FEATURE_VOICE_RECG 0x00000008 /* Voice recognition */
|
||||
#define BT_HFP_HF_FEATURE_VOLUME 0x00000010 /* Remote volume control */
|
||||
#define BT_HFP_HF_FEATURE_ECS 0x00000020 /* Enhanced call status */
|
||||
#define BT_HFP_HF_FEATURE_ECC 0x00000040 /* Enhanced call control */
|
||||
#define BT_HFP_HF_FEATURE_CODEC_NEG 0x00000080 /* CODEC Negotiation */
|
||||
#define BT_HFP_HF_FEATURE_HF_IND 0x00000100 /* HF Indicators */
|
||||
#define BT_HFP_HF_FEATURE_ESCO_S4 0x00000200 /* eSCO S4 Settings */
|
||||
|
||||
/* HFP HF Supported features */
|
||||
#define BT_HFP_HF_SUPPORTED_FEATURES (BT_HFP_HF_FEATURE_ECNR | \
|
||||
BT_HFP_HF_FEATURE_CLI | \
|
||||
BT_HFP_HF_FEATURE_VOLUME | \
|
||||
BT_HFP_HF_FEATURE_CODEC_NEG)
|
||||
|
||||
#define HF_MAX_BUF_LEN BT_HF_CLIENT_MAX_PDU
|
||||
#define HF_MAX_AG_INDICATORS 20
|
||||
|
||||
struct bt_hfp_hf {
|
||||
struct bt_rfcomm_dlc rfcomm_dlc;
|
||||
char hf_buffer[HF_MAX_BUF_LEN];
|
||||
struct at_client at;
|
||||
uint32_t hf_features;
|
||||
uint32_t ag_features;
|
||||
int8_t ind_table[HF_MAX_AG_INDICATORS];
|
||||
};
|
||||
|
||||
enum hfp_hf_ag_indicators {
|
||||
HF_SERVICE_IND,
|
||||
HF_CALL_IND,
|
||||
HF_CALL_SETUP_IND,
|
||||
HF_CALL_HELD_IND,
|
||||
HF_SINGNAL_IND,
|
||||
HF_ROAM_IND,
|
||||
HF_BATTERY_IND
|
||||
};
|
1110
components/ble/ble_stack/host/iso.c
Normal file
1110
components/ble/ble_stack/host/iso.c
Normal file
File diff suppressed because it is too large
Load diff
110
components/ble/ble_stack/host/iso_internal.h
Normal file
110
components/ble/ble_stack/host/iso_internal.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/** @file
|
||||
* @brief Internal APIs for Bluetooth ISO handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <iso.h>
|
||||
|
||||
#define BT_ISO_DATA_PATH_DISABLED 0xFF
|
||||
|
||||
struct iso_data {
|
||||
/** BT_BUF_ISO_IN */
|
||||
uint8_t type;
|
||||
|
||||
/* Index into the bt_conn storage array */
|
||||
uint8_t index;
|
||||
|
||||
/** ISO connection handle */
|
||||
uint16_t handle;
|
||||
|
||||
/** ISO timestamp */
|
||||
uint32_t ts;
|
||||
};
|
||||
|
||||
#define iso(buf) ((struct iso_data *)net_buf_user_data(buf))
|
||||
|
||||
#if defined(CONFIG_BT_MAX_ISO_CONN)
|
||||
extern struct bt_conn iso_conns[CONFIG_BT_MAX_ISO_CONN];
|
||||
#endif
|
||||
|
||||
/* Process ISO buffer */
|
||||
void hci_iso(struct net_buf *buf);
|
||||
|
||||
/* Allocates RX buffer */
|
||||
struct net_buf *bt_iso_get_rx(uint32_t timeout);
|
||||
|
||||
/* Create new ISO connecting */
|
||||
struct bt_conn *iso_new(void);
|
||||
|
||||
/* Process CIS Estabilished event */
|
||||
void hci_le_cis_estabilished(struct net_buf *buf);
|
||||
|
||||
/* Process CIS Request event */
|
||||
void hci_le_cis_req(struct net_buf *buf);
|
||||
|
||||
/* Notify ISO channels of a new connection */
|
||||
int bt_iso_accept(struct bt_conn *conn);
|
||||
|
||||
/* Notify ISO channels of a new connection */
|
||||
void bt_iso_connected(struct bt_conn *conn);
|
||||
|
||||
/* Notify ISO channels of a disconnect event */
|
||||
void bt_iso_disconnected(struct bt_conn *conn);
|
||||
|
||||
/* Allocate ISO PDU */
|
||||
#if defined(CONFIG_NET_BUF_LOG)
|
||||
struct net_buf *bt_iso_create_pdu_timeout_debug(struct net_buf_pool *pool,
|
||||
size_t reserve,
|
||||
k_timeout_t timeout,
|
||||
const char *func, int line);
|
||||
#define bt_iso_create_pdu_timeout(_pool, _reserve, _timeout) \
|
||||
bt_iso_create_pdu_timeout_debug(_pool, _reserve, _timeout, \
|
||||
__func__, __LINE__)
|
||||
|
||||
#define bt_iso_create_pdu(_pool, _reserve) \
|
||||
bt_iso_create_pdu_timeout_debug(_pool, _reserve, K_FOREVER, \
|
||||
__func__, __line__)
|
||||
#else
|
||||
struct net_buf *bt_iso_create_pdu_timeout(struct net_buf_pool *pool,
|
||||
size_t reserve, uint32_t timeout);
|
||||
|
||||
#define bt_iso_create_pdu(_pool, _reserve) \
|
||||
bt_iso_create_pdu_timeout(_pool, _reserve, K_FOREVER)
|
||||
#endif
|
||||
|
||||
/* Allocate ISO Fragment */
|
||||
#if defined(CONFIG_NET_BUF_LOG)
|
||||
struct net_buf *bt_iso_create_frag_timeout_debug(size_t reserve,
|
||||
k_timeout_t timeout,
|
||||
const char *func, int line);
|
||||
|
||||
#define bt_iso_create_frag_timeout(_reserve, _timeout) \
|
||||
bt_iso_create_frag_timeout_debug(_reserve, _timeout, \
|
||||
__func__, __LINE__)
|
||||
|
||||
#define bt_iso_create_frag(_reserve) \
|
||||
bt_iso_create_frag_timeout_debug(_reserve, K_FOREVER, \
|
||||
__func__, __LINE__)
|
||||
#else
|
||||
struct net_buf *bt_iso_create_frag_timeout(size_t reserve, uint32_t timeout);
|
||||
|
||||
#define bt_iso_create_frag(_reserve) \
|
||||
bt_iso_create_frag_timeout(_reserve, K_FOREVER)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_AUDIO_DEBUG_ISO)
|
||||
void bt_iso_chan_set_state_debug(struct bt_iso_chan *chan, uint8_t state,
|
||||
const char *func, int line);
|
||||
#define bt_iso_chan_set_state(_chan, _state) \
|
||||
bt_iso_chan_set_state_debug(_chan, _state, __func__, __LINE__)
|
||||
#else
|
||||
void bt_iso_chan_set_state(struct bt_iso_chan *chan, uint8_t state);
|
||||
#endif /* CONFIG_BT_AUDIO_DEBUG_ISO */
|
||||
|
||||
/* Process incoming data for a connection */
|
||||
void bt_iso_recv(struct bt_conn *conn, struct net_buf *buf, uint8_t flags);
|
507
components/ble/ble_stack/host/keys.c
Normal file
507
components/ble/ble_stack/host/keys.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/* keys.c - Bluetooth key handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <atomic.h>
|
||||
#include <misc/util.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <conn.h>
|
||||
#include <hci_host.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_KEYS)
|
||||
#include "log.h"
|
||||
|
||||
#include "rpa.h"
|
||||
#include "gatt_internal.h"
|
||||
#include "hci_core.h"
|
||||
#include "smp.h"
|
||||
#include "settings.h"
|
||||
#include "keys.h"
|
||||
#if defined(BFLB_BLE)
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
#include "easyflash.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static struct bt_keys key_pool[CONFIG_BT_MAX_PAIRED];
|
||||
|
||||
#define BT_KEYS_STORAGE_LEN_COMPAT (BT_KEYS_STORAGE_LEN - sizeof(uint32_t))
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
static u32_t aging_counter_val;
|
||||
static struct bt_keys *last_keys_updated;
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
|
||||
struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
struct bt_keys *keys;
|
||||
int i;
|
||||
size_t first_free_slot = ARRAY_SIZE(key_pool);
|
||||
|
||||
BT_DBG("%s", bt_addr_le_str(addr));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
keys = &key_pool[i];
|
||||
|
||||
if (keys->id == id && !bt_addr_le_cmp(&keys->addr, addr)) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
if (first_free_slot == ARRAY_SIZE(key_pool) &&
|
||||
(!bt_addr_le_cmp(&keys->addr, BT_ADDR_LE_ANY) ||
|
||||
!keys->enc_size)) {
|
||||
first_free_slot = i;
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
if (first_free_slot == ARRAY_SIZE(key_pool)) {
|
||||
struct bt_keys *oldest = &key_pool[0];
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(key_pool); i++) {
|
||||
struct bt_keys *current = &key_pool[i];
|
||||
|
||||
if (current->aging_counter < oldest->aging_counter) {
|
||||
oldest = current;
|
||||
}
|
||||
}
|
||||
|
||||
bt_unpair(oldest->id, &oldest->addr);
|
||||
if (!bt_addr_le_cmp(&oldest->addr, BT_ADDR_LE_ANY)) {
|
||||
first_free_slot = oldest - &key_pool[0];
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
if (first_free_slot < ARRAY_SIZE(key_pool)) {
|
||||
keys = &key_pool[first_free_slot];
|
||||
keys->id = id;
|
||||
bt_addr_le_copy(&keys->addr, addr);
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
keys->aging_counter = ++aging_counter_val;
|
||||
last_keys_updated = keys;
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
BT_DBG("created %p for %s", keys, bt_addr_le_str(addr));
|
||||
return keys;
|
||||
}
|
||||
|
||||
BT_DBG("unable to create keys for %s", bt_addr_le_str(addr));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bt_foreach_bond(u8_t id, void (*func)(const struct bt_bond_info *info,
|
||||
void *user_data),
|
||||
void *user_data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
struct bt_keys *keys = &key_pool[i];
|
||||
|
||||
if (keys->keys && keys->id == id) {
|
||||
struct bt_bond_info info;
|
||||
|
||||
bt_addr_le_copy(&info.addr, &keys->addr);
|
||||
func(&info, user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bt_keys_foreach(int type, void (*func)(struct bt_keys *keys, void *data),
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
if ((key_pool[i].keys & type)) {
|
||||
func(&key_pool[i], data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct bt_keys *bt_keys_find(int type, u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
BT_DBG("type %d %s", type, bt_addr_le_str(addr));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
if ((key_pool[i].keys & type) && key_pool[i].id == id &&
|
||||
!bt_addr_le_cmp(&key_pool[i].addr, addr)) {
|
||||
return &key_pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bt_keys *bt_keys_get_type(int type, u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
struct bt_keys *keys;
|
||||
|
||||
BT_DBG("type %d %s", type, bt_addr_le_str(addr));
|
||||
|
||||
keys = bt_keys_find(type, id, addr);
|
||||
if (keys) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
keys = bt_keys_get_addr(id, addr);
|
||||
if (!keys) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bt_keys_add_type(keys, type);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
struct bt_keys *bt_keys_find_irk(u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
BT_DBG("%s", bt_addr_le_str(addr));
|
||||
|
||||
if (!bt_addr_le_is_rpa(addr)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
if (!(key_pool[i].keys & BT_KEYS_IRK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key_pool[i].id == id &&
|
||||
!bt_addr_cmp(&addr->a, &key_pool[i].irk.rpa)) {
|
||||
BT_DBG("cached RPA %s for %s",
|
||||
bt_addr_str(&key_pool[i].irk.rpa),
|
||||
bt_addr_le_str(&key_pool[i].addr));
|
||||
return &key_pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
if (!(key_pool[i].keys & BT_KEYS_IRK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key_pool[i].id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bt_rpa_irk_matches(key_pool[i].irk.val, &addr->a)) {
|
||||
BT_DBG("RPA %s matches %s",
|
||||
bt_addr_str(&key_pool[i].irk.rpa),
|
||||
bt_addr_le_str(&key_pool[i].addr));
|
||||
|
||||
bt_addr_copy(&key_pool[i].irk.rpa, &addr->a);
|
||||
|
||||
return &key_pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
BT_DBG("No IRK for %s", bt_addr_le_str(addr));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bt_keys *bt_keys_find_addr(u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
BT_DBG("%s", bt_addr_le_str(addr));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
if (key_pool[i].id == id &&
|
||||
!bt_addr_le_cmp(&key_pool[i].addr, addr)) {
|
||||
return &key_pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BLE_AT_CMD)
|
||||
bt_addr_le_t *bt_get_keys_address(u8_t id)
|
||||
{
|
||||
bt_addr_le_t addr;
|
||||
|
||||
memset(&addr,0,sizeof(bt_addr_le_t));
|
||||
if(id < ARRAY_SIZE(key_pool)){
|
||||
if (bt_addr_le_cmp(&key_pool[id].addr, &addr)) {
|
||||
return &key_pool[id].addr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void bt_keys_add_type(struct bt_keys *keys, int type)
|
||||
{
|
||||
keys->keys |= type;
|
||||
}
|
||||
|
||||
void bt_keys_clear(struct bt_keys *keys)
|
||||
{
|
||||
#if defined(BFLB_BLE)
|
||||
if (keys->keys & BT_KEYS_IRK) {
|
||||
bt_id_del(keys);
|
||||
}
|
||||
|
||||
memset(keys, 0, sizeof(*keys));
|
||||
|
||||
#if defined (CONFIG_BT_SETTINGS)
|
||||
ef_del_env(NV_KEY_POOL);
|
||||
#endif
|
||||
#else
|
||||
BT_DBG("%s (keys 0x%04x)", bt_addr_le_str(&keys->addr), keys->keys);
|
||||
|
||||
if (keys->keys & BT_KEYS_IRK) {
|
||||
bt_id_del(keys);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
|
||||
/* Delete stored keys from flash */
|
||||
if (keys->id) {
|
||||
char id[4];
|
||||
|
||||
u8_to_dec(id, sizeof(id), keys->id);
|
||||
bt_settings_encode_key(key, sizeof(key), "keys",
|
||||
&keys->addr, id);
|
||||
} else {
|
||||
bt_settings_encode_key(key, sizeof(key), "keys",
|
||||
&keys->addr, NULL);
|
||||
}
|
||||
|
||||
BT_DBG("Deleting key %s", log_strdup(key));
|
||||
settings_delete(key);
|
||||
}
|
||||
|
||||
(void)memset(keys, 0, sizeof(*keys));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void keys_clear_id(struct bt_keys *keys, void *data)
|
||||
{
|
||||
u8_t *id = data;
|
||||
|
||||
if (*id == keys->id) {
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
bt_gatt_clear(*id, &keys->addr);
|
||||
}
|
||||
|
||||
bt_keys_clear(keys);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_keys_clear_all(u8_t id)
|
||||
{
|
||||
bt_keys_foreach(BT_KEYS_ALL, keys_clear_id, &id);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
int bt_keys_store(struct bt_keys *keys)
|
||||
{
|
||||
#if defined(BFLB_BLE)
|
||||
int err;
|
||||
err = bt_settings_set_bin(NV_KEY_POOL, (const u8_t *)&key_pool[0], sizeof(key_pool));
|
||||
return err;
|
||||
#else
|
||||
char val[BT_SETTINGS_SIZE(BT_KEYS_STORAGE_LEN)];
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
char *str;
|
||||
int err;
|
||||
|
||||
str = settings_str_from_bytes(keys->storage_start, BT_KEYS_STORAGE_LEN,
|
||||
val, sizeof(val));
|
||||
if (!str) {
|
||||
BT_ERR("Unable to encode bt_keys as value");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (keys->id) {
|
||||
char id[4];
|
||||
|
||||
u8_to_dec(id, sizeof(id), keys->id);
|
||||
bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr,
|
||||
id);
|
||||
} else {
|
||||
bt_settings_encode_key(key, sizeof(key), "keys", &keys->addr,
|
||||
NULL);
|
||||
}
|
||||
|
||||
err = settings_save_one(key, keys->storage_start, BT_KEYS_STORAGE_LEN);
|
||||
if (err) {
|
||||
BT_ERR("Failed to save keys (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_DBG("Stored keys for %s (%s)", bt_addr_le_str(&keys->addr),
|
||||
log_strdup(key));
|
||||
|
||||
return 0;
|
||||
#endif //BFLB_BLE
|
||||
}
|
||||
|
||||
#if !defined(BFLB_BLE)
|
||||
static int keys_set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct bt_keys *keys;
|
||||
bt_addr_le_t addr;
|
||||
u8_t id;
|
||||
size_t len;
|
||||
int err;
|
||||
char val[BT_KEYS_STORAGE_LEN];
|
||||
const char *next;
|
||||
|
||||
if (!name) {
|
||||
BT_ERR("Insufficient number of arguments");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = read_cb(cb_arg, val, sizeof(val));
|
||||
if (len < 0) {
|
||||
BT_ERR("Failed to read value (err %zu)", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("name %s val %s", log_strdup(name),
|
||||
(len) ? bt_hex(val, sizeof(val)) : "(null)");
|
||||
|
||||
err = bt_settings_decode_key(name, &addr);
|
||||
if (err) {
|
||||
BT_ERR("Unable to decode address %s", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
settings_name_next(name, &next);
|
||||
|
||||
if (!next) {
|
||||
id = BT_ID_DEFAULT;
|
||||
} else {
|
||||
id = strtol(next, NULL, 10);
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
keys = bt_keys_find(BT_KEYS_ALL, id, &addr);
|
||||
if (keys) {
|
||||
(void)memset(keys, 0, sizeof(*keys));
|
||||
BT_DBG("Cleared keys for %s", bt_addr_le_str(&addr));
|
||||
} else {
|
||||
BT_WARN("Unable to find deleted keys for %s",
|
||||
bt_addr_le_str(&addr));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
keys = bt_keys_get_addr(id, &addr);
|
||||
if (!keys) {
|
||||
BT_ERR("Failed to allocate keys for %s", bt_addr_le_str(&addr));
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (len != BT_KEYS_STORAGE_LEN) {
|
||||
do {
|
||||
/* Load shorter structure for compatibility with old
|
||||
* records format with no counter.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) &&
|
||||
len == BT_KEYS_STORAGE_LEN_COMPAT) {
|
||||
BT_WARN("Keys for %s have no aging counter",
|
||||
bt_addr_le_str(&addr));
|
||||
memcpy(keys->storage_start, val, len);
|
||||
continue;
|
||||
}
|
||||
|
||||
BT_ERR("Invalid key length %zu != %zu", len,
|
||||
BT_KEYS_STORAGE_LEN);
|
||||
bt_keys_clear(keys);
|
||||
|
||||
return -EINVAL;
|
||||
} while (0);
|
||||
} else {
|
||||
memcpy(keys->storage_start, val, len);
|
||||
}
|
||||
|
||||
BT_DBG("Successfully restored keys for %s", bt_addr_le_str(&addr));
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
if (aging_counter_val < keys->aging_counter) {
|
||||
aging_counter_val = keys->aging_counter;
|
||||
}
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
return 0;
|
||||
}
|
||||
#endif //!(BFLB_BLE)
|
||||
|
||||
static void id_add(struct bt_keys *keys, void *user_data)
|
||||
{
|
||||
bt_id_add(keys);
|
||||
}
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
int keys_commit(void)
|
||||
#else
|
||||
static int keys_commit(void)
|
||||
#endif
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
/* We do this in commit() rather than add() since add() may get
|
||||
* called multiple times for the same address, especially if
|
||||
* the keys were already removed.
|
||||
*/
|
||||
bt_keys_foreach(BT_KEYS_IRK, id_add, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//SETTINGS_STATIC_HANDLER_DEFINE(bt_keys, "bt/keys", NULL, keys_set, keys_commit,
|
||||
// NULL);
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
int bt_keys_load(void)
|
||||
{
|
||||
return bt_settings_get_bin(NV_KEY_POOL, (u8_t *)&key_pool[0], sizeof(key_pool), NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_BT_SETTINGS */
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
void bt_keys_update_usage(u8_t id, const bt_addr_le_t *addr)
|
||||
{
|
||||
struct bt_keys *keys = bt_keys_find_addr(id, addr);
|
||||
|
||||
if (!keys) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_keys_updated == keys) {
|
||||
return;
|
||||
}
|
||||
|
||||
keys->aging_counter = ++aging_counter_val;
|
||||
last_keys_updated = keys;
|
||||
|
||||
BT_DBG("Aging counter for %s is set to %u", bt_addr_le_str(addr),
|
||||
keys->aging_counter);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) {
|
||||
bt_keys_store(keys);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
123
components/ble/ble_stack/host/keys.h
Normal file
123
components/ble/ble_stack/host/keys.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/* keys.h - Bluetooth key handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
enum {
|
||||
BT_KEYS_SLAVE_LTK = BIT(0),
|
||||
BT_KEYS_IRK = BIT(1),
|
||||
BT_KEYS_LTK = BIT(2),
|
||||
BT_KEYS_LOCAL_CSRK = BIT(3),
|
||||
BT_KEYS_REMOTE_CSRK = BIT(4),
|
||||
BT_KEYS_LTK_P256 = BIT(5),
|
||||
|
||||
BT_KEYS_ALL = (BT_KEYS_SLAVE_LTK | BT_KEYS_IRK | \
|
||||
BT_KEYS_LTK | BT_KEYS_LOCAL_CSRK | \
|
||||
BT_KEYS_REMOTE_CSRK | BT_KEYS_LTK_P256),
|
||||
};
|
||||
|
||||
enum {
|
||||
BT_KEYS_AUTHENTICATED = BIT(0),
|
||||
BT_KEYS_DEBUG = BIT(1),
|
||||
BT_KEYS_ID_PENDING_ADD = BIT(2),
|
||||
BT_KEYS_ID_PENDING_DEL = BIT(3),
|
||||
BT_KEYS_SC = BIT(4),
|
||||
};
|
||||
|
||||
struct bt_ltk {
|
||||
u8_t rand[8];
|
||||
u8_t ediv[2];
|
||||
u8_t val[16];
|
||||
};
|
||||
|
||||
struct bt_irk {
|
||||
u8_t val[16];
|
||||
bt_addr_t rpa;
|
||||
};
|
||||
|
||||
struct bt_csrk {
|
||||
u8_t val[16];
|
||||
u32_t cnt;
|
||||
};
|
||||
|
||||
struct bt_keys {
|
||||
u8_t id;
|
||||
bt_addr_le_t addr;
|
||||
#if !defined(BFLB_BLE)
|
||||
u8_t storage_start[0];
|
||||
#endif
|
||||
u8_t enc_size;
|
||||
u8_t flags;
|
||||
u16_t keys;
|
||||
struct bt_ltk ltk;
|
||||
struct bt_irk irk;
|
||||
#if defined(CONFIG_BT_SIGNING)
|
||||
struct bt_csrk local_csrk;
|
||||
struct bt_csrk remote_csrk;
|
||||
#endif /* BT_SIGNING */
|
||||
#if !defined(CONFIG_BT_SMP_SC_PAIR_ONLY)
|
||||
struct bt_ltk slave_ltk;
|
||||
#endif /* CONFIG_BT_SMP_SC_PAIR_ONLY */
|
||||
#if (defined(CONFIG_BT_KEYS_OVERWRITE_OLDEST))
|
||||
u32_t aging_counter;
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
};
|
||||
|
||||
#if !defined(BFLB_BLE)
|
||||
#define BT_KEYS_STORAGE_LEN (sizeof(struct bt_keys) - \
|
||||
offsetof(struct bt_keys, storage_start))
|
||||
#endif
|
||||
|
||||
void bt_keys_foreach(int type, void (*func)(struct bt_keys *keys, void *data),
|
||||
void *data);
|
||||
|
||||
struct bt_keys *bt_keys_get_addr(u8_t id, const bt_addr_le_t *addr);
|
||||
struct bt_keys *bt_keys_get_type(int type, u8_t id, const bt_addr_le_t *addr);
|
||||
struct bt_keys *bt_keys_find(int type, u8_t id, const bt_addr_le_t *addr);
|
||||
struct bt_keys *bt_keys_find_irk(u8_t id, const bt_addr_le_t *addr);
|
||||
struct bt_keys *bt_keys_find_addr(u8_t id, const bt_addr_le_t *addr);
|
||||
#if defined(CONFIG_BLE_AT_CMD)
|
||||
bt_addr_le_t *bt_get_keys_address(u8_t id);
|
||||
#endif
|
||||
|
||||
void bt_keys_add_type(struct bt_keys *keys, int type);
|
||||
void bt_keys_clear(struct bt_keys *keys);
|
||||
void bt_keys_clear_all(u8_t id);
|
||||
#if defined(BFLB_BLE)
|
||||
int keys_commit(void);
|
||||
int bt_keys_load(void);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
int bt_keys_store(struct bt_keys *keys);
|
||||
#else
|
||||
static inline int bt_keys_store(struct bt_keys *keys)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BT_LINK_KEY_AUTHENTICATED = BIT(0),
|
||||
BT_LINK_KEY_DEBUG = BIT(1),
|
||||
BT_LINK_KEY_SC = BIT(2),
|
||||
};
|
||||
|
||||
struct bt_keys_link_key {
|
||||
bt_addr_t addr;
|
||||
u8_t flags;
|
||||
u8_t val[16];
|
||||
};
|
||||
|
||||
struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr);
|
||||
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr);
|
||||
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key);
|
||||
void bt_keys_link_key_clear_addr(const bt_addr_t *addr);
|
||||
|
||||
/* This function is used to signal that the key has been used for paring */
|
||||
/* It updates the aging counter and saves it to flash if configuration option */
|
||||
/* BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING is enabled */
|
||||
void bt_keys_update_usage(u8_t id, const bt_addr_le_t *addr);
|
241
components/ble/ble_stack/host/keys_br.c
Normal file
241
components/ble/ble_stack/host/keys_br.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/* keys_br.c - Bluetooth BR/EDR key handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <string.h>
|
||||
#include <atomic.h>
|
||||
#include <util.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <conn.h>
|
||||
#include <hci_host.h>
|
||||
#include <settings.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_KEYS)
|
||||
#define LOG_MODULE_NAME bt_keys_br
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "settings.h"
|
||||
#include "keys.h"
|
||||
|
||||
static struct bt_keys_link_key key_pool[CONFIG_BT_MAX_PAIRED];
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
static uint32_t aging_counter_val;
|
||||
static struct bt_keys_link_key *last_keys_updated;
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
|
||||
struct bt_keys_link_key *bt_keys_find_link_key(const bt_addr_t *addr)
|
||||
{
|
||||
struct bt_keys_link_key *key;
|
||||
int i;
|
||||
|
||||
BT_DBG("%s", bt_addr_str(addr));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
key = &key_pool[i];
|
||||
|
||||
if (!bt_addr_cmp(&key->addr, addr)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bt_keys_link_key *bt_keys_get_link_key(const bt_addr_t *addr)
|
||||
{
|
||||
struct bt_keys_link_key *key;
|
||||
|
||||
key = bt_keys_find_link_key(addr);
|
||||
if (key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
key = bt_keys_find_link_key(BT_ADDR_ANY);
|
||||
#if 0//IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) //MBHJ
|
||||
if (!key) {
|
||||
int i;
|
||||
|
||||
key = &key_pool[0];
|
||||
for (i = 1; i < ARRAY_SIZE(key_pool); i++) {
|
||||
struct bt_keys_link_key *current = &key_pool[i];
|
||||
|
||||
if (current->aging_counter < key->aging_counter) {
|
||||
key = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (key) {
|
||||
bt_keys_link_key_clear(key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (key) {
|
||||
bt_addr_copy(&key->addr, addr);
|
||||
#if 0 //IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST) //MBHJ
|
||||
key->aging_counter = ++aging_counter_val;
|
||||
last_keys_updated = key;
|
||||
#endif
|
||||
BT_DBG("created %p for %s", key, bt_addr_str(addr));
|
||||
return key;
|
||||
}
|
||||
|
||||
BT_DBG("unable to create keys for %s", bt_addr_str(addr));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bt_keys_link_key_clear(struct bt_keys_link_key *link_key)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
bt_addr_le_t le_addr;
|
||||
|
||||
le_addr.type = BT_ADDR_LE_PUBLIC;
|
||||
bt_addr_copy(&le_addr.a, &link_key->addr);
|
||||
bt_settings_encode_key(key, sizeof(key), "link_key",
|
||||
&le_addr, NULL);
|
||||
settings_delete(key);
|
||||
}
|
||||
|
||||
BT_DBG("%s", bt_addr_str(&link_key->addr));
|
||||
(void)memset(link_key, 0, sizeof(*link_key));
|
||||
}
|
||||
|
||||
void bt_keys_link_key_clear_addr(const bt_addr_t *addr)
|
||||
{
|
||||
int i;
|
||||
struct bt_keys_link_key *key;
|
||||
|
||||
if (!addr) {
|
||||
for (i = 0; i < ARRAY_SIZE(key_pool); i++) {
|
||||
key = &key_pool[i];
|
||||
bt_keys_link_key_clear(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
key = bt_keys_find_link_key(addr);
|
||||
if (key) {
|
||||
bt_keys_link_key_clear(key);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_keys_link_key_store(struct bt_keys_link_key *link_key)
|
||||
{
|
||||
#if 0 //MBHJ
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
int err;
|
||||
char key[BT_SETTINGS_KEY_MAX];
|
||||
bt_addr_le_t le_addr;
|
||||
|
||||
le_addr.type = BT_ADDR_LE_PUBLIC;
|
||||
bt_addr_copy(&le_addr.a, &link_key->addr);
|
||||
bt_settings_encode_key(key, sizeof(key), "link_key",
|
||||
&le_addr, NULL);
|
||||
|
||||
err = settings_save_one(key, link_key->storage_start,
|
||||
BT_KEYS_LINK_KEY_STORAGE_LEN);
|
||||
if (err) {
|
||||
BT_ERR("Failed to svae link key (err %d)", err);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
|
||||
static int link_key_set(const char *name, size_t len_rd,
|
||||
settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
int err;
|
||||
ssize_t len;
|
||||
bt_addr_le_t le_addr;
|
||||
struct bt_keys_link_key *link_key;
|
||||
char val[BT_KEYS_LINK_KEY_STORAGE_LEN];
|
||||
|
||||
if (!name) {
|
||||
BT_ERR("Insufficient number of arguments");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = read_cb(cb_arg, val, sizeof(val));
|
||||
if (len < 0) {
|
||||
BT_ERR("Failed to read value (err %zu)", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("name %s val %s", log_strdup(name),
|
||||
len ? bt_hex(val, sizeof(val)) : "(null)");
|
||||
|
||||
err = bt_settings_decode_key(name, &le_addr);
|
||||
if (err) {
|
||||
BT_ERR("Unable to decode address %s", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
link_key = bt_keys_get_link_key(&le_addr.a);
|
||||
if (len != BT_KEYS_LINK_KEY_STORAGE_LEN) {
|
||||
if (link_key) {
|
||||
bt_keys_link_key_clear(link_key);
|
||||
BT_DBG("Clear keys for %s", bt_addr_le_str(&le_addr));
|
||||
} else {
|
||||
BT_WARN("Unable to find deleted keys for %s",
|
||||
bt_addr_le_str(&le_addr));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(link_key->storage_start, val, len);
|
||||
BT_DBG("Successfully restored link key for %s",
|
||||
bt_addr_le_str(&le_addr));
|
||||
#if IS_ENABLED(CONFIG_BT_KEYS_OVERWRITE_OLDEST)
|
||||
if (aging_counter_val < link_key->aging_counter) {
|
||||
aging_counter_val = link_key->aging_counter;
|
||||
}
|
||||
#endif /* CONFIG_BT_KEYS_OVERWRITE_OLDEST */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_key_commit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(bt_link_key, "bt/link_key", NULL, link_key_set,
|
||||
link_key_commit, NULL);
|
||||
|
||||
void bt_keys_link_key_update_usage(const bt_addr_t *addr)
|
||||
{
|
||||
struct bt_keys_link_key *link_key = bt_keys_find_link_key(addr);
|
||||
|
||||
if (!link_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_keys_updated == link_key) {
|
||||
return;
|
||||
}
|
||||
|
||||
link_key->aging_counter = ++aging_counter_val;
|
||||
last_keys_updated = link_key;
|
||||
|
||||
BT_DBG("Aging counter for %s is set to %u", bt_addr_str(addr),
|
||||
link_key->aging_counter);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_KEYS_SAVE_AGING_COUNTER_ON_PAIRING)) {
|
||||
bt_keys_link_key_store(link_key);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
1955
components/ble/ble_stack/host/l2cap.c
Normal file
1955
components/ble/ble_stack/host/l2cap.c
Normal file
File diff suppressed because it is too large
Load diff
1569
components/ble/ble_stack/host/l2cap_br.c
Normal file
1569
components/ble/ble_stack/host/l2cap_br.c
Normal file
File diff suppressed because it is too large
Load diff
340
components/ble/ble_stack/host/l2cap_internal.h
Normal file
340
components/ble/ble_stack/host/l2cap_internal.h
Normal file
|
@ -0,0 +1,340 @@
|
|||
/** @file
|
||||
* @brief Internal APIs for Bluetooth L2CAP handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <l2cap.h>
|
||||
|
||||
enum l2cap_conn_list_action {
|
||||
BT_L2CAP_CHAN_LOOKUP,
|
||||
BT_L2CAP_CHAN_DETACH,
|
||||
};
|
||||
|
||||
#define BT_L2CAP_CID_BR_SIG 0x0001
|
||||
#define BT_L2CAP_CID_ATT 0x0004
|
||||
#define BT_L2CAP_CID_LE_SIG 0x0005
|
||||
#define BT_L2CAP_CID_SMP 0x0006
|
||||
#define BT_L2CAP_CID_BR_SMP 0x0007
|
||||
|
||||
#define BT_L2CAP_PSM_RFCOMM 0x0003
|
||||
|
||||
struct bt_l2cap_hdr {
|
||||
u16_t len;
|
||||
u16_t cid;
|
||||
} __packed;
|
||||
|
||||
struct bt_l2cap_sig_hdr {
|
||||
u8_t code;
|
||||
u8_t ident;
|
||||
u16_t len;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_REJ_NOT_UNDERSTOOD 0x0000
|
||||
#define BT_L2CAP_REJ_MTU_EXCEEDED 0x0001
|
||||
#define BT_L2CAP_REJ_INVALID_CID 0x0002
|
||||
|
||||
#define BT_L2CAP_CMD_REJECT 0x01
|
||||
struct bt_l2cap_cmd_reject {
|
||||
u16_t reason;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
struct bt_l2cap_cmd_reject_cid_data {
|
||||
u16_t scid;
|
||||
u16_t dcid;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_CONN_REQ 0x02
|
||||
struct bt_l2cap_conn_req {
|
||||
u16_t psm;
|
||||
u16_t scid;
|
||||
} __packed;
|
||||
|
||||
/* command statuses in reposnse */
|
||||
#define BT_L2CAP_CS_NO_INFO 0x0000
|
||||
#define BT_L2CAP_CS_AUTHEN_PEND 0x0001
|
||||
|
||||
/* valid results in conn response on BR/EDR */
|
||||
#define BT_L2CAP_BR_SUCCESS 0x0000
|
||||
#define BT_L2CAP_BR_PENDING 0x0001
|
||||
#define BT_L2CAP_BR_ERR_PSM_NOT_SUPP 0x0002
|
||||
#define BT_L2CAP_BR_ERR_SEC_BLOCK 0x0003
|
||||
#define BT_L2CAP_BR_ERR_NO_RESOURCES 0x0004
|
||||
#define BT_L2CAP_BR_ERR_INVALID_SCID 0x0006
|
||||
#define BT_L2CAP_BR_ERR_SCID_IN_USE 0x0007
|
||||
|
||||
#define BT_L2CAP_CONN_RSP 0x03
|
||||
struct bt_l2cap_conn_rsp {
|
||||
u16_t dcid;
|
||||
u16_t scid;
|
||||
u16_t result;
|
||||
u16_t status;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_CONF_SUCCESS 0x0000
|
||||
#define BT_L2CAP_CONF_UNACCEPT 0x0001
|
||||
#define BT_L2CAP_CONF_REJECT 0x0002
|
||||
|
||||
#define BT_L2CAP_CONF_REQ 0x04
|
||||
struct bt_l2cap_conf_req {
|
||||
u16_t dcid;
|
||||
u16_t flags;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_CONF_RSP 0x05
|
||||
struct bt_l2cap_conf_rsp {
|
||||
u16_t scid;
|
||||
u16_t flags;
|
||||
u16_t result;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
/* Option type used by MTU config request data */
|
||||
#define BT_L2CAP_CONF_OPT_MTU 0x01
|
||||
/* Options bits selecting most significant bit (hint) in type field */
|
||||
#define BT_L2CAP_CONF_HINT 0x80
|
||||
#define BT_L2CAP_CONF_MASK 0x7f
|
||||
|
||||
struct bt_l2cap_conf_opt {
|
||||
u8_t type;
|
||||
u8_t len;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_DISCONN_REQ 0x06
|
||||
struct bt_l2cap_disconn_req {
|
||||
u16_t dcid;
|
||||
u16_t scid;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_DISCONN_RSP 0x07
|
||||
struct bt_l2cap_disconn_rsp {
|
||||
u16_t dcid;
|
||||
u16_t scid;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_INFO_FEAT_MASK 0x0002
|
||||
#define BT_L2CAP_INFO_FIXED_CHAN 0x0003
|
||||
|
||||
#define BT_L2CAP_INFO_REQ 0x0a
|
||||
struct bt_l2cap_info_req {
|
||||
u16_t type;
|
||||
} __packed;
|
||||
|
||||
/* info result */
|
||||
#define BT_L2CAP_INFO_SUCCESS 0x0000
|
||||
#define BT_L2CAP_INFO_NOTSUPP 0x0001
|
||||
|
||||
#define BT_L2CAP_INFO_RSP 0x0b
|
||||
struct bt_l2cap_info_rsp {
|
||||
u16_t type;
|
||||
u16_t result;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_CONN_PARAM_REQ 0x12
|
||||
struct bt_l2cap_conn_param_req {
|
||||
u16_t min_interval;
|
||||
u16_t max_interval;
|
||||
u16_t latency;
|
||||
u16_t timeout;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_CONN_PARAM_ACCEPTED 0x0000
|
||||
#define BT_L2CAP_CONN_PARAM_REJECTED 0x0001
|
||||
|
||||
#define BT_L2CAP_CONN_PARAM_RSP 0x13
|
||||
struct bt_l2cap_conn_param_rsp {
|
||||
u16_t result;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_LE_CONN_REQ 0x14
|
||||
struct bt_l2cap_le_conn_req {
|
||||
u16_t psm;
|
||||
u16_t scid;
|
||||
u16_t mtu;
|
||||
u16_t mps;
|
||||
u16_t credits;
|
||||
} __packed;
|
||||
|
||||
/* valid results in conn response on LE */
|
||||
#define BT_L2CAP_LE_SUCCESS 0x0000
|
||||
#define BT_L2CAP_LE_ERR_PSM_NOT_SUPP 0x0002
|
||||
#define BT_L2CAP_LE_ERR_NO_RESOURCES 0x0004
|
||||
#define BT_L2CAP_LE_ERR_AUTHENTICATION 0x0005
|
||||
#define BT_L2CAP_LE_ERR_AUTHORIZATION 0x0006
|
||||
#define BT_L2CAP_LE_ERR_KEY_SIZE 0x0007
|
||||
#define BT_L2CAP_LE_ERR_ENCRYPTION 0x0008
|
||||
#define BT_L2CAP_LE_ERR_INVALID_SCID 0x0009
|
||||
#define BT_L2CAP_LE_ERR_SCID_IN_USE 0x000A
|
||||
#define BT_L2CAP_LE_ERR_UNACCEPT_PARAMS 0x000B
|
||||
|
||||
#define BT_L2CAP_LE_CONN_RSP 0x15
|
||||
struct bt_l2cap_le_conn_rsp {
|
||||
u16_t dcid;
|
||||
u16_t mtu;
|
||||
u16_t mps;
|
||||
u16_t credits;
|
||||
u16_t result;
|
||||
};
|
||||
|
||||
#define BT_L2CAP_LE_CREDITS 0x16
|
||||
struct bt_l2cap_le_credits {
|
||||
u16_t cid;
|
||||
u16_t credits;
|
||||
} __packed;
|
||||
|
||||
#define BT_L2CAP_SDU_HDR_LEN 2
|
||||
|
||||
#if defined(CONFIG_BT_HCI_ACL_FLOW_CONTROL)
|
||||
#define BT_L2CAP_RX_MTU CONFIG_BT_L2CAP_RX_MTU
|
||||
#else
|
||||
#define BT_L2CAP_RX_MTU (CONFIG_BT_RX_BUF_LEN - \
|
||||
BT_HCI_ACL_HDR_SIZE - BT_L2CAP_HDR_SIZE)
|
||||
#endif
|
||||
|
||||
struct bt_l2cap_fixed_chan {
|
||||
u16_t cid;
|
||||
int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan);
|
||||
sys_snode_t node;
|
||||
};
|
||||
|
||||
#define BT_L2CAP_CHANNEL_DEFINE(_name, _cid, _accept) \
|
||||
const Z_STRUCT_SECTION_ITERABLE(bt_l2cap_fixed_chan, _name) = { \
|
||||
.cid = _cid, \
|
||||
.accept = _accept, \
|
||||
}
|
||||
|
||||
/* Need a name different than bt_l2cap_fixed_chan for a different section */
|
||||
struct bt_l2cap_br_fixed_chan {
|
||||
u16_t cid;
|
||||
int (*accept)(struct bt_conn *conn, struct bt_l2cap_chan **chan);
|
||||
};
|
||||
|
||||
#define BT_L2CAP_BR_CHANNEL_DEFINE(_name, _cid, _accept) \
|
||||
const Z_STRUCT_SECTION_ITERABLE(bt_l2cap_br_fixed_chan, _name) = { \
|
||||
.cid = _cid, \
|
||||
.accept = _accept, \
|
||||
}
|
||||
|
||||
void l2cap_chan_sdu_sent(struct bt_conn *conn, void *user_data);
|
||||
/* Register a fixed L2CAP channel for L2CAP */
|
||||
void bt_l2cap_le_fixed_chan_register(struct bt_l2cap_fixed_chan *chan);
|
||||
|
||||
/* Notify L2CAP channels of a new connection */
|
||||
void bt_l2cap_connected(struct bt_conn *conn);
|
||||
|
||||
/* Notify L2CAP channels of a disconnect event */
|
||||
void bt_l2cap_disconnected(struct bt_conn *conn);
|
||||
|
||||
/* Add channel to the connection */
|
||||
void bt_l2cap_chan_add(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
||||
bt_l2cap_chan_destroy_t destroy);
|
||||
|
||||
/* Remove channel from the connection */
|
||||
void bt_l2cap_chan_remove(struct bt_conn *conn, struct bt_l2cap_chan *chan);
|
||||
|
||||
/* Delete channel */
|
||||
void bt_l2cap_chan_del(struct bt_l2cap_chan *chan);
|
||||
|
||||
const char *bt_l2cap_chan_state_str(bt_l2cap_chan_state_t state);
|
||||
|
||||
#if defined(CONFIG_BT_DEBUG_L2CAP)
|
||||
void bt_l2cap_chan_set_state_debug(struct bt_l2cap_chan *chan,
|
||||
bt_l2cap_chan_state_t state,
|
||||
const char *func, int line);
|
||||
#define bt_l2cap_chan_set_state(_chan, _state) \
|
||||
bt_l2cap_chan_set_state_debug(_chan, _state, __func__, __LINE__)
|
||||
#else
|
||||
void bt_l2cap_chan_set_state(struct bt_l2cap_chan *chan,
|
||||
bt_l2cap_chan_state_t state);
|
||||
#endif /* CONFIG_BT_DEBUG_L2CAP */
|
||||
|
||||
/*
|
||||
* Notify L2CAP channels of a change in encryption state passing additionally
|
||||
* HCI status of performed security procedure.
|
||||
*/
|
||||
void bt_l2cap_encrypt_change(struct bt_conn *conn, u8_t hci_status);
|
||||
|
||||
/* Prepare an L2CAP PDU to be sent over a connection */
|
||||
struct net_buf *bt_l2cap_create_pdu_timeout(struct net_buf_pool *pool,
|
||||
size_t reserve, s32_t timeout);
|
||||
|
||||
#define bt_l2cap_create_pdu(_pool, _reserve) \
|
||||
bt_l2cap_create_pdu_timeout(_pool, _reserve, K_FOREVER)
|
||||
|
||||
/* Prepare a L2CAP Response PDU to be sent over a connection */
|
||||
struct net_buf *bt_l2cap_create_rsp(struct net_buf *buf, size_t reserve);
|
||||
|
||||
/* Send L2CAP PDU over a connection
|
||||
*
|
||||
* Buffer ownership is transferred to stack so either in case of success
|
||||
* or error the buffer will be unref internally.
|
||||
*
|
||||
* Calling this from RX thread is assumed to never fail so the return can be
|
||||
* ignored.
|
||||
*/
|
||||
int bt_l2cap_send_cb(struct bt_conn *conn, u16_t cid, struct net_buf *buf,
|
||||
bt_conn_tx_cb_t cb, void *user_data);
|
||||
|
||||
static inline void bt_l2cap_send(struct bt_conn *conn, u16_t cid,
|
||||
struct net_buf *buf)
|
||||
{
|
||||
bt_l2cap_send_cb(conn, cid, buf, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Receive a new L2CAP PDU from a connection */
|
||||
void bt_l2cap_recv(struct bt_conn *conn, struct net_buf *buf);
|
||||
|
||||
/* Perform connection parameter update request */
|
||||
int bt_l2cap_update_conn_param(struct bt_conn *conn,
|
||||
const struct bt_le_conn_param *param);
|
||||
|
||||
/* Initialize L2CAP and supported channels */
|
||||
void bt_l2cap_init(void);
|
||||
|
||||
/* Lookup channel by Transmission CID */
|
||||
struct bt_l2cap_chan *bt_l2cap_le_lookup_tx_cid(struct bt_conn *conn,
|
||||
u16_t cid);
|
||||
|
||||
/* Lookup channel by Receiver CID */
|
||||
struct bt_l2cap_chan *bt_l2cap_le_lookup_rx_cid(struct bt_conn *conn,
|
||||
u16_t cid);
|
||||
|
||||
/* Initialize BR/EDR L2CAP signal layer */
|
||||
void bt_l2cap_br_init(void);
|
||||
|
||||
/* Register fixed channel */
|
||||
void bt_l2cap_br_fixed_chan_register(struct bt_l2cap_fixed_chan *chan);
|
||||
|
||||
/* Notify BR/EDR L2CAP channels about established new ACL connection */
|
||||
void bt_l2cap_br_connected(struct bt_conn *conn);
|
||||
|
||||
/* Lookup BR/EDR L2CAP channel by Receiver CID */
|
||||
struct bt_l2cap_chan *bt_l2cap_br_lookup_rx_cid(struct bt_conn *conn,
|
||||
u16_t cid);
|
||||
|
||||
/* Disconnects dynamic channel */
|
||||
int bt_l2cap_br_chan_disconnect(struct bt_l2cap_chan *chan);
|
||||
|
||||
/* Make connection to peer psm server */
|
||||
int bt_l2cap_br_chan_connect(struct bt_conn *conn, struct bt_l2cap_chan *chan,
|
||||
u16_t psm);
|
||||
|
||||
/* Send packet data to connected peer */
|
||||
int bt_l2cap_br_chan_send(struct bt_l2cap_chan *chan, struct net_buf *buf);
|
||||
|
||||
/*
|
||||
* Handle security level changed on link passing HCI status of performed
|
||||
* security procedure.
|
||||
*/
|
||||
void l2cap_br_encrypt_change(struct bt_conn *conn, u8_t hci_status);
|
||||
|
||||
/* Handle received data */
|
||||
void bt_l2cap_br_recv(struct bt_conn *conn, struct net_buf *buf);
|
29
components/ble/ble_stack/host/monitor.c
Normal file
29
components/ble/ble_stack/host/monitor.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/** @file
|
||||
* @brief Custom logging over UART
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#if defined(CONFIG_BT_DEBUG_MONITOR)
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <buf.h>
|
||||
#include "monitor.h"
|
||||
#include "log.h"
|
||||
|
||||
void bt_monitor_send(uint16_t opcode, const void *data, size_t len)
|
||||
{
|
||||
const uint8_t *buf = data;
|
||||
|
||||
BT_WARN("[Hci]:pkt_type:[0x%x],pkt_data:[%s]\r\n",opcode,bt_hex(buf,len));
|
||||
}
|
||||
|
||||
void bt_monitor_new_index(uint8_t type, uint8_t bus, bt_addr_t *addr,
|
||||
const char *name)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
102
components/ble/ble_stack/host/monitor.h
Normal file
102
components/ble/ble_stack/host/monitor.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/** @file
|
||||
* @brief Custom monitor protocol logging over UART
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_BT_DEBUG_MONITOR)
|
||||
|
||||
#define BT_MONITOR_NEW_INDEX 0
|
||||
#define BT_MONITOR_DEL_INDEX 1
|
||||
#define BT_MONITOR_COMMAND_PKT 2
|
||||
#define BT_MONITOR_EVENT_PKT 3
|
||||
#define BT_MONITOR_ACL_TX_PKT 4
|
||||
#define BT_MONITOR_ACL_RX_PKT 5
|
||||
#define BT_MONITOR_SCO_TX_PKT 6
|
||||
#define BT_MONITOR_SCO_RX_PKT 7
|
||||
#define BT_MONITOR_OPEN_INDEX 8
|
||||
#define BT_MONITOR_CLOSE_INDEX 9
|
||||
#define BT_MONITOR_INDEX_INFO 10
|
||||
#define BT_MONITOR_VENDOR_DIAG 11
|
||||
#define BT_MONITOR_SYSTEM_NOTE 12
|
||||
#define BT_MONITOR_USER_LOGGING 13
|
||||
#define BT_MONITOR_NOP 255
|
||||
|
||||
#define BT_MONITOR_TYPE_PRIMARY 0
|
||||
#define BT_MONITOR_TYPE_AMP 1
|
||||
|
||||
/* Extended header types */
|
||||
#define BT_MONITOR_COMMAND_DROPS 1
|
||||
#define BT_MONITOR_EVENT_DROPS 2
|
||||
#define BT_MONITOR_ACL_RX_DROPS 3
|
||||
#define BT_MONITOR_ACL_TX_DROPS 4
|
||||
#define BT_MONITOR_SCO_RX_DROPS 5
|
||||
#define BT_MONITOR_SCO_TX_DROPS 6
|
||||
#define BT_MONITOR_OTHER_DROPS 7
|
||||
#define BT_MONITOR_TS32 8
|
||||
|
||||
#define BT_MONITOR_BASE_HDR_LEN 6
|
||||
|
||||
#if defined(CONFIG_BT_BREDR)
|
||||
#define BT_MONITOR_EXT_HDR_MAX 19
|
||||
#else
|
||||
#define BT_MONITOR_EXT_HDR_MAX 15
|
||||
#endif
|
||||
|
||||
struct bt_monitor_hdr {
|
||||
u16_t data_len;
|
||||
u16_t opcode;
|
||||
u8_t flags;
|
||||
u8_t hdr_len;
|
||||
|
||||
u8_t ext[BT_MONITOR_EXT_HDR_MAX];
|
||||
} __packed;
|
||||
|
||||
struct bt_monitor_ts32 {
|
||||
u8_t type;
|
||||
u32_t ts32;
|
||||
} __packed;
|
||||
|
||||
struct bt_monitor_new_index {
|
||||
u8_t type;
|
||||
u8_t bus;
|
||||
u8_t bdaddr[6];
|
||||
char name[8];
|
||||
} __packed;
|
||||
|
||||
struct bt_monitor_user_logging {
|
||||
u8_t priority;
|
||||
u8_t ident_len;
|
||||
} __packed;
|
||||
|
||||
static inline u8_t bt_monitor_opcode(struct net_buf *buf)
|
||||
{
|
||||
switch (bt_buf_get_type(buf)) {
|
||||
case BT_BUF_CMD:
|
||||
return BT_MONITOR_COMMAND_PKT;
|
||||
case BT_BUF_EVT:
|
||||
return BT_MONITOR_EVENT_PKT;
|
||||
case BT_BUF_ACL_OUT:
|
||||
return BT_MONITOR_ACL_TX_PKT;
|
||||
case BT_BUF_ACL_IN:
|
||||
return BT_MONITOR_ACL_RX_PKT;
|
||||
default:
|
||||
return BT_MONITOR_NOP;
|
||||
}
|
||||
}
|
||||
|
||||
void bt_monitor_send(u16_t opcode, const void *data, size_t len);
|
||||
|
||||
void bt_monitor_new_index(u8_t type, u8_t bus, bt_addr_t *addr,
|
||||
const char *name);
|
||||
|
||||
#else /* !CONFIG_BT_DEBUG_MONITOR */
|
||||
|
||||
#define bt_monitor_send(opcode, data, len)
|
||||
#define bt_monitor_new_index(type, bus, addr, name)
|
||||
|
||||
#endif
|
519
components/ble/ble_stack/host/multi_adv.c
Normal file
519
components/ble/ble_stack/host/multi_adv.c
Normal file
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* xx
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <util.h>
|
||||
|
||||
//#include <net/buf.h>
|
||||
#include <bluetooth.h>
|
||||
#include <hci_core.h>
|
||||
|
||||
#include "multi_adv.h"
|
||||
#include "work_q.h"
|
||||
|
||||
static struct multi_adv_instant g_multi_adv_list[MAX_MULTI_ADV_INSTANT];
|
||||
static struct multi_adv_scheduler g_multi_adv_scheduler;
|
||||
static struct k_delayed_work g_multi_adv_timer;
|
||||
|
||||
void multi_adv_schedule_timeslot(struct multi_adv_scheduler *adv_scheduler);
|
||||
int multi_adv_schedule_timer_stop(void);
|
||||
|
||||
int multi_adv_get_instant_num(void)
|
||||
{
|
||||
int i, num = 0;
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
if (inst[i].inuse_flag)
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
struct multi_adv_instant *multi_adv_alloc_unused_instant(void)
|
||||
{
|
||||
int i;
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
if (inst[i].inuse_flag == 0) {
|
||||
inst[i].inuse_flag = 1;
|
||||
inst[i].instant_id = i+1;
|
||||
return &(inst[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multi_adv_delete_instant_by_id(int instant_id)
|
||||
{
|
||||
int i;
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) {
|
||||
inst[i].inuse_flag = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct multi_adv_instant *multi_adv_find_instant_by_id(int instant_id)
|
||||
{
|
||||
int i;
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) {
|
||||
return &(inst[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct multi_adv_instant *multi_adv_find_instant_by_order(int order)
|
||||
{
|
||||
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
if (inst[order].inuse_flag) {
|
||||
return &(inst[order]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multi_adv_set_ad_data(uint8_t * ad_data, const struct bt_data *ad, size_t ad_len)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
memset(ad_data, 0, MAX_AD_DATA_LEN);
|
||||
len = 0;
|
||||
for (i = 0; i < ad_len; i++) {
|
||||
/* Check if ad fit in the remaining buffer */
|
||||
if (len + ad[i].data_len + 2 > MAX_AD_DATA_LEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
ad_data[len++] = ad[i].data_len + 1;
|
||||
ad_data[len++] = ad[i].type;
|
||||
|
||||
memcpy(&ad_data[len], ad[i].data, ad[i].data_len);
|
||||
len += ad[i].data_len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int change_to_tick(int min_interval, int max_interval)
|
||||
{
|
||||
int tick;
|
||||
|
||||
if (max_interval/SLOT_PER_PERIOD != min_interval/SLOT_PER_PERIOD) {
|
||||
tick = min_interval/SLOT_PER_PERIOD;
|
||||
if (min_interval%SLOT_PER_PERIOD)
|
||||
tick++;
|
||||
} else {
|
||||
tick = min_interval/SLOT_PER_PERIOD;
|
||||
}
|
||||
if (tick <= 1)
|
||||
tick = 1;
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
int calculate_min_multi(int a, int b)
|
||||
{
|
||||
int x = a, y = b, z;
|
||||
|
||||
while (y != 0) {
|
||||
z = x%y;
|
||||
x = y;
|
||||
y = z;
|
||||
}
|
||||
|
||||
return a*b/x;
|
||||
}
|
||||
|
||||
void multi_adv_reorder(int inst_num, uint16_t inst_interval[], uint8_t inst_order[])
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < inst_num; i++) {
|
||||
int max = inst_interval[0];
|
||||
int max_idx = 0;
|
||||
int temp;
|
||||
|
||||
for (j = 1; j < inst_num-i ; j++) {
|
||||
if (max < inst_interval[j]) {
|
||||
max = inst_interval[j];
|
||||
max_idx = j;
|
||||
}
|
||||
}
|
||||
|
||||
temp = inst_interval[inst_num-i-1];
|
||||
inst_interval[inst_num-i-1] = inst_interval[max_idx];
|
||||
inst_interval[max_idx] = temp;
|
||||
|
||||
temp = inst_order[inst_num-i-1];
|
||||
inst_order[inst_num-i-1] = inst_order[max_idx];
|
||||
inst_order[max_idx] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int calculate_offset(uint16_t interval[], uint16_t offset[], int num, int duration)
|
||||
{
|
||||
int i, j, k, curr_offset = 0;
|
||||
int curr_max_instants, min_max_instants, instants;
|
||||
int offset_range;
|
||||
|
||||
offset_range = interval[num];
|
||||
if (offset_range > duration)
|
||||
offset_range = duration;
|
||||
|
||||
if (num == 0)
|
||||
return 0;
|
||||
|
||||
min_max_instants = 0x7fffffff;
|
||||
/* using 0-interval-1 as offset */
|
||||
for (i = 0; i < offset_range; i++) {
|
||||
|
||||
curr_max_instants = 0;
|
||||
/* search slot form 0 - duration to get the max instants number */
|
||||
for (j = 0; j < duration; j++) {
|
||||
|
||||
/* get instant number in each slot */
|
||||
instants = 0;
|
||||
for (k = 0; k < num; k++) {
|
||||
if (j%interval[k] == offset[k]) {
|
||||
instants++;
|
||||
}
|
||||
}
|
||||
if (j%interval[num] == i)
|
||||
instants++;
|
||||
if (curr_max_instants < instants) {
|
||||
curr_max_instants = instants;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if min max instants */
|
||||
if (min_max_instants > curr_max_instants) {
|
||||
min_max_instants = curr_max_instants;
|
||||
curr_offset = i;
|
||||
}
|
||||
}
|
||||
return curr_offset;
|
||||
|
||||
}
|
||||
|
||||
void multi_adv_schedule_table(int inst_num, uint16_t inst_interval[], uint16_t inst_offset[])
|
||||
{
|
||||
int i, min_multi, last_min_multi;
|
||||
/* calculate min multi */
|
||||
last_min_multi = min_multi = inst_interval[0];
|
||||
for (i = 1; i < inst_num; i++) {
|
||||
min_multi = calculate_min_multi(min_multi, inst_interval[i]);
|
||||
if (min_multi > MAX_MIN_MULTI) {
|
||||
min_multi = last_min_multi;
|
||||
break;
|
||||
}
|
||||
last_min_multi = min_multi;
|
||||
}
|
||||
|
||||
/* offset calcute for schedule just for small interval range */
|
||||
for (i = 0; i < inst_num; i++) {
|
||||
inst_offset[i] = calculate_offset(inst_interval, inst_offset, i, min_multi);
|
||||
}
|
||||
}
|
||||
|
||||
int multi_adv_start_adv_instant(struct multi_adv_instant *adv_instant)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bt_le_adv_start_instant(&adv_instant->param,
|
||||
adv_instant->ad, adv_instant->ad_len,
|
||||
adv_instant->sd, adv_instant->sd_len);
|
||||
if (ret) {
|
||||
BT_WARN("adv start instant failed: inst_id %d, err %d\r\n", adv_instant->instant_id, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void multi_adv_schedule_timer_handle(void)
|
||||
{
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
|
||||
multi_adv_schedule_timer_stop();
|
||||
if (adv_scheduler->schedule_state == SCHEDULE_STOP)
|
||||
return;
|
||||
|
||||
adv_scheduler->slot_clock = adv_scheduler->next_slot_clock;
|
||||
adv_scheduler->slot_offset = adv_scheduler->next_slot_offset;
|
||||
|
||||
multi_adv_schedule_timeslot(adv_scheduler);
|
||||
return;
|
||||
}
|
||||
|
||||
void multi_adv_schedule_timer_callback(struct k_work *timer)
|
||||
{
|
||||
multi_adv_schedule_timer_handle();
|
||||
return;
|
||||
}
|
||||
|
||||
int multi_adv_schedule_timer_start(int timeout)
|
||||
{
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
multi_adv_schedule_timer_stop();
|
||||
|
||||
k_delayed_work_submit(&g_multi_adv_timer, timeout);
|
||||
adv_scheduler->schedule_timer_active = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int multi_adv_schedule_timer_stop(void)
|
||||
{
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
|
||||
if (adv_scheduler->schedule_timer_active) {
|
||||
k_delayed_work_cancel(&g_multi_adv_timer);
|
||||
adv_scheduler->schedule_timer_active = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void multi_adv_schedule_timeslot(struct multi_adv_scheduler *adv_scheduler)
|
||||
{
|
||||
int i, inst_num;
|
||||
int inst_clk, inst_off, match, insts = 0, next_slot, min_next_slot;
|
||||
struct multi_adv_instant *adv_instant;
|
||||
uint16_t inst_interval[MAX_MULTI_ADV_INSTANT];
|
||||
uint16_t inst_offset[MAX_MULTI_ADV_INSTANT];
|
||||
uint8_t inst_order[MAX_MULTI_ADV_INSTANT];
|
||||
uint8_t match_order[MAX_MULTI_ADV_INSTANT];
|
||||
|
||||
inst_num = 0;
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
adv_instant = multi_adv_find_instant_by_order(i);
|
||||
if (adv_instant) {
|
||||
inst_interval[inst_num] = adv_instant->instant_interval;
|
||||
inst_offset[inst_num] = adv_instant->instant_offset;
|
||||
inst_order[inst_num] = i;
|
||||
inst_num++;
|
||||
}
|
||||
}
|
||||
|
||||
inst_clk = adv_scheduler->slot_clock;
|
||||
inst_off = adv_scheduler->slot_offset;
|
||||
match = 0;
|
||||
for (i = 0; i < inst_num; i++) {
|
||||
if ((inst_clk%inst_interval[i]) == inst_offset[i]) {
|
||||
match_order[match] = i;
|
||||
match++;
|
||||
}
|
||||
}
|
||||
|
||||
// BT_DBG("multi_adv_schedule_timeslot, num = %d, match = %d", inst_num, match);
|
||||
if (match) {
|
||||
int offset_per_instant, diff;
|
||||
offset_per_instant = TIME_PRIOD_MS/match;
|
||||
diff = inst_off - (inst_off+offset_per_instant/2)/offset_per_instant*offset_per_instant;//TODO may be error
|
||||
|
||||
/* means this is the time to start */
|
||||
if (diff <= 2) {
|
||||
insts = (inst_off+offset_per_instant/2)/offset_per_instant;
|
||||
|
||||
/* start instant */
|
||||
adv_instant = multi_adv_find_instant_by_order(inst_order[match_order[insts]]);
|
||||
if(adv_instant)
|
||||
multi_adv_start_adv_instant(adv_instant);
|
||||
}
|
||||
|
||||
/* next instant in the same slot */
|
||||
if (match-insts > 1) {
|
||||
adv_scheduler->next_slot_offset = adv_scheduler->slot_offset+offset_per_instant;
|
||||
adv_scheduler->next_slot_clock = adv_scheduler->slot_clock;
|
||||
|
||||
if ((adv_scheduler->next_slot_offset >= (TIME_PRIOD_MS-2))&&(adv_scheduler->slot_offset <= (TIME_PRIOD_MS+2))) {
|
||||
adv_scheduler->next_slot_clock++;
|
||||
adv_scheduler->next_slot_offset = 0;
|
||||
}
|
||||
multi_adv_schedule_timer_start(offset_per_instant);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* next instant not in the same slot */
|
||||
min_next_slot = 0x7fffffff;
|
||||
for (i = 0; i < inst_num; i++) {
|
||||
if (inst_clk-inst_offset[i] < 0) {
|
||||
match = 0;
|
||||
} else {
|
||||
match = (inst_clk-inst_offset[i])/inst_interval[i]+1;
|
||||
}
|
||||
next_slot = match*inst_interval[i]+inst_offset[i];
|
||||
if (next_slot < min_next_slot) {
|
||||
min_next_slot = next_slot;
|
||||
}
|
||||
}
|
||||
adv_scheduler->next_slot_offset = 0;
|
||||
adv_scheduler->next_slot_clock = min_next_slot;
|
||||
|
||||
next_slot = (adv_scheduler->next_slot_clock - adv_scheduler->slot_clock)*TIME_PRIOD_MS+(adv_scheduler->next_slot_offset-adv_scheduler->slot_offset);
|
||||
multi_adv_schedule_timer_start(next_slot);
|
||||
return;
|
||||
}
|
||||
|
||||
void multi_adv_schedule_stop(void)
|
||||
{
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
|
||||
multi_adv_schedule_timer_stop();
|
||||
adv_scheduler->schedule_state = SCHEDULE_STOP;
|
||||
}
|
||||
|
||||
void multi_adv_schedule_start(void)
|
||||
{
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
|
||||
/* get all instant and calculate ticks and */
|
||||
if (adv_scheduler->schedule_state == SCHEDULE_START) {
|
||||
multi_adv_schedule_stop();
|
||||
}
|
||||
|
||||
/* reinit scheduler */
|
||||
adv_scheduler->slot_clock = 0;
|
||||
adv_scheduler->slot_offset = 0;
|
||||
adv_scheduler->schedule_state = SCHEDULE_START;
|
||||
multi_adv_schedule_timeslot(adv_scheduler);
|
||||
}
|
||||
|
||||
void multi_adv_new_schedule(void)
|
||||
{
|
||||
int i;
|
||||
struct multi_adv_instant *adv_instant, *high_duty_instant;
|
||||
struct multi_adv_scheduler *adv_scheduler = &g_multi_adv_scheduler;
|
||||
uint16_t inst_offset[MAX_MULTI_ADV_INSTANT];
|
||||
uint16_t inst_interval[MAX_MULTI_ADV_INSTANT];
|
||||
uint8_t inst_order[MAX_MULTI_ADV_INSTANT];
|
||||
int inst_num = 0;
|
||||
|
||||
if (adv_scheduler->schedule_state == SCHEDULE_START) {
|
||||
multi_adv_schedule_stop();
|
||||
}
|
||||
/* get all instant and calculate ticks and */
|
||||
high_duty_instant = 0;
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
adv_instant = multi_adv_find_instant_by_order(i);
|
||||
if (adv_instant) {
|
||||
/* if high duty cycle adv found */
|
||||
if (adv_instant->param.interval_min < HIGH_DUTY_CYCLE_INTERVAL) {
|
||||
high_duty_instant = adv_instant;
|
||||
break;
|
||||
}
|
||||
|
||||
inst_interval[inst_num] = change_to_tick(adv_instant->param.interval_min, adv_instant->param.interval_max);
|
||||
inst_order[inst_num] = i;
|
||||
inst_num++;
|
||||
}
|
||||
}
|
||||
|
||||
if (high_duty_instant) {
|
||||
//BT_WARN("High Duty Cycle Instants, id = %d, interval = %d\n", adv_instant->instant_id, adv_instant->param.interval_min);
|
||||
multi_adv_start_adv_instant(adv_instant);
|
||||
return;
|
||||
}
|
||||
|
||||
/* instant number equal 0 and 1 */
|
||||
if (inst_num == 0) {
|
||||
bt_le_adv_stop();
|
||||
return;
|
||||
}
|
||||
if (inst_num == 1) {
|
||||
adv_instant = multi_adv_find_instant_by_order(inst_order[0]);
|
||||
if(!adv_instant)
|
||||
return;
|
||||
multi_adv_start_adv_instant(adv_instant);
|
||||
return;
|
||||
}
|
||||
|
||||
/* reorder by inst_interval */
|
||||
multi_adv_reorder(inst_num, inst_interval, inst_order);
|
||||
|
||||
/* calcuate schedule table */
|
||||
multi_adv_schedule_table(inst_num, inst_interval, inst_offset);
|
||||
|
||||
/* set interval and offset to instant */
|
||||
for (i = 0; i < inst_num; i++) {
|
||||
adv_instant = multi_adv_find_instant_by_order(inst_order[i]);
|
||||
if(!adv_instant){
|
||||
continue;
|
||||
}
|
||||
adv_instant->instant_interval = inst_interval[i];
|
||||
adv_instant->instant_offset = inst_offset[i];
|
||||
|
||||
//BT_WARN("adv_instant id = %d, interval = %d, offset = %d\n", adv_instant->instant_id, adv_instant->instant_interval, adv_instant->instant_offset);
|
||||
}
|
||||
|
||||
multi_adv_schedule_start();
|
||||
}
|
||||
|
||||
int bt_le_multi_adv_thread_init(void)
|
||||
{
|
||||
/* timer and event init */
|
||||
k_delayed_work_init(&g_multi_adv_timer, multi_adv_schedule_timer_callback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_le_multi_adv_start(const struct bt_le_adv_param *param,
|
||||
const struct bt_data *ad, size_t ad_len,
|
||||
const struct bt_data *sd, size_t sd_len, int *instant_id)
|
||||
{
|
||||
int instant_num;
|
||||
struct multi_adv_instant *adv_instant;
|
||||
|
||||
instant_num = multi_adv_get_instant_num();
|
||||
if (instant_num >= MAX_MULTI_ADV_INSTANT)
|
||||
return -1;
|
||||
|
||||
adv_instant = multi_adv_alloc_unused_instant();
|
||||
if (adv_instant == 0)
|
||||
return -1;
|
||||
|
||||
memcpy(&(adv_instant->param), param, sizeof(struct bt_le_adv_param));
|
||||
|
||||
adv_instant->ad_len = multi_adv_set_ad_data(adv_instant->ad, ad, ad_len);
|
||||
adv_instant->sd_len = multi_adv_set_ad_data(adv_instant->sd, sd, sd_len);
|
||||
|
||||
multi_adv_new_schedule();
|
||||
|
||||
*instant_id = adv_instant->instant_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_le_multi_adv_stop(int instant_id)
|
||||
{
|
||||
if (multi_adv_find_instant_by_id(instant_id) == 0)
|
||||
return -1;
|
||||
|
||||
//BT_WARN("%s id[%d]\n", __func__, instant_id);
|
||||
multi_adv_delete_instant_by_id(instant_id);
|
||||
multi_adv_new_schedule();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bt_le_multi_adv_id_is_vaild(int instant_id)
|
||||
{
|
||||
int i;
|
||||
struct multi_adv_instant *inst = &(g_multi_adv_list[0]);
|
||||
|
||||
for (i = 0; i < MAX_MULTI_ADV_INSTANT; i++) {
|
||||
if ((inst[i].inuse_flag) && (instant_id == (inst[i].instant_id))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
68
components/ble/ble_stack/host/multi_adv.h
Normal file
68
components/ble/ble_stack/host/multi_adv.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* xx
|
||||
*/
|
||||
|
||||
#ifndef _MULTI_ADV_H_
|
||||
#define _MULTI_ADV_H_
|
||||
|
||||
#define MAX_MULTI_ADV_INSTANT 4
|
||||
#define MAX_AD_DATA_LEN 31
|
||||
|
||||
#define TIME_PRIOD_MS (10*(MAX_MULTI_ADV_INSTANT -2))
|
||||
#define SLOT_PER_PERIOD (TIME_PRIOD_MS*8/5)
|
||||
|
||||
#define MAX_MIN_MULTI (30000/TIME_PRIOD_MS)
|
||||
|
||||
#define HIGH_DUTY_CYCLE_INTERVAL (20*8/5)
|
||||
|
||||
|
||||
struct multi_adv_instant {
|
||||
uint8_t inuse_flag;
|
||||
|
||||
/* for parameters */
|
||||
struct bt_le_adv_param param;
|
||||
uint8_t ad[MAX_AD_DATA_LEN];
|
||||
uint8_t ad_len;
|
||||
uint8_t sd[MAX_AD_DATA_LEN];
|
||||
uint8_t sd_len;
|
||||
|
||||
/* own address maybe used */
|
||||
bt_addr_t own_addr;
|
||||
uint8_t own_addr_valid;
|
||||
|
||||
/* for schedule */
|
||||
int instant_id;
|
||||
int instant_interval;
|
||||
int instant_offset;
|
||||
uint32_t clock;
|
||||
uint32_t clock_instant_offset;
|
||||
uint32_t clock_instant_total;
|
||||
uint32_t next_wakeup_time;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SCHEDULE_IDLE,
|
||||
SCHEDULE_READY,
|
||||
SCHEDULE_START,
|
||||
SCHEDULE_STOP,
|
||||
}SCHEDULE_STATE;
|
||||
|
||||
struct multi_adv_scheduler {
|
||||
SCHEDULE_STATE schedule_state;
|
||||
uint8_t schedule_timer_active;
|
||||
uint32_t slot_clock;
|
||||
uint16_t slot_offset;
|
||||
uint16_t next_slot_offset;
|
||||
uint32_t next_slot_clock;
|
||||
};
|
||||
|
||||
int bt_le_multi_adv_thread_init(void);
|
||||
int bt_le_multi_adv_start(const struct bt_le_adv_param *param,
|
||||
const struct bt_data *ad, size_t ad_len,
|
||||
const struct bt_data *sd, size_t sd_len, int *instant_id);
|
||||
int bt_le_multi_adv_stop(int instant_id);
|
||||
|
||||
bool bt_le_multi_adv_id_is_vaild(int instant_id);
|
||||
|
||||
#endif
|
||||
|
1746
components/ble/ble_stack/host/rfcomm.c
Normal file
1746
components/ble/ble_stack/host/rfcomm.c
Normal file
File diff suppressed because it is too large
Load diff
213
components/ble/ble_stack/host/rfcomm_internal.h
Normal file
213
components/ble/ble_stack/host/rfcomm_internal.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/** @file
|
||||
* @brief Internal APIs for Bluetooth RFCOMM handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <rfcomm.h>
|
||||
|
||||
typedef enum {
|
||||
BT_RFCOMM_CFC_UNKNOWN,
|
||||
BT_RFCOMM_CFC_NOT_SUPPORTED,
|
||||
BT_RFCOMM_CFC_SUPPORTED,
|
||||
} __packed bt_rfcomm_cfc_t;
|
||||
|
||||
/* RFCOMM signalling connection specific context */
|
||||
struct bt_rfcomm_session {
|
||||
/* L2CAP channel this context is associated with */
|
||||
struct bt_l2cap_br_chan br_chan;
|
||||
/* Response Timeout eXpired (RTX) timer */
|
||||
struct k_delayed_work rtx_work;
|
||||
/* Binary sem for aggregate fc */
|
||||
struct k_sem fc;
|
||||
struct bt_rfcomm_dlc *dlcs;
|
||||
uint16_t mtu;
|
||||
uint8_t state;
|
||||
bt_rfcomm_role_t role;
|
||||
bt_rfcomm_cfc_t cfc;
|
||||
};
|
||||
|
||||
enum {
|
||||
BT_RFCOMM_STATE_IDLE,
|
||||
BT_RFCOMM_STATE_INIT,
|
||||
BT_RFCOMM_STATE_SECURITY_PENDING,
|
||||
BT_RFCOMM_STATE_CONNECTING,
|
||||
BT_RFCOMM_STATE_CONNECTED,
|
||||
BT_RFCOMM_STATE_CONFIG,
|
||||
BT_RFCOMM_STATE_USER_DISCONNECT,
|
||||
BT_RFCOMM_STATE_DISCONNECTING,
|
||||
BT_RFCOMM_STATE_DISCONNECTED,
|
||||
};
|
||||
|
||||
struct bt_rfcomm_hdr {
|
||||
uint8_t address;
|
||||
uint8_t control;
|
||||
uint8_t length;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_SABM 0x2f
|
||||
#define BT_RFCOMM_UA 0x63
|
||||
#define BT_RFCOMM_UIH 0xef
|
||||
|
||||
struct bt_rfcomm_msg_hdr {
|
||||
uint8_t type;
|
||||
uint8_t len;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_PN 0x20
|
||||
struct bt_rfcomm_pn {
|
||||
uint8_t dlci;
|
||||
uint8_t flow_ctrl;
|
||||
uint8_t priority;
|
||||
uint8_t ack_timer;
|
||||
uint16_t mtu;
|
||||
uint8_t max_retrans;
|
||||
uint8_t credits;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_MSC 0x38
|
||||
struct bt_rfcomm_msc {
|
||||
uint8_t dlci;
|
||||
uint8_t v24_signal;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_DISC 0x43
|
||||
#define BT_RFCOMM_DM 0x0f
|
||||
|
||||
#define BT_RFCOMM_RLS 0x14
|
||||
struct bt_rfcomm_rls {
|
||||
uint8_t dlci;
|
||||
uint8_t line_status;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_RPN 0x24
|
||||
struct bt_rfcomm_rpn {
|
||||
uint8_t dlci;
|
||||
uint8_t baud_rate;
|
||||
uint8_t line_settings;
|
||||
uint8_t flow_control;
|
||||
uint8_t xon_char;
|
||||
uint8_t xoff_char;
|
||||
uint16_t param_mask;
|
||||
} __packed;
|
||||
|
||||
#define BT_RFCOMM_TEST 0x08
|
||||
#define BT_RFCOMM_NSC 0x04
|
||||
|
||||
#define BT_RFCOMM_FCON 0x28
|
||||
#define BT_RFCOMM_FCOFF 0x18
|
||||
|
||||
/* Default RPN Settings */
|
||||
#define BT_RFCOMM_RPN_BAUD_RATE_9600 0x03
|
||||
#define BT_RFCOMM_RPN_DATA_BITS_8 0x03
|
||||
#define BT_RFCOMM_RPN_STOP_BITS_1 0x00
|
||||
#define BT_RFCOMM_RPN_PARITY_NONE 0x00
|
||||
#define BT_RFCOMM_RPN_FLOW_NONE 0x00
|
||||
#define BT_RFCOMM_RPN_XON_CHAR 0x11
|
||||
#define BT_RFCOMM_RPN_XOFF_CHAR 0x13
|
||||
|
||||
/* Set 1 to all the param mask except reserved */
|
||||
#define BT_RFCOMM_RPN_PARAM_MASK_ALL 0x3f7f
|
||||
|
||||
#define BT_RFCOMM_SET_LINE_SETTINGS(data, stop, parity) ((data & 0x3) | \
|
||||
((stop & 0x1) << 2) | \
|
||||
((parity & 0x7) << 3))
|
||||
|
||||
/* DV = 1 IC = 0 RTR = 1 RTC = 1 FC = 0 EXT = 0 */
|
||||
#define BT_RFCOMM_DEFAULT_V24_SIG 0x8d
|
||||
|
||||
#define BT_RFCOMM_GET_FC(v24_signal) (((v24_signal) & 0x02) >> 1)
|
||||
|
||||
#define BT_RFCOMM_SIG_MIN_MTU 23
|
||||
#define BT_RFCOMM_SIG_MAX_MTU 32767
|
||||
|
||||
#define BT_RFCOMM_CHECK_MTU(mtu) (!!((mtu) >= BT_RFCOMM_SIG_MIN_MTU && \
|
||||
(mtu) <= BT_RFCOMM_SIG_MAX_MTU))
|
||||
|
||||
/* Helper to calculate needed outgoing buffer size.
|
||||
* Length in rfcomm header can be two bytes depending on user data length.
|
||||
* One byte in the tail should be reserved for FCS.
|
||||
*/
|
||||
#define BT_RFCOMM_BUF_SIZE(mtu) (BT_BUF_RESERVE + \
|
||||
BT_HCI_ACL_HDR_SIZE + BT_L2CAP_HDR_SIZE + \
|
||||
sizeof(struct bt_rfcomm_hdr) + 1 + (mtu) + \
|
||||
BT_RFCOMM_FCS_SIZE)
|
||||
|
||||
#define BT_RFCOMM_GET_DLCI(addr) (((addr) & 0xfc) >> 2)
|
||||
#define BT_RFCOMM_GET_FRAME_TYPE(ctrl) ((ctrl) & 0xef)
|
||||
#define BT_RFCOMM_GET_MSG_TYPE(type) (((type) & 0xfc) >> 2)
|
||||
#define BT_RFCOMM_GET_MSG_CR(type) (((type) & 0x02) >> 1)
|
||||
#define BT_RFCOMM_GET_LEN(len) (((len) & 0xfe) >> 1)
|
||||
#define BT_RFCOMM_GET_CHANNEL(dlci) ((dlci) >> 1)
|
||||
#define BT_RFCOMM_GET_PF(ctrl) (((ctrl) & 0x10) >> 4)
|
||||
|
||||
#define BT_RFCOMM_SET_ADDR(dlci, cr) ((((dlci) & 0x3f) << 2) | \
|
||||
((cr) << 1) | 0x01)
|
||||
#define BT_RFCOMM_SET_CTRL(type, pf) (((type) & 0xef) | ((pf) << 4))
|
||||
#define BT_RFCOMM_SET_LEN_8(len) (((len) << 1) | 1)
|
||||
#define BT_RFCOMM_SET_LEN_16(len) ((len) << 1)
|
||||
#define BT_RFCOMM_SET_MSG_TYPE(type, cr) (((type) << 2) | (cr << 1) | 0x01)
|
||||
|
||||
#define BT_RFCOMM_LEN_EXTENDED(len) (!((len) & 0x01))
|
||||
|
||||
/* For CR in UIH Packet header
|
||||
* Initiating station have the C/R bit set to 1 and those sent by the
|
||||
* responding station have the C/R bit set to 0
|
||||
*/
|
||||
#define BT_RFCOMM_UIH_CR(role) ((role) == BT_RFCOMM_ROLE_INITIATOR)
|
||||
|
||||
/* For CR in Non UIH Packet header
|
||||
* Command
|
||||
* Initiator --> Responder 1
|
||||
* Responder --> Initiator 0
|
||||
* Response
|
||||
* Initiator --> Responder 0
|
||||
* Responder --> Initiator 1
|
||||
*/
|
||||
#define BT_RFCOMM_CMD_CR(role) ((role) == BT_RFCOMM_ROLE_INITIATOR)
|
||||
#define BT_RFCOMM_RESP_CR(role) ((role) == BT_RFCOMM_ROLE_ACCEPTOR)
|
||||
|
||||
/* For CR in MSG header
|
||||
* If the C/R bit is set to 1 the message is a command,
|
||||
* if it is set to 0 the message is a response.
|
||||
*/
|
||||
#define BT_RFCOMM_MSG_CMD_CR 1
|
||||
#define BT_RFCOMM_MSG_RESP_CR 0
|
||||
|
||||
#define BT_RFCOMM_DLCI(role, channel) ((((channel) & 0x1f) << 1) | \
|
||||
((role) == BT_RFCOMM_ROLE_ACCEPTOR))
|
||||
|
||||
/* Excluding ext bit */
|
||||
#define BT_RFCOMM_MAX_LEN_8 127
|
||||
|
||||
/* Length can be 2 bytes depending on data size */
|
||||
#define BT_RFCOMM_HDR_SIZE (sizeof(struct bt_rfcomm_hdr) + 1)
|
||||
#define BT_RFCOMM_FCS_SIZE 1
|
||||
|
||||
#define BT_RFCOMM_FCS_LEN_UIH 2
|
||||
#define BT_RFCOMM_FCS_LEN_NON_UIH 3
|
||||
|
||||
/* For non UIH packets
|
||||
* The P bit set to 1 shall be used to solicit a response frame with the
|
||||
* F bit set to 1 from the other station.
|
||||
*/
|
||||
#define BT_RFCOMM_PF_NON_UIH 1
|
||||
|
||||
/* For UIH packets
|
||||
* Both stations set the P-bit to 0
|
||||
* If credit based flow control is used, If P/F is 1 then one credit byte
|
||||
* will be there after control in the frame else no credit byte.
|
||||
*/
|
||||
#define BT_RFCOMM_PF_UIH 0
|
||||
#define BT_RFCOMM_PF_UIH_CREDIT 1
|
||||
#define BT_RFCOMM_PF_UIH_NO_CREDIT 0
|
||||
|
||||
#define BT_RFCOMM_PN_CFC_CMD 0xf0
|
||||
#define BT_RFCOMM_PN_CFC_RESP 0xe0
|
||||
|
||||
/* Initialize RFCOMM signal layer */
|
||||
void bt_rfcomm_init(void);
|
2545
components/ble/ble_stack/host/sdp.c
Normal file
2545
components/ble/ble_stack/host/sdp.c
Normal file
File diff suppressed because it is too large
Load diff
74
components/ble/ble_stack/host/sdp_internal.h
Normal file
74
components/ble/ble_stack/host/sdp_internal.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/* sdp_internal.h - Service Discovery Protocol handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* The PDU identifiers of SDP packets between client and server
|
||||
*/
|
||||
#define BT_SDP_ERROR_RSP 0x01
|
||||
#define BT_SDP_SVC_SEARCH_REQ 0x02
|
||||
#define BT_SDP_SVC_SEARCH_RSP 0x03
|
||||
#define BT_SDP_SVC_ATTR_REQ 0x04
|
||||
#define BT_SDP_SVC_ATTR_RSP 0x05
|
||||
#define BT_SDP_SVC_SEARCH_ATTR_REQ 0x06
|
||||
#define BT_SDP_SVC_SEARCH_ATTR_RSP 0x07
|
||||
|
||||
/*
|
||||
* Some additions to support service registration.
|
||||
* These are outside the scope of the Bluetooth specification
|
||||
*/
|
||||
#define BT_SDP_SVC_REGISTER_REQ 0x75
|
||||
#define BT_SDP_SVC_REGISTER_RSP 0x76
|
||||
#define BT_SDP_SVC_UPDATE_REQ 0x77
|
||||
#define BT_SDP_SVC_UPDATE_RSP 0x78
|
||||
#define BT_SDP_SVC_REMOVE_REQ 0x79
|
||||
#define BT_SDP_SVC_REMOVE_RSP 0x80
|
||||
|
||||
/*
|
||||
* SDP Error codes
|
||||
*/
|
||||
#define BT_SDP_INVALID_VERSION 0x0001
|
||||
#define BT_SDP_INVALID_RECORD_HANDLE 0x0002
|
||||
#define BT_SDP_INVALID_SYNTAX 0x0003
|
||||
#define BT_SDP_INVALID_PDU_SIZE 0x0004
|
||||
#define BT_SDP_INVALID_CSTATE 0x0005
|
||||
|
||||
#define BT_SDP_MAX_SERVICES 10
|
||||
|
||||
struct bt_sdp_data_elem_seq {
|
||||
uint8_t type; /* Type: Will be data element sequence */
|
||||
uint16_t size; /* We only support 2 byte sizes for now */
|
||||
} __packed;
|
||||
|
||||
struct bt_sdp_hdr {
|
||||
uint8_t op_code;
|
||||
uint16_t tid;
|
||||
uint16_t param_len;
|
||||
} __packed;
|
||||
|
||||
struct bt_sdp_svc_rsp {
|
||||
uint16_t total_recs;
|
||||
uint16_t current_recs;
|
||||
} __packed;
|
||||
|
||||
struct bt_sdp_att_rsp {
|
||||
uint16_t att_list_len;
|
||||
} __packed;
|
||||
|
||||
/* Allowed attributes length in SSA Request PDU to be taken from server */
|
||||
#define BT_SDP_MAX_ATTR_LEN 0xffff
|
||||
|
||||
/* Max allowed length of PDU Continuation State */
|
||||
#define BT_SDP_MAX_PDU_CSTATE_LEN 16
|
||||
|
||||
/* Type mapping SDP PDU Continuation State */
|
||||
struct bt_sdp_pdu_cstate {
|
||||
uint8_t length;
|
||||
uint8_t data[BT_SDP_MAX_PDU_CSTATE_LEN];
|
||||
} __packed;
|
||||
|
||||
void bt_sdp_init(void);
|
428
components/ble/ble_stack/host/settings.c
Normal file
428
components/ble/ble_stack/host/settings.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr.h>
|
||||
#include <bluetooth.h>
|
||||
#include <conn.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_SETTINGS)
|
||||
#define LOG_MODULE_NAME bt_settings
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "settings.h"
|
||||
#include "keys.h"
|
||||
#include "gatt.h"
|
||||
#if defined(BFLB_BLE)
|
||||
#include <stdlib.h>
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
#include "easyflash.h"
|
||||
#endif
|
||||
#include <FreeRTOS.h>
|
||||
#include "portable.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_SETTINGS_USE_PRINTK)
|
||||
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
||||
bt_addr_le_t *addr, const char *key)
|
||||
{
|
||||
if (key) {
|
||||
snprintk(path, path_size,
|
||||
"bt/%s/%02x%02x%02x%02x%02x%02x%u/%s", subsys,
|
||||
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
||||
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
||||
addr->type, key);
|
||||
} else {
|
||||
snprintk(path, path_size,
|
||||
"bt/%s/%02x%02x%02x%02x%02x%02x%u", subsys,
|
||||
addr->a.val[5], addr->a.val[4], addr->a.val[3],
|
||||
addr->a.val[2], addr->a.val[1], addr->a.val[0],
|
||||
addr->type);
|
||||
}
|
||||
|
||||
BT_DBG("Encoded path %s", log_strdup(path));
|
||||
}
|
||||
#else
|
||||
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
||||
bt_addr_le_t *addr, const char *key)
|
||||
{
|
||||
size_t len = 3;
|
||||
|
||||
/* Skip if path_size is less than 3; strlen("bt/") */
|
||||
if (len < path_size) {
|
||||
/* Key format:
|
||||
* "bt/<subsys>/<addr><type>/<key>", "/<key>" is optional
|
||||
*/
|
||||
strcpy(path, "bt/");
|
||||
strncpy(&path[len], subsys, path_size - len);
|
||||
len = strlen(path);
|
||||
if (len < path_size) {
|
||||
path[len] = '/';
|
||||
len++;
|
||||
}
|
||||
|
||||
for (s8_t i = 5; i >= 0 && len < path_size; i--) {
|
||||
len += bin2hex(&addr->a.val[i], 1, &path[len],
|
||||
path_size - len);
|
||||
}
|
||||
|
||||
if (len < path_size) {
|
||||
/* Type can be either BT_ADDR_LE_PUBLIC or
|
||||
* BT_ADDR_LE_RANDOM (value 0 or 1)
|
||||
*/
|
||||
path[len] = '0' + addr->type;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (key && len < path_size) {
|
||||
path[len] = '/';
|
||||
len++;
|
||||
strncpy(&path[len], key, path_size - len);
|
||||
len += strlen(&path[len]);
|
||||
}
|
||||
|
||||
if (len >= path_size) {
|
||||
/* Truncate string */
|
||||
path[path_size - 1] = '\0';
|
||||
}
|
||||
} else if (path_size > 0) {
|
||||
*path = '\0';
|
||||
}
|
||||
|
||||
BT_DBG("Encoded path %s", log_strdup(path));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(BFLB_BLE)
|
||||
int bt_settings_decode_key(const char *key, bt_addr_le_t *addr)
|
||||
{
|
||||
if (settings_name_next(key, NULL) != 13) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (key[12] == '0') {
|
||||
addr->type = BT_ADDR_LE_PUBLIC;
|
||||
} else if (key[12] == '1') {
|
||||
addr->type = BT_ADDR_LE_RANDOM;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (u8_t i = 0; i < 6; i++) {
|
||||
hex2bin(&key[i * 2], 2, &addr->a.val[5 - i], 1);
|
||||
}
|
||||
|
||||
BT_DBG("Decoded %s as %s", log_strdup(key), bt_addr_le_str(addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set(const char *name, size_t len_rd, settings_read_cb read_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
ssize_t len;
|
||||
const char *next;
|
||||
|
||||
if (!name) {
|
||||
BT_ERR("Insufficient number of arguments");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
len = settings_name_next(name, &next);
|
||||
|
||||
if (!strncmp(name, "id", len)) {
|
||||
/* Any previously provided identities supersede flash */
|
||||
if (atomic_test_bit(bt_dev.flags, BT_DEV_PRESET_ID)) {
|
||||
BT_WARN("Ignoring identities stored in flash");
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = read_cb(cb_arg, &bt_dev.id_addr, sizeof(bt_dev.id_addr));
|
||||
if (len < sizeof(bt_dev.id_addr[0])) {
|
||||
if (len < 0) {
|
||||
BT_ERR("Failed to read ID address from storage"
|
||||
" (err %zu)", len);
|
||||
} else {
|
||||
BT_ERR("Invalid length ID address in storage");
|
||||
BT_HEXDUMP_DBG(&bt_dev.id_addr, len,
|
||||
"data read");
|
||||
}
|
||||
(void)memset(bt_dev.id_addr, 0,
|
||||
sizeof(bt_dev.id_addr));
|
||||
bt_dev.id_count = 0U;
|
||||
} else {
|
||||
int i;
|
||||
|
||||
bt_dev.id_count = len / sizeof(bt_dev.id_addr[0]);
|
||||
for (i = 0; i < bt_dev.id_count; i++) {
|
||||
BT_DBG("ID[%d] %s", i,
|
||||
bt_addr_le_str(&bt_dev.id_addr[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
||||
if (!strncmp(name, "name", len)) {
|
||||
len = read_cb(cb_arg, &bt_dev.name, sizeof(bt_dev.name) - 1);
|
||||
if (len < 0) {
|
||||
BT_ERR("Failed to read device name from storage"
|
||||
" (err %zu)", len);
|
||||
} else {
|
||||
bt_dev.name[len] = '\0';
|
||||
|
||||
BT_DBG("Name set to %s", log_strdup(bt_dev.name));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
if (!strncmp(name, "irk", len)) {
|
||||
len = read_cb(cb_arg, bt_dev.irk, sizeof(bt_dev.irk));
|
||||
if (len < sizeof(bt_dev.irk[0])) {
|
||||
if (len < 0) {
|
||||
BT_ERR("Failed to read IRK from storage"
|
||||
" (err %zu)", len);
|
||||
} else {
|
||||
BT_ERR("Invalid length IRK in storage");
|
||||
(void)memset(bt_dev.irk, 0, sizeof(bt_dev.irk));
|
||||
}
|
||||
} else {
|
||||
int i, count;
|
||||
|
||||
count = len / sizeof(bt_dev.irk[0]);
|
||||
for (i = 0; i < count; i++) {
|
||||
BT_DBG("IRK[%d] %s", i,
|
||||
bt_hex(bt_dev.irk[i], 16));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BT_PRIVACY */
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#define ID_DATA_LEN(array) (bt_dev.id_count * sizeof(array[0]))
|
||||
|
||||
static void save_id(struct k_work *work)
|
||||
{
|
||||
int err;
|
||||
BT_INFO("Saving ID");
|
||||
err = settings_save_one("bt/id", &bt_dev.id_addr,
|
||||
ID_DATA_LEN(bt_dev.id_addr));
|
||||
if (err) {
|
||||
BT_ERR("Failed to save ID (err %d)", err);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
err = settings_save_one("bt/irk", bt_dev.irk, ID_DATA_LEN(bt_dev.irk));
|
||||
if (err) {
|
||||
BT_ERR("Failed to save IRK (err %d)", err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(save_id_work, save_id);
|
||||
#endif //!BFLB_BLE
|
||||
#if defined (BFLB_BLE)
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
bool ef_ready_flag = false;
|
||||
int bt_check_if_ef_ready()
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if(!ef_ready_flag){
|
||||
err = easyflash_init();
|
||||
if(!err)
|
||||
ef_ready_flag = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_settings_set_bin(const char *key, const uint8_t *value, size_t length)
|
||||
{
|
||||
const char *lookup = "0123456789abcdef";
|
||||
char *str_value;
|
||||
int err;
|
||||
|
||||
err = bt_check_if_ef_ready();
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
str_value = pvPortMalloc(length*2 + 1);
|
||||
|
||||
BT_ASSERT(str_value != NULL);
|
||||
|
||||
for(size_t i = 0; i < length; i++){
|
||||
str_value[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F];
|
||||
str_value[(i * 2) + 1] = lookup[value[i] & 0x0F];
|
||||
}
|
||||
str_value[length * 2] = '\0';
|
||||
|
||||
err = ef_set_env(key, (const char *)str_value);
|
||||
|
||||
vPortFree(str_value);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int bt_settings_get_bin(const char *key, u8_t *value, size_t exp_len, size_t *real_len)
|
||||
{
|
||||
char *str_value;
|
||||
size_t str_value_len;
|
||||
char rand[3];
|
||||
int err;
|
||||
|
||||
err = bt_check_if_ef_ready();
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
|
||||
str_value = ef_get_env(key);
|
||||
if(str_value == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
str_value_len = strlen(str_value);
|
||||
|
||||
if((str_value_len % 2) != 0 || (exp_len >0 && str_value_len > exp_len*2))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(real_len)
|
||||
*real_len = str_value_len/2;
|
||||
|
||||
for(size_t i = 0; i < str_value_len/2; i++){
|
||||
strncpy(rand, str_value+2*i, 2);
|
||||
rand[2] = '\0';
|
||||
value[i] = strtol(rand, NULL, 16);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int settings_delete(const char *key)
|
||||
{
|
||||
return ef_del_env(key);
|
||||
}
|
||||
|
||||
int settings_save_one(const char *key, const u8_t *value, size_t length)
|
||||
{
|
||||
return bt_settings_set_bin(key, value, length);
|
||||
}
|
||||
#endif //CONFIG_BT_SETTINGS
|
||||
#endif
|
||||
|
||||
void bt_settings_save_id(void)
|
||||
{
|
||||
#if defined(BFLB_BLE)
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
if(bt_check_if_ef_ready())
|
||||
return;
|
||||
bt_settings_set_bin(NV_LOCAL_ID_ADDR, (const u8_t *)&bt_dev.id_addr[0], sizeof(bt_addr_le_t)*CONFIG_BT_ID_MAX);
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
bt_settings_set_bin(NV_LOCAL_IRK, (const u8_t *)&bt_dev.irk[0], 16*CONFIG_BT_ID_MAX);
|
||||
#endif //CONFIG_BT_PRIVACY
|
||||
#endif //CONFIG_BT_SETTINGS
|
||||
#else
|
||||
k_work_submit(&save_id_work);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
#if defined(CONFIG_BT_SETTINGS)
|
||||
void bt_settings_save_name(void)
|
||||
{
|
||||
if(bt_check_if_ef_ready())
|
||||
return;
|
||||
|
||||
ef_set_env(NV_LOCAL_NAME, bt_dev.name);
|
||||
}
|
||||
|
||||
void bt_local_info_load(void)
|
||||
{
|
||||
if(bt_check_if_ef_ready())
|
||||
return;
|
||||
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
||||
char *dev_name;
|
||||
uint8_t len;
|
||||
dev_name = ef_get_env(NV_LOCAL_NAME);
|
||||
if(dev_name != NULL){
|
||||
len = ((strlen(dev_name)+1) < CONFIG_BT_DEVICE_NAME_MAX)? (strlen(dev_name)+1):CONFIG_BT_DEVICE_NAME_MAX;
|
||||
memcpy(bt_dev.name, dev_name, len);
|
||||
}
|
||||
#endif
|
||||
bt_settings_get_bin(NV_LOCAL_ID_ADDR, (u8_t *)&bt_dev.id_addr[0], sizeof(bt_addr_le_t)*CONFIG_BT_ID_MAX, NULL);
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
bt_settings_get_bin(NV_LOCAL_IRK, (u8_t *)&bt_dev.irk[0][0], 16*CONFIG_BT_ID_MAX, NULL);
|
||||
#endif
|
||||
}
|
||||
#endif //CONFIG_BT_SETTINGS
|
||||
#endif
|
||||
|
||||
#if !defined(BFLB_BLE)
|
||||
static int commit(void)
|
||||
{
|
||||
BT_DBG("");
|
||||
|
||||
#if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC)
|
||||
if (bt_dev.name[0] == '\0') {
|
||||
bt_set_name(CONFIG_BT_DEVICE_NAME);
|
||||
}
|
||||
#endif
|
||||
if (!bt_dev.id_count) {
|
||||
int err;
|
||||
|
||||
err = bt_setup_id_addr();
|
||||
if (err) {
|
||||
BT_ERR("Unable to setup an identity address");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that the identities created by bt_id_create after
|
||||
* bt_enable is saved to persistent storage. */
|
||||
if (!atomic_test_bit(bt_dev.flags, BT_DEV_PRESET_ID)) {
|
||||
bt_settings_save_id();
|
||||
}
|
||||
|
||||
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
|
||||
bt_finalize_init();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(bt, "bt", NULL, set, commit, NULL);
|
||||
|
||||
#endif //!BFLB_BLE
|
||||
|
||||
int bt_settings_init(void)
|
||||
{
|
||||
#if defined(BFLB_BLE)
|
||||
return 0;
|
||||
#else
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
err = settings_subsys_init();
|
||||
if (err) {
|
||||
BT_ERR("settings_subsys_init failed (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
39
components/ble/ble_stack/host/settings.h
Normal file
39
components/ble/ble_stack/host/settings.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
#include "addr.h"
|
||||
#endif
|
||||
|
||||
/* Max settings key length (with all components) */
|
||||
#define BT_SETTINGS_KEY_MAX 36
|
||||
|
||||
/* Base64-encoded string buffer size of in_size bytes */
|
||||
#define BT_SETTINGS_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
|
||||
|
||||
/* Helpers for keys containing a bdaddr */
|
||||
void bt_settings_encode_key(char *path, size_t path_size, const char *subsys,
|
||||
bt_addr_le_t *addr, const char *key);
|
||||
int bt_settings_decode_key(const char *key, bt_addr_le_t *addr);
|
||||
|
||||
void bt_settings_save_id(void);
|
||||
|
||||
int bt_settings_init(void);
|
||||
|
||||
#if defined(BFLB_BLE)
|
||||
#define NV_LOCAL_NAME "LOCAL_NAME"
|
||||
#define NV_LOCAL_ID_ADDR "LOCAL_ID_ADDR"
|
||||
#define NV_LOCAL_IRK "LOCAL_IRK"
|
||||
#define NV_KEY_POOL "KEY_POOL"
|
||||
#define NV_IMG_info "IMG_INFO"
|
||||
|
||||
int bt_settings_get_bin(const char *key, u8_t *value, size_t exp_len, size_t *real_len);
|
||||
int bt_settings_set_bin(const char *key, const u8_t *value, size_t length);
|
||||
int settings_delete(const char *key);
|
||||
int settings_save_one(const char *key, const u8_t *value, size_t length);
|
||||
void bt_settings_save_name(void);
|
||||
void bt_local_info_load(void);
|
||||
#endif
|
5593
components/ble/ble_stack/host/smp.c
Normal file
5593
components/ble/ble_stack/host/smp.c
Normal file
File diff suppressed because it is too large
Load diff
187
components/ble/ble_stack/host/smp.h
Normal file
187
components/ble/ble_stack/host/smp.h
Normal file
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* @file smp.h
|
||||
* Security Manager Protocol implementation header
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
struct bt_smp_hdr {
|
||||
u8_t code;
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_ERR_PASSKEY_ENTRY_FAILED 0x01
|
||||
#define BT_SMP_ERR_OOB_NOT_AVAIL 0x02
|
||||
#define BT_SMP_ERR_AUTH_REQUIREMENTS 0x03
|
||||
#define BT_SMP_ERR_CONFIRM_FAILED 0x04
|
||||
#define BT_SMP_ERR_PAIRING_NOTSUPP 0x05
|
||||
#define BT_SMP_ERR_ENC_KEY_SIZE 0x06
|
||||
#define BT_SMP_ERR_CMD_NOTSUPP 0x07
|
||||
#define BT_SMP_ERR_UNSPECIFIED 0x08
|
||||
#define BT_SMP_ERR_REPEATED_ATTEMPTS 0x09
|
||||
#define BT_SMP_ERR_INVALID_PARAMS 0x0a
|
||||
#define BT_SMP_ERR_DHKEY_CHECK_FAILED 0x0b
|
||||
#define BT_SMP_ERR_NUMERIC_COMP_FAILED 0x0c
|
||||
#define BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS 0x0d
|
||||
#define BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED 0x0e
|
||||
|
||||
#define BT_SMP_IO_DISPLAY_ONLY 0x00
|
||||
#define BT_SMP_IO_DISPLAY_YESNO 0x01
|
||||
#define BT_SMP_IO_KEYBOARD_ONLY 0x02
|
||||
#define BT_SMP_IO_NO_INPUT_OUTPUT 0x03
|
||||
#define BT_SMP_IO_KEYBOARD_DISPLAY 0x04
|
||||
|
||||
#define BT_SMP_OOB_DATA_MASK 0x01
|
||||
#define BT_SMP_OOB_NOT_PRESENT 0x00
|
||||
#define BT_SMP_OOB_PRESENT 0x01
|
||||
|
||||
#define BT_SMP_MIN_ENC_KEY_SIZE 7
|
||||
#define BT_SMP_MAX_ENC_KEY_SIZE 16
|
||||
|
||||
#define BT_SMP_DIST_ENC_KEY 0x01
|
||||
#define BT_SMP_DIST_ID_KEY 0x02
|
||||
#define BT_SMP_DIST_SIGN 0x04
|
||||
#define BT_SMP_DIST_LINK_KEY 0x08
|
||||
|
||||
#define BT_SMP_DIST_MASK 0x0f
|
||||
|
||||
#define BT_SMP_AUTH_NONE 0x00
|
||||
#define BT_SMP_AUTH_BONDING 0x01
|
||||
#define BT_SMP_AUTH_MITM 0x04
|
||||
#define BT_SMP_AUTH_SC 0x08
|
||||
#define BT_SMP_AUTH_KEYPRESS 0x10
|
||||
#define BT_SMP_AUTH_CT2 0x20
|
||||
|
||||
#define BT_SMP_CMD_PAIRING_REQ 0x01
|
||||
#define BT_SMP_CMD_PAIRING_RSP 0x02
|
||||
struct bt_smp_pairing {
|
||||
u8_t io_capability;
|
||||
u8_t oob_flag;
|
||||
u8_t auth_req;
|
||||
u8_t max_key_size;
|
||||
u8_t init_key_dist;
|
||||
u8_t resp_key_dist;
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_PAIRING_CONFIRM 0x03
|
||||
struct bt_smp_pairing_confirm {
|
||||
u8_t val[16];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_PAIRING_RANDOM 0x04
|
||||
struct bt_smp_pairing_random {
|
||||
u8_t val[16];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_PAIRING_FAIL 0x05
|
||||
struct bt_smp_pairing_fail {
|
||||
u8_t reason;
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_ENCRYPT_INFO 0x06
|
||||
struct bt_smp_encrypt_info {
|
||||
u8_t ltk[16];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_MASTER_IDENT 0x07
|
||||
struct bt_smp_master_ident {
|
||||
u8_t ediv[2];
|
||||
u8_t rand[8];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_IDENT_INFO 0x08
|
||||
struct bt_smp_ident_info {
|
||||
u8_t irk[16];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_IDENT_ADDR_INFO 0x09
|
||||
struct bt_smp_ident_addr_info {
|
||||
bt_addr_le_t addr;
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_SIGNING_INFO 0x0a
|
||||
struct bt_smp_signing_info {
|
||||
u8_t csrk[16];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_SECURITY_REQUEST 0x0b
|
||||
struct bt_smp_security_request {
|
||||
u8_t auth_req;
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_CMD_PUBLIC_KEY 0x0c
|
||||
struct bt_smp_public_key {
|
||||
u8_t x[32];
|
||||
u8_t y[32];
|
||||
} __packed;
|
||||
|
||||
#define BT_SMP_DHKEY_CHECK 0x0d
|
||||
struct bt_smp_dhkey_check {
|
||||
u8_t e[16];
|
||||
} __packed;
|
||||
|
||||
int bt_smp_start_security(struct bt_conn *conn);
|
||||
bool bt_smp_request_ltk(struct bt_conn *conn, u64_t rand, u16_t ediv,
|
||||
u8_t *ltk);
|
||||
|
||||
void bt_smp_update_keys(struct bt_conn *conn);
|
||||
|
||||
int bt_smp_br_send_pairing_req(struct bt_conn *conn);
|
||||
|
||||
int bt_smp_init(void);
|
||||
|
||||
int bt_smp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey);
|
||||
int bt_smp_auth_passkey_confirm(struct bt_conn *conn);
|
||||
int bt_smp_auth_pairing_confirm(struct bt_conn *conn);
|
||||
int bt_smp_auth_cancel(struct bt_conn *conn);
|
||||
|
||||
int bt_smp_le_oob_generate_sc_data(struct bt_le_oob_sc_data *le_sc_oob);
|
||||
int bt_smp_le_oob_set_sc_data(struct bt_conn *conn,
|
||||
const struct bt_le_oob_sc_data *oobd_local,
|
||||
const struct bt_le_oob_sc_data *oobd_remote);
|
||||
int bt_smp_le_oob_get_sc_data(struct bt_conn *conn,
|
||||
const struct bt_le_oob_sc_data **oobd_local,
|
||||
const struct bt_le_oob_sc_data **oobd_remote);
|
||||
|
||||
/** brief Verify signed message
|
||||
*
|
||||
* @param conn Bluetooth connection
|
||||
* @param buf received packet buffer with message and signature
|
||||
*
|
||||
* @return 0 in success, error code otherwise
|
||||
*/
|
||||
int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf);
|
||||
|
||||
/** brief Sign message
|
||||
*
|
||||
* @param conn Bluetooth connection
|
||||
* @param buf message buffer
|
||||
*
|
||||
* @return 0 in success, error code otherwise
|
||||
*/
|
||||
int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf);
|
||||
|
||||
#if defined(CONFIG_AUTO_PTS)
|
||||
int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_BLE_AT_CMD)
|
||||
struct smp_parameters{
|
||||
u8_t auth;
|
||||
u8_t iocap;
|
||||
u16_t key_size;
|
||||
u8_t init_key;
|
||||
u8_t rsp_key;
|
||||
u8_t set;
|
||||
};
|
||||
|
||||
struct smp_parameters user_smp_paras;
|
||||
int ble_set_smp_paramters(const struct smp_parameters *paras);
|
||||
int ble_get_smp_paramters(const struct bt_conn *conn,struct smp_parameters *paras);
|
||||
#endif
|
||||
#if defined(BFLB_BLE_SMP_LOCAL_AUTH)
|
||||
void smp_set_auth(u8_t auth);
|
||||
#endif
|
101
components/ble/ble_stack/host/smp_null.c
Normal file
101
components/ble/ble_stack/host/smp_null.c
Normal file
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* @file smp_null.c
|
||||
* Security Manager Protocol stub
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <errno.h>
|
||||
#include <atomic.h>
|
||||
#include <misc/util.h>
|
||||
|
||||
#include <bluetooth.h>
|
||||
#include <conn.h>
|
||||
#include <../include/bluetooth/buf.h>
|
||||
|
||||
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
|
||||
#define LOG_MODULE_NAME bt_smp
|
||||
#include "log.h"
|
||||
|
||||
#include "hci_core.h"
|
||||
#include "conn_internal.h"
|
||||
#include "l2cap_internal.h"
|
||||
#include "smp.h"
|
||||
|
||||
static struct bt_l2cap_le_chan bt_smp_pool[CONFIG_BT_MAX_CONN];
|
||||
|
||||
int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int bt_smp_recv(struct bt_l2cap_chan *chan, struct net_buf *buf)
|
||||
{
|
||||
struct bt_conn *conn = chan->conn;
|
||||
struct bt_smp_pairing_fail *rsp;
|
||||
struct bt_smp_hdr *hdr;
|
||||
|
||||
/* If a device does not support pairing then it shall respond with
|
||||
* a Pairing Failed command with the reason set to "Pairing Not
|
||||
* Supported" when any command is received.
|
||||
* Core Specification Vol. 3, Part H, 3.3
|
||||
*/
|
||||
|
||||
buf = bt_l2cap_create_pdu(NULL, 0);
|
||||
/* NULL is not a possible return due to K_FOREVER */
|
||||
|
||||
hdr = net_buf_add(buf, sizeof(*hdr));
|
||||
hdr->code = BT_SMP_CMD_PAIRING_FAIL;
|
||||
|
||||
rsp = net_buf_add(buf, sizeof(*rsp));
|
||||
rsp->reason = BT_SMP_ERR_PAIRING_NOTSUPP;
|
||||
|
||||
bt_l2cap_send(conn, BT_L2CAP_CID_SMP, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_smp_accept(struct bt_conn *conn, struct bt_l2cap_chan **chan)
|
||||
{
|
||||
int i;
|
||||
static struct bt_l2cap_chan_ops ops = {
|
||||
.recv = bt_smp_recv,
|
||||
};
|
||||
|
||||
BT_DBG("conn %p handle %u", conn, conn->handle);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bt_smp_pool); i++) {
|
||||
struct bt_l2cap_le_chan *smp = &bt_smp_pool[i];
|
||||
|
||||
if (smp->chan.conn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
smp->chan.ops = &ops;
|
||||
|
||||
*chan = &smp->chan;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_ERR("No available SMP context for conn %p", conn);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
BT_L2CAP_CHANNEL_DEFINE(smp_fixed_chan, BT_L2CAP_CID_SMP, bt_smp_accept);
|
||||
|
||||
int bt_smp_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
139
components/ble/ble_stack/host/uuid.c
Normal file
139
components/ble/ble_stack/host/uuid.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
/* uuid.c - Bluetooth UUID handling */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <misc/printk.h>
|
||||
|
||||
#include <uuid.h>
|
||||
|
||||
#define UUID_16_BASE_OFFSET 12
|
||||
|
||||
/* TODO: Decide whether to continue using BLE format or switch to RFC 4122 */
|
||||
|
||||
/* Base UUID : 0000[0000]-0000-1000-8000-00805F9B34FB
|
||||
* 0x2800 : 0000[2800]-0000-1000-8000-00805F9B34FB
|
||||
* little endian 0x2800 : [00 28] -> no swapping required
|
||||
* big endian 0x2800 : [28 00] -> swapping required
|
||||
*/
|
||||
static const struct bt_uuid_128 uuid128_base = {
|
||||
.uuid = { BT_UUID_TYPE_128 },
|
||||
.val = { BT_UUID_128_ENCODE(
|
||||
0x00000000, 0x0000, 0x1000, 0x8000, 0x00805F9B34FB) }
|
||||
};
|
||||
|
||||
static void uuid_to_uuid128(const struct bt_uuid *src, struct bt_uuid_128 *dst)
|
||||
{
|
||||
switch (src->type) {
|
||||
case BT_UUID_TYPE_16:
|
||||
*dst = uuid128_base;
|
||||
sys_put_le16(BT_UUID_16(src)->val,
|
||||
&dst->val[UUID_16_BASE_OFFSET]);
|
||||
return;
|
||||
case BT_UUID_TYPE_32:
|
||||
*dst = uuid128_base;
|
||||
sys_put_le32(BT_UUID_32(src)->val,
|
||||
&dst->val[UUID_16_BASE_OFFSET]);
|
||||
return;
|
||||
case BT_UUID_TYPE_128:
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int uuid128_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2)
|
||||
{
|
||||
struct bt_uuid_128 uuid1, uuid2;
|
||||
|
||||
uuid_to_uuid128(u1, &uuid1);
|
||||
uuid_to_uuid128(u2, &uuid2);
|
||||
|
||||
return memcmp(uuid1.val, uuid2.val, 16);
|
||||
}
|
||||
|
||||
int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2)
|
||||
{
|
||||
/* Convert to 128 bit if types don't match */
|
||||
if (u1->type != u2->type) {
|
||||
return uuid128_cmp(u1, u2);
|
||||
}
|
||||
|
||||
switch (u1->type) {
|
||||
case BT_UUID_TYPE_16:
|
||||
return (int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val;
|
||||
case BT_UUID_TYPE_32:
|
||||
return (int)BT_UUID_32(u1)->val - (int)BT_UUID_32(u2)->val;
|
||||
case BT_UUID_TYPE_128:
|
||||
return memcmp(BT_UUID_128(u1)->val, BT_UUID_128(u2)->val, 16);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bool bt_uuid_create(struct bt_uuid *uuid, const u8_t *data, u8_t data_len)
|
||||
{
|
||||
/* Copy UUID from packet data/internal variable to internal bt_uuid */
|
||||
switch (data_len) {
|
||||
case 2:
|
||||
uuid->type = BT_UUID_TYPE_16;
|
||||
BT_UUID_16(uuid)->val = sys_get_le16(data);
|
||||
break;
|
||||
case 4:
|
||||
uuid->type = BT_UUID_TYPE_32;
|
||||
BT_UUID_32(uuid)->val = sys_get_le32(data);
|
||||
break;
|
||||
case 16:
|
||||
uuid->type = BT_UUID_TYPE_128;
|
||||
memcpy(&BT_UUID_128(uuid)->val, data, 16);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_DEBUG)
|
||||
void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len)
|
||||
{
|
||||
u32_t tmp1, tmp5;
|
||||
u16_t tmp0, tmp2, tmp3, tmp4;
|
||||
|
||||
switch (uuid->type) {
|
||||
case BT_UUID_TYPE_16:
|
||||
snprintk(str, len, "%04x", BT_UUID_16(uuid)->val);
|
||||
break;
|
||||
case BT_UUID_TYPE_32:
|
||||
snprintk(str, len, "%04x", BT_UUID_32(uuid)->val);
|
||||
break;
|
||||
case BT_UUID_TYPE_128:
|
||||
memcpy(&tmp0, &BT_UUID_128(uuid)->val[0], sizeof(tmp0));
|
||||
memcpy(&tmp1, &BT_UUID_128(uuid)->val[2], sizeof(tmp1));
|
||||
memcpy(&tmp2, &BT_UUID_128(uuid)->val[6], sizeof(tmp2));
|
||||
memcpy(&tmp3, &BT_UUID_128(uuid)->val[8], sizeof(tmp3));
|
||||
memcpy(&tmp4, &BT_UUID_128(uuid)->val[10], sizeof(tmp4));
|
||||
memcpy(&tmp5, &BT_UUID_128(uuid)->val[12], sizeof(tmp5));
|
||||
|
||||
snprintk(str, len, "%08x-%04x-%04x-%04x-%08x%04x",
|
||||
tmp5, tmp4, tmp3, tmp2, tmp1, tmp0);
|
||||
break;
|
||||
default:
|
||||
(void)memset(str, 0, len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *bt_uuid_str_real(const struct bt_uuid *uuid)
|
||||
{
|
||||
static char str[37];
|
||||
|
||||
bt_uuid_to_str(uuid, str, sizeof(str));
|
||||
|
||||
return str;
|
||||
}
|
||||
#endif /* CONFIG_BT_DEBUG */
|
Loading…
Add table
Add a link
Reference in a new issue