/****************************************************************************** * * 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 #include "bt_defs.h" #include "bt_trace.h" #include "bt_types.h" #include "buffer_allocator.h" #include "fixed_queue.h" #include "hci_hal.h" #include "hci_internals.h" #include "hci_layer.h" #include "thread.h" #include "bt.h" #define HCI_HAL_SERIAL_BUFFER_SIZE 1026 #define HCI_BLE_EVENT 0x3e #define PACKET_TYPE_TO_INBOUND_INDEX(type) ((type) - 2) #define PACKET_TYPE_TO_INDEX(type) ((type) - 1) 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 { const allocator_t *allocator; size_t buffer_size; fixed_queue_t *rx_q; } hci_hal_env_t; static hci_hal_env_t hci_hal_env; static const hci_hal_t interface; static const hci_hal_callbacks_t *callbacks; static const vhci_host_callback_t vhci_host_cb; static xTaskHandle xHciH4TaskHandle; static xQueueHandle xHciH4Queue; 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_rx_handler(void *arg); static void event_uart_has_bytes(fixed_queue_t *queue); static void hci_hal_env_init( size_t buffer_size, size_t max_buffer_count) { assert(buffer_size > 0); assert(max_buffer_count > 0); hci_hal_env.allocator = buffer_allocator_get_interface(); hci_hal_env.buffer_size = buffer_size; hci_hal_env.rx_q = fixed_queue_new(max_buffer_count); if (hci_hal_env.rx_q) fixed_queue_register_dequeue(hci_hal_env.rx_q, event_uart_has_bytes); else LOG_ERROR("%s unable to create rx queue.\n", __func__); return; } static void hci_hal_env_deinit(void) { fixed_queue_free(hci_hal_env.rx_q, hci_hal_env.allocator->free); } static bool hal_open(const hci_hal_callbacks_t *upper_callbacks) { assert(upper_callbacks != NULL); callbacks = upper_callbacks; hci_hal_env_init(HCI_HAL_SERIAL_BUFFER_SIZE, SIZE_MAX); xHciH4Queue = xQueueCreate(60, sizeof(void *)); xTaskCreate(hci_hal_h4_rx_handler, "HciH4T", 4096+2048, NULL, configMAX_PRIORITIES - 3, &xHciH4TaskHandle); //register vhci host cb API_vhci_host_register_callback(&vhci_host_cb); return true; error: interface.close(); return false; } static void hal_close() { hci_hal_env_deinit(); /* delete task and queue */ vTaskDelete(xHciH4TaskHandle); vQueueDelete(xHciH4Queue); } /** * 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) { LOG_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 API_vhci_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_hal_h4_rx_handler(void *arg) { BtTaskEvt_t *e; for (;;) { if (pdTRUE == xQueueReceive(xHciH4Queue, &e, (portTickType)portMAX_DELAY)) { if (e->sig == 0xff) { fixed_queue_process(hci_hal_env.rx_q); } osi_free(e); } } } void hci_hal_h4_task_post(void) { BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t)); if (evt == NULL) return; evt->sig = 0xff; evt->par = 0; if (xQueueSend(xHciH4Queue, &evt, 10/portTICK_RATE_MS) != pdTRUE) { LOG_ERROR("xHciH4Queue failed\n"); } } static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet) { uint8_t type, hdr_size; uint16_t length; uint8_t *stream = packet->data + packet->offset; if (!packet) return; STREAM_TO_UINT8(type, stream); packet->offset++; packet->len--; if (type == HCI_BLE_EVENT) { uint8_t len; STREAM_TO_UINT8(len, stream); LOG_ERROR("Workround stream corrupted during LE SCAN: pkt_len=%d ble_event_len=%d", packet->len, len); hci_hal_env.allocator->free(packet); return; } if (type < DATA_TYPE_ACL || type > DATA_TYPE_EVENT) { LOG_ERROR("%d Unknown HCI message type. Dropping this byte 0x%x," " min %x, max %x", __func__, type, DATA_TYPE_ACL, DATA_TYPE_EVENT); hci_hal_env.allocator->free(packet); return; } hdr_size = preamble_sizes[type - 1]; if (packet->len < hdr_size) { LOG_ERROR("Wrong packet length type=%s pkt_len=%d hdr_len=%d", type, packet->len, hdr_size); hci_hal_env.allocator->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) { LOG_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d " "pkt_len=%d", type, hdr_size, length, packet->len); hci_hal_env.allocator->free(packet); return; } packet->event = outbound_event_types[PACKET_TYPE_TO_INDEX(type)]; callbacks->packet_ready(packet); } static void event_uart_has_bytes(fixed_queue_t *queue) { BT_HDR *packet; while (!fixed_queue_is_empty(queue)) { packet = fixed_queue_dequeue(queue); hci_hal_h4_hdl_rx_packet(packet); } } 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_host_task_post(); } 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; size_t pkt_size; pkt_size = BT_HDR_SIZE + len; pkt = (BT_HDR *)hci_hal_env.allocator->alloc(pkt_size); if (!pkt) { LOG_ERROR("%s couldn't aquire memory for inbound data buffer.", __func__); return -1; } pkt->offset = 0; pkt->len = len; pkt->layer_specific = 0; memcpy(pkt->data, data, len); fixed_queue_enqueue(hci_hal_env.rx_q, pkt); hci_hal_h4_task_post(); BTTRC_DUMP_BUFFER("Recv Pkt", pkt->data, len); return 0; } static const vhci_host_callback_t vhci_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() { return &interface; }