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