/******************************************************************************
 *
 *  Copyright (C) 2009-2012 Broadcom Corporation
 *
 *  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.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  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;

    if (event == MSG_STACK_TO_HC_HCI_CMD)
    {
        uint8_t *p_tmp = p;

        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_tmp++;
        STREAM_TO_UINT16(lay_spec, p_tmp);
    }

    userial_write(event, (uint8_t *) p, p_msg->len);


    /* 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
};


