diff options
Diffstat (limited to 'hci/src/hci_hal_h4.c')
-rw-r--r-- | hci/src/hci_hal_h4.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/hci/src/hci_hal_h4.c b/hci/src/hci_hal_h4.c new file mode 100644 index 000000000..35a939588 --- /dev/null +++ b/hci/src/hci_hal_h4.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * 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 "hci_hal_h4" + +#include <assert.h> +#include <utils/Log.h> + +#include "eager_reader.h" +#include "hci_hal.h" +#include "osi.h" +#include "reactor.h" +#include "vendor.h" + +#define HCI_HAL_SERIAL_BUFFER_SIZE 1026 + +// Our interface and modules we import +static const hci_hal_interface_t interface; +static const hci_hal_callbacks_t *callbacks; +static const vendor_interface_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 void event_uart_has_bytes(eager_reader_t *reader, void *context); + +// 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() { + // 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) { + ALOGE("%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 == -1) { + ALOGE("%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) { + ALOGE("%s unable to create eager reader for the uart serial port.", __func__); + goto error; + } + + stream_has_interpretation = false; + eager_reader_register(uart_stream, thread_get_reactor(thread), event_uart_has_bytes, NULL); + + return true; + +error: + interface.close(); + return false; +} + +static void hal_close() { + eager_reader_free(uart_stream); + vendor->send_command(VENDOR_CLOSE_USERIAL, NULL); +} + +static uint8_t read_byte(serial_data_type_t type) { + if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { + ALOGE("%s invalid data type: %d", __func__, type); + return 0; + } else if (!stream_has_interpretation) { + ALOGE("%s read byte with no valid stream intepretation.", __func__); + return 0; + } else if (current_data_type != type) { + ALOGE("%s read byte with different type than existing interpretation.", __func__); + return 0; + } + + return eager_reader_read_byte(uart_stream); +} + +static bool has_byte(serial_data_type_t type) { + if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { + ALOGE("%s invalid data type: %d", __func__, type); + return 0; + } else if (!stream_has_interpretation) { + ALOGE("%s has byte with no valid stream intepretation.", __func__); + return 0; + } else if (current_data_type != type) { + ALOGE("%s has byte with different type than existing interpretation.", __func__); + return 0; + } + + return eager_reader_has_byte(uart_stream); +} + +static void packet_finished(serial_data_type_t type) { + if (!stream_has_interpretation) + ALOGE("%s packet finished with no existing stream interpretation.", __func__); + else if (current_data_type != type) + ALOGE("%s packed finished 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) { + ALOGE("%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 = write(uart_fd, data + transmitted_length, length); + switch (ret) { + case -1: + ALOGE("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 + +// See what data is waiting, and notify the upper layer +static void event_uart_has_bytes(eager_reader_t *reader, UNUSED_ATTR void *context) { + uint8_t type_byte; + + if (stream_has_interpretation) { + // Reentry in the the case that the upper layer couldn't read the entire + // packet the first time around + type_byte = current_data_type; + } + else { + type_byte = eager_reader_read_byte(reader); + if (type_byte < DATA_TYPE_ACL || type_byte > DATA_TYPE_EVENT) { + ALOGE("[h4] Unknown HCI message type. Dropping this byte 0x%x, min %x, max %x", type_byte, DATA_TYPE_ACL, DATA_TYPE_EVENT); + return; + } + + stream_has_interpretation = true; + current_data_type = type_byte; + } + + if (has_byte(current_data_type)) + callbacks->data_ready(type_byte); +} + +static const hci_hal_interface_t interface = { + hal_init, + + hal_open, + hal_close, + + read_byte, + has_byte, + packet_finished, + transmit_data, +}; + +const hci_hal_interface_t *hci_hal_get_interface() { + vendor = vendor_get_interface(); + return &interface; +} + +const hci_hal_interface_t *hci_hal_get_test_interface(vendor_interface_t *vendor_interface) { + vendor = vendor_interface; + return &interface; +} |