Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
#include "hci/hci_audio.h"
|
||||
|
||||
void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state)
|
||||
{
|
||||
//todo
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,652 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <string.h>
|
||||
#include "common/bt_defs.h"
|
||||
#include "common/bt_trace.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "hci/hci_hal.h"
|
||||
#include "hci/hci_internals.h"
|
||||
#include "hci/hci_layer.h"
|
||||
#include "hci/hci_trans_int.h"
|
||||
#include "osi/thread.h"
|
||||
#include "osi/pkt_queue.h"
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
#include "osi/mutex.h"
|
||||
#include "osi/alarm.h"
|
||||
#endif
|
||||
#if (BT_CONTROLLER_INCLUDED == TRUE)
|
||||
#include "esp_bt.h"
|
||||
#endif
|
||||
#include "esp_bluedroid_hci.h"
|
||||
#include "stack/hcimsgs.h"
|
||||
|
||||
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
|
||||
#include "nimble/ble_hci_trans.h"
|
||||
#endif
|
||||
|
||||
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
|
||||
#include "l2c_int.h"
|
||||
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
|
||||
#include "stack/hcimsgs.h"
|
||||
|
||||
#define HCI_BLE_EVENT 0x3e
|
||||
#define PACKET_TYPE_TO_INBOUND_INDEX(type) ((type) - 2)
|
||||
#define PACKET_TYPE_TO_INDEX(type) ((type) - 1)
|
||||
#define HCI_UPSTREAM_DATA_QUEUE_IDX (1)
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
#define HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE (10)
|
||||
#define HCI_ADV_FLOW_MONITOR_PERIOD_MS (500)
|
||||
#else
|
||||
#define HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX (200)
|
||||
#endif
|
||||
|
||||
extern bool BTU_check_queue_is_congest(void);
|
||||
|
||||
|
||||
static const uint8_t preamble_sizes[] = {
|
||||
HCI_COMMAND_PREAMBLE_SIZE,
|
||||
HCI_ACL_PREAMBLE_SIZE,
|
||||
HCI_SCO_PREAMBLE_SIZE,
|
||||
HCI_EVENT_PREAMBLE_SIZE
|
||||
};
|
||||
|
||||
static const uint16_t outbound_event_types[] = {
|
||||
MSG_HC_TO_STACK_HCI_ERR,
|
||||
MSG_HC_TO_STACK_HCI_ACL,
|
||||
MSG_HC_TO_STACK_HCI_SCO,
|
||||
MSG_HC_TO_STACK_HCI_EVT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
fixed_queue_t *rx_q;
|
||||
struct pkt_queue *adv_rpt_q;
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
osi_mutex_t adv_flow_lock;
|
||||
osi_alarm_t *adv_flow_monitor;
|
||||
int adv_credits;
|
||||
int adv_credits_to_release;
|
||||
pkt_linked_item_t *adv_fc_cmd_buf;
|
||||
bool cmd_buf_in_use;
|
||||
#endif
|
||||
hci_hal_callbacks_t *callbacks;
|
||||
osi_thread_t *hci_h4_thread;
|
||||
struct osi_event *upstream_data_ready;
|
||||
} hci_hal_env_t;
|
||||
|
||||
|
||||
static hci_hal_env_t hci_hal_env;
|
||||
static const hci_hal_t interface;
|
||||
static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb;
|
||||
|
||||
static void host_send_pkt_available_cb(void);
|
||||
static int host_recv_pkt_cb(uint8_t *data, uint16_t len);
|
||||
static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet);
|
||||
static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt);
|
||||
static void hci_upstream_data_handler(void *arg);
|
||||
static bool hci_upstream_data_post(uint32_t timeout);
|
||||
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
static void hci_adv_flow_monitor(void *context);
|
||||
static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt);
|
||||
#endif
|
||||
|
||||
static bool hci_hal_env_init(const hci_hal_callbacks_t *upper_callbacks, osi_thread_t *task_thread)
|
||||
{
|
||||
assert(upper_callbacks != NULL);
|
||||
assert(task_thread != NULL);
|
||||
|
||||
hci_hal_env.hci_h4_thread = task_thread;
|
||||
hci_hal_env.callbacks = (hci_hal_callbacks_t *)upper_callbacks;
|
||||
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_hal_env.adv_fc_cmd_buf = osi_calloc(HCI_CMD_LINKED_BUF_SIZE(HCIC_PARAM_SIZE_BLE_UPDATE_ADV_FLOW_CONTROL));
|
||||
assert(hci_hal_env.adv_fc_cmd_buf != NULL);
|
||||
osi_mutex_new(&hci_hal_env.adv_flow_lock);
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
hci_hal_env.adv_credits = BLE_ADV_REPORT_FLOW_CONTROL_NUM;
|
||||
hci_hal_env.adv_credits_to_release = 0;
|
||||
hci_hal_env.cmd_buf_in_use = false;
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
hci_hal_env.adv_flow_monitor = osi_alarm_new("adv_fc_mon", hci_adv_flow_monitor, NULL, HCI_ADV_FLOW_MONITOR_PERIOD_MS);
|
||||
assert (hci_hal_env.adv_flow_monitor != NULL);
|
||||
#endif
|
||||
|
||||
hci_hal_env.rx_q = fixed_queue_new(QUEUE_SIZE_MAX);
|
||||
assert(hci_hal_env.rx_q != NULL);
|
||||
|
||||
hci_hal_env.adv_rpt_q = pkt_queue_create();
|
||||
assert(hci_hal_env.adv_rpt_q != NULL);
|
||||
|
||||
struct osi_event *event = osi_event_create(hci_upstream_data_handler, NULL);
|
||||
assert(event != NULL);
|
||||
hci_hal_env.upstream_data_ready = event;
|
||||
osi_event_bind(hci_hal_env.upstream_data_ready, hci_hal_env.hci_h4_thread, HCI_UPSTREAM_DATA_QUEUE_IDX);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hci_hal_env_deinit(void)
|
||||
{
|
||||
fixed_queue_t *rx_q = hci_hal_env.rx_q;
|
||||
struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q;
|
||||
struct osi_event *upstream_data_ready = hci_hal_env.upstream_data_ready;
|
||||
|
||||
hci_hal_env.rx_q = NULL;
|
||||
hci_hal_env.adv_rpt_q = NULL;
|
||||
hci_hal_env.upstream_data_ready = NULL;
|
||||
|
||||
fixed_queue_free(rx_q, osi_free_func);
|
||||
|
||||
pkt_queue_destroy(adv_rpt_q, NULL);
|
||||
|
||||
osi_event_delete(upstream_data_ready);
|
||||
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_hal_env.cmd_buf_in_use = true;
|
||||
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
|
||||
osi_alarm_free(hci_hal_env.adv_flow_monitor);
|
||||
hci_hal_env.adv_flow_monitor = NULL;
|
||||
osi_mutex_free(&hci_hal_env.adv_flow_lock);
|
||||
osi_free(hci_hal_env.adv_fc_cmd_buf);
|
||||
hci_hal_env.adv_fc_cmd_buf = NULL;
|
||||
#endif
|
||||
|
||||
hci_hal_env.hci_h4_thread = NULL;
|
||||
|
||||
memset(&hci_hal_env, 0, sizeof(hci_hal_env_t));
|
||||
}
|
||||
|
||||
static bool hal_open(const hci_hal_callbacks_t *upper_callbacks, void *task_thread)
|
||||
{
|
||||
hci_hal_env_init(upper_callbacks, (osi_thread_t *)task_thread);
|
||||
|
||||
//register vhci host cb
|
||||
if (hci_host_register_callback(&hci_host_cb) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hal_close(void)
|
||||
{
|
||||
hci_hal_env_deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function: transmit_data -TX data to low-layer
|
||||
* It is ported from Bluedroid source code, so it is not
|
||||
* needed to use write() to send data.
|
||||
* TODO: Just use firmware API to send data.
|
||||
*/
|
||||
static uint16_t transmit_data(serial_data_type_t type,
|
||||
uint8_t *data, uint16_t length)
|
||||
{
|
||||
uint8_t previous_byte;
|
||||
|
||||
assert(data != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) {
|
||||
HCI_TRACE_ERROR("%s invalid data type: %d", __func__, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write the signal byte right before the data
|
||||
--data;
|
||||
previous_byte = *data;
|
||||
*(data) = type;
|
||||
++length;
|
||||
|
||||
BTTRC_DUMP_BUFFER("Transmit Pkt", data, length);
|
||||
|
||||
// TX Data to target
|
||||
hci_host_send_packet(data, length);
|
||||
|
||||
// Be nice and restore the old value of that byte
|
||||
*(data) = previous_byte;
|
||||
|
||||
return length - 1;
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
static void hci_upstream_data_handler(void *arg)
|
||||
{
|
||||
fixed_queue_t *rx_q = hci_hal_env.rx_q;
|
||||
struct pkt_queue *adv_rpt_q = hci_hal_env.adv_rpt_q;
|
||||
size_t pkts_to_process;
|
||||
|
||||
do {
|
||||
pkts_to_process = fixed_queue_length(rx_q);
|
||||
for (size_t i = 0; i < pkts_to_process; i++) {
|
||||
BT_HDR *packet = fixed_queue_dequeue(rx_q, 0);
|
||||
if (packet != NULL) {
|
||||
hci_hal_h4_hdl_rx_packet(packet);
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
do {
|
||||
pkts_to_process = pkt_queue_length(adv_rpt_q);
|
||||
for (size_t i = 0; i < pkts_to_process; i++) {
|
||||
pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(adv_rpt_q);
|
||||
if (linked_pkt != NULL) {
|
||||
hci_hal_h4_hdl_rx_adv_rpt(linked_pkt);
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (!fixed_queue_is_empty(rx_q) || pkt_queue_length(adv_rpt_q) > 0) {
|
||||
hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static bool hci_upstream_data_post(uint32_t timeout)
|
||||
{
|
||||
return osi_thread_post_event(hci_hal_env.upstream_data_ready, timeout);
|
||||
}
|
||||
|
||||
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
|
||||
static void hci_packet_complete(BT_HDR *packet){
|
||||
uint8_t type;
|
||||
uint16_t handle;
|
||||
uint16_t num_packets = 1;
|
||||
uint8_t *stream = packet->data + packet->offset;
|
||||
|
||||
STREAM_TO_UINT8(type, stream);
|
||||
if (type == DATA_TYPE_ACL/* || type == DATA_TYPE_SCO*/) {
|
||||
STREAM_TO_UINT16(handle, stream);
|
||||
handle = handle & HCI_DATA_HANDLE_MASK;
|
||||
btsnd_hcic_host_num_xmitted_pkts(1, &handle, &num_packets);
|
||||
}
|
||||
}
|
||||
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
|
||||
|
||||
bool host_recv_adv_packet(uint8_t *packet)
|
||||
{
|
||||
assert(packet);
|
||||
if(packet[0] == DATA_TYPE_EVENT && packet[1] == HCI_BLE_EVENT) {
|
||||
if(packet[3] == HCI_BLE_ADV_PKT_RPT_EVT || packet[3] == HCI_BLE_DIRECT_ADV_EVT
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
|| packet[3] == HCI_BLE_ADV_DISCARD_REPORT_EVT
|
||||
#endif
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
static void hci_adv_flow_monitor(void *context)
|
||||
{
|
||||
hci_adv_credits_force_release(0);
|
||||
}
|
||||
|
||||
static void hci_adv_credits_consumed(uint16_t num)
|
||||
{
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
assert(hci_hal_env.adv_credits >= num);
|
||||
hci_hal_env.adv_credits -= num;
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
}
|
||||
|
||||
int hci_adv_credits_prep_to_release(uint16_t num)
|
||||
{
|
||||
if (num == 0) {
|
||||
return hci_hal_env.adv_credits_to_release;
|
||||
}
|
||||
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
int credits_to_release = hci_hal_env.adv_credits_to_release + num;
|
||||
assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
|
||||
hci_hal_env.adv_credits_to_release = credits_to_release;
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
|
||||
if (credits_to_release == num && num != 0) {
|
||||
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
|
||||
osi_alarm_set(hci_hal_env.adv_flow_monitor, HCI_ADV_FLOW_MONITOR_PERIOD_MS);
|
||||
}
|
||||
return credits_to_release;
|
||||
}
|
||||
|
||||
static int hci_adv_credits_release(void)
|
||||
{
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
int credits_released = hci_hal_env.adv_credits_to_release;
|
||||
hci_hal_env.adv_credits += credits_released;
|
||||
hci_hal_env.adv_credits_to_release -= credits_released;
|
||||
assert(hci_hal_env.adv_credits <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
|
||||
assert(hci_hal_env.adv_credits_to_release >= 0);
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
|
||||
if (hci_hal_env.adv_credits_to_release == 0) {
|
||||
osi_alarm_cancel(hci_hal_env.adv_flow_monitor);
|
||||
}
|
||||
return credits_released;
|
||||
}
|
||||
|
||||
static int hci_adv_credits_release_rollback(uint16_t num)
|
||||
{
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
hci_hal_env.adv_credits -= num;
|
||||
hci_hal_env.adv_credits_to_release += num;
|
||||
assert(hci_hal_env.adv_credits >=0);
|
||||
assert(hci_hal_env.adv_credits_to_release <= BLE_ADV_REPORT_FLOW_CONTROL_NUM);
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static void hci_adv_flow_cmd_free_cb(pkt_linked_item_t *linked_pkt)
|
||||
{
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
hci_hal_env.cmd_buf_in_use = false;
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
hci_adv_credits_try_release(0);
|
||||
}
|
||||
|
||||
bool hci_adv_flow_try_send_command(uint16_t credits_released)
|
||||
{
|
||||
bool sent = false;
|
||||
bool use_static_buffer = false;
|
||||
|
||||
/* first try using static buffer, then dynamic buffer */
|
||||
if (!hci_hal_env.cmd_buf_in_use) {
|
||||
osi_mutex_lock(&hci_hal_env.adv_flow_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (!hci_hal_env.cmd_buf_in_use) {
|
||||
hci_hal_env.cmd_buf_in_use = true;
|
||||
use_static_buffer = true;
|
||||
}
|
||||
osi_mutex_unlock(&hci_hal_env.adv_flow_lock);
|
||||
}
|
||||
|
||||
if (use_static_buffer) {
|
||||
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(hci_hal_env.adv_fc_cmd_buf->data);
|
||||
BT_HDR *static_buffer = &metadata->command;
|
||||
metadata->command_free_cb = hci_adv_flow_cmd_free_cb;
|
||||
sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, static_buffer);
|
||||
} else {
|
||||
sent = btsnd_hcic_ble_update_adv_report_flow_control(credits_released, NULL);
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
int hci_adv_credits_try_release(uint16_t num)
|
||||
{
|
||||
int credits_released = 0;
|
||||
if (hci_adv_credits_prep_to_release(num) >= HCI_BLE_ADV_MIN_CREDITS_TO_RELEASE) {
|
||||
credits_released = hci_adv_credits_release();
|
||||
if (credits_released > 0) {
|
||||
if (!hci_adv_flow_try_send_command(credits_released)) {
|
||||
hci_adv_credits_release_rollback(credits_released);
|
||||
}
|
||||
} else {
|
||||
assert (credits_released == 0);
|
||||
}
|
||||
}
|
||||
return credits_released;
|
||||
}
|
||||
|
||||
int hci_adv_credits_force_release(uint16_t num)
|
||||
{
|
||||
hci_adv_credits_prep_to_release(num);
|
||||
int credits_released = hci_adv_credits_release();
|
||||
if (credits_released > 0) {
|
||||
if (!hci_adv_flow_try_send_command(credits_released)) {
|
||||
hci_adv_credits_release_rollback(credits_released);
|
||||
}
|
||||
}
|
||||
|
||||
return credits_released;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet)
|
||||
{
|
||||
uint8_t type, hdr_size;
|
||||
uint16_t length;
|
||||
uint8_t *stream = NULL;
|
||||
|
||||
if (!packet) {
|
||||
return;
|
||||
}
|
||||
stream = packet->data + packet->offset;
|
||||
|
||||
#if (C2H_FLOW_CONTROL_INCLUDED == TRUE)
|
||||
hci_packet_complete(packet);
|
||||
#endif ///C2H_FLOW_CONTROL_INCLUDED == TRUE
|
||||
|
||||
STREAM_TO_UINT8(type, stream);
|
||||
packet->offset++;
|
||||
packet->len--;
|
||||
if (type == HCI_BLE_EVENT) {
|
||||
#if (!CONFIG_BT_STACK_NO_LOG)
|
||||
uint8_t len = 0;
|
||||
STREAM_TO_UINT8(len, stream);
|
||||
#endif
|
||||
HCI_TRACE_ERROR("Workround stream corrupted during LE SCAN: pkt_len=%d ble_event_len=%d\n",
|
||||
packet->len, len);
|
||||
osi_free(packet);
|
||||
return;
|
||||
}
|
||||
if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) {
|
||||
HCI_TRACE_ERROR("%s Unknown HCI message type. Dropping this byte 0x%x,"
|
||||
" min %x, max %x\n", __func__, type,
|
||||
DATA_TYPE_ACL, DATA_TYPE_EVENT);
|
||||
osi_free(packet);
|
||||
return;
|
||||
}
|
||||
hdr_size = preamble_sizes[type - 1];
|
||||
if (packet->len < hdr_size) {
|
||||
HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d",
|
||||
type, packet->len, hdr_size);
|
||||
osi_free(packet);
|
||||
return;
|
||||
}
|
||||
if (type == DATA_TYPE_ACL) {
|
||||
stream += hdr_size - 2;
|
||||
STREAM_TO_UINT16(length, stream);
|
||||
} else {
|
||||
stream += hdr_size - 1;
|
||||
STREAM_TO_UINT8(length, stream);
|
||||
}
|
||||
|
||||
if ((length + hdr_size) != packet->len) {
|
||||
HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d "
|
||||
"pkt_len=%d", type, hdr_size, length, packet->len);
|
||||
osi_free(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)];
|
||||
hci_hal_env.callbacks->packet_ready(packet);
|
||||
}
|
||||
|
||||
static void hci_hal_h4_hdl_rx_adv_rpt(pkt_linked_item_t *linked_pkt)
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t hdr_size;
|
||||
uint16_t length;
|
||||
uint8_t *stream = NULL;
|
||||
|
||||
if (!linked_pkt) {
|
||||
return;
|
||||
}
|
||||
|
||||
BT_HDR* packet = (BT_HDR *)linked_pkt->data;
|
||||
stream = packet->data + packet->offset;
|
||||
|
||||
assert(host_recv_adv_packet(stream) == true);
|
||||
|
||||
STREAM_TO_UINT8(type, stream);
|
||||
packet->offset++;
|
||||
packet->len--;
|
||||
hdr_size = preamble_sizes[type - 1];
|
||||
|
||||
if (packet->len < hdr_size) {
|
||||
HCI_TRACE_ERROR("Wrong packet length type=%d pkt_len=%d hdr_len=%d",
|
||||
type, packet->len, hdr_size);
|
||||
goto _discard_packet;
|
||||
}
|
||||
|
||||
stream += hdr_size - 1;
|
||||
STREAM_TO_UINT8(length, stream);
|
||||
if ((length + hdr_size) != packet->len) {
|
||||
HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d "
|
||||
"pkt_len=%d", type, hdr_size, length, packet->len);
|
||||
goto _discard_packet;
|
||||
}
|
||||
|
||||
#if SCAN_QUEUE_CONGEST_CHECK
|
||||
if(BTU_check_queue_is_congest()) {
|
||||
HCI_TRACE_DEBUG("BtuQueue is congested");
|
||||
goto _discard_packet;
|
||||
}
|
||||
#endif
|
||||
|
||||
packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)];
|
||||
hci_hal_env.callbacks->adv_rpt_ready(linked_pkt);
|
||||
|
||||
return;
|
||||
|
||||
_discard_packet:
|
||||
osi_free(linked_pkt);
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_adv_credits_prep_to_release(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void host_send_pkt_available_cb(void)
|
||||
{
|
||||
//Controller rx cache buffer is ready for receiving new host packet
|
||||
//Just Call Host main thread task to process pending packets.
|
||||
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
static int host_recv_pkt_cb(uint8_t *data, uint16_t len)
|
||||
{
|
||||
//Target has packet to host, malloc new buffer for packet
|
||||
BT_HDR *pkt = NULL;
|
||||
pkt_linked_item_t *linked_pkt = NULL;
|
||||
size_t pkt_size;
|
||||
|
||||
if (hci_hal_env.rx_q == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_adv_rpt = host_recv_adv_packet(data);
|
||||
|
||||
if (!is_adv_rpt) {
|
||||
pkt_size = BT_HDR_SIZE + len;
|
||||
pkt = (BT_HDR *) osi_calloc(pkt_size);
|
||||
if (!pkt) {
|
||||
HCI_TRACE_ERROR("%s couldn't aquire memory for inbound data buffer.\n", __func__);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
pkt->offset = 0;
|
||||
pkt->len = len;
|
||||
pkt->layer_specific = 0;
|
||||
memcpy(pkt->data, data, len);
|
||||
fixed_queue_enqueue(hci_hal_env.rx_q, pkt, FIXED_QUEUE_MAX_TIMEOUT);
|
||||
} else {
|
||||
#if !BLE_ADV_REPORT_FLOW_CONTROL
|
||||
// drop the packets if pkt_queue length goes beyond upper limit
|
||||
if (pkt_queue_length(hci_hal_env.adv_rpt_q) > HCI_HAL_BLE_ADV_RPT_QUEUE_LEN_MAX) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
pkt_size = BT_PKT_LINKED_HDR_SIZE + BT_HDR_SIZE + len;
|
||||
linked_pkt = (pkt_linked_item_t *) osi_calloc(pkt_size);
|
||||
if (!linked_pkt) {
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_adv_credits_consumed(1);
|
||||
hci_adv_credits_prep_to_release(1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
pkt = (BT_HDR *)linked_pkt->data;
|
||||
pkt->offset = 0;
|
||||
pkt->len = len;
|
||||
pkt->layer_specific = 0;
|
||||
memcpy(pkt->data, data, len);
|
||||
pkt_queue_enqueue(hci_hal_env.adv_rpt_q, linked_pkt);
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_adv_credits_consumed(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
hci_upstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
|
||||
BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
|
||||
|
||||
int
|
||||
ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg)
|
||||
{
|
||||
if(esp_bluedroid_get_status() == ESP_BLUEDROID_STATUS_UNINITIALIZED) {
|
||||
ble_hci_trans_buf_free(hci_ev);
|
||||
return 0;
|
||||
}
|
||||
uint16_t len = hci_ev[1] + 3;
|
||||
uint8_t *data = (uint8_t *)malloc(len);
|
||||
assert(data != NULL);
|
||||
data[0] = 0x04;
|
||||
memcpy(&data[1], hci_ev, len - 1);
|
||||
ble_hci_trans_buf_free(hci_ev);
|
||||
host_recv_pkt_cb(data, len);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ble_hs_rx_data(struct os_mbuf *om, void *arg)
|
||||
{
|
||||
uint16_t len = OS_MBUF_PKTHDR(om)->omp_len + 1;
|
||||
uint8_t *data = (uint8_t *)malloc(len);
|
||||
assert(data != NULL);
|
||||
data[0] = 0x02;
|
||||
os_mbuf_copydata(om, 0, len - 1, &data[1]);
|
||||
host_recv_pkt_cb(data, len);
|
||||
free(data);
|
||||
os_mbuf_free_chain(om);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
static const esp_bluedroid_hci_driver_callbacks_t hci_host_cb = {
|
||||
.notify_host_send_available = host_send_pkt_available_cb,
|
||||
.notify_host_recv = host_recv_pkt_cb,
|
||||
};
|
||||
|
||||
static const hci_hal_t interface = {
|
||||
hal_open,
|
||||
hal_close,
|
||||
transmit_data,
|
||||
};
|
||||
|
||||
const hci_hal_t *hci_hal_h4_get_interface(void)
|
||||
{
|
||||
return &interface;
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "common/bt_target.h"
|
||||
#if (BT_CONTROLLER_INCLUDED == TRUE)
|
||||
#include "esp_bt.h"
|
||||
#endif
|
||||
|
||||
#include "common/bt_defs.h"
|
||||
#include "common/bt_trace.h"
|
||||
#include "stack/hcidefs.h"
|
||||
#include "stack/hcimsgs.h"
|
||||
#include "stack/btu.h"
|
||||
#include "common/bt_vendor_lib.h"
|
||||
#include "hci/hci_internals.h"
|
||||
#include "hci/hci_hal.h"
|
||||
#include "hci/hci_layer.h"
|
||||
#include "hci/hci_trans_int.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "hci/packet_fragmenter.h"
|
||||
#include "osi/list.h"
|
||||
#include "osi/alarm.h"
|
||||
#include "osi/thread.h"
|
||||
#include "osi/mutex.h"
|
||||
#include "osi/fixed_queue.h"
|
||||
#include "osi/fixed_pkt_queue.h"
|
||||
|
||||
#define HCI_HOST_TASK_PINNED_TO_CORE (TASK_PINNED_TO_CORE)
|
||||
#define HCI_HOST_TASK_STACK_SIZE (2048 + BT_TASK_EXTRA_STACK_SIZE)
|
||||
#define HCI_HOST_TASK_PRIO (BT_TASK_MAX_PRIORITIES - 3)
|
||||
#define HCI_HOST_TASK_NAME "hciT"
|
||||
#define HCI_HOST_TASK_WORKQUEUE_NUM (2)
|
||||
#define HCI_HOST_TASK_WORKQUEUE0_LEN (1) // for downstream datapath
|
||||
#define HCI_HOST_TASK_WORKQUEUE1_LEN (1) // for upstream datapath
|
||||
|
||||
#define HCI_DOWNSTREAM_DATA_QUEUE_IDX (0)
|
||||
|
||||
typedef struct {
|
||||
bool timer_is_set;
|
||||
osi_alarm_t *command_response_timer;
|
||||
list_t *commands_pending_response;
|
||||
osi_mutex_t commands_pending_response_lock;
|
||||
} command_waiting_response_t;
|
||||
|
||||
typedef struct {
|
||||
int command_credits;
|
||||
fixed_pkt_queue_t *command_queue;
|
||||
fixed_queue_t *packet_queue;
|
||||
struct osi_event *downstream_data_ready;
|
||||
command_waiting_response_t cmd_waiting_q;
|
||||
|
||||
/*
|
||||
non_repeating_timer_t *command_response_timer;
|
||||
list_t *commands_pending_response;
|
||||
osi_mutex_t commands_pending_response_lock;
|
||||
*/
|
||||
} hci_host_env_t;
|
||||
|
||||
// Using a define here, because it can be stringified for the property lookup
|
||||
static const uint32_t COMMAND_PENDING_TIMEOUT = 8000;
|
||||
|
||||
// Our interface
|
||||
static bool interface_created;
|
||||
static hci_t interface;
|
||||
static hci_host_env_t hci_host_env;
|
||||
static osi_thread_t *hci_host_thread;
|
||||
static bool hci_host_startup_flag;
|
||||
|
||||
// Modules we import and callbacks we export
|
||||
static const hci_hal_t *hal;
|
||||
static const hci_hal_callbacks_t hal_callbacks;
|
||||
static const packet_fragmenter_t *packet_fragmenter;
|
||||
static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks;
|
||||
|
||||
static int hci_layer_init_env(void);
|
||||
static void hci_layer_deinit_env(void);
|
||||
static void hci_downstream_data_handler(void *arg);
|
||||
static void event_command_ready(fixed_pkt_queue_t *queue);
|
||||
static void event_packet_ready(fixed_queue_t *queue);
|
||||
static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q);
|
||||
static void command_timed_out(void *context);
|
||||
static void hal_says_packet_ready(BT_HDR *packet);
|
||||
static bool filter_incoming_event(BT_HDR *packet);
|
||||
static serial_data_type_t event_to_data_type(uint16_t event);
|
||||
static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode);
|
||||
static void dispatch_reassembled(BT_HDR *packet);
|
||||
static void dispatch_adv_report(pkt_linked_item_t *linked_pkt);
|
||||
|
||||
// Module lifecycle functions
|
||||
int hci_start_up(void)
|
||||
{
|
||||
if (hci_layer_init_env()) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
const size_t workqueue_len[] = {HCI_HOST_TASK_WORKQUEUE0_LEN, HCI_HOST_TASK_WORKQUEUE1_LEN};
|
||||
hci_host_thread = osi_thread_create(HCI_HOST_TASK_NAME, HCI_HOST_TASK_STACK_SIZE, HCI_HOST_TASK_PRIO, HCI_HOST_TASK_PINNED_TO_CORE,
|
||||
HCI_HOST_TASK_WORKQUEUE_NUM, workqueue_len);
|
||||
if (hci_host_thread == NULL) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
osi_event_bind(hci_host_env.downstream_data_ready, hci_host_thread, HCI_DOWNSTREAM_DATA_QUEUE_IDX);
|
||||
|
||||
packet_fragmenter->init(&packet_fragmenter_callbacks);
|
||||
hal->open(&hal_callbacks, hci_host_thread);
|
||||
|
||||
hci_host_startup_flag = true;
|
||||
return 0;
|
||||
error:
|
||||
hci_shut_down();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void hci_shut_down(void)
|
||||
{
|
||||
hci_host_startup_flag = false;
|
||||
hci_layer_deinit_env();
|
||||
|
||||
packet_fragmenter->cleanup();
|
||||
|
||||
//low_power_manager->cleanup();
|
||||
hal->close();
|
||||
|
||||
osi_thread_free(hci_host_thread);
|
||||
hci_host_thread = NULL;
|
||||
}
|
||||
|
||||
bool hci_downstream_data_post(uint32_t timeout)
|
||||
{
|
||||
return osi_thread_post_event(hci_host_env.downstream_data_ready, timeout);
|
||||
}
|
||||
|
||||
static int hci_layer_init_env(void)
|
||||
{
|
||||
command_waiting_response_t *cmd_wait_q;
|
||||
|
||||
// The host is only allowed to send at most one command initially,
|
||||
// as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control)
|
||||
// This value can change when you get a command complete or command status event.
|
||||
hci_host_env.command_credits = 1;
|
||||
hci_host_env.command_queue = fixed_pkt_queue_new(QUEUE_SIZE_MAX);
|
||||
if (hci_host_env.command_queue) {
|
||||
fixed_pkt_queue_register_dequeue(hci_host_env.command_queue, event_command_ready);
|
||||
} else {
|
||||
HCI_TRACE_ERROR("%s unable to create pending command queue.", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct osi_event *event = osi_event_create(hci_downstream_data_handler, NULL);
|
||||
assert(event != NULL);
|
||||
hci_host_env.downstream_data_ready = event;
|
||||
|
||||
hci_host_env.packet_queue = fixed_queue_new(QUEUE_SIZE_MAX);
|
||||
if (hci_host_env.packet_queue) {
|
||||
fixed_queue_register_dequeue(hci_host_env.packet_queue, event_packet_ready);
|
||||
} else {
|
||||
HCI_TRACE_ERROR("%s unable to create pending packet queue.", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Init Commands waiting response list and timer
|
||||
cmd_wait_q = &hci_host_env.cmd_waiting_q;
|
||||
cmd_wait_q->timer_is_set = false;
|
||||
cmd_wait_q->commands_pending_response = list_new(NULL);
|
||||
if (!cmd_wait_q->commands_pending_response) {
|
||||
HCI_TRACE_ERROR("%s unable to create list for commands pending response.", __func__);
|
||||
return -1;
|
||||
}
|
||||
osi_mutex_new(&cmd_wait_q->commands_pending_response_lock);
|
||||
cmd_wait_q->command_response_timer = osi_alarm_new("cmd_rsp_to", command_timed_out, cmd_wait_q, COMMAND_PENDING_TIMEOUT);
|
||||
if (!cmd_wait_q->command_response_timer) {
|
||||
HCI_TRACE_ERROR("%s unable to create command response timer.", __func__);
|
||||
return -1;
|
||||
}
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
btsnd_hcic_ble_sync_sem_init();
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_layer_deinit_env(void)
|
||||
{
|
||||
command_waiting_response_t *cmd_wait_q;
|
||||
|
||||
osi_event_delete(hci_host_env.downstream_data_ready);
|
||||
hci_host_env.downstream_data_ready = NULL;
|
||||
|
||||
if (hci_host_env.command_queue) {
|
||||
fixed_pkt_queue_free(hci_host_env.command_queue, (fixed_pkt_queue_free_cb)osi_free_func);
|
||||
}
|
||||
if (hci_host_env.packet_queue) {
|
||||
fixed_queue_free(hci_host_env.packet_queue, osi_free_func);
|
||||
}
|
||||
|
||||
cmd_wait_q = &hci_host_env.cmd_waiting_q;
|
||||
list_free(cmd_wait_q->commands_pending_response);
|
||||
osi_mutex_free(&cmd_wait_q->commands_pending_response_lock);
|
||||
osi_alarm_free(cmd_wait_q->command_response_timer);
|
||||
cmd_wait_q->command_response_timer = NULL;
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
btsnd_hcic_ble_sync_sem_deinit();
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
}
|
||||
|
||||
static void hci_downstream_data_handler(void *arg)
|
||||
{
|
||||
/*
|
||||
* Previous task handles RX queue and two TX Queues, Since there is
|
||||
* a RX Thread Task in H4 layer which receives packet from driver layer.
|
||||
* Now HCI Host Task has been optimized to only process TX Queue
|
||||
* including command and data queue. And command queue has high priority,
|
||||
* All packets will be directly copied to single queue in driver layer with
|
||||
* H4 type header added (1 byte).
|
||||
*/
|
||||
while (hci_host_check_send_available()) {
|
||||
/*Now Target only allowed one packet per TX*/
|
||||
BT_HDR *pkt = packet_fragmenter->fragment_current_packet();
|
||||
if (pkt != NULL) {
|
||||
packet_fragmenter->fragment_and_dispatch(pkt);
|
||||
} else if (!fixed_pkt_queue_is_empty(hci_host_env.command_queue) &&
|
||||
hci_host_env.command_credits > 0) {
|
||||
fixed_pkt_queue_process(hci_host_env.command_queue);
|
||||
} else if (!fixed_queue_is_empty(hci_host_env.packet_queue)) {
|
||||
fixed_queue_process(hci_host_env.packet_queue);
|
||||
} else {
|
||||
// No downstream packet to send, stop processing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void transmit_command(
|
||||
BT_HDR *command,
|
||||
command_complete_cb complete_callback,
|
||||
command_status_cb status_callback,
|
||||
void *context)
|
||||
{
|
||||
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command);
|
||||
pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata);
|
||||
|
||||
assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA);
|
||||
metadata->flags_vnd |= HCI_CMD_MSG_F_VND_QUEUED;
|
||||
|
||||
// Store the command message type in the event field
|
||||
// in case the upper layer didn't already
|
||||
command->event = MSG_STACK_TO_HC_HCI_CMD;
|
||||
|
||||
HCI_TRACE_DEBUG("HCI Enqueue Comamnd opcode=0x%x\n", metadata->opcode);
|
||||
BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len);
|
||||
|
||||
fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT);
|
||||
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
|
||||
}
|
||||
|
||||
static future_t *transmit_command_futured(BT_HDR *command)
|
||||
{
|
||||
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(command);
|
||||
pkt_linked_item_t *linked_pkt = HCI_GET_CMD_LINKED_STRUCT(metadata);
|
||||
|
||||
assert(command->layer_specific == HCI_CMD_BUF_TYPE_METADATA);
|
||||
metadata->flags_vnd |= (HCI_CMD_MSG_F_VND_QUEUED | HCI_CMD_MSG_F_VND_FUTURE);
|
||||
|
||||
future_t *future = future_new();
|
||||
|
||||
metadata->complete_future = future;
|
||||
|
||||
// Store the command message type in the event field
|
||||
// in case the upper layer didn't already
|
||||
command->event = MSG_STACK_TO_HC_HCI_CMD;
|
||||
|
||||
fixed_pkt_queue_enqueue(hci_host_env.command_queue, linked_pkt, FIXED_PKT_QUEUE_MAX_TIMEOUT);
|
||||
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
return future;
|
||||
}
|
||||
|
||||
static void transmit_downward(uint16_t type, void *data)
|
||||
{
|
||||
if (type == MSG_STACK_TO_HC_HCI_CMD) {
|
||||
HCI_TRACE_ERROR("%s legacy transmit of command. Use transmit_command instead.\n", __func__);
|
||||
assert(0);
|
||||
} else {
|
||||
fixed_queue_enqueue(hci_host_env.packet_queue, data, FIXED_QUEUE_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
// Command/packet transmitting functions
|
||||
static void event_command_ready(fixed_pkt_queue_t *queue)
|
||||
{
|
||||
pkt_linked_item_t *wait_entry = NULL;
|
||||
command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q;
|
||||
|
||||
wait_entry = fixed_pkt_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
|
||||
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
|
||||
metadata->flags_vnd |= HCI_CMD_MSG_F_VND_SENT;
|
||||
metadata->flags_vnd &= ~HCI_CMD_MSG_F_VND_QUEUED;
|
||||
|
||||
if (metadata->flags_src & HCI_CMD_MSG_F_SRC_NOACK) {
|
||||
packet_fragmenter->fragment_and_dispatch(&metadata->command);
|
||||
hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func;
|
||||
free_func(wait_entry);
|
||||
return;
|
||||
}
|
||||
hci_host_env.command_credits--;
|
||||
// Move it to the list of commands awaiting response
|
||||
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
list_append(cmd_wait_q->commands_pending_response, wait_entry);
|
||||
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
|
||||
|
||||
// Send it off
|
||||
packet_fragmenter->fragment_and_dispatch(&metadata->command);
|
||||
|
||||
restart_command_waiting_response_timer(cmd_wait_q);
|
||||
}
|
||||
|
||||
static void event_packet_ready(fixed_queue_t *queue)
|
||||
{
|
||||
BT_HDR *packet = (BT_HDR *)fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
|
||||
// The queue may be the command queue or the packet queue, we don't care
|
||||
|
||||
packet_fragmenter->fragment_and_dispatch(packet);
|
||||
}
|
||||
|
||||
// Callback for the fragmenter to send a fragment
|
||||
static void transmit_fragment(BT_HDR *packet, bool send_transmit_finished)
|
||||
{
|
||||
uint16_t event = packet->event & MSG_EVT_MASK;
|
||||
serial_data_type_t type = event_to_data_type(event);
|
||||
|
||||
hal->transmit_data(type, packet->data + packet->offset, packet->len);
|
||||
|
||||
if (event != MSG_STACK_TO_HC_HCI_CMD && send_transmit_finished) {
|
||||
osi_free(packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void fragmenter_transmit_finished(BT_HDR *packet, bool all_fragments_sent)
|
||||
{
|
||||
if (all_fragments_sent) {
|
||||
osi_free(packet);
|
||||
} else {
|
||||
// This is kind of a weird case, since we're dispatching a partially sent packet
|
||||
// up to a higher layer.
|
||||
// TODO(zachoverflow): rework upper layer so this isn't necessary.
|
||||
//osi_free(packet);
|
||||
|
||||
/* dispatch_reassembled(packet) will send the packet back to the higher layer
|
||||
when controller buffer is not enough. hci will send the remain packet back
|
||||
to the l2cap layer and saved in the Link Queue (p_lcb->link_xmit_data_q).
|
||||
The l2cap layer will resend the packet to lower layer when controller buffer
|
||||
can be used.
|
||||
*/
|
||||
|
||||
dispatch_reassembled(packet);
|
||||
//data_dispatcher_dispatch(interface.event_dispatcher, packet->event & MSG_EVT_MASK, packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void restart_command_waiting_response_timer(command_waiting_response_t *cmd_wait_q)
|
||||
{
|
||||
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
if (cmd_wait_q->timer_is_set) {
|
||||
osi_alarm_cancel(cmd_wait_q->command_response_timer);
|
||||
cmd_wait_q->timer_is_set = false;
|
||||
}
|
||||
if (!list_is_empty(cmd_wait_q->commands_pending_response)) {
|
||||
osi_alarm_set(cmd_wait_q->command_response_timer, COMMAND_PENDING_TIMEOUT);
|
||||
cmd_wait_q->timer_is_set = true;
|
||||
}
|
||||
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
|
||||
}
|
||||
|
||||
static void command_timed_out(void *context)
|
||||
{
|
||||
command_waiting_response_t *cmd_wait_q = (command_waiting_response_t *)context;
|
||||
pkt_linked_item_t *wait_entry;
|
||||
|
||||
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
wait_entry = (list_is_empty(cmd_wait_q->commands_pending_response) ?
|
||||
NULL : list_front(cmd_wait_q->commands_pending_response));
|
||||
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
|
||||
|
||||
if (wait_entry == NULL) {
|
||||
HCI_TRACE_ERROR("%s with no commands pending response", __func__);
|
||||
} else
|
||||
// We shouldn't try to recover the stack from this command timeout.
|
||||
// If it's caused by a software bug, fix it. If it's a hardware bug, fix it.
|
||||
{
|
||||
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
|
||||
HCI_TRACE_ERROR("%s hci layer timeout waiting for response to a command. opcode: 0x%x", __func__, metadata->opcode);
|
||||
UNUSED(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
// Event/packet receiving functions
|
||||
static void hal_says_packet_ready(BT_HDR *packet)
|
||||
{
|
||||
if (packet->event != MSG_HC_TO_STACK_HCI_EVT) {
|
||||
packet_fragmenter->reassemble_and_dispatch(packet);
|
||||
} else if (!filter_incoming_event(packet)) {
|
||||
dispatch_reassembled(packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void hal_says_adv_rpt_ready(pkt_linked_item_t *linked_pkt)
|
||||
{
|
||||
dispatch_adv_report(linked_pkt);
|
||||
}
|
||||
|
||||
// Returns true if the event was intercepted and should not proceed to
|
||||
// higher layers. Also inspects an incoming event for interesting
|
||||
// information, like how many commands are now able to be sent.
|
||||
static bool filter_incoming_event(BT_HDR *packet)
|
||||
{
|
||||
pkt_linked_item_t *wait_entry = NULL;
|
||||
hci_cmd_metadata_t *metadata = NULL;
|
||||
uint8_t *stream = packet->data + packet->offset;
|
||||
uint8_t event_code;
|
||||
command_opcode_t opcode;
|
||||
|
||||
STREAM_TO_UINT8(event_code, stream);
|
||||
STREAM_SKIP_UINT8(stream); // Skip the parameter total length field
|
||||
|
||||
HCI_TRACE_DEBUG("Receive packet event_code=0x%x\n", event_code);
|
||||
|
||||
if (event_code == HCI_COMMAND_COMPLETE_EVT) {
|
||||
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
|
||||
STREAM_TO_UINT16(opcode, stream);
|
||||
wait_entry = get_waiting_command(opcode);
|
||||
metadata = (hci_cmd_metadata_t *)(wait_entry->data);
|
||||
if (!wait_entry) {
|
||||
HCI_TRACE_WARNING("%s command complete event with no matching command. opcode: 0x%x.", __func__, opcode);
|
||||
} else if (metadata->command_complete_cb) {
|
||||
metadata->command_complete_cb(packet, metadata->context);
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
BlE_SYNC *sync_info = btsnd_hcic_ble_get_sync_info();
|
||||
if(!sync_info) {
|
||||
HCI_TRACE_WARNING("%s sync_info is NULL. opcode = 0x%x", __func__, opcode);
|
||||
} else {
|
||||
if (sync_info->sync_sem && sync_info->opcode == opcode) {
|
||||
osi_sem_give(&sync_info->sync_sem);
|
||||
sync_info->opcode = 0;
|
||||
}
|
||||
}
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
} else if (metadata->flags_vnd & HCI_CMD_MSG_F_VND_FUTURE) {
|
||||
future_ready((future_t *)(metadata->complete_future), packet);
|
||||
}
|
||||
|
||||
goto intercepted;
|
||||
} else if (event_code == HCI_COMMAND_STATUS_EVT) {
|
||||
uint8_t status;
|
||||
STREAM_TO_UINT8(status, stream);
|
||||
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
|
||||
STREAM_TO_UINT16(opcode, stream);
|
||||
|
||||
// If a command generates a command status event, it won't be getting a command complete event
|
||||
|
||||
wait_entry = get_waiting_command(opcode);
|
||||
metadata = (hci_cmd_metadata_t *)(wait_entry->data);
|
||||
if (!wait_entry) {
|
||||
HCI_TRACE_WARNING("%s command status event with no matching command. opcode: 0x%x", __func__, opcode);
|
||||
} else if (metadata->command_status_cb) {
|
||||
metadata->command_status_cb(status, &metadata->command, metadata->context);
|
||||
}
|
||||
|
||||
goto intercepted;
|
||||
}
|
||||
|
||||
return false;
|
||||
intercepted:
|
||||
restart_command_waiting_response_timer(&hci_host_env.cmd_waiting_q);
|
||||
|
||||
/*Tell HCI Host Task to continue TX Pending commands*/
|
||||
if (hci_host_env.command_credits &&
|
||||
!fixed_pkt_queue_is_empty(hci_host_env.command_queue)) {
|
||||
hci_downstream_data_post(OSI_THREAD_MAX_TIMEOUT);
|
||||
}
|
||||
|
||||
if (wait_entry) {
|
||||
// If it has a callback, it's responsible for freeing the packet
|
||||
if (event_code == HCI_COMMAND_STATUS_EVT ||
|
||||
(!metadata->command_complete_cb && !metadata->complete_future)) {
|
||||
osi_free(packet);
|
||||
}
|
||||
|
||||
// If it has a callback, it's responsible for freeing the command
|
||||
if (event_code == HCI_COMMAND_COMPLETE_EVT || !metadata->command_status_cb) {
|
||||
hci_cmd_free_cb free_func = metadata->command_free_cb ? metadata->command_free_cb : (hci_cmd_free_cb) osi_free_func;
|
||||
free_func(wait_entry);
|
||||
}
|
||||
} else {
|
||||
osi_free(packet);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback for the fragmenter to dispatch up a completely reassembled packet
|
||||
static void dispatch_reassembled(BT_HDR *packet)
|
||||
{
|
||||
// Events should already have been dispatched before this point
|
||||
//Tell Up-layer received packet.
|
||||
if (btu_task_post(SIG_BTU_HCI_MSG, packet, OSI_THREAD_MAX_TIMEOUT) == false) {
|
||||
osi_free(packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatch_adv_report(pkt_linked_item_t *linked_pkt)
|
||||
{
|
||||
// Events should already have been dispatched before this point
|
||||
//Tell Up-layer received packet.
|
||||
if (btu_task_post(SIG_BTU_HCI_ADV_RPT_MSG, linked_pkt, OSI_THREAD_MAX_TIMEOUT) == false) {
|
||||
osi_free(linked_pkt);
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
hci_adv_credits_try_release(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// Misc internal functions
|
||||
|
||||
// TODO(zachoverflow): we seem to do this a couple places, like the HCI inject module. #centralize
|
||||
static serial_data_type_t event_to_data_type(uint16_t event)
|
||||
{
|
||||
if (event == MSG_STACK_TO_HC_HCI_ACL) {
|
||||
return DATA_TYPE_ACL;
|
||||
} else if (event == MSG_STACK_TO_HC_HCI_SCO) {
|
||||
return DATA_TYPE_SCO;
|
||||
} else if (event == MSG_STACK_TO_HC_HCI_CMD) {
|
||||
return DATA_TYPE_COMMAND;
|
||||
} else {
|
||||
HCI_TRACE_ERROR("%s invalid event type, could not translate 0x%x\n", __func__, event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pkt_linked_item_t *get_waiting_command(command_opcode_t opcode)
|
||||
{
|
||||
command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q;
|
||||
osi_mutex_lock(&cmd_wait_q->commands_pending_response_lock, OSI_MUTEX_MAX_TIMEOUT);
|
||||
|
||||
for (const list_node_t *node = list_begin(cmd_wait_q->commands_pending_response);
|
||||
node != list_end(cmd_wait_q->commands_pending_response);
|
||||
node = list_next(node)) {
|
||||
pkt_linked_item_t *wait_entry = list_node(node);
|
||||
if (wait_entry) {
|
||||
hci_cmd_metadata_t *metadata = (hci_cmd_metadata_t *)(wait_entry->data);
|
||||
if (metadata->opcode == opcode) {
|
||||
list_remove(cmd_wait_q->commands_pending_response, wait_entry);
|
||||
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
|
||||
return wait_entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osi_mutex_unlock(&cmd_wait_q->commands_pending_response_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void init_layer_interface(void)
|
||||
{
|
||||
if (!interface_created) {
|
||||
interface.transmit_command = transmit_command;
|
||||
interface.transmit_command_futured = transmit_command_futured;
|
||||
interface.transmit_downward = transmit_downward;
|
||||
interface_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
static const hci_hal_callbacks_t hal_callbacks = {
|
||||
hal_says_packet_ready,
|
||||
hal_says_adv_rpt_ready,
|
||||
};
|
||||
|
||||
static const packet_fragmenter_callbacks_t packet_fragmenter_callbacks = {
|
||||
transmit_fragment,
|
||||
dispatch_reassembled,
|
||||
fragmenter_transmit_finished
|
||||
};
|
||||
|
||||
const hci_t *hci_layer_get_interface(void)
|
||||
{
|
||||
hal = hci_hal_h4_get_interface();
|
||||
packet_fragmenter = packet_fragmenter_get_interface();
|
||||
|
||||
init_layer_interface();
|
||||
return &interface;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "common/bt_defs.h"
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "stack/hcidefs.h"
|
||||
#include "stack/hcimsgs.h"
|
||||
#include "hci/hci_internals.h"
|
||||
#include "hci/hci_layer.h"
|
||||
#include "hci/hci_packet_factory.h"
|
||||
|
||||
|
||||
static BT_HDR *make_command_no_params(uint16_t opcode);
|
||||
static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out);
|
||||
|
||||
// Interface functions
|
||||
|
||||
static BT_HDR *make_reset(void)
|
||||
{
|
||||
return make_command_no_params(HCI_RESET);
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_buffer_size(void)
|
||||
{
|
||||
return make_command_no_params(HCI_READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
static BT_HDR *make_set_c2h_flow_control(uint8_t enable)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_SET_HC_TO_HOST_FLOW_CTRL, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, enable);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_set_adv_report_flow_control(uint8_t enable, uint16_t num, uint16_t lost_threshold)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1 + 2 + 2;
|
||||
BT_HDR *packet = make_command(HCI_VENDOR_BLE_SET_ADV_FLOW_CONTROL, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, enable);
|
||||
UINT16_TO_STREAM(stream, num);
|
||||
UINT16_TO_STREAM(stream, lost_threshold);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_host_buffer_size(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 2 + 1 + 2 + 2; // from each of the parameters
|
||||
BT_HDR *packet = make_command(HCI_HOST_BUFFER_SIZE, parameter_size, &stream);
|
||||
|
||||
UINT16_TO_STREAM(stream, acl_size);
|
||||
UINT8_TO_STREAM(stream, sco_size);
|
||||
UINT16_TO_STREAM(stream, acl_count);
|
||||
UINT16_TO_STREAM(stream, sco_count);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_local_version_info(void)
|
||||
{
|
||||
return make_command_no_params(HCI_READ_LOCAL_VERSION_INFO);
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_bd_addr(void)
|
||||
{
|
||||
return make_command_no_params(HCI_READ_BD_ADDR);
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_local_supported_commands(void)
|
||||
{
|
||||
return make_command_no_params(HCI_READ_LOCAL_SUPPORTED_CMDS);
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_local_supported_features(void)
|
||||
{
|
||||
return make_command_no_params(HCI_READ_LOCAL_FEATURES);
|
||||
}
|
||||
|
||||
static BT_HDR *make_read_local_extended_features(uint8_t page_number)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_READ_LOCAL_EXT_FEATURES, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, page_number);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_write_simple_pairing_mode(uint8_t mode)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_WRITE_SIMPLE_PAIRING_MODE, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, mode);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_write_secure_connections_host_support(uint8_t mode)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_WRITE_SECURE_CONNS_SUPPORT, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, mode);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_set_event_mask(const bt_event_mask_t *event_mask)
|
||||
{
|
||||
uint8_t *stream;
|
||||
uint8_t parameter_size = sizeof(bt_event_mask_t);
|
||||
BT_HDR *packet = make_command(HCI_SET_EVENT_MASK, parameter_size, &stream);
|
||||
|
||||
ARRAY8_TO_STREAM(stream, event_mask->as_array);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_write_host_support(uint8_t supported_host, uint8_t simultaneous_host)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1 + 1;
|
||||
BT_HDR *packet = make_command(HCI_WRITE_LE_HOST_SUPPORT, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, supported_host);
|
||||
UINT8_TO_STREAM(stream, simultaneous_host);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_white_list_size(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_WHITE_LIST_SIZE);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_buffer_size(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_supported_states(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_SUPPORTED_STATES);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_local_supported_features(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_LOCAL_SPT_FEAT);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_resolving_list_size(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_RESOLVING_LIST_SIZE);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_read_suggested_default_data_length(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_READ_DEFAULT_DATA_LENGTH);
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_write_suggested_default_data_length(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime)
|
||||
{
|
||||
uint8_t *stream;
|
||||
uint8_t parameter_size = sizeof(uint16_t) + sizeof(uint16_t);
|
||||
BT_HDR *packet = make_command(HCI_BLE_WRITE_DEFAULT_DATA_LENGTH, parameter_size, &stream);
|
||||
|
||||
UINT16_TO_STREAM(stream, SuggestedMaxTxOctets);
|
||||
UINT16_TO_STREAM(stream, SuggestedMaxTxTime);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_ble_set_event_mask(const bt_event_mask_t *event_mask)
|
||||
{
|
||||
uint8_t *stream;
|
||||
uint8_t parameter_size = sizeof(bt_event_mask_t);
|
||||
BT_HDR *packet = make_command(HCI_BLE_SET_EVENT_MASK, parameter_size, &stream);
|
||||
|
||||
ARRAY8_TO_STREAM(stream, event_mask->as_array);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_write_sync_flow_control_enable(uint8_t enable)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_WRITE_SCO_FLOW_CTRL_ENABLE, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, enable);
|
||||
return packet;
|
||||
}
|
||||
|
||||
static BT_HDR *make_write_default_erroneous_data_report(uint8_t enable)
|
||||
{
|
||||
uint8_t *stream;
|
||||
const uint8_t parameter_size = 1;
|
||||
BT_HDR *packet = make_command(HCI_WRITE_ERRONEOUS_DATA_RPT, parameter_size, &stream);
|
||||
|
||||
UINT8_TO_STREAM(stream, enable);
|
||||
return packet;
|
||||
}
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
static BT_HDR *make_read_max_adv_data_len(void)
|
||||
{
|
||||
return make_command_no_params(HCI_BLE_RD_MAX_ADV_DATA_LEN);
|
||||
}
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
// Internal functions
|
||||
|
||||
static BT_HDR *make_command_no_params(uint16_t opcode)
|
||||
{
|
||||
return make_command(opcode, 0, NULL);
|
||||
}
|
||||
|
||||
static BT_HDR *make_command(uint16_t opcode, size_t parameter_size, uint8_t **stream_out)
|
||||
{
|
||||
BT_HDR *packet = HCI_GET_CMD_BUF(parameter_size);
|
||||
hci_cmd_metadata_t *metadata = HCI_GET_CMD_METAMSG(packet);
|
||||
metadata->opcode = opcode;
|
||||
|
||||
uint8_t *stream = packet->data;
|
||||
UINT16_TO_STREAM(stream, opcode);
|
||||
UINT8_TO_STREAM(stream, parameter_size);
|
||||
|
||||
if (stream_out != NULL) {
|
||||
*stream_out = stream;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
static const hci_packet_factory_t interface = {
|
||||
make_reset,
|
||||
make_read_buffer_size,
|
||||
make_set_c2h_flow_control,
|
||||
make_set_adv_report_flow_control,
|
||||
make_host_buffer_size,
|
||||
make_read_local_version_info,
|
||||
make_read_bd_addr,
|
||||
make_read_local_supported_commands,
|
||||
make_read_local_supported_features,
|
||||
make_read_local_extended_features,
|
||||
make_write_simple_pairing_mode,
|
||||
make_write_secure_connections_host_support,
|
||||
make_set_event_mask,
|
||||
make_ble_write_host_support,
|
||||
make_ble_read_white_list_size,
|
||||
make_ble_read_buffer_size,
|
||||
make_ble_read_supported_states,
|
||||
make_ble_read_local_supported_features,
|
||||
make_ble_read_resolving_list_size,
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
make_read_max_adv_data_len,
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
make_ble_read_suggested_default_data_length,
|
||||
make_ble_write_suggested_default_data_length,
|
||||
make_ble_set_event_mask,
|
||||
make_write_sync_flow_control_enable,
|
||||
make_write_default_erroneous_data_report,
|
||||
};
|
||||
|
||||
const hci_packet_factory_t *hci_packet_factory_get_interface(void)
|
||||
{
|
||||
return &interface;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "common/bt_defs.h"
|
||||
|
||||
#include "stack/bt_types.h"
|
||||
#include "stack/hcimsgs.h"
|
||||
#include "hci/hci_layer.h"
|
||||
#include "hci/hci_packet_parser.h"
|
||||
|
||||
static const command_opcode_t NO_OPCODE_CHECKING = 0;
|
||||
|
||||
static uint8_t *read_command_complete_header(
|
||||
BT_HDR *response,
|
||||
command_opcode_t expected_opcode,
|
||||
size_t minimum_bytes_after);
|
||||
|
||||
static void parse_generic_command_complete(BT_HDR *response)
|
||||
{
|
||||
read_command_complete_header(response, NO_OPCODE_CHECKING, 0 /* bytes after */);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_buffer_size_response(
|
||||
BT_HDR *response,
|
||||
uint16_t *acl_data_size_ptr,
|
||||
uint16_t *acl_buffer_count_ptr,
|
||||
uint8_t *sco_data_size_ptr,
|
||||
uint16_t *sco_buffer_count_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_BUFFER_SIZE, 7 /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_UINT16(*acl_data_size_ptr, stream);
|
||||
STREAM_TO_UINT8(*sco_data_size_ptr, stream);
|
||||
STREAM_TO_UINT16(*acl_buffer_count_ptr, stream);
|
||||
STREAM_TO_UINT16(*sco_buffer_count_ptr, stream);
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_local_version_info_response(
|
||||
BT_HDR *response,
|
||||
bt_version_t *bt_version)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_VERSION_INFO, 8 /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_UINT8(bt_version->hci_version, stream);
|
||||
STREAM_TO_UINT16(bt_version->hci_revision, stream);
|
||||
STREAM_TO_UINT8(bt_version->lmp_version, stream);
|
||||
STREAM_TO_UINT16(bt_version->manufacturer, stream);
|
||||
STREAM_TO_UINT16(bt_version->lmp_subversion, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_bd_addr_response(
|
||||
BT_HDR *response,
|
||||
bt_bdaddr_t *address_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_BD_ADDR, sizeof(bt_bdaddr_t) /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_BDADDR(address_ptr->address, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_local_supported_commands_response(
|
||||
BT_HDR *response,
|
||||
uint8_t *supported_commands_ptr,
|
||||
size_t supported_commands_length)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_SUPPORTED_CMDS, supported_commands_length /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_ARRAY(supported_commands_ptr, stream, (int)supported_commands_length);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_local_supported_features_response(
|
||||
BT_HDR *response,
|
||||
bt_device_features_t *feature_pages)
|
||||
{
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_FEATURES, sizeof(bt_device_features_t) /* bytes after */);
|
||||
if (stream != NULL) {
|
||||
STREAM_TO_ARRAY(feature_pages->as_array, stream, (int)sizeof(bt_device_features_t));
|
||||
}
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_read_local_extended_features_response(
|
||||
BT_HDR *response,
|
||||
uint8_t *page_number_ptr,
|
||||
uint8_t *max_page_number_ptr,
|
||||
bt_device_features_t *feature_pages,
|
||||
size_t feature_pages_count)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_READ_LOCAL_EXT_FEATURES, 2 + sizeof(bt_device_features_t) /* bytes after */);
|
||||
if (stream != NULL) {
|
||||
STREAM_TO_UINT8(*page_number_ptr, stream);
|
||||
STREAM_TO_UINT8(*max_page_number_ptr, stream);
|
||||
|
||||
assert(*page_number_ptr < feature_pages_count);
|
||||
STREAM_TO_ARRAY(feature_pages[*page_number_ptr].as_array, stream, (int)sizeof(bt_device_features_t));
|
||||
} else {
|
||||
HCI_TRACE_ERROR("%s() - WARNING: READING EXTENDED FEATURES FAILED. "
|
||||
"THIS MAY INDICATE A FIRMWARE/CONTROLLER ISSUE.", __func__);
|
||||
}
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_white_list_size_response(
|
||||
BT_HDR *response,
|
||||
uint8_t *white_list_size_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_WHITE_LIST_SIZE, 1 /* byte after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_UINT8(*white_list_size_ptr, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_buffer_size_response(
|
||||
BT_HDR *response,
|
||||
uint16_t *data_size_ptr,
|
||||
uint8_t *acl_buffer_count_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_BUFFER_SIZE, 3 /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_UINT16(*data_size_ptr, stream);
|
||||
STREAM_TO_UINT8(*acl_buffer_count_ptr, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_supported_states_response(
|
||||
BT_HDR *response,
|
||||
uint8_t *supported_states,
|
||||
size_t supported_states_size)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_SUPPORTED_STATES, supported_states_size /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_ARRAY(supported_states, stream, (int)supported_states_size);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_local_supported_features_response(
|
||||
BT_HDR *response,
|
||||
bt_device_features_t *supported_features)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_LOCAL_SPT_FEAT, sizeof(bt_device_features_t) /* bytes after */);
|
||||
assert(stream != NULL);
|
||||
STREAM_TO_ARRAY(supported_features->as_array, stream, (int)sizeof(bt_device_features_t));
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_resolving_list_size_response(
|
||||
BT_HDR *response,
|
||||
uint8_t *resolving_list_size_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_RESOLVING_LIST_SIZE, 1 /* bytes after */);
|
||||
STREAM_TO_UINT8(*resolving_list_size_ptr, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
|
||||
static void parse_ble_read_suggested_default_data_length_response(
|
||||
BT_HDR *response,
|
||||
uint16_t *ble_default_packet_length_ptr,
|
||||
uint16_t *ble_default_packet_txtime_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_DEFAULT_DATA_LENGTH, 2 /* bytes after */);
|
||||
STREAM_TO_UINT16(*ble_default_packet_length_ptr, stream);
|
||||
STREAM_TO_UINT16(*ble_default_packet_txtime_ptr, stream);
|
||||
osi_free(response);
|
||||
}
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
static void parse_ble_read_adv_max_len_response(
|
||||
BT_HDR *response,
|
||||
uint16_t *adv_max_len_ptr)
|
||||
{
|
||||
|
||||
uint8_t *stream = read_command_complete_header(response, HCI_BLE_RD_MAX_ADV_DATA_LEN, 1 /* bytes after */);
|
||||
// Size: 2 Octets ; Value: 0x001F – 0x0672 ; Maximum supported advertising data length
|
||||
STREAM_TO_UINT16(*adv_max_len_ptr, stream);
|
||||
|
||||
osi_free(response);
|
||||
}
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
|
||||
|
||||
// Internal functions
|
||||
|
||||
static uint8_t *read_command_complete_header(
|
||||
BT_HDR *response,
|
||||
command_opcode_t expected_opcode,
|
||||
size_t minimum_bytes_after)
|
||||
{
|
||||
|
||||
uint8_t *stream = response->data + response->offset;
|
||||
|
||||
// Read the event header
|
||||
uint8_t event_code __attribute__((unused));
|
||||
uint8_t parameter_length __attribute__((unused));
|
||||
STREAM_TO_UINT8(event_code, stream);
|
||||
STREAM_TO_UINT8(parameter_length, stream);
|
||||
|
||||
const size_t parameter_bytes_we_read_here __attribute__((unused)) = 4;
|
||||
|
||||
// Check the event header values against what we expect
|
||||
assert(event_code == HCI_COMMAND_COMPLETE_EVT);
|
||||
assert(parameter_length >= (parameter_bytes_we_read_here + minimum_bytes_after));
|
||||
|
||||
// Read the command complete header
|
||||
command_opcode_t opcode __attribute__((unused));
|
||||
uint8_t status;
|
||||
STREAM_SKIP_UINT8(stream); // skip the number of hci command packets field
|
||||
STREAM_TO_UINT16(opcode, stream);
|
||||
|
||||
// Check the command complete header values against what we expect
|
||||
if (expected_opcode != NO_OPCODE_CHECKING) {
|
||||
assert(opcode == expected_opcode);
|
||||
}
|
||||
|
||||
// Assume the next field is the status field
|
||||
STREAM_TO_UINT8(status, stream);
|
||||
|
||||
if (status != HCI_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static const hci_packet_parser_t interface = {
|
||||
parse_generic_command_complete,
|
||||
parse_read_buffer_size_response,
|
||||
parse_read_local_version_info_response,
|
||||
parse_read_bd_addr_response,
|
||||
parse_read_local_supported_commands_response,
|
||||
parse_read_local_supported_features_response,
|
||||
parse_read_local_extended_features_response,
|
||||
parse_ble_read_white_list_size_response,
|
||||
parse_ble_read_buffer_size_response,
|
||||
parse_ble_read_supported_states_response,
|
||||
parse_ble_read_local_supported_features_response,
|
||||
parse_ble_read_resolving_list_size_response,
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
parse_ble_read_adv_max_len_response,
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
parse_ble_read_suggested_default_data_length_response
|
||||
};
|
||||
|
||||
const hci_packet_parser_t *hci_packet_parser_get_interface(void)
|
||||
{
|
||||
return &interface;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef BT_VENDOR_LIB_H
|
||||
#define BT_VENDOR_LIB_H
|
||||
|
||||
#include <stdint.h>
|
||||
//#include <sys/cdefs.h>
|
||||
//#include <sys/types.h>
|
||||
|
||||
/** Struct types */
|
||||
|
||||
|
||||
/** Typedefs and defines */
|
||||
|
||||
/** Vendor specific operations OPCODE */
|
||||
typedef enum {
|
||||
/* [operation]
|
||||
* Power on or off the BT Controller.
|
||||
* [input param]
|
||||
* A pointer to int type with content of bt_vendor_power_state_t.
|
||||
* Typecasting conversion: (int *) param.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_POWER_CTRL,
|
||||
|
||||
/* [operation]
|
||||
* Perform any vendor specific initialization or configuration
|
||||
* on the BT Controller. This is called before stack initialization.
|
||||
* [input param]
|
||||
* None.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* Must call fwcfg_cb to notify the stack of the completion of vendor
|
||||
* specific initialization once it has been done.
|
||||
*/
|
||||
BT_VND_OP_FW_CFG,
|
||||
|
||||
/* [operation]
|
||||
* Perform any vendor specific SCO/PCM configuration on the BT Controller.
|
||||
* This is called after stack initialization.
|
||||
* [input param]
|
||||
* None.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* Must call scocfg_cb to notify the stack of the completion of vendor
|
||||
* specific SCO configuration once it has been done.
|
||||
*/
|
||||
BT_VND_OP_SCO_CFG,
|
||||
|
||||
/* [operation]
|
||||
* Open UART port on where the BT Controller is attached.
|
||||
* This is called before stack initialization.
|
||||
* [input param]
|
||||
* A pointer to int array type for open file descriptors.
|
||||
* The mapping of HCI channel to fd slot in the int array is given in
|
||||
* bt_vendor_hci_channels_t.
|
||||
* And, it requires the vendor lib to fill up the content before returning
|
||||
* the call.
|
||||
* Typecasting conversion: (int (*)[]) param.
|
||||
* [return]
|
||||
* Numbers of opened file descriptors.
|
||||
* Valid number:
|
||||
* 1 - CMD/EVT/ACL-In/ACL-Out via the same fd (e.g. UART)
|
||||
* 2 - CMD/EVT on one fd, and ACL-In/ACL-Out on the other fd
|
||||
* 4 - CMD, EVT, ACL-In, ACL-Out are on their individual fd
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_USERIAL_OPEN,
|
||||
|
||||
/* [operation]
|
||||
* Close the previously opened UART port.
|
||||
* [input param]
|
||||
* None.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_USERIAL_CLOSE,
|
||||
|
||||
/* [operation]
|
||||
* Get the LPM idle timeout in milliseconds.
|
||||
* The stack uses this information to launch a timer delay before it
|
||||
* attempts to de-assert LPM WAKE signal once downstream HCI packet
|
||||
* has been delivered.
|
||||
* [input param]
|
||||
* A pointer to uint32_t type which is passed in by the stack. And, it
|
||||
* requires the vendor lib to fill up the content before returning
|
||||
* the call.
|
||||
* Typecasting conversion: (uint32_t *) param.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_GET_LPM_IDLE_TIMEOUT,
|
||||
|
||||
/* [operation]
|
||||
* Enable or disable LPM mode on BT Controller.
|
||||
* [input param]
|
||||
* A pointer to uint8_t type with content of bt_vendor_lpm_mode_t.
|
||||
* Typecasting conversion: (uint8_t *) param.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* Must call lpm_cb to notify the stack of the completion of LPM
|
||||
* disable/enable process once it has been done.
|
||||
*/
|
||||
BT_VND_OP_LPM_SET_MODE,
|
||||
|
||||
/* [operation]
|
||||
* Assert or Deassert LPM WAKE on BT Controller.
|
||||
* [input param]
|
||||
* A pointer to uint8_t type with content of bt_vendor_lpm_wake_state_t.
|
||||
* Typecasting conversion: (uint8_t *) param.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_LPM_WAKE_SET_STATE,
|
||||
|
||||
/* [operation]
|
||||
* Perform any vendor specific commands related to audio state changes.
|
||||
* [input param]
|
||||
* a pointer to bt_vendor_op_audio_state_t indicating what audio state is
|
||||
* set.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* None.
|
||||
*/
|
||||
BT_VND_OP_SET_AUDIO_STATE,
|
||||
|
||||
/* [operation]
|
||||
* The epilog call to the vendor module so that it can perform any
|
||||
* vendor-specific processes (e.g. send a HCI_RESET to BT Controller)
|
||||
* before the caller calls for cleanup().
|
||||
* [input param]
|
||||
* None.
|
||||
* [return]
|
||||
* 0 - default, don't care.
|
||||
* [callback]
|
||||
* Must call epilog_cb to notify the stack of the completion of vendor
|
||||
* specific epilog process once it has been done.
|
||||
*/
|
||||
BT_VND_OP_EPILOG,
|
||||
} bt_vendor_opcode_t;
|
||||
|
||||
/** Power on/off control states */
|
||||
typedef enum {
|
||||
BT_VND_PWR_OFF,
|
||||
BT_VND_PWR_ON,
|
||||
} bt_vendor_power_state_t;
|
||||
|
||||
/** Define HCI channel identifier in the file descriptors array
|
||||
used in BT_VND_OP_USERIAL_OPEN operation.
|
||||
*/
|
||||
typedef enum {
|
||||
CH_CMD, // HCI Command channel
|
||||
CH_EVT, // HCI Event channel
|
||||
CH_ACL_OUT, // HCI ACL downstream channel
|
||||
CH_ACL_IN, // HCI ACL upstream channel
|
||||
|
||||
CH_MAX // Total channels
|
||||
} bt_vendor_hci_channels_t;
|
||||
|
||||
/** LPM disable/enable request */
|
||||
typedef enum {
|
||||
BT_VND_LPM_DISABLE,
|
||||
BT_VND_LPM_ENABLE,
|
||||
} bt_vendor_lpm_mode_t;
|
||||
|
||||
/** LPM WAKE set state request */
|
||||
typedef enum {
|
||||
BT_VND_LPM_WAKE_ASSERT,
|
||||
BT_VND_LPM_WAKE_DEASSERT,
|
||||
} bt_vendor_lpm_wake_state_t;
|
||||
|
||||
/** Callback result values */
|
||||
typedef enum {
|
||||
BT_VND_OP_RESULT_SUCCESS,
|
||||
BT_VND_OP_RESULT_FAIL,
|
||||
} bt_vendor_op_result_t;
|
||||
|
||||
/** audio (SCO) state changes triggering VS commands for configuration */
|
||||
typedef struct {
|
||||
uint16_t handle;
|
||||
uint16_t peer_codec;
|
||||
uint16_t state;
|
||||
} bt_vendor_op_audio_state_t;
|
||||
|
||||
/*
|
||||
* Bluetooth Host/Controller Vendor callback structure.
|
||||
*/
|
||||
|
||||
/* vendor initialization/configuration callback */
|
||||
typedef void (*cfg_result_cb)(bt_vendor_op_result_t result);
|
||||
|
||||
/* datapath buffer allocation callback (callout)
|
||||
*
|
||||
* Vendor lib needs to request a buffer through the alloc callout function
|
||||
* from HCI lib if the buffer is for constructing a HCI Command packet which
|
||||
* will be sent through xmit_cb to BT Controller.
|
||||
*
|
||||
* For each buffer allocation, the requested size needs to be big enough to
|
||||
* accommodate the below header plus a complete HCI packet --
|
||||
* typedef struct
|
||||
* {
|
||||
* uint16_t event;
|
||||
* uint16_t len;
|
||||
* uint16_t offset;
|
||||
* uint16_t layer_specific;
|
||||
* } HC_BT_HDR;
|
||||
*
|
||||
* HCI lib returns a pointer to the buffer where Vendor lib should use to
|
||||
* construct a HCI command packet as below format:
|
||||
*
|
||||
* --------------------------------------------
|
||||
* | HC_BT_HDR | HCI command |
|
||||
* --------------------------------------------
|
||||
* where
|
||||
* HC_BT_HDR.event = 0x2000;
|
||||
* HC_BT_HDR.len = Length of HCI command;
|
||||
* HC_BT_HDR.offset = 0;
|
||||
* HC_BT_HDR.layer_specific = 0;
|
||||
*
|
||||
* For example, a HCI_RESET Command will be formed as
|
||||
* ------------------------
|
||||
* | HC_BT_HDR |03|0c|00|
|
||||
* ------------------------
|
||||
* with
|
||||
* HC_BT_HDR.event = 0x2000;
|
||||
* HC_BT_HDR.len = 3;
|
||||
* HC_BT_HDR.offset = 0;
|
||||
* HC_BT_HDR.layer_specific = 0;
|
||||
*/
|
||||
typedef void *(*malloc_cb)(int size);
|
||||
|
||||
/* datapath buffer deallocation callback (callout) */
|
||||
typedef void (*mdealloc_cb)(void *p_buf);
|
||||
|
||||
/* define callback of the cmd_xmit_cb
|
||||
*
|
||||
* The callback function which HCI lib will call with the return of command
|
||||
* complete packet. Vendor lib is responsible for releasing the buffer passed
|
||||
* in at the p_mem parameter by calling dealloc callout function.
|
||||
*/
|
||||
typedef void (*tINT_CMD_CBACK)(void *p_mem);
|
||||
|
||||
/* hci command packet transmit callback (callout)
|
||||
*
|
||||
* Vendor lib calls xmit_cb callout function in order to send a HCI Command
|
||||
* packet to BT Controller. The buffer carrying HCI Command packet content
|
||||
* needs to be first allocated through the alloc callout function.
|
||||
* HCI lib will release the buffer for Vendor lib once it has delivered the
|
||||
* packet content to BT Controller.
|
||||
*
|
||||
* Vendor lib needs also provide a callback function (p_cback) which HCI lib
|
||||
* will call with the return of command complete packet.
|
||||
*
|
||||
* The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
|
||||
* HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
|
||||
* packet.
|
||||
*/
|
||||
typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void *p_buf, tINT_CMD_CBACK p_cback);
|
||||
|
||||
typedef struct {
|
||||
/** set to sizeof(bt_vendor_callbacks_t) */
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* Callback and callout functions have implemented in HCI libray
|
||||
* (libbt-hci.so).
|
||||
*/
|
||||
|
||||
/* notifies caller result of firmware configuration request */
|
||||
cfg_result_cb fwcfg_cb;
|
||||
|
||||
/* notifies caller result of sco configuration request */
|
||||
cfg_result_cb scocfg_cb;
|
||||
|
||||
/* notifies caller result of lpm enable/disable */
|
||||
cfg_result_cb lpm_cb;
|
||||
|
||||
/* notifies the result of codec setting */
|
||||
cfg_result_cb audio_state_cb;
|
||||
|
||||
/* buffer allocation request */
|
||||
malloc_cb alloc;
|
||||
|
||||
/* buffer deallocation request */
|
||||
mdealloc_cb dealloc;
|
||||
|
||||
/* hci command packet transmit request */
|
||||
cmd_xmit_cb xmit_cb;
|
||||
|
||||
/* notifies caller completion of epilog process */
|
||||
cfg_result_cb epilog_cb;
|
||||
} bt_vendor_callbacks_t;
|
||||
|
||||
/*
|
||||
* Bluetooth Host/Controller VENDOR Interface
|
||||
*/
|
||||
typedef struct {
|
||||
/** Set to sizeof(bt_vndor_interface_t) */
|
||||
size_t size;
|
||||
|
||||
/*
|
||||
* Functions need to be implemented in Vendor libray (libbt-vendor.so).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Caller will open the interface and pass in the callback routines
|
||||
* to the implemenation of this interface.
|
||||
*/
|
||||
int (*init)(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr);
|
||||
|
||||
/** Vendor specific operations */
|
||||
int (*op)(bt_vendor_opcode_t opcode, void *param);
|
||||
|
||||
/** Closes the interface */
|
||||
void (*cleanup)(void);
|
||||
} bt_vendor_interface_t;
|
||||
|
||||
|
||||
/*
|
||||
* External shared lib functions/data
|
||||
*/
|
||||
|
||||
/* Entry point of DLib --
|
||||
* Vendor library needs to implement the body of bt_vendor_interface_t
|
||||
* structure and uses the below name as the variable name. HCI library
|
||||
* will use this symbol name to get address of the object through the
|
||||
* dlsym call.
|
||||
*/
|
||||
//extern const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE;
|
||||
|
||||
#endif /* BT_VENDOR_LIB_H */
|
||||
@@ -0,0 +1,42 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_AUDIO_H_
|
||||
#define _HCI_AUDIO_H_
|
||||
#include <stdint.h>
|
||||
|
||||
// Audio state definitions.
|
||||
typedef enum {
|
||||
SCO_STATE_OFF = 0, // Audio is off.
|
||||
SCO_STATE_OFF_TRANSFER, // Closed pending final transfer of audio.
|
||||
SCO_STATE_ON, // Audio is on.
|
||||
SCO_STATE_SETUP, // Open pending completion of audio setup.
|
||||
} sco_state_t;
|
||||
|
||||
// Codec type definitions.
|
||||
typedef enum {
|
||||
SCO_CODEC_NONE = 0x0000,
|
||||
SCO_CODEC_CVSD = 0x0001,
|
||||
SCO_CODEC_MSBC = 0x0002,
|
||||
} sco_codec_t;
|
||||
|
||||
// Set the audio state on the controller for SCO (PCM, WBS, ...) using the
|
||||
// vendor library.
|
||||
void set_audio_state(uint16_t handle, sco_codec_t codec, sco_state_t state);
|
||||
|
||||
#endif /* _HCI_AUDIO_H_ */
|
||||
@@ -0,0 +1,96 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_HAL_H_
|
||||
#define _HCI_HAL_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "common/bt_target.h"
|
||||
#include "osi/pkt_queue.h"
|
||||
#include "stack/bt_types.h"
|
||||
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
|
||||
#include "os/os_mbuf.h"
|
||||
#endif
|
||||
typedef enum {
|
||||
DATA_TYPE_COMMAND = 1,
|
||||
DATA_TYPE_ACL = 2,
|
||||
DATA_TYPE_SCO = 3,
|
||||
DATA_TYPE_EVENT = 4
|
||||
} serial_data_type_t;
|
||||
|
||||
typedef void (*packet_ready_cb)(BT_HDR *packet);
|
||||
typedef void (*adv_rpt_ready_cb)(pkt_linked_item_t *linked_pkt);
|
||||
|
||||
typedef struct {
|
||||
// Called when the HAL detects inbound data.
|
||||
// Data |type| may be ACL, SCO, or EVENT.
|
||||
// Executes in the context of the thread supplied to |init|.
|
||||
packet_ready_cb packet_ready;
|
||||
adv_rpt_ready_cb adv_rpt_ready;
|
||||
|
||||
/*
|
||||
// Called when the HAL detects inbound astronauts named Dave.
|
||||
// HAL will deny all requests to open the pod bay doors after this.
|
||||
dave_ready_cb dave_ready;
|
||||
*/
|
||||
} hci_hal_callbacks_t;
|
||||
|
||||
typedef struct hci_hal_t {
|
||||
// Initialize the HAL, with |upper_callbacks| and |upper_thread| to run in the context of.
|
||||
//bool (*init)(const hci_hal_callbacks_t *upper_callbacks);
|
||||
|
||||
// Connect to the underlying hardware, and let data start flowing.
|
||||
bool (*open)(const hci_hal_callbacks_t *upper_callbacks, void *task_thread);
|
||||
// Disconnect from the underlying hardware, and close the HAL.
|
||||
// "Daisy, Daisy..."
|
||||
void (*close)(void);
|
||||
|
||||
// Retrieve up to |max_size| bytes for ACL, SCO, or EVENT data packets into
|
||||
// |buffer|, blocking until max_size bytes read if |block| is true.
|
||||
// Only guaranteed to be correct in the context of a data_ready callback
|
||||
// of the corresponding type.
|
||||
//size_t (*read_data)(serial_data_type_t type, uint8_t *buffer, size_t max_size);
|
||||
// The upper layer must call this to notify the HAL that it has finished
|
||||
// reading a packet of the specified |type|. Underlying implementations that
|
||||
// use shared channels for multiple data types depend on this to know when
|
||||
// to reinterpret the data stream.
|
||||
//void (*packet_finished)(serial_data_type_t type);
|
||||
// Transmit COMMAND, ACL, or SCO data packets.
|
||||
// |data| may not be NULL. |length| must be greater than zero.
|
||||
//
|
||||
// IMPORTANT NOTE:
|
||||
// Depending on the underlying implementation, the byte right
|
||||
// before the beginning of |data| may be borrowed during this call
|
||||
// and then restored to its original value.
|
||||
// This is safe in the bluetooth context, because there is always a buffer
|
||||
// header that prefixes data you're sending.
|
||||
uint16_t (*transmit_data)(serial_data_type_t type, uint8_t *data, uint16_t length);
|
||||
} hci_hal_t;
|
||||
|
||||
|
||||
// Gets the correct hal implementation, as compiled for.
|
||||
const hci_hal_t *hci_hal_h4_get_interface(void);
|
||||
#if ((BT_CONTROLLER_INCLUDED == TRUE) && SOC_ESP_NIMBLE_CONTROLLER)
|
||||
int ble_hs_hci_rx_evt(uint8_t *hci_ev, void *arg);
|
||||
|
||||
int ble_hs_rx_data(struct os_mbuf *om, void *arg);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _HCI_HAL_H */
|
||||
@@ -0,0 +1,31 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_INTERNALS_H_
|
||||
#define _HCI_INTERNALS_H_
|
||||
|
||||
// 2 bytes for opcode, 1 byte for parameter length (Volume 2, Part E, 5.4.1)
|
||||
#define HCI_COMMAND_PREAMBLE_SIZE 3
|
||||
// 2 bytes for handle, 2 bytes for data length (Volume 2, Part E, 5.4.2)
|
||||
#define HCI_ACL_PREAMBLE_SIZE 4
|
||||
// 2 bytes for handle, 1 byte for data length (Volume 2, Part E, 5.4.3)
|
||||
#define HCI_SCO_PREAMBLE_SIZE 3
|
||||
// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
|
||||
#define HCI_EVENT_PREAMBLE_SIZE 2
|
||||
|
||||
#endif /* _HCI_INTERNALS_H_ */
|
||||
@@ -0,0 +1,113 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_LAYER_H_
|
||||
#define _HCI_LAYER_H_
|
||||
|
||||
#include "common/bt_target.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/osi.h"
|
||||
#include "osi/future.h"
|
||||
#include "osi/thread.h"
|
||||
#include "osi/pkt_queue.h"
|
||||
|
||||
///// LEGACY DEFINITIONS /////
|
||||
|
||||
/* Message event mask across Host/Controller lib and stack */
|
||||
#define MSG_EVT_MASK 0xFF00 /* eq. BT_EVT_MASK */
|
||||
#define MSG_SUB_EVT_MASK 0x00FF /* eq. BT_SUB_EVT_MASK */
|
||||
|
||||
/* Message event ID passed from Host/Controller lib to stack */
|
||||
#define MSG_HC_TO_STACK_HCI_ERR 0x1300 /* eq. BT_EVT_TO_BTU_HCIT_ERR */
|
||||
#define MSG_HC_TO_STACK_HCI_ACL 0x1100 /* eq. BT_EVT_TO_BTU_HCI_ACL */
|
||||
#define MSG_HC_TO_STACK_HCI_SCO 0x1200 /* eq. BT_EVT_TO_BTU_HCI_SCO */
|
||||
#define MSG_HC_TO_STACK_HCI_EVT 0x1000 /* eq. BT_EVT_TO_BTU_HCI_EVT */
|
||||
#define MSG_HC_TO_STACK_L2C_SEG_XMIT 0x1900 /* eq. BT_EVT_TO_BTU_L2C_SEG_XMIT */
|
||||
|
||||
/* Message event ID passed from stack to vendor lib */
|
||||
#define MSG_STACK_TO_HC_HCI_ACL 0x2100 /* eq. BT_EVT_TO_LM_HCI_ACL */
|
||||
#define MSG_STACK_TO_HC_HCI_SCO 0x2200 /* eq. BT_EVT_TO_LM_HCI_SCO */
|
||||
#define MSG_STACK_TO_HC_HCI_CMD 0x2000 /* eq. BT_EVT_TO_LM_HCI_CMD */
|
||||
|
||||
/* Local Bluetooth Controller ID for BR/EDR */
|
||||
#define LOCAL_BR_EDR_CONTROLLER_ID 0
|
||||
|
||||
#define HCI_CMD_MSG_F_VND_FUTURE (0x01)
|
||||
#define HCI_CMD_MSG_F_VND_QUEUED (0x02)
|
||||
#define HCI_CMD_MSG_F_VND_SENT (0x04)
|
||||
///// END LEGACY DEFINITIONS /////
|
||||
|
||||
typedef struct hci_hal_t hci_hal_t;
|
||||
//typedef struct btsnoop_t btsnoop_t;
|
||||
typedef struct controller_t controller_t;
|
||||
//typedef struct hci_inject_t hci_inject_t;
|
||||
typedef struct packet_fragmenter_t packet_fragmenter_t;
|
||||
//typedef struct vendor_t vendor_t;
|
||||
//typedef struct low_power_manager_t low_power_manager_t;
|
||||
|
||||
//typedef unsigned char * bdaddr_t;
|
||||
typedef uint16_t command_opcode_t;
|
||||
|
||||
/*
|
||||
typedef enum {
|
||||
LPM_DISABLE,
|
||||
LPM_ENABLE,
|
||||
LPM_WAKE_ASSERT,
|
||||
LPM_WAKE_DEASSERT
|
||||
} low_power_command_t;
|
||||
*/
|
||||
|
||||
typedef void (*command_complete_cb)(BT_HDR *response, void *context);
|
||||
typedef void (*command_status_cb)(uint8_t status, BT_HDR *command, void *context);
|
||||
|
||||
typedef struct hci_t {
|
||||
// Send a low power command, if supported and the low power manager is enabled.
|
||||
//void (*send_low_power_command)(low_power_command_t command);
|
||||
|
||||
// Do the postload sequence (call after the rest of the BT stack initializes).
|
||||
void (*do_postload)(void);
|
||||
|
||||
// Send a command through the HCI layer
|
||||
void (*transmit_command)(
|
||||
BT_HDR *command,
|
||||
command_complete_cb complete_callback,
|
||||
command_status_cb status_cb,
|
||||
void *context
|
||||
);
|
||||
|
||||
future_t *(*transmit_command_futured)(BT_HDR *command);
|
||||
|
||||
// Send some data downward through the HCI layer
|
||||
void (*transmit_downward)(uint16_t type, void *data);
|
||||
} hci_t;
|
||||
|
||||
const hci_t *hci_layer_get_interface(void);
|
||||
|
||||
int hci_start_up(void);
|
||||
void hci_shut_down(void);
|
||||
|
||||
bool hci_downstream_data_post(uint32_t timeout);
|
||||
|
||||
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
|
||||
int hci_adv_credits_prep_to_release(uint16_t num);
|
||||
int hci_adv_credits_try_release(uint16_t num);
|
||||
int hci_adv_credits_force_release(uint16_t num);
|
||||
#endif
|
||||
|
||||
#endif /* _HCI_LAYER_H_ */
|
||||
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_PACKET_FACTORY_H_
|
||||
#define _HCI_PACKET_FACTORY_H_
|
||||
|
||||
#include "stack/bt_types.h"
|
||||
#include "device/event_mask.h"
|
||||
|
||||
typedef struct {
|
||||
BT_HDR *(*make_reset)(void);
|
||||
BT_HDR *(*make_read_buffer_size)(void);
|
||||
BT_HDR *(*make_set_c2h_flow_control)(uint8_t enable);
|
||||
BT_HDR *(*make_set_adv_report_flow_control)(uint8_t enable, uint16_t num, uint16_t lost_threshold);
|
||||
BT_HDR *(*make_host_buffer_size)(uint16_t acl_size, uint8_t sco_size, uint16_t acl_count, uint16_t sco_count);
|
||||
BT_HDR *(*make_read_local_version_info)(void);
|
||||
BT_HDR *(*make_read_bd_addr)(void);
|
||||
BT_HDR *(*make_read_local_supported_commands)(void);
|
||||
BT_HDR *(*make_read_local_supported_features)(void);
|
||||
BT_HDR *(*make_read_local_extended_features)(uint8_t page_number);
|
||||
BT_HDR *(*make_write_simple_pairing_mode)(uint8_t mode);
|
||||
BT_HDR *(*make_write_secure_connections_host_support)(uint8_t mode);
|
||||
BT_HDR *(*make_set_event_mask)(const bt_event_mask_t *event_mask);
|
||||
BT_HDR *(*make_ble_write_host_support)(uint8_t supported_host, uint8_t simultaneous_host);
|
||||
BT_HDR *(*make_ble_read_white_list_size)(void);
|
||||
BT_HDR *(*make_ble_read_buffer_size)(void);
|
||||
BT_HDR *(*make_ble_read_supported_states)(void);
|
||||
BT_HDR *(*make_ble_read_local_supported_features)(void);
|
||||
BT_HDR *(*make_ble_read_resolving_list_size)(void);
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
BT_HDR *(*make_read_max_adv_data_len)(void);
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
BT_HDR *(*make_ble_read_suggested_default_data_length)(void);
|
||||
BT_HDR *(*make_ble_write_suggested_default_data_length)(uint16_t SuggestedMaxTxOctets, uint16_t SuggestedMaxTxTime);
|
||||
BT_HDR *(*make_ble_set_event_mask)(const bt_event_mask_t *event_mask);
|
||||
BT_HDR *(*make_write_sync_flow_control_enable)(uint8_t enable);
|
||||
BT_HDR *(*make_write_default_erroneous_data_report)(uint8_t enable);
|
||||
} hci_packet_factory_t;
|
||||
|
||||
const hci_packet_factory_t *hci_packet_factory_get_interface(void);
|
||||
|
||||
#endif /*_HCI_PACKET_FACTORY_H_*/
|
||||
@@ -0,0 +1,112 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _HCI_PACKET_PARSER_H_
|
||||
#define _HCI_PACKET_PARSER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "device/bdaddr.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "device/device_features.h"
|
||||
//#include "features.h"
|
||||
#include "device/version.h"
|
||||
|
||||
typedef struct {
|
||||
void (*parse_generic_command_complete)(BT_HDR *response);
|
||||
|
||||
void (*parse_read_buffer_size_response)(
|
||||
BT_HDR *response,
|
||||
uint16_t *acl_data_size_ptr,
|
||||
uint16_t *acl_buffer_count_ptr,
|
||||
uint8_t *sco_data_size_ptr,
|
||||
uint16_t *sco_buffer_count_ptr
|
||||
);
|
||||
|
||||
void (*parse_read_local_version_info_response)(
|
||||
BT_HDR *response,
|
||||
bt_version_t *bt_version_ptr
|
||||
);
|
||||
|
||||
void (*parse_read_bd_addr_response)(
|
||||
BT_HDR *response,
|
||||
bt_bdaddr_t *address_ptr
|
||||
);
|
||||
|
||||
void (*parse_read_local_supported_commands_response)(
|
||||
BT_HDR *response,
|
||||
uint8_t *supported_commands_ptr,
|
||||
size_t supported_commands_length
|
||||
);
|
||||
|
||||
void (*parse_read_local_supported_features_response)(
|
||||
BT_HDR *response,
|
||||
bt_device_features_t *feature_pages
|
||||
);
|
||||
|
||||
void (*parse_read_local_extended_features_response)(
|
||||
BT_HDR *response,
|
||||
uint8_t *page_number_ptr,
|
||||
uint8_t *max_page_number_ptr,
|
||||
bt_device_features_t *feature_pages,
|
||||
size_t feature_pages_count
|
||||
);
|
||||
|
||||
void (*parse_ble_read_white_list_size_response)(
|
||||
BT_HDR *response,
|
||||
uint8_t *white_list_size_ptr
|
||||
);
|
||||
|
||||
void (*parse_ble_read_buffer_size_response)(
|
||||
BT_HDR *response,
|
||||
uint16_t *data_size_ptr,
|
||||
uint8_t *acl_buffer_count_ptr
|
||||
);
|
||||
|
||||
void (*parse_ble_read_supported_states_response)(
|
||||
BT_HDR *response,
|
||||
uint8_t *supported_states,
|
||||
size_t supported_states_size
|
||||
);
|
||||
|
||||
void (*parse_ble_read_local_supported_features_response)(
|
||||
BT_HDR *response,
|
||||
bt_device_features_t *supported_features
|
||||
);
|
||||
|
||||
void (*parse_ble_read_resolving_list_size_response) (
|
||||
BT_HDR *response,
|
||||
uint8_t *resolving_list_size_ptr
|
||||
);
|
||||
#if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
void (*parse_ble_read_adv_max_len_response) (
|
||||
BT_HDR *respone,
|
||||
uint16_t *ble_ext_adv_data_max_len_ptr
|
||||
);
|
||||
#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
|
||||
void (*parse_ble_read_suggested_default_data_length_response)(
|
||||
BT_HDR *response,
|
||||
uint16_t *ble_default_packet_length_ptr,
|
||||
uint16_t *ble_default_packet_txtime_ptr
|
||||
);
|
||||
} hci_packet_parser_t;
|
||||
|
||||
const hci_packet_parser_t *hci_packet_parser_get_interface(void);
|
||||
|
||||
#endif /*_HCI_PACKET_PARSER_H_*/
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __HCI_TRANS_INT_H__
|
||||
#define __HCI_TRANS_INT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_bluedroid_hci.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief host checks whether it can send data to controller
|
||||
*
|
||||
* @return true if host can send data, false otherwise
|
||||
*/
|
||||
bool hci_host_check_send_available(void);
|
||||
|
||||
/**
|
||||
* @brief host sends packet to controller
|
||||
*
|
||||
* @param[in] data pointer to data buffer
|
||||
* @param[in] len length of data in byte
|
||||
*/
|
||||
void hci_host_send_packet(uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief register the HCI function interface
|
||||
*
|
||||
* @param[in] callback HCI function interface
|
||||
*
|
||||
* @return ESP_OK register successfully, ESP_FAIL otherwise
|
||||
*/
|
||||
esp_err_t hci_host_register_callback(const esp_bluedroid_hci_driver_callbacks_t *callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HCI_TRANS_INT_H__ */
|
||||
@@ -0,0 +1,62 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _PACKET_FRAGMENTER_H_
|
||||
#define _PACKET_FRAGMENTER_H_
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "hci/hci_layer.h"
|
||||
|
||||
typedef void (*transmit_finished_cb)(BT_HDR *packet, bool all_fragments_sent);
|
||||
typedef void (*packet_reassembled_cb)(BT_HDR *packet);
|
||||
typedef void (*packet_fragmented_cb)(BT_HDR *packet, bool send_transmit_finished);
|
||||
|
||||
typedef struct {
|
||||
// Called for every packet fragment.
|
||||
packet_fragmented_cb fragmented;
|
||||
|
||||
// Called for every completely reassembled packet.
|
||||
packet_reassembled_cb reassembled;
|
||||
|
||||
// Called when the fragmenter finishes sending all requested fragments,
|
||||
// but the packet has not been entirely sent.
|
||||
transmit_finished_cb transmit_finished;
|
||||
} packet_fragmenter_callbacks_t;
|
||||
|
||||
typedef struct packet_fragmenter_t {
|
||||
// Initialize the fragmenter, specifying the |result_callbacks|.
|
||||
void (*init)(const packet_fragmenter_callbacks_t *result_callbacks);
|
||||
|
||||
// Release all resources associated with the fragmenter.
|
||||
void (*cleanup)(void);
|
||||
|
||||
// Check if Current fragmenter is ongoing
|
||||
BT_HDR *(*fragment_current_packet)(void);
|
||||
|
||||
// Fragments |packet| if necessary and hands off everything to the fragmented callback.
|
||||
void (*fragment_and_dispatch)(BT_HDR *packet);
|
||||
// If |packet| is a complete packet, forwards to the reassembled callback. Otherwise
|
||||
// holds onto it until all fragments arrive, at which point the reassembled callback is called
|
||||
// with the reassembled data.
|
||||
void (*reassemble_and_dispatch)(BT_HDR *packet);
|
||||
} packet_fragmenter_t;
|
||||
|
||||
const packet_fragmenter_t *packet_fragmenter_get_interface(void);
|
||||
|
||||
#endif /* _PACKET_FRAGMENTER_H_ */
|
||||
@@ -0,0 +1,238 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
#include <string.h>
|
||||
#include "common/bt_trace.h"
|
||||
#include "common/bt_defs.h"
|
||||
#include "device/controller.h"
|
||||
#include "hci/hci_internals.h"
|
||||
#include "hci/hci_layer.h"
|
||||
#include "hci/packet_fragmenter.h"
|
||||
|
||||
#include "osi/hash_map.h"
|
||||
#include "osi/hash_functions.h"
|
||||
#include "common/bt_trace.h"
|
||||
|
||||
|
||||
#define APPLY_CONTINUATION_FLAG(handle) (((handle) & 0xCFFF) | 0x1000)
|
||||
#define APPLY_START_FLAG(handle) (((handle) & 0xCFFF) | 0x2000)
|
||||
#define SUB_EVENT(event) ((event) & MSG_SUB_EVT_MASK)
|
||||
#define GET_BOUNDARY_FLAG(handle) (((handle) >> 12) & 0x0003)
|
||||
|
||||
#define HANDLE_MASK 0x0FFF
|
||||
#define START_PACKET_BOUNDARY 2
|
||||
#define CONTINUATION_PACKET_BOUNDARY 1
|
||||
#define L2CAP_HEADER_SIZE 4
|
||||
|
||||
// TODO(zachoverflow): find good value for this
|
||||
#define NUMBER_OF_BUCKETS 42
|
||||
|
||||
// Our interface and callbacks
|
||||
static const packet_fragmenter_t interface;
|
||||
static const controller_t *controller;
|
||||
static const packet_fragmenter_callbacks_t *callbacks;
|
||||
static hash_map_t *partial_packets;
|
||||
static BT_HDR *current_fragment_packet;
|
||||
|
||||
static void init(const packet_fragmenter_callbacks_t *result_callbacks)
|
||||
{
|
||||
current_fragment_packet = NULL;
|
||||
callbacks = result_callbacks;
|
||||
partial_packets = hash_map_new(NUMBER_OF_BUCKETS, hash_function_naive, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
if (partial_packets) {
|
||||
hash_map_free(partial_packets);
|
||||
}
|
||||
}
|
||||
|
||||
static BT_HDR *fragment_get_current_packet(void)
|
||||
{
|
||||
return current_fragment_packet;
|
||||
}
|
||||
|
||||
static void fragment_and_dispatch(BT_HDR *packet)
|
||||
{
|
||||
uint16_t continuation_handle;
|
||||
uint16_t max_data_size, max_packet_size, remaining_length;
|
||||
uint16_t event = packet->event & MSG_EVT_MASK;
|
||||
uint8_t *stream = packet->data + packet->offset;
|
||||
|
||||
assert(packet != NULL);
|
||||
|
||||
// We only fragment ACL packets
|
||||
if (event != MSG_STACK_TO_HC_HCI_ACL) {
|
||||
callbacks->fragmented(packet, true);
|
||||
return;
|
||||
}
|
||||
|
||||
max_data_size =
|
||||
SUB_EVENT(packet->event) == LOCAL_BR_EDR_CONTROLLER_ID ?
|
||||
controller->get_acl_data_size_classic() :
|
||||
controller->get_acl_data_size_ble();
|
||||
|
||||
max_packet_size = max_data_size + HCI_ACL_PREAMBLE_SIZE;
|
||||
if((packet->len > max_packet_size) && (packet->layer_specific == 0) && (event == MSG_STACK_TO_HC_HCI_ACL)) {
|
||||
packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
|
||||
current_fragment_packet = NULL;
|
||||
callbacks->transmit_finished(packet, false);
|
||||
return;
|
||||
|
||||
}
|
||||
remaining_length = packet->len;
|
||||
STREAM_TO_UINT16(continuation_handle, stream);
|
||||
continuation_handle = APPLY_CONTINUATION_FLAG(continuation_handle);
|
||||
if (remaining_length > max_packet_size) {
|
||||
current_fragment_packet = packet;
|
||||
UINT16_TO_STREAM(stream, max_data_size);
|
||||
packet->len = max_packet_size;
|
||||
callbacks->fragmented(packet, false);
|
||||
packet->offset += max_data_size;
|
||||
remaining_length -= max_data_size;
|
||||
packet->len = remaining_length;
|
||||
|
||||
// Write the ACL header for the next fragment
|
||||
stream = packet->data + packet->offset;
|
||||
UINT16_TO_STREAM(stream, continuation_handle);
|
||||
UINT16_TO_STREAM(stream, remaining_length - HCI_ACL_PREAMBLE_SIZE);
|
||||
// Apparently L2CAP can set layer_specific to a max number of segments to transmit
|
||||
if (packet->layer_specific) {
|
||||
packet->layer_specific--;
|
||||
if (packet->layer_specific == 0) {
|
||||
packet->event = MSG_HC_TO_STACK_L2C_SEG_XMIT;
|
||||
|
||||
/* The remain packet will send back to the l2cap layer when controller buffer is not enough
|
||||
current_fragment_packet must be NULL, otherwise hci_host_thread_handler() will
|
||||
connitue handle the remain packet. then the remain packet will be freed.
|
||||
*/
|
||||
|
||||
current_fragment_packet = NULL;
|
||||
callbacks->transmit_finished(packet, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_fragment_packet = NULL;
|
||||
callbacks->fragmented(packet, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void reassemble_and_dispatch(BT_HDR *packet)
|
||||
{
|
||||
HCI_TRACE_DEBUG("reassemble_and_dispatch\n");
|
||||
|
||||
if ((packet->event & MSG_EVT_MASK) == MSG_HC_TO_STACK_HCI_ACL) {
|
||||
uint8_t *stream = packet->data + packet->offset;
|
||||
uint16_t handle;
|
||||
uint16_t l2cap_length;
|
||||
uint16_t acl_length __attribute__((unused));
|
||||
|
||||
STREAM_TO_UINT16(handle, stream);
|
||||
STREAM_TO_UINT16(acl_length, stream);
|
||||
STREAM_TO_UINT16(l2cap_length, stream);
|
||||
|
||||
assert(acl_length == packet->len - HCI_ACL_PREAMBLE_SIZE);
|
||||
|
||||
uint8_t boundary_flag = GET_BOUNDARY_FLAG(handle);
|
||||
handle = handle & HANDLE_MASK;
|
||||
|
||||
BT_HDR *partial_packet = (BT_HDR *)hash_map_get(partial_packets, (void *)(uintptr_t)handle);
|
||||
|
||||
if (boundary_flag == START_PACKET_BOUNDARY) {
|
||||
if (partial_packet) {
|
||||
HCI_TRACE_WARNING("%s found unfinished packet for handle with start packet. Dropping old.\n", __func__);
|
||||
hash_map_erase(partial_packets, (void *)(uintptr_t)handle);
|
||||
osi_free(partial_packet);
|
||||
}
|
||||
|
||||
uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE;
|
||||
if (full_length <= packet->len) {
|
||||
if (full_length < packet->len) {
|
||||
HCI_TRACE_WARNING("%s found l2cap full length %d less than the hci length %d.\n", __func__, l2cap_length, packet->len);
|
||||
}
|
||||
|
||||
callbacks->reassembled(packet);
|
||||
return;
|
||||
}
|
||||
partial_packet = (BT_HDR *)osi_calloc(full_length + sizeof(BT_HDR));
|
||||
partial_packet->event = packet->event;
|
||||
partial_packet->len = full_length;
|
||||
partial_packet->offset = packet->len;
|
||||
|
||||
memcpy(partial_packet->data, packet->data + packet->offset, packet->len);
|
||||
|
||||
// Update the ACL data size to indicate the full expected length
|
||||
stream = partial_packet->data;
|
||||
STREAM_SKIP_UINT16(stream); // skip the handle
|
||||
UINT16_TO_STREAM(stream, full_length - HCI_ACL_PREAMBLE_SIZE);
|
||||
|
||||
hash_map_set(partial_packets, (void *)(uintptr_t)handle, partial_packet);
|
||||
// Free the old packet buffer, since we don't need it anymore
|
||||
osi_free(packet);
|
||||
} else {
|
||||
if (!partial_packet) {
|
||||
HCI_TRACE_ERROR("%s got continuation for unknown packet. Dropping it.\n", __func__);
|
||||
osi_free(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
packet->offset += HCI_ACL_PREAMBLE_SIZE; // skip ACL preamble
|
||||
packet->len -= HCI_ACL_PREAMBLE_SIZE;
|
||||
uint16_t projected_offset = partial_packet->offset + packet->len;
|
||||
if (projected_offset > partial_packet->len) { // len stores the expected length
|
||||
HCI_TRACE_ERROR("%s got packet which would exceed expected length of %d. Truncating.\n", __func__, partial_packet->len);
|
||||
packet->len = partial_packet->len - partial_packet->offset;
|
||||
projected_offset = partial_packet->len;
|
||||
}
|
||||
|
||||
memcpy(
|
||||
partial_packet->data + partial_packet->offset,
|
||||
packet->data + packet->offset,
|
||||
packet->len
|
||||
);
|
||||
|
||||
// Free the old packet buffer, since we don't need it anymore
|
||||
osi_free(packet);
|
||||
partial_packet->offset = projected_offset;
|
||||
|
||||
if (partial_packet->offset == partial_packet->len) {
|
||||
hash_map_erase(partial_packets, (void *)(uintptr_t)handle);
|
||||
partial_packet->offset = 0;
|
||||
callbacks->reassembled(partial_packet);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callbacks->reassembled(packet);
|
||||
}
|
||||
}
|
||||
|
||||
static const packet_fragmenter_t interface = {
|
||||
init,
|
||||
cleanup,
|
||||
|
||||
fragment_get_current_packet,
|
||||
fragment_and_dispatch,
|
||||
reassemble_and_dispatch
|
||||
};
|
||||
|
||||
const packet_fragmenter_t *packet_fragmenter_get_interface(void)
|
||||
{
|
||||
controller = controller_get_interface();
|
||||
return &interface;
|
||||
}
|
||||
Reference in New Issue
Block a user