diff options
Diffstat (limited to 'hci/src/hci_mct.c')
-rw-r--r-- | hci/src/hci_mct.c | 1204 |
1 files changed, 1204 insertions, 0 deletions
diff --git a/hci/src/hci_mct.c b/hci/src/hci_mct.c new file mode 100644 index 0000000..23337c2 --- /dev/null +++ b/hci/src/hci_mct.c @@ -0,0 +1,1204 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * This program is the proprietary software of Broadcom Corporation and/or its + * licensors, and may only be used, duplicated, modified or distributed + * pursuant to the terms and conditions of a separate, written license + * agreement executed between you and Broadcom (an "Authorized License"). + * Except as set forth in an Authorized License, Broadcom grants no license + * (express or implied), right to use, or waiver of any kind with respect to + * the Software, and Broadcom expressly reserves all rights in and to the + * Software and all intellectual property rights therein. + * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS + * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE + * ALL USE OF THE SOFTWARE. + * + * Except as expressly set forth in the Authorized License, + * + * 1. This program, including its structure, sequence and organization, + * constitutes the valuable trade secrets of Broadcom, and you shall + * use all reasonable efforts to protect the confidentiality thereof, + * and to use this information only in connection with your use of + * Broadcom integrated circuit products. + * + * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED + * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, + * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, + * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY + * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, + * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES, + * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR + * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT + * OF USE OR PERFORMANCE OF THE SOFTWARE. + * + * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR + * ITS LICENSORS BE LIABLE FOR + * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY + * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO + * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM + * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR + * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE + * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE + * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF + * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Filename: hci_mct.c + * + * Description: Contains HCI transport send/receive functions + * for Multi-Channels Transport + * + ******************************************************************************/ + +#define LOG_TAG "bt_mct" + +#include <utils/Log.h> +#include <stdlib.h> +#include <fcntl.h> +#include "bt_hci_bdroid.h" +#include "hci.h" +#include "userial.h" +#include "utils.h" + +/****************************************************************************** +** Constants & Macros +******************************************************************************/ + +#ifndef HCI_DBG +#define HCI_DBG FALSE +#endif + +#if (HCI_DBG == TRUE) +#define HCIDBG(param, ...) {LOGD(param, ## __VA_ARGS__);} +#else +#define HCIDBG(param, ...) {} +#endif + +/* Preamble length for HCI Commands: +** 2-bytes for opcode and 1 byte for length +*/ +#define HCI_CMD_PREAMBLE_SIZE 3 + +/* Preamble length for HCI Events: +** 1-byte for opcode and 1 byte for length +*/ +#define HCI_EVT_PREAMBLE_SIZE 2 + +/* Preamble length for SCO Data: +** 2-byte for Handle and 1 byte for length +*/ +#define HCI_SCO_PREAMBLE_SIZE 3 + +/* Preamble length for ACL Data: +** 2-byte for Handle and 2 byte for length +*/ +#define HCI_ACL_PREAMBLE_SIZE 4 + +#define ACL_RX_PKT_START 2 +#define ACL_RX_PKT_CONTINUE 1 +#define L2CAP_HEADER_SIZE 4 + +/* Maximum numbers of allowed internal +** outstanding command packets at any time +*/ +#define INT_CMD_PKT_MAX_COUNT 8 +#define INT_CMD_PKT_IDX_MASK 0x07 + +#define HCI_COMMAND_COMPLETE_EVT 0x0E +#define HCI_COMMAND_STATUS_EVT 0x0F +#define HCI_READ_BUFFER_SIZE 0x1005 +#define HCI_LE_READ_BUFFER_SIZE 0x2002 + +/****************************************************************************** +** Local type definitions +******************************************************************************/ + +/* MCT Rx States */ +typedef enum { + MCT_RX_NEWMSG_ST, + MCT_RX_LEN_ST, + MCT_RX_DATA_ST, + MCT_RX_IGNORE_ST +} tHCI_MCT_RCV_STATE; + +/* Callback function for the returned event of internal issued command */ +typedef void (*tINT_CMD_CBACK)(void *p_mem); + +typedef struct +{ + uint16_t opcode; /* OPCODE of outstanding internal commands */ + tINT_CMD_CBACK cback; /* Callback function when return of internal + * command is received */ +} tINT_CMD_Q; + +typedef struct +{ + HC_BT_HDR *p_rcv_msg; /* Buffer to hold current rx HCI message */ + uint16_t rcv_len; /* Size of current incoming message */ + tHCI_MCT_RCV_STATE rcv_state; /* Receive state of current rx message */ + uint8_t preload_count; /* Count numbers of preload bytes */ + uint8_t preload_buffer[6]; /* HCI_ACL_PREAMBLE_SIZE + 2 */ +} tHCI_RCV_CB; + +/* Control block for HCISU_MCT */ +typedef struct +{ + tHCI_RCV_CB rcv_evt; + tHCI_RCV_CB rcv_acl; + uint16_t hc_acl_data_size; /* Controller's max ACL data length */ + uint16_t hc_ble_acl_data_size; /* Controller's max BLE ACL data length */ + BUFFER_Q acl_rx_q; /* Queue of base buffers for fragmented ACL pkts */ + int int_cmd_rsp_pending; /* Num of internal cmds pending for ack */ + uint8_t int_cmd_rd_idx; /* Read index of int_cmd_opcode queue */ + uint8_t int_cmd_wrt_idx; /* Write index of int_cmd_opcode queue */ + tINT_CMD_Q int_cmd[INT_CMD_PKT_MAX_COUNT]; /* FIFO queue */ +} tHCI_MCT_CB; + +/****************************************************************************** +** Externs +******************************************************************************/ + +extern BUFFER_Q tx_q; + +void btsnoop_init(void); +void btsnoop_close(void); +void btsnoop_cleanup (void); +void btsnoop_capture(HC_BT_HDR *p_buf, uint8_t is_rcvd); +uint8_t hci_mct_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \ + tINT_CMD_CBACK p_cback); +void lpm_wake_assert(void); +void lpm_tx_done(uint8_t is_tx_done); + +/****************************************************************************** +** Variables +******************************************************************************/ + +/* Num of allowed outstanding HCI CMD packets */ +volatile int num_hci_cmd_pkts = 1; + +/****************************************************************************** +** Static variables +******************************************************************************/ + +static tHCI_MCT_CB mct_cb; + +/****************************************************************************** +** Static functions +******************************************************************************/ + +/******************************************************************************* +** +** Function get_acl_data_length_cback +** +** Description Callback function for HCI_READ_BUFFER_SIZE and +** HCI_LE_READ_BUFFER_SIZE commands if they were sent because +** of internal request. +** +** Returns None +** +*******************************************************************************/ +void get_acl_data_length_cback(void *p_mem) +{ + uint8_t *p, status; + uint16_t opcode, len=0; + HC_BT_HDR *p_buf = (HC_BT_HDR *) p_mem; + + p = (uint8_t *)(p_buf + 1) + 3; + STREAM_TO_UINT16(opcode, p) + status = *p++; + if (status == 0) /* Success */ + STREAM_TO_UINT16(len, p) + + if (opcode == HCI_READ_BUFFER_SIZE) + { + if (status == 0) + mct_cb.hc_acl_data_size = len; + + /* reuse the rx buffer for sending HCI_LE_READ_BUFFER_SIZE command */ + p_buf->event = MSG_STACK_TO_HC_HCI_CMD; + p_buf->offset = 0; + p_buf->layer_specific = 0; + p_buf->len = 3; + + p = (uint8_t *) (p_buf + 1); + UINT16_TO_STREAM(p, HCI_LE_READ_BUFFER_SIZE); + *p = 0; + + if ((status = hci_mct_send_int_cmd(HCI_LE_READ_BUFFER_SIZE, p_buf, \ + get_acl_data_length_cback)) == FALSE) + { + bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1)); + bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS); + } + } + else if (opcode == HCI_LE_READ_BUFFER_SIZE) + { + if (status == 0) + mct_cb.hc_ble_acl_data_size = (len) ? len : mct_cb.hc_acl_data_size; + + if (bt_hc_cbacks) + { + bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1)); + ALOGE("hci lib postload completed"); + bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_SUCCESS); + } + } +} + + +/******************************************************************************* +** +** Function internal_event_intercept +** +** Description This function is called to parse received HCI event and +** - update the Num_HCI_Command_Packets +** - intercept the event if it is the result of an early +** issued internal command. +** +** Returns TRUE : if the event had been intercepted for internal process +** FALSE : send this event to core stack +** +*******************************************************************************/ +uint8_t internal_event_intercept(void) +{ + uint8_t *p; + uint8_t event_code; + uint16_t opcode, len; + tHCI_MCT_CB *p_cb = &mct_cb; + + p = (uint8_t *)(p_cb->rcv_evt.p_rcv_msg + 1); + + event_code = *p++; + len = *p++; + + if (event_code == HCI_COMMAND_COMPLETE_EVT) + { + utils_lock(); + num_hci_cmd_pkts = *p++; + utils_unlock(); + + // Signal TX event so the worker thread can check if it has anything + // to send + bthc_signal_event(HC_EVENT_TX); + + if (p_cb->int_cmd_rsp_pending > 0) + { + STREAM_TO_UINT16(opcode, p) + + if (opcode == p_cb->int_cmd[p_cb->int_cmd_rd_idx].opcode) + { + HCIDBG( \ + "Intercept CommandCompleteEvent for internal command (0x%04X)",\ + opcode); + if (p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback != NULL) + { + p_cb->int_cmd[p_cb->int_cmd_rd_idx].cback(p_cb->rcv_evt.p_rcv_msg); + } + else + { + // Missing cback function! + // Release the p_rcv_msg buffer. + if (bt_hc_cbacks) + { + bt_hc_cbacks->dealloc((TRANSAC) p_cb->rcv_evt.p_rcv_msg, \ + (char *) (p_cb->rcv_evt.p_rcv_msg + 1)); + } + } + p_cb->int_cmd_rd_idx = ((p_cb->int_cmd_rd_idx+1) & \ + INT_CMD_PKT_IDX_MASK); + p_cb->int_cmd_rsp_pending--; + return TRUE; + } + } + } + else if (event_code == HCI_COMMAND_STATUS_EVT) + { + utils_lock(); + num_hci_cmd_pkts = *(++p); + utils_unlock(); + + // Signal TX event so the worker thread can check if it has anything + // to send + bthc_signal_event(HC_EVENT_TX); + } + + return FALSE; +} + +/******************************************************************************* +** +** Function acl_rx_frame_buffer_alloc +** +** Description This function is called from the HCI transport when the +** first 4 or 6 bytes of an HCI ACL packet have been received: +** - Allocate a new buffer if it is a start pakcet of L2CAP +** message. +** - Return the buffer address of the starting L2CAP message +** frame if the packet is the next segment of a fragmented +** L2CAP message. +** +** Returns the address of the receive buffer caller should use +** (CR419: Modified to return NULL in case of error.) +** +** NOTE This assumes that the L2CAP MTU size is less than the size +** of an HCI ACL buffer, so the maximum L2CAP message will fit +** into one buffer. +** +*******************************************************************************/ +static HC_BT_HDR *acl_rx_frame_buffer_alloc (void) +{ + uint8_t *p; + uint16_t handle; + uint16_t hci_len; + uint16_t total_len; + uint8_t pkt_type; + HC_BT_HDR *p_return_buf = NULL; + tHCI_MCT_CB *p_cb = &mct_cb; + + + p = p_cb->rcv_acl.preload_buffer; + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (hci_len, p); + STREAM_TO_UINT16 (total_len, p); + + pkt_type = (uint8_t)(((handle) >> 12) & 0x0003); + handle = (uint16_t)((handle) & 0x0FFF); + + if (p_cb->acl_rx_q.count) + { + uint16_t save_handle; + HC_BT_HDR *p_hdr = p_cb->acl_rx_q.p_first; + + while (p_hdr != NULL) + { + p = (uint8_t *)(p_hdr + 1); + STREAM_TO_UINT16 (save_handle, p); + save_handle = (uint16_t)((save_handle) & 0x0FFF); + if (save_handle == handle) + { + p_return_buf = p_hdr; + break; + } + p_hdr = utils_getnext(p_hdr); + } + } + + if (pkt_type == ACL_RX_PKT_START) /*** START PACKET ***/ + { + /* Might have read 2 bytes for the L2CAP payload length */ + p_cb->rcv_acl.rcv_len = (hci_len) ? (hci_len - 2) : 0; + + /* Start of packet. If we were in the middle of receiving */ + /* a packet on the same ACL handle, the original packet is incomplete. + * Drop it. */ + if (p_return_buf) + { + ALOGW("dropping incomplete ACL frame"); + + utils_remove_from_queue(&(p_cb->acl_rx_q), p_return_buf); + + if (bt_hc_cbacks) + { + bt_hc_cbacks->dealloc((TRANSAC) p_return_buf, \ + (char *) (p_return_buf + 1)); + } + p_return_buf = NULL; + } + + /* Allocate a buffer for message */ + if (bt_hc_cbacks) + { + int len = total_len + HCI_ACL_PREAMBLE_SIZE + L2CAP_HEADER_SIZE + \ + BT_HC_HDR_SIZE; + p_return_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(len); + } + + if (p_return_buf) + { + /* Initialize buffer with preloaded data */ + p_return_buf->offset = 0; + p_return_buf->layer_specific = 0; + p_return_buf->event = MSG_HC_TO_STACK_HCI_ACL; + p_return_buf->len = p_cb->rcv_acl.preload_count; + memcpy((uint8_t *)(p_return_buf + 1), p_cb->rcv_acl.preload_buffer, \ + p_cb->rcv_acl.preload_count); + + if (hci_len && ((total_len + L2CAP_HEADER_SIZE) > hci_len)) + { + /* Will expect to see fragmented ACL packets */ + /* Keep the base buffer address in the watching queue */ + utils_enqueue(&(p_cb->acl_rx_q), p_return_buf); + } + } + } + else /*** CONTINUATION PACKET ***/ + { + p_cb->rcv_acl.rcv_len = hci_len; + + if (p_return_buf) + { + /* Packet continuation and found the original rx buffer */ + uint8_t *p_f = p = (uint8_t *)(p_return_buf + 1) + 2; + + STREAM_TO_UINT16 (total_len, p); + + /* Update HCI header of first segment (base buffer) with new len */ + total_len += hci_len; + UINT16_TO_STREAM (p_f, total_len); + } + } + + return (p_return_buf); +} + +/******************************************************************************* +** +** Function acl_rx_frame_end_chk +** +** Description This function is called from the HCI transport when the last +** byte of an HCI ACL packet has been received. It checks if +** the L2CAP message is complete, i.e. no more continuation +** packets are expected. +** +** Returns TRUE if message complete, FALSE if continuation expected +** +*******************************************************************************/ +static uint8_t acl_rx_frame_end_chk (void) +{ + uint8_t *p; + uint16_t handle, hci_len, l2cap_len; + HC_BT_HDR *p_buf; + tHCI_MCT_CB *p_cb = &mct_cb; + uint8_t frame_end=TRUE; + + p_buf = p_cb->rcv_acl.p_rcv_msg; + p = (uint8_t *)(p_buf + 1); + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (hci_len, p); + STREAM_TO_UINT16 (l2cap_len, p); + + if (hci_len > 0) + { + if (l2cap_len > (p_buf->len-(HCI_ACL_PREAMBLE_SIZE+L2CAP_HEADER_SIZE)) ) + { + /* If the L2CAP length has not been reached, tell caller not to send + * this buffer to stack */ + frame_end = FALSE; + } + else + { + /* + * The current buffer coulb be in the watching list. + * Remove it from the list if it is in. + */ + if (p_cb->acl_rx_q.count) + utils_remove_from_queue(&(p_cb->acl_rx_q), p_buf); + } + } + + /**** + ** Print snoop trace + ****/ + if (p_buf->offset) + { + /* CONTINUATION PACKET */ + + /* save original p_buf->len content */ + uint16_t tmp_u16 = p_buf->len; + + /* borrow HCI_ACL_PREAMBLE_SIZE bytes from the payload section */ + p = (uint8_t *)(p_buf + 1) + p_buf->offset - HCI_ACL_PREAMBLE_SIZE; + + /* save contents */ + memcpy(p_cb->rcv_acl.preload_buffer, p, HCI_ACL_PREAMBLE_SIZE); + + /* Set packet boundary flags to "continuation packet" */ + handle = (handle & 0xCFFF) | 0x1000; + + /* write handl & length info */ + UINT16_TO_STREAM (p, handle); + UINT16_TO_STREAM (p, (p_buf->len - p_buf->offset)); + + /* roll pointer back */ + p = p - HCI_ACL_PREAMBLE_SIZE; + + /* adjust `p_buf->offset` & `p_buf->len` + * before calling btsnoop_capture() */ + p_buf->offset = p_buf->offset - HCI_ACL_PREAMBLE_SIZE; + p_buf->len = p_buf->len - p_buf->offset; + + btsnoop_capture(p_buf, TRUE); + + /* restore contents */ + memcpy(p, p_cb->rcv_acl.preload_buffer, HCI_ACL_PREAMBLE_SIZE); + + /* restore p_buf->len */ + p_buf->len = tmp_u16; + } + else + { + /* START PACKET */ + btsnoop_capture(p_buf, TRUE); + } + + if (frame_end == TRUE) + p_buf->offset = 0; + else + p_buf->offset = p_buf->len; /* save current buffer-end position */ + + return frame_end; +} + +/***************************************************************************** +** HCI MCT INTERFACE FUNCTIONS +*****************************************************************************/ + +/******************************************************************************* +** +** Function hci_mct_init +** +** Description Initialize MCT module +** +** Returns None +** +*******************************************************************************/ +void hci_mct_init(void) +{ + HCIDBG("hci_mct_init"); + + memset(&mct_cb, 0, sizeof(tHCI_MCT_CB)); + utils_queue_init(&(mct_cb.acl_rx_q)); + + /* Per HCI spec., always starts with 1 */ + num_hci_cmd_pkts = 1; + + /* Give an initial values of Host Controller's ACL data packet length + * Will update with an internal HCI(_LE)_Read_Buffer_Size request + */ + mct_cb.hc_acl_data_size = 1021; + mct_cb.hc_ble_acl_data_size = 27; + + btsnoop_init(); +} + +/******************************************************************************* +** +** Function hci_mct_cleanup +** +** Description Clean MCT module +** +** Returns None +** +*******************************************************************************/ +void hci_mct_cleanup(void) +{ + HCIDBG("hci_mct_cleanup"); + + btsnoop_close(); + btsnoop_cleanup(); +} + +/******************************************************************************* +** +** Function hci_mct_send_msg +** +** Description Determine message type, then send message through according +** channel +** +** Returns None +** +*******************************************************************************/ +void hci_mct_send_msg(HC_BT_HDR *p_msg) +{ + uint16_t handle; + uint16_t lay_spec; + uint8_t *p = ((uint8_t *)(p_msg + 1)) + p_msg->offset; + uint16_t event = p_msg->event & MSG_EVT_MASK; + uint16_t sub_event = p_msg->event & MSG_SUB_EVT_MASK; + uint16_t acl_pkt_size = 0, acl_data_size = 0; + + /* wake up BT device if its in sleep mode */ + lpm_wake_assert(); + + if (sub_event == LOCAL_BR_EDR_CONTROLLER_ID) + { + acl_data_size = mct_cb.hc_acl_data_size; + acl_pkt_size = mct_cb.hc_acl_data_size + HCI_ACL_PREAMBLE_SIZE; + } + else + { + acl_data_size = mct_cb.hc_ble_acl_data_size; + acl_pkt_size = mct_cb.hc_ble_acl_data_size + HCI_ACL_PREAMBLE_SIZE; + } + + /* Check if sending ACL data that needs fragmenting */ + if ((event == MSG_STACK_TO_HC_HCI_ACL) && (p_msg->len > acl_pkt_size)) + { + /* Get the handle from the packet */ + STREAM_TO_UINT16 (handle, p); + + /* Set packet boundary flags to "continuation packet" */ + handle = (handle & 0xCFFF) | 0x1000; + + /* Do all the first chunks */ + while (p_msg->len > acl_pkt_size) + { + p = ((uint8_t *)(p_msg + 1)) + p_msg->offset; + + userial_write(event, (uint8_t *) p, acl_pkt_size); + + /* generate snoop trace message */ + btsnoop_capture(p_msg, FALSE); + + /* Adjust offset and length for what we just sent */ + p_msg->offset += acl_data_size; + p_msg->len -= acl_data_size; + + p = ((uint8_t *)(p_msg + 1)) + p_msg->offset; + + UINT16_TO_STREAM (p, handle); + + if (p_msg->len > acl_pkt_size) + { + UINT16_TO_STREAM (p, acl_data_size); + } + else + { + UINT16_TO_STREAM (p, p_msg->len - HCI_ACL_PREAMBLE_SIZE); + } + + /* If we were only to send partial buffer, stop when done. */ + /* Send the buffer back to L2CAP to send the rest of it later */ + if (p_msg->layer_specific) + { + if (--p_msg->layer_specific == 0) + { + p_msg->event = MSG_HC_TO_STACK_L2C_SEG_XMIT; + + if (bt_hc_cbacks) + { + bt_hc_cbacks->tx_result((TRANSAC) p_msg, \ + (char *) (p_msg + 1), \ + BT_HC_TX_FRAGMENT); + } + + return; + } + } + } + } + + p = ((uint8_t *)(p_msg + 1)) + p_msg->offset; + userial_write(event, (uint8_t *) p, p_msg->len); + + if (event == MSG_STACK_TO_HC_HCI_CMD) + { + utils_lock(); + num_hci_cmd_pkts--; + utils_unlock(); + + /* If this is an internal Cmd packet, the layer_specific field would + * have stored with the opcode of HCI command. + * Retrieve the opcode from the Cmd packet. + */ + p++; + STREAM_TO_UINT16(lay_spec, p); + } + + /* generate snoop trace message */ + btsnoop_capture(p_msg, FALSE); + + if (bt_hc_cbacks) + { + if ((event == MSG_STACK_TO_HC_HCI_CMD) && \ + (mct_cb.int_cmd_rsp_pending > 0) && \ + (p_msg->layer_specific == lay_spec)) + { + /* dealloc buffer of internal command */ + bt_hc_cbacks->dealloc((TRANSAC) p_msg, (char *) (p_msg + 1)); + } + else + { + bt_hc_cbacks->tx_result((TRANSAC) p_msg, (char *) (p_msg + 1), \ + BT_HC_TX_SUCCESS); + } + } + + lpm_tx_done(TRUE); + + return; +} + + +/******************************************************************************* +** +** Function hci_mct_receive_evt_msg +** +** Description Construct HCI EVENT packet +** +** Returns Number of read bytes +** +*******************************************************************************/ +uint16_t hci_mct_receive_evt_msg(void) +{ + uint16_t bytes_read = 0; + uint8_t byte; + uint16_t msg_len, len; + uint8_t msg_received; + tHCI_RCV_CB *p_cb=&mct_cb.rcv_evt; + uint8_t continue_fetch_looping = TRUE; + + while (continue_fetch_looping) + { + /* Read one byte to see if there is anything waiting to be read */ + if (userial_read(MSG_HC_TO_STACK_HCI_EVT, &byte, 1) == 0) + { + break; + } + + bytes_read++; + msg_received = FALSE; + + switch (p_cb->rcv_state) + { + case MCT_RX_NEWMSG_ST: + /* Start of new message */ + /* Initialize rx parameters */ + memset(p_cb->preload_buffer, 0 , 6); + p_cb->preload_buffer[0] = byte; + p_cb->preload_count = 1; + p_cb->rcv_len = HCI_EVT_PREAMBLE_SIZE - 1; + // p_cb->p_rcv_msg = NULL; + p_cb->rcv_state = MCT_RX_LEN_ST; /* Next, wait for length to come */ + break; + + case MCT_RX_LEN_ST: + /* Receiving preamble */ + p_cb->preload_buffer[p_cb->preload_count++] = byte; + p_cb->rcv_len--; + + /* Check if we received entire preamble yet */ + if (p_cb->rcv_len == 0) + { + /* Received entire preamble. + * Length is in the last received byte */ + msg_len = byte; + p_cb->rcv_len = msg_len; + + /* Allocate a buffer for message */ + if (bt_hc_cbacks) + { + len = msg_len + p_cb->preload_count + BT_HC_HDR_SIZE; + p_cb->p_rcv_msg = \ + (HC_BT_HDR *) bt_hc_cbacks->alloc(len); + } + + if (p_cb->p_rcv_msg) + { + /* Initialize buffer with preloaded data */ + p_cb->p_rcv_msg->offset = 0; + p_cb->p_rcv_msg->layer_specific = 0; + p_cb->p_rcv_msg->event = MSG_HC_TO_STACK_HCI_EVT; + p_cb->p_rcv_msg->len = p_cb->preload_count; + memcpy((uint8_t *)(p_cb->p_rcv_msg + 1), \ + p_cb->preload_buffer, p_cb->preload_count); + } + else + { + /* Unable to acquire message buffer. */ + ALOGE( \ + "Unable to acquire buffer for incoming HCI message." \ + ); + + if (msg_len == 0) + { + /* Wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + else + { + /* Ignore rest of the packet */ + p_cb->rcv_state = MCT_RX_IGNORE_ST; + } + + break; + } + + /* Message length is valid */ + if (msg_len) + { + /* Read rest of message */ + p_cb->rcv_state = MCT_RX_DATA_ST; + } + else + { + /* Message has no additional parameters. + * (Entire message has been received) */ + msg_received = TRUE; + + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + } + break; + + case MCT_RX_DATA_ST: + *((uint8_t *)(p_cb->p_rcv_msg + 1) + p_cb->p_rcv_msg->len++) = byte; + p_cb->rcv_len--; + + if (p_cb->rcv_len > 0) + { + /* Read in the rest of the message */ + len = userial_read(MSG_HC_TO_STACK_HCI_EVT, \ + ((uint8_t *)(p_cb->p_rcv_msg+1) + p_cb->p_rcv_msg->len), \ + p_cb->rcv_len); + p_cb->p_rcv_msg->len += len; + p_cb->rcv_len -= len; + bytes_read += len; + } + + /* Check if we read in entire message yet */ + if (p_cb->rcv_len == 0) + { + /* Received entire packet. */ + msg_received = TRUE; + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + break; + + + case MCT_RX_IGNORE_ST: + /* Ignore reset of packet */ + p_cb->rcv_len--; + + /* Check if we read in entire message yet */ + if (p_cb->rcv_len == 0) + { + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + break; + } + + + /* If we received entire message, then send it to the task */ + if (msg_received) + { + uint8_t intercepted = FALSE; + + /* generate snoop trace message */ + btsnoop_capture(p_cb->p_rcv_msg, TRUE); + + intercepted = internal_event_intercept(); + + if ((bt_hc_cbacks) && (intercepted == FALSE)) + { + bt_hc_cbacks->data_ind((TRANSAC) p_cb->p_rcv_msg, \ + (char *) (p_cb->p_rcv_msg + 1), \ + p_cb->p_rcv_msg->len + BT_HC_HDR_SIZE); + } + p_cb->p_rcv_msg = NULL; + } + } + + return (bytes_read); +} + + +/******************************************************************************* +** +** Function hci_mct_receive_acl_msg +** +** Description Construct HCI ACL packet +** +** Returns Number of read bytes +** +*******************************************************************************/ +uint16_t hci_mct_receive_acl_msg(void) +{ + uint16_t bytes_read = 0; + uint8_t byte; + uint16_t msg_len, len; + uint8_t msg_received; + tHCI_RCV_CB *p_cb=&mct_cb.rcv_acl; + uint8_t continue_fetch_looping = TRUE; + + while (continue_fetch_looping) + { + /* Read one byte to see if there is anything waiting to be read */ + if (userial_read(MSG_HC_TO_STACK_HCI_ACL, &byte, 1) == 0) + { + break; + } + + bytes_read++; + msg_received = FALSE; + + switch (p_cb->rcv_state) + { + case MCT_RX_NEWMSG_ST: + /* Start of new message */ + /* Initialize rx parameters */ + memset(p_cb->preload_buffer, 0 , 6); + p_cb->preload_buffer[0] = byte; + p_cb->preload_count = 1; + p_cb->rcv_len = HCI_ACL_PREAMBLE_SIZE - 1; + // p_cb->p_rcv_msg = NULL; + p_cb->rcv_state = MCT_RX_LEN_ST; /* Next, wait for length to come */ + break; + + case MCT_RX_LEN_ST: + /* Receiving preamble */ + p_cb->preload_buffer[p_cb->preload_count++] = byte; + p_cb->rcv_len--; + + /* Check if we received entire preamble yet */ + if (p_cb->rcv_len == 0) + { + /* ACL data lengths are 16-bits */ + msg_len = p_cb->preload_buffer[3]; + msg_len = (msg_len << 8) + p_cb->preload_buffer[2]; + + if (msg_len && (p_cb->preload_count == 4)) + { + /* Check if this is a start packet */ + byte = ((p_cb->preload_buffer[1] >> 4) & 0x03); + + if (byte == ACL_RX_PKT_START) + { + /* + * A start packet & with non-zero data payload length. + * We want to read 2 more bytes to get L2CAP payload + * length. + */ + p_cb->rcv_len = 2; + + break; + } + } + + /* + * Check for segmented packets. If this is a continuation + * packet, then we will continue appending data to the + * original rcv buffer. + */ + p_cb->p_rcv_msg = acl_rx_frame_buffer_alloc(); + + if (p_cb->p_rcv_msg == NULL) + { + /* Unable to acquire message buffer. */ + ALOGE( \ + "Unable to acquire buffer for incoming HCI message." \ + ); + + if (msg_len == 0) + { + /* Wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + else + { + /* Ignore rest of the packet */ + p_cb->rcv_state = MCT_RX_IGNORE_ST; + } + + break; + } + + /* Message length is valid */ + if (msg_len) + { + /* Read rest of message */ + p_cb->rcv_state = MCT_RX_DATA_ST; + } + else + { + /* Message has no additional parameters. + * (Entire message has been received) */ + acl_rx_frame_end_chk(); /* to print snoop trace */ + + msg_received = TRUE; + + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + } + break; + + case MCT_RX_DATA_ST: + *((uint8_t *)(p_cb->p_rcv_msg + 1) + p_cb->p_rcv_msg->len++) = byte; + p_cb->rcv_len--; + + if (p_cb->rcv_len > 0) + { + /* Read in the rest of the message */ + len = userial_read(MSG_HC_TO_STACK_HCI_ACL, \ + ((uint8_t *)(p_cb->p_rcv_msg+1) + p_cb->p_rcv_msg->len), \ + p_cb->rcv_len); + p_cb->p_rcv_msg->len += len; + p_cb->rcv_len -= len; + bytes_read += len; + } + + /* Check if we read in entire message yet */ + if (p_cb->rcv_len == 0) + { + /* Received entire packet. */ + /* Check for segmented l2cap packets */ + if (acl_rx_frame_end_chk()) + { + msg_received = TRUE; + } + + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + break; + + + case MCT_RX_IGNORE_ST: + /* Ignore reset of packet */ + p_cb->rcv_len--; + + /* Check if we read in entire message yet */ + if (p_cb->rcv_len == 0) + { + /* Next, wait for next message */ + p_cb->rcv_state = MCT_RX_NEWMSG_ST; + continue_fetch_looping = FALSE; + } + break; + } + + + /* If we received entire message, then send it to the task */ + if (msg_received) + { + if (bt_hc_cbacks) + { + bt_hc_cbacks->data_ind((TRANSAC) p_cb->p_rcv_msg, \ + (char *) (p_cb->p_rcv_msg + 1), \ + p_cb->p_rcv_msg->len + BT_HC_HDR_SIZE); + } + p_cb->p_rcv_msg = NULL; + } + } + + return (bytes_read); +} + +/******************************************************************************* +** +** Function hci_mct_send_int_cmd +** +** Description Place the internal commands (issued internally by hci or +** vendor lib) in the tx_q. +** +** Returns TRUE/FALSE +** +*******************************************************************************/ +uint8_t hci_mct_send_int_cmd(uint16_t opcode, HC_BT_HDR *p_buf, \ + tINT_CMD_CBACK p_cback) +{ + if (mct_cb.int_cmd_rsp_pending > INT_CMD_PKT_MAX_COUNT) + { + ALOGE( \ + "Allow only %d outstanding internal commands at a time [Reject 0x%04X]"\ + , INT_CMD_PKT_MAX_COUNT, opcode); + return FALSE; + } + + mct_cb.int_cmd_rsp_pending++; + mct_cb.int_cmd[mct_cb.int_cmd_wrt_idx].opcode = opcode; + mct_cb.int_cmd[mct_cb.int_cmd_wrt_idx].cback = p_cback; + mct_cb.int_cmd_wrt_idx = ((mct_cb.int_cmd_wrt_idx+1) & INT_CMD_PKT_IDX_MASK); + + /* stamp signature to indicate an internal command */ + p_buf->layer_specific = opcode; + + utils_enqueue(&tx_q, (void *) p_buf); + bthc_signal_event(HC_EVENT_TX); + + return TRUE; +} + + +/******************************************************************************* +** +** Function hci_mct_get_acl_data_length +** +** Description Issue HCI_READ_BUFFER_SIZE command to retrieve Controller's +** ACL data length setting +** +** Returns None +** +*******************************************************************************/ +void hci_mct_get_acl_data_length(void) +{ + HC_BT_HDR *p_buf = NULL; + uint8_t *p, ret; + + if (bt_hc_cbacks) + { + p_buf = (HC_BT_HDR *) bt_hc_cbacks->alloc(BT_HC_HDR_SIZE + \ + HCI_CMD_PREAMBLE_SIZE); + } + + if (p_buf) + { + p_buf->event = MSG_STACK_TO_HC_HCI_CMD; + p_buf->offset = 0; + p_buf->layer_specific = 0; + p_buf->len = HCI_CMD_PREAMBLE_SIZE; + + p = (uint8_t *) (p_buf + 1); + UINT16_TO_STREAM(p, HCI_READ_BUFFER_SIZE); + *p = 0; + + if ((ret = hci_mct_send_int_cmd(HCI_READ_BUFFER_SIZE, p_buf, \ + get_acl_data_length_cback)) == FALSE) + { + bt_hc_cbacks->dealloc((TRANSAC) p_buf, (char *) (p_buf + 1)); + } + else + return; + } + + if (bt_hc_cbacks) + { + ALOGE("hci lib postload aborted"); + bt_hc_cbacks->postload_cb(NULL, BT_HC_POSTLOAD_FAIL); + } +} + + +/****************************************************************************** +** HCI MCT Services interface table +******************************************************************************/ + +const tHCI_IF hci_mct_func_table = +{ + hci_mct_init, + hci_mct_cleanup, + hci_mct_send_msg, + hci_mct_send_int_cmd, + hci_mct_get_acl_data_length, + hci_mct_receive_evt_msg, + hci_mct_receive_acl_msg +}; + + |