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