blob: ec1cf0f9f8d22aa15f719dd2f7f2247308647bb8 [file] [log] [blame]
/******************************************************************************
*
* 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.
*
******************************************************************************/
#define LOG_TAG "bt_hci_h4"
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "hci_hal.h"
#include "osi/include/eager_reader.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/reactor.h"
#include "osi/include/thread.h"
#include "vendor.h"
#define HCI_HAL_SERIAL_BUFFER_SIZE 1026
#define HCI_BLE_EVENT 0x3e
// Increased HCI thread priority to keep up with the audio sub-system
// when streaming time sensitive data (A2DP).
#define HCI_THREAD_PRIORITY (-19)
#define BT_HCI_UNKNOWN_MESSAGE_TYPE_NUM 1010002
static bool hal_init(const hci_hal_callbacks_t* upper_callbacks,
thread_t* upper_thread);
static bool hal_open(void);
static void hal_close(void);
static void packet_finished(serial_data_type_t type);
static uint16_t transmit_data(serial_data_type_t type, uint8_t* data,
uint16_t length);
static size_t read_data(serial_data_type_t type, uint8_t* buffer,
size_t max_size);
static void event_uart_has_bytes(eager_reader_t* reader, void* context);
// Our interface and modules we import
static const hci_hal_t interface = {
hal_init, hal_open, hal_close, read_data, packet_finished, transmit_data,
};
static const hci_hal_callbacks_t* callbacks;
static const vendor_t* vendor;
static thread_t* thread; // Not owned by us
static int uart_fd;
static eager_reader_t* uart_stream;
static serial_data_type_t current_data_type;
static bool stream_has_interpretation;
static bool stream_corruption_detected;
static uint8_t stream_corruption_bytes_to_ignore;
// Interface functions
static bool hal_init(const hci_hal_callbacks_t* upper_callbacks,
thread_t* upper_thread) {
assert(upper_callbacks != NULL);
assert(upper_thread != NULL);
callbacks = upper_callbacks;
thread = upper_thread;
return true;
}
static bool hal_open(void) {
LOG_INFO(LOG_TAG, "%s", __func__);
// TODO(zachoverflow): close if already open / or don't reopen (maybe at the
// hci layer level)
int fd_array[CH_MAX];
int number_of_ports = vendor->send_command(VENDOR_OPEN_USERIAL, &fd_array);
if (number_of_ports != 1) {
LOG_ERROR(LOG_TAG,
"%s opened the wrong number of ports: got %d, expected 1.",
__func__, number_of_ports);
goto error;
}
uart_fd = fd_array[0];
if (uart_fd == INVALID_FD) {
LOG_ERROR(LOG_TAG, "%s unable to open the uart serial port.", __func__);
goto error;
}
uart_stream =
eager_reader_new(uart_fd, &allocator_malloc, HCI_HAL_SERIAL_BUFFER_SIZE,
SIZE_MAX, "hci_single_channel");
if (!uart_stream) {
LOG_ERROR(LOG_TAG,
"%s unable to create eager reader for the uart serial port.",
__func__);
goto error;
}
stream_has_interpretation = false;
stream_corruption_detected = false;
stream_corruption_bytes_to_ignore = 0;
eager_reader_register(uart_stream, thread_get_reactor(thread),
event_uart_has_bytes, NULL);
// Raise thread priorities to keep up with audio
thread_set_priority(thread, HCI_THREAD_PRIORITY);
thread_set_priority(eager_reader_get_read_thread(uart_stream),
HCI_THREAD_PRIORITY);
return true;
error:
interface.close();
return false;
}
static void hal_close(void) {
LOG_INFO(LOG_TAG, "%s", __func__);
eager_reader_free(uart_stream);
uart_stream = NULL;
vendor->send_command(VENDOR_CLOSE_USERIAL, NULL);
uart_fd = INVALID_FD;
}
static size_t read_data(serial_data_type_t type, uint8_t* buffer,
size_t max_size) {
if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) {
LOG_ERROR(LOG_TAG, "%s invalid data type: %d", __func__, type);
return 0;
} else if (!stream_has_interpretation) {
LOG_ERROR(LOG_TAG, "%s with no valid stream intepretation.", __func__);
return 0;
} else if (current_data_type != type) {
LOG_ERROR(LOG_TAG, "%s with different type than existing interpretation.",
__func__);
return 0;
}
return eager_reader_read(uart_stream, buffer, max_size);
}
static void packet_finished(serial_data_type_t type) {
if (!stream_has_interpretation)
LOG_ERROR(LOG_TAG, "%s with no existing stream interpretation.", __func__);
else if (current_data_type != type)
LOG_ERROR(LOG_TAG, "%s with different type than existing interpretation.",
__func__);
stream_has_interpretation = false;
}
static uint16_t transmit_data(serial_data_type_t type, uint8_t* data,
uint16_t length) {
assert(data != NULL);
assert(length > 0);
if (type < DATA_TYPE_COMMAND || type > DATA_TYPE_SCO) {
LOG_ERROR(LOG_TAG, "%s invalid data type: %d", __func__, type);
return 0;
}
// Write the signal byte right before the data
--data;
uint8_t previous_byte = *data;
*(data) = type;
++length;
uint16_t transmitted_length = 0;
while (length > 0) {
ssize_t ret;
OSI_NO_INTR(ret = write(uart_fd, data + transmitted_length, length));
switch (ret) {
case -1:
if (errno == EAGAIN) continue;
LOG_ERROR(LOG_TAG, "In %s, error writing to the uart serial port: %s",
__func__, strerror(errno));
goto done;
case 0:
// If we wrote nothing, don't loop more because we
// can't go to infinity or beyond
goto done;
default:
transmitted_length += ret;
length -= ret;
break;
}
}
done:;
// Be nice and restore the old value of that byte
*(data) = previous_byte;
// Remove the signal byte from our transmitted length, if it was actually
// written
if (transmitted_length > 0) --transmitted_length;
return transmitted_length;
}
// Internal functions
// WORKAROUND:
// As exhibited by b/23934838, during result-heavy LE scans, the UART byte
// stream can get corrupted, leading to assertions caused by mis-interpreting
// the bytes following the corruption.
// This workaround looks for tell-tale signs of a BLE event and attempts to
// skip the correct amount of bytes in the stream to re-synchronize onto
// a packet boundary.
// Function returns true if |byte_read| has been processed by the workaround.
static bool stream_corrupted_during_le_scan_workaround(
const uint8_t byte_read) {
if (!stream_corruption_detected && byte_read == HCI_BLE_EVENT) {
LOG_ERROR(LOG_TAG, "%s HCI stream corrupted (message type 0x3E)!",
__func__);
stream_corruption_detected = true;
return true;
}
if (stream_corruption_detected) {
if (stream_corruption_bytes_to_ignore == 0) {
stream_corruption_bytes_to_ignore = byte_read;
LOG_ERROR(LOG_TAG, "%s About to skip %d bytes...", __func__,
stream_corruption_bytes_to_ignore);
} else {
--stream_corruption_bytes_to_ignore;
}
if (stream_corruption_bytes_to_ignore == 0) {
LOG_ERROR(LOG_TAG, "%s Back to our regularly scheduled program...",
__func__);
stream_corruption_detected = false;
}
return true;
}
return false;
}
// See what data is waiting, and notify the upper layer
static void event_uart_has_bytes(eager_reader_t* reader,
UNUSED_ATTR void* context) {
if (stream_has_interpretation) {
callbacks->data_ready(current_data_type);
} else {
uint8_t type_byte;
if (eager_reader_read(reader, &type_byte, 1) == 0) {
LOG_ERROR(LOG_TAG, "%s could not read HCI message type", __func__);
return;
}
if (stream_corrupted_during_le_scan_workaround(type_byte)) return;
if (type_byte < DATA_TYPE_ACL || type_byte > DATA_TYPE_EVENT) {
LOG_ERROR(
LOG_TAG,
"%s Unknown HCI message type 0x%x (min=0x%x max=0x%x). Aborting...",
__func__, type_byte, DATA_TYPE_ACL, DATA_TYPE_EVENT);
LOG_EVENT_INT(BT_HCI_UNKNOWN_MESSAGE_TYPE_NUM, type_byte);
assert(false && "Unknown HCI message type");
return;
}
stream_has_interpretation = true;
current_data_type = static_cast<serial_data_type_t>(type_byte);
}
}
const hci_hal_t* hci_hal_h4_get_interface() {
vendor = vendor_get_interface();
return &interface;
}
const hci_hal_t* hci_hal_h4_get_test_interface(vendor_t* vendor_interface) {
vendor = vendor_interface;
return &interface;
}