mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 06:11:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			585 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			585 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/******************************************************************************
 | 
						|
 *
 | 
						|
 *  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 "esp_bt.h"
 | 
						|
 | 
						|
#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 "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"
 | 
						|
 | 
						|
#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"
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    uint16_t opcode;
 | 
						|
    future_t *complete_future;
 | 
						|
    command_complete_cb complete_callback;
 | 
						|
    command_status_cb status_callback;
 | 
						|
    void *context;
 | 
						|
    BT_HDR *command;
 | 
						|
} waiting_command_t;
 | 
						|
 | 
						|
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_queue_t *command_queue;
 | 
						|
    fixed_queue_t *packet_queue;
 | 
						|
 | 
						|
    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_host_thread_handler(void *arg);
 | 
						|
static void event_command_ready(fixed_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 waiting_command_t *get_waiting_command(command_opcode_t opcode);
 | 
						|
static void dispatch_reassembled(BT_HDR *packet);
 | 
						|
 | 
						|
// Module lifecycle functions
 | 
						|
int hci_start_up(void)
 | 
						|
{
 | 
						|
    if (hci_layer_init_env()) {
 | 
						|
        goto error;
 | 
						|
    }
 | 
						|
 | 
						|
    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, 2);
 | 
						|
    if (hci_host_thread == NULL) {
 | 
						|
        return -2;
 | 
						|
    }
 | 
						|
 | 
						|
    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_host_task_post(uint32_t timeout)
 | 
						|
{
 | 
						|
    return osi_thread_post(hci_host_thread, hci_host_thread_handler, NULL, 0, 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_queue_new(QUEUE_SIZE_MAX);
 | 
						|
    if (hci_host_env.command_queue) {
 | 
						|
        fixed_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;
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
 | 
						|
    if (hci_host_env.command_queue) {
 | 
						|
        fixed_queue_free(hci_host_env.command_queue, 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_host_thread_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).
 | 
						|
     */
 | 
						|
    if (esp_vhci_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_queue_is_empty(hci_host_env.command_queue) &&
 | 
						|
                    hci_host_env.command_credits > 0) {
 | 
						|
                fixed_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);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void transmit_command(
 | 
						|
    BT_HDR *command,
 | 
						|
    command_complete_cb complete_callback,
 | 
						|
    command_status_cb status_callback,
 | 
						|
    void *context)
 | 
						|
{
 | 
						|
    uint8_t *stream;
 | 
						|
    waiting_command_t *wait_entry = osi_calloc(sizeof(waiting_command_t));
 | 
						|
    if (!wait_entry) {
 | 
						|
        HCI_TRACE_ERROR("%s couldn't allocate space for wait entry.", __func__);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    stream = command->data + command->offset;
 | 
						|
    STREAM_TO_UINT16(wait_entry->opcode, stream);
 | 
						|
    wait_entry->complete_callback = complete_callback;
 | 
						|
    wait_entry->status_callback = status_callback;
 | 
						|
    wait_entry->command = command;
 | 
						|
    wait_entry->context = context;
 | 
						|
 | 
						|
    // 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", wait_entry->opcode);
 | 
						|
    BTTRC_DUMP_BUFFER(NULL, command->data + command->offset, command->len);
 | 
						|
 | 
						|
    fixed_queue_enqueue(hci_host_env.command_queue, wait_entry, FIXED_QUEUE_MAX_TIMEOUT);
 | 
						|
    hci_host_task_post(OSI_THREAD_MAX_TIMEOUT);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static future_t *transmit_command_futured(BT_HDR *command)
 | 
						|
{
 | 
						|
    waiting_command_t *wait_entry = osi_calloc(sizeof(waiting_command_t));
 | 
						|
    assert(wait_entry != NULL);
 | 
						|
 | 
						|
    future_t *future = future_new();
 | 
						|
 | 
						|
    uint8_t *stream = command->data + command->offset;
 | 
						|
    STREAM_TO_UINT16(wait_entry->opcode, stream);
 | 
						|
    wait_entry->complete_future = future;
 | 
						|
    wait_entry->command = command;
 | 
						|
 | 
						|
    // 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_queue_enqueue(hci_host_env.command_queue, wait_entry, FIXED_QUEUE_MAX_TIMEOUT);
 | 
						|
    hci_host_task_post(OSI_THREAD_MAX_TIMEOUT);
 | 
						|
    return future;
 | 
						|
}
 | 
						|
 | 
						|
static void transmit_downward(uint16_t type, void *data)
 | 
						|
{
 | 
						|
    if (type == MSG_STACK_TO_HC_HCI_CMD) {
 | 
						|
        transmit_command((BT_HDR *)data, NULL, NULL, NULL);
 | 
						|
        HCI_TRACE_WARNING("%s legacy transmit of command. Use transmit_command instead.\n", __func__);
 | 
						|
    } else {
 | 
						|
        fixed_queue_enqueue(hci_host_env.packet_queue, data, FIXED_QUEUE_MAX_TIMEOUT);
 | 
						|
    }
 | 
						|
 | 
						|
    hci_host_task_post(OSI_THREAD_MAX_TIMEOUT);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// Command/packet transmitting functions
 | 
						|
static void event_command_ready(fixed_queue_t *queue)
 | 
						|
{
 | 
						|
    waiting_command_t *wait_entry = NULL;
 | 
						|
    command_waiting_response_t *cmd_wait_q = &hci_host_env.cmd_waiting_q;
 | 
						|
 | 
						|
    wait_entry = fixed_queue_dequeue(queue, FIXED_QUEUE_MAX_TIMEOUT);
 | 
						|
 | 
						|
    if(wait_entry->opcode == HCI_HOST_NUM_PACKETS_DONE
 | 
						|
#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE)
 | 
						|
    || wait_entry->opcode == HCI_VENDOR_BLE_ADV_REPORT_FLOW_CONTROL
 | 
						|
#endif
 | 
						|
    ){
 | 
						|
        packet_fragmenter->fragment_and_dispatch(wait_entry->command);
 | 
						|
        osi_free(wait_entry->command);
 | 
						|
        osi_free(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(wait_entry->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;
 | 
						|
    waiting_command_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_TRACE_ERROR("%s hci layer timeout waiting for response to a command. opcode: 0x%x", __func__, wait_entry->opcode);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// 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);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// 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)
 | 
						|
{
 | 
						|
    waiting_command_t *wait_entry = 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);
 | 
						|
        if (!wait_entry) {
 | 
						|
            HCI_TRACE_WARNING("%s command complete event with no matching command. opcode: 0x%x.", __func__, opcode);
 | 
						|
        } else if (wait_entry->complete_callback) {
 | 
						|
            wait_entry->complete_callback(packet, wait_entry->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 (wait_entry->complete_future) {
 | 
						|
            future_ready(wait_entry->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);
 | 
						|
        if (!wait_entry) {
 | 
						|
            HCI_TRACE_WARNING("%s command status event with no matching command. opcode: 0x%x", __func__, opcode);
 | 
						|
        } else if (wait_entry->status_callback) {
 | 
						|
            wait_entry->status_callback(status, wait_entry->command, wait_entry->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_queue_is_empty(hci_host_env.command_queue)) {
 | 
						|
        hci_host_task_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 ||
 | 
						|
                (!wait_entry->complete_callback && !wait_entry->complete_future)) {
 | 
						|
            osi_free(packet);
 | 
						|
        }
 | 
						|
 | 
						|
        // If it has a callback, it's responsible for freeing the command
 | 
						|
        if (event_code == HCI_COMMAND_COMPLETE_EVT || !wait_entry->status_callback) {
 | 
						|
            osi_free(wait_entry->command);
 | 
						|
        }
 | 
						|
 | 
						|
        osi_free(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);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// 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 waiting_command_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)) {
 | 
						|
        waiting_command_t *wait_entry = list_node(node);
 | 
						|
        if (!wait_entry || wait_entry->opcode != opcode) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        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
 | 
						|
};
 | 
						|
 | 
						|
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;
 | 
						|
}
 |